美文网首页docker
发往Docker容器内的数据包源地址被修改的研究

发往Docker容器内的数据包源地址被修改的研究

作者: 汤尼房 | 来源:发表于2019-01-06 10:09 被阅读74次
    引言

    此文档是对发往Docker容器内的数据包源地址被修改的研究做的实践记录。

    实践环境一
    Centos7.2+Docker1.12.6
    
    关闭firewalld:
    systemctl stop firewalld
    
    设置selinux为Permissive模式:
    setenforce 0
    
    /etc/docker/daemon.json内容:
    {
      "userland-proxy": true
    }
    
    创建名为python的容器:
    docker run -itd --name python --network bridge -p 8000:9999/udp --entrypoint bash python:2.7.15
    
    容器内接收udp数据包的程序内容为:
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    import socket
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # 绑定端口:
    s.bind(('0.0.0.0', 9999))
    print 'Bind UDP on 9999...'
    
    while 1:
        # 接收数据:
        data, addr = s.recvfrom(1024)
        print 'addr: {0}, data: {1}'.format(addr, data)
    

    路由信息:

    网桥docker0设备信息:

    一、通过另一台主机向当前主机发送数据包
    1.192.168.84.75主机上运行接收udp数据包的程序,运行配置基于实践环境,在192.168.84.79主机上通过 echo mac | nc -s 192.168.84.79 -p 42731 -u 192.168.84.75 8000(产生数据包的源地址、源端口与目的地址、目的端口为:192.168.84.79.42731 > 192.168.84.75.8000) 向192.168.84.75发送数据包。

    对192.168.84.79主机的网卡进行抓包处理:
    tcpdump -i eno16777984 udp port 8000 -nn -vv
    tcpdump: listening on eno16777984, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:33:15.227775 IP (tos 0x0, ttl 64, id 58746, offset 0, flags [DF], proto UDP (17), length 32)
        192.168.84.79.42731 > 192.168.84.75.8000: [bad udp cksum 0x2a09 -> 0x3f53!] UDP, length 4
    
    分别对192.168.84.75主机的lo、物理网卡(eno16777984)、docker0以及容器的网卡进行抓包处理:
    tcpdump -i lo -nn -vv
    tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
    无内容
    
    tcpdump -i eno16777984 udp port 8000 -nn -vv
    tcpdump: listening on eno16777984, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:33:25.690285 IP (tos 0x0, ttl 64, id 58746, offset 0, flags [DF], proto UDP (17), length 32)
        192.168.84.79.42731 > 192.168.84.75.8000: [udp sum ok] UDP, length 4
    
    tcpdump -i docker0 -nn -vv
    tcpdump: listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:33:25.690319 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    13:33:25.690354 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    13:33:25.690362 IP (tos 0x0, ttl 63, id 58746, offset 0, flags [DF], proto UDP (17), length 32)
        192.168.84.79.42731 > 172.17.0.2.9999: [udp sum ok] UDP, length 4
    
    tcpdump -i vethc4d778f -nn -vv
    tcpdump: listening on vethc4d778f, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:33:25.690332 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    13:33:25.690354 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    13:33:25.690364 IP (tos 0x0, ttl 63, id 58746, offset 0, flags [DF], proto UDP (17), length 32)
        192.168.84.79.42731 > 172.17.0.2.9999: [udp sum ok] UDP, length 4
    
    由上述的时间可知数据包先经过84.79主机的网卡,然后数据包被发往84.75主机,在84.75主机上经过的设备依次为:主机网卡(eno16777984 )---> docker0 ---> 容器网卡(vethc4d778f)
    

    数据包发送前与发送后84.75主机的nat表的对比:
    发送前执行 iptables -t nat -nvL > pre
    发送后执行 iptables -t nat -nvL > now
    然后通过diff命令对比两个文件的内容:

    分析:84.79主机发送的数据包源地址、源端口与目的地址、目的端口分别为:192.168.84.79.42731 > 192.168.84.75.8000。数据包从主机网卡eno16777984流出后,首先经过PREROUTING链,此时匹配到动作DOCKER,DOCKER链的规则是:-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER,即当数据包的地址类型为本地地址LOCAL时,将执行DOCKER链的规则匹配。LOCAL本地地址不仅指127.0.0.1,也包括本机IP。因此数据包(192.168.84.79.42731 > 192.168.84.75.8000)能够匹配到DOCKER。DOCKER链的第一条规则:-A DOCKER -i docker0 -j RETURN,表示流入docker0的设备的数据包将返回到DOCKER链的调用处;第二条规则:-A DOCKER ! -i docker0 -p udp -m udp --dport 8000 -j DNAT --to-destination 172.17.0.2:9999,表示不是流入docker0设备、udp协议、目的端口是8000的数据包将其目的地址修改为172.17.0.2,目的端口修改为9999。数据包满足第二条规则此时数据包的源地址、源端口与目的地址、目的端口变为:192.168.84.79.42731 > 172.17.0.2.9999。数据包经过路由判断目的地址(172.17.0.2)不是发往本机,于是经过FORWARD到达POSTROUTING链,POSTROUTING链的第一条规则:-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE,表示对不是从docker0网卡流出且源地址是172.17网段的数据包进行源地址伪装。数据包不满足这条匹配规则。第二条规则:-A POSTROUTING -s 172.17.0.2/32 -d 172.17.0.2/32 -p udp -m udp --dport 9999 -j MASQUERADE,表示对源地址、目的地址均为172.17.0.2/32、udp协议、目的端口为9999的数据包进行源地址伪装,同样数据包也不满足第二条规则。此时数据包的源地址、端口与目的地址、端口没有发生变化依然是:192.168.84.79.42731 > 172.17.0.2.9999。数据包从POSTROUTING链出来后根据路由规则(172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0)会被发往docker0设备,发往docker0设备的数据包源地址、端口与目的地址、端口同样没有发生变化(通过上述对docker0的抓包分析也可知)。docker0设备通过ARP找到ip地址为172.17.0.2的设备vethc4d778f,进而将数据包发往vethc4d778f此设备,vethc4d778f设备接收的数据包的源地址、源端口与目的地址、目的端口依然为:192.168.84.79.42731 > 172.17.0.2.9999。因此程序接收到的数据包的源地址为:192.168.84.79,程序的输出结果也可证实:addr: ('192.168.84.79', 42731), data: mac
    1. 在84.79主机上执行 echo mac | nc -s 127.0.0.1 -p 42731 -u 192.168.84.75 8000 ,报错误信息Ncat: Invalid argument.

    二、本机上通过网卡ip地址发送数据包

    1. 在192.168.84.75主机上执行 echo mac | nc -s 192.168.84.75 -p 34191 -u 192.168.84.75 8000
      分别lo、物理网卡(eno16777984)、docker0以及容器的网卡进行抓包处理:
    tcpdump -i lo -nn -vv 
    tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
    无内容
    
    tcpdump -i eno16777984 udp port 8000 -nn -vv 
    tcpdump: listening on eno16777984, link-type EN10MB (Ethernet), capture size 262144 bytes
    无内容
    
    tcpdump -i docker0 -nn -vv
    tcpdump: listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:37:07.072740 IP (tos 0x0, ttl 64, id 64295, offset 0, flags [DF], proto UDP (17), length 32)
        192.168.84.75.34191 > 172.17.0.2.9999: [bad udp cksum 0xc124 -> 0xc1c4!] UDP, length 4
    13:37:12.075322 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    13:37:12.075355 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    tcpdump -i vethc4d778f -nn -vv
    tcpdump: listening on vethc4d778f, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:37:07.072758 IP (tos 0x0, ttl 64, id 64295, offset 0, flags [DF], proto UDP (17), length 32)
        192.168.84.75.34191 > 172.17.0.2.9999: [bad udp cksum 0xc124 -> 0xc1c4!] UDP, length 4
    13:37:12.075332 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    13:37:12.075355 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    可知数据包仅经过docker0然后去往容器设备vethc4d778f,并没有经过绑定192.168.84.75 IP的网卡eno16777984
    

    数据包发送前与发送后84.75主机的nat表的对比:

    分析:echo mac | nc -s 192.168.84.75 -p 34191 -u 192.168.84.75 8000发出的数据包源地址、源端口与目的地址、目的端口为:192.168.84.75.34191 > 192.168.84.75.8000。经过实践(实践本地访问的数据包经过的链与设备的次序一文)可知,在本机上通过ip地址访问发出的数据包仅经过OUTPUT与POSTROUTING链。数据包从本地进程发出后,经过OUTPUT链,匹配到DOCKER链的第二条规则,即将数据包的目的地址、目的端口更改为172.17.0.2:9999,因此经过OUTPUT链后的数据包源地址、源端口与目的地址、目的端口变为:192.168.84.75.34191 > 172.17.0.2.9999,紧接着经过POSTROUTING链,数据包仅被POSTROUTING链的默认规则匹配,而没有被更改源地址的两条规则匹配到,因此从POSTROUTING链出来的数据包源地址、源端口与目的地址、目的端口依然为:192.168.84.75.34191 > 172.17.0.2.9999。根据路由规则,数据包会被发往docker0设备,然后被发往容器设备vethc4d778f,最后程序收到的数据包的源ip为192.168.84.75,结果可证实:addr: ('192.168.84.75', 34191), data: mac。
    1. echo mac | nc -s 127.0.0.1 -p 34191 -u 192.168.84.75 8000 Ncat: Invalid argument.

    三、本机上通过127.0.0.1地址发送数据包

    1. 在192.168.84.75主机上执行 echo mac | nc -s 127.0.0.1 -p 33089 -u 127.0.0.1 8000
      分别对主机的lo、物理网卡(eno16777984)、docker0以及容器的网卡进行抓包处理:
    tcpdump -i lo -nn -vv
    tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:41:30.463055 IP (tos 0x0, ttl 64, id 54997, offset 0, flags [DF], proto UDP (17), length 32)
        127.0.0.1.33089 > 127.0.0.1.8000: [bad udp cksum 0xfe1f -> 0x90e6!] UDP, length 4
    
    tcpdump -i eno16777984 udp port 8000 -nn -vv
    tcpdump: listening on eno16777984, link-type EN10MB (Ethernet), capture size 262144 bytes
    无内容
    
    tcpdump -i docker0 -nn -vv
    tcpdump: listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:41:30.463419 IP (tos 0x0, ttl 64, id 35536, offset 0, flags [DF], proto UDP (17), length 32)
        172.17.0.1.33987 > 172.17.0.2.9999: [bad udp cksum 0x5843 -> 0x2b72!] UDP, length 4
    13:41:35.467315 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    13:41:35.467348 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    tcpdump -i vethc4d778f -nn -vv
    tcpdump: listening on vethc4d778f, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:41:30.463430 IP (tos 0x0, ttl 64, id 35536, offset 0, flags [DF], proto UDP (17), length 32)
        172.17.0.1.33987 > 172.17.0.2.9999: [bad udp cksum 0x5843 -> 0x2b72!] UDP, length 4
    13:41:35.467325 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    13:41:35.467348 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    可知数据包在主机上经过的设备次序为:lo ---> docker0 ---> vethc4d778f
    

    数据包发送前与发送后主机nat表的对比如下:

    分析:这种访问方式数据包经过的设备与链的次序是:OUTPUT ---> POSTROUTING ---> lo设备 ---> PREROUTING ---> INPUT ---> 用户空间(docker-proxy)---> OUTPUT ---> POSTROUTING。本地监听8000端口的进程是docker-proxy(/usr/bin/docker-proxy -proto udp -host-ip 0.0.0.0 -host-port 8000 -container-ip 172.17.0.2 -container-port 9999),查看如下: 从lo设备流出的数据包(127.0.0.1.33089 > 127.0.0.1.8000),经过PREROUTING链与INPUT链后被本地8000进程docker-proxy处理,处理后数据包的源地址、源端口与目的地址、目的端口变为: 172.17.0.1.33987 > 172.17.0.2.9999;然后数据包经过docker0后发往容器设备vethc4d778f,最后被容器内的程序接收到,程序的输出为:addr: ('172.17.0.1', 33987), data: mac
    1. echo mac | nc -s 192.168.84.75 -p 33089 -u 127.0.0.1 8000(程序能够收到数据包)
      lo设备、docker0与容器网卡抓包结果:
    tcpdump -i lo -nn -vv
    tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:46:14.238701 IP (tos 0x0, ttl 64, id 54998, offset 0, flags [DF], proto UDP (17), length 32)
        192.168.84.75.33089 > 127.0.0.1.8000: [bad udp cksum 0x9412 -> 0xfaf3!] UDP, length 4
    
    tcpdump -i docker0 -nn -vv
    tcpdump: listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:46:14.238980 IP (tos 0x0, ttl 64, id 35537, offset 0, flags [DF], proto UDP (17), length 32)
        172.17.0.1.36234 > 172.17.0.2.9999: [bad udp cksum 0x5843 -> 0x22ab!] UDP, length 4
    13:46:19.243339 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    13:46:19.243377 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    tcpdump -i vethc4d778f -nn -vv
    tcpdump: listening on vethc4d778f, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:46:14.238992 IP (tos 0x0, ttl 64, id 35537, offset 0, flags [DF], proto UDP (17), length 32)
        172.17.0.1.36234 > 172.17.0.2.9999: [bad udp cksum 0x5843 -> 0x22ab!] UDP, length 4
    13:46:19.243350 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    13:46:19.243377 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    程序输出结果:addr: ('172.17.0.1', 36234), data: mac
    

    四、通过容器ip地址发送数据包

    1. 192.168.84.75主机上容器python的ip地址为172.17.0.2,可通过下面方式获得:

      在主机上执行 echo mac | nc -s 172.17.0.1 -p 42883 -u 172.17.0.2 9999分别对192.168.84.75主机的lo、物理网卡(eno16777984)、docker0以及容器的网卡进行抓包处理:

    tcpdump -i lo -nn -vv
    tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
    无内容
    
    tcpdump -i eno16777984 udp port 9999 -nn -vv
    tcpdump: listening on eno16777984, link-type EN10MB (Ethernet), capture size 262144 bytes
    无内容
    
    tcpdump -i docker0 -nn -vv
    tcpdump: listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:49:05.123460 IP (tos 0x0, ttl 64, id 35538, offset 0, flags [DF], proto UDP (17), length 32)
        172.17.0.1.42883 > 172.17.0.2.9999: [bad udp cksum 0x5843 -> 0x08b2!] UDP, length 4
    13:49:10.139337 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    13:49:10.139376 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    tcpdump -i vethc4d778f -nn -vv
    tcpdump: listening on vethc4d778f, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:49:05.123479 IP (tos 0x0, ttl 64, id 35538, offset 0, flags [DF], proto UDP (17), length 32)
        172.17.0.1.42883 > 172.17.0.2.9999: [bad udp cksum 0x5843 -> 0x08b2!] UDP, length 4
    13:49:10.139347 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    13:49:10.139376 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    数据包经过的设备依次为:docker0 ---> vethc4d778f
    

    数据包发送前与发送后主机nat对比:

    分析: echo mac | nc -s 172.17.0.1 -p 42883 -u 172.17.0.2 9999 产生的数据包的源地址、源端口与目的地址、目的端口为:172.17.0.1.42883 > 172.17.0.2.9999。实践发现,数据包在用户空间内产生,然后从OUTPUT链流向POSTROUTING链。紧接着数据包经过docker0,经过容器设备vethc4d778f被程序接收,程序的输出结果为:addr: ('172.17.0.1', 42883), data: mac
    1. echo mac | nc -s 172.17.0.2 -p 42883 -u 172.17.0.2 9999
      libnsock mksock_bind_addr(): Bind to 172.17.0.2:42883 failed (IOD #1): Cannot assign requested address (99)
    docker0与容器网卡抓包结果:
    tcpdump -i docker0 -nn -vv(数据包的源地址、源端口已经被修改)
    tcpdump: listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:51:26.388949 IP (tos 0x0, ttl 64, id 35539, offset 0, flags [DF], proto UDP (17), length 32)
        172.17.0.1.54670 > 172.17.0.2.9999: [bad udp cksum 0x5843 -> 0xdaa6!] UDP, length 4
    13:51:31.403315 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    13:51:31.403347 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    tcpdump -i vethc4d778f -nn -vv
    tcpdump: listening on vethc4d778f, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:51:26.388966 IP (tos 0x0, ttl 64, id 35539, offset 0, flags [DF], proto UDP (17), length 32)
        172.17.0.1.54670 > 172.17.0.2.9999: [bad udp cksum 0x5843 -> 0xdaa6!] UDP, length 4
    13:51:31.403325 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    13:51:31.403347 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    程序结果:addr: ('172.17.0.1', 54670), data: mac
    
    1. echo mac | nc -s 127.0.0.1 -p 42883 -u 172.17.0.2 9999 Ncat: Invalid argument.

    实践环境二
    Centos7.2 + Docker1.12.6
    
    关闭firewalld:
    systemctl stop firewalld
    
    设置selinux为Permissive模式:
    setenforce 0
    
    /etc/docker/daemon.json内容:
    {
      "userland-proxy": false
    }
    
    创建名为python的容器:
    docker run -itd --name python --network bridge -p 8000:9999/udp --entrypoint bash python:2.7.15
    
    容器内接收udp数据包的程序内容为:
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    import socket
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # 绑定端口:
    s.bind(('0.0.0.0', 9999))
    print 'Bind UDP on 9999...'
    
    while 1:
        # 接收数据:
        data, addr = s.recvfrom(1024)
        print 'addr: {0}, data: {1}'.format(addr, data)
    
    路由信息: 网桥docker0设备信息: 对dockerd进程添加 userland-proxy=false 的配置后,基于上述命令创建完容器与之前对比存在以下三点不同:
    1. docker-proxy进程消失
    2. dockerd进程监听8000端口
    3. nat表的POSTROUTING链多了一条源地址伪装的规则
      -A POSTROUTING -o docker0 -m addrtype --src-type LOCAL -j MASQUERADE
      (将源地址类型为LOCAL且从docker0设备流出的数据包的源地址伪装成docker0设备的地址)

    一、通过另一台主机向当前主机发送数据包

    1. 192.168.84.75主机上运行接收udp数据包的程序,运行配置基于实践环境,在192.168.84.79主机上通过 echo mac | nc -s 192.168.84.79 -p 55902 -u 192.168.84.75 8000 发送数据包。
    对192.168.84.79主机的网卡进行抓包处理:
    tcpdump -i eno16777984 udp port 8000 -nn -vv
    tcpdump: listening on eno16777984, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:57:35.975290 IP (tos 0x0, ttl 64, id 58747, offset 0, flags [DF], proto UDP (17), length 32)
        192.168.84.79.55902 > 192.168.84.75.8000: [bad udp cksum 0x2a09 -> 0x0be0!] UDP, length 4
    
    分别对192.168.84.75主机的lo、物理网卡(eno16777984)、docker0以及容器的网卡进行抓包处理:
    tcpdump -i lo -nn -vv 
    tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
    无内容
    
    tcpdump -i eno16777984 udp port 8000 -nn -vv
    tcpdump: listening on eno16777984, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:57:46.437799 IP (tos 0x0, ttl 64, id 58747, offset 0, flags [DF], proto UDP (17), length 32)
        192.168.84.79.55902 > 192.168.84.75.8000: [udp sum ok] UDP, length 4
    
    tcpdump -i docker0 -nn -vv
    tcpdump: listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:57:46.437850 IP (tos 0x0, ttl 63, id 58747, offset 0, flags [DF], proto UDP (17), length 32)
        192.168.84.79.55902 > 172.17.0.2.9999: [udp sum ok] UDP, length 4
    13:57:51.451317 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    13:57:51.451355 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    tcpdump -i vethcbcc4e9 -nn -vv
    tcpdump: listening on vethcbcc4e9, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:57:46.437863 IP (tos 0x0, ttl 63, id 58747, offset 0, flags [DF], proto UDP (17), length 32)
        192.168.84.79.55902 > 172.17.0.2.9999: [udp sum ok] UDP, length 4
    13:57:51.451327 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    13:57:51.451355 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    数据包流经设备的顺序依次为:eno16777984(84.79) ---> eno16777984(84.75) ---> docker0 ---> vethcbcc4e9
    

    数据包发送前与发送后主机nat表对比:

    分析:84.75主机eno16777984网卡接收的数据包的源地址、源端口与目的地址、目的端口为:192.168.84.79.55902 > 192.168.84.75.8000,此时数据包经过PREROUTING链被DOCKER链的-A DOCKER -p udp -m udp --dport 8000 -j DNAT --to-destination 172.17.0.2:9999规则匹配,数据包的源地址、源端口与目的地址、目的端口变为:192.168.84.79.55902 > 172.17.0.2.9999,紧接着数据包经过路由判断,判断其不是发往本机的包,则通过FORWARD到达POSTROUTING链。POSTROUTING链此时有3条规则,第一条规则-A POSTROUTING -o docker0 -m addrtype --src-type LOCAL -j MASQUERADE,表示将源地址类型为LOCAL且从docker0设备流出的数据包的源地址修改为docker0设备的地址,这一条不满足条件;第二条规则-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE,表示对源地址为172.17网段且不是从docker0设备流出的包进行源地址伪装,这一条不满足条件。第三条规则-A POSTROUTING -s 172.17.0.2/32 -d 172.17.0.2/32 -p udp -m udp --dport 9999 -j MASQUERADE,表示对源地址与目的地址均为172.17.0.2且目的端口为udp协议的9999的数据包进行源地址伪装,第三条同样不满足条件。POSTROUTING链出来的数据包源地址、源端口与目的地址、目的端口为:192.168.84.79.55902 > 172.17.0.2.9999,接着数据包被发往docker0,然后发往容器设备vethcbcc4e9,最后被程序接收,程序的输出结果为:addr: addr: ('192.168.84.79', 55902), data: mac
    1. 192.168.84.79主机上执行 echo mac | nc -s 127.0.0.1 -p 55902 -u 192.168.84.75 8000 Ncat: Invalid argument.

    二、本机上通过网卡ip地址发送数据包

    1. 在192.168.84.75主机上执行 echo mac | nc -s 192.168.84.75 -p 41003 -u 192.168.84.75 8000
      分别lo设备、物理网卡(eno16777984)、docker0以及容器网卡进行抓包处理:
    tcpdump -i lo -nn -vv 
    tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
    
    tcpdump -i eno16777984 udp port 8000 -nn -vv 
    tcpdump: listening on eno16777984, link-type EN10MB (Ethernet), capture size 262144 bytes
    
    tcpdump -i docker0 -nn -vv
    tcpdump: listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
    14:03:41.089945 IP (tos 0x0, ttl 64, id 64296, offset 0, flags [DF], proto UDP (17), length 32)
        172.17.0.1.41003 > 172.17.0.2.9999: [bad udp cksum 0x5843 -> 0x100a!] UDP, length 4
    14:03:46.091328 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    14:03:46.091387 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    tcpdump -i vethcbcc4e9 -nn -vv
    tcpdump: listening on vethcbcc4e9, link-type EN10MB (Ethernet), capture size 262144 bytes
    14:03:41.089961 IP (tos 0x0, ttl 64, id 64296, offset 0, flags [DF], proto UDP (17), length 32)
        172.17.0.1.41003 > 172.17.0.2.9999: [bad udp cksum 0x5843 -> 0x100a!] UDP, length 4
    14:03:46.091343 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    14:03:46.091387 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    可知,数据包仅经过docker0与容器设备vethcbcc4e9
    

    数据包发送前与发送后主机nat表对比:

    分析:echo mac | nc -s 192.168.84.75 -p 41003 -u 192.168.84.75 8000 发出的数据包的源地址、源端口与目的地址、目的端口为:192.168.84.75.41003 > 192.168.84.75.8000,内核将数据包发送给本地进程dockerd处理,dockerd只负责接收而并没有做任何的处理,此时数据包经过OUTPUT链被DOCKER链的规则 -A DOCKER -p udp -m udp --dport 8000 -j DNAT --to-destination 172.17.0.2:9999(将udp协议、目的端口为8000的数据包的目的地址与端口修改为172.17.0.2.9999)匹配,此时数据包的源地址、源端口与目的地址、目的端口变为:192.168.84.75.41003 > 172.17.0.2.9999。紧接着数据包经过POSTROUTING链,被规则 -A POSTROUTING -o docker0 -m addrtype --src-type LOCAL -j MASQUERADE匹配,这条规则表示从docker0设备流出且源地址类型为LOCAL的数据包将其源地址修改为docker0的地址(这里是172.17.0.1),为什么数据包(192.168.84.75.41003 > 172.17.0.2.9999)能够匹配到这条规则呢,不妨分析一下,首先数据包的源地址为192.168.84.75符合 -m addrtype --src-type LOCAL定义;为什么数据包能够满足 -o docker0的定义呢,数据包分明还没有经过docker0设备,此处为何就匹配到了从docker0设备流出的规则呢?按照自身理解,数据包将会流入docker0设备,当然也会从docker0设备流出,但流入docker0设备在前从docker0设备流出在后,因此这里想要将-A POSTROUTING -o docker0 -m addrtype --src-type LOCAL -j MASQUERADE规则换成-A POSTROUTING -i docker0 -m addrtype --src-type LOCAL -j MASQUERADE,结果报出错误“iptables v1.4.21: Can't use -i with POSTROUTING”,即POSTROUTING链只能通过 -o 指定数据包流出的设备,只要数据包经过的下一个设备会是docker0且会从docker0流出则此处会匹配到这条规则,此时数据包的源地址、源端口与目的地址、目的端口变为:172.17.0.1.41003 > 172.17.0.2.9999。接着数据包经过docker0设备发往容器设备vethcbcc4e9,进而被程序接收,程序的输出结果为:addr: ('172.17.0.1', 41003), data: mac
    1. echo mac | nc -s 127.0.0.1 -p 41003 -u 192.168.84.75 8000
    docker0与容器网卡抓包结果:
    tcpdump -i docker0 -nn -vv(数据包的源地址已经被修改)
    tcpdump: listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
    14:06:19.694983 IP (tos 0x0, ttl 64, id 64297, offset 0, flags [DF], proto UDP (17), length 32)
        172.17.0.1.41003 > 172.17.0.2.9999: [bad udp cksum 0x5843 -> 0x100a!] UDP, length 4
    14:06:24.699338 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    14:06:24.699375 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    tcpdump -i vethcbcc4e9 -nn -vv
    tcpdump: listening on vethcbcc4e9, link-type EN10MB (Ethernet), capture size 262144 bytes
    14:06:19.695000 IP (tos 0x0, ttl 64, id 64297, offset 0, flags [DF], proto UDP (17), length 32)
        172.17.0.1.41003 > 172.17.0.2.9999: [bad udp cksum 0x5843 -> 0x100a!] UDP, length 4
    14:06:24.699348 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    14:06:24.699375 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    程序结果:addr: ('172.17.0.1', 41003), data: mac
    

    三、本机上通过127.0.0.1发送数据包

    1. 在192.168.84.75主机上执行 echo mac | nc -s 127.0.0.1 -p 44818 -u 127.0.0.1 8000
      分别主机的lo设备、物理网卡(eno16777984)、docker0以及容器网卡进行抓包处理:
    tcpdump -i lo -nn -vv 
    tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
    
    tcpdump -i eno16777984 udp port 8000 -nn -vv 
    tcpdump: listening on eno16777984, link-type EN10MB (Ethernet), capture size 262144 bytes
    
    tcpdump -i docker0 -nn -vv
    tcpdump: listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
    14:08:58.946048 IP (tos 0x0, ttl 64, id 54997, offset 0, flags [DF], proto UDP (17), length 32)
        172.17.0.1.44818 > 172.17.0.2.9999: [bad udp cksum 0x5843 -> 0x0123!] UDP, length 4
    14:09:03.947336 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    14:09:03.947379 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    tcpdump -i vethcbcc4e9 -nn -vv
    tcpdump: listening on vethcbcc4e9, link-type EN10MB (Ethernet), capture size 262144 bytes
    14:08:58.946065 IP (tos 0x0, ttl 64, id 54997, offset 0, flags [DF], proto UDP (17), length 32)
        172.17.0.1.44818 > 172.17.0.2.9999: [bad udp cksum 0x5843 -> 0x0123!] UDP, length 4
    14:09:03.947349 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    14:09:03.947379 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    数据包经过的设备依次为:docker0 ---> vethcbcc4e9
    

    数据包发送前与发送后nat表规则对比:

    分析:echo mac | nc -s 127.0.0.1 -p 44818 -u 127.0.0.1 8000 产生的数据包的源地址、源端口与目的地址、目的端口为:127.0.0.1.44818 > 127.0.0.1.8000。这种方式数据包仅经过OUTPUT与POSTROUTING链,当数据包经过OUTPUT链时被DOCKER链的-A DOCKER -p udp -m udp --dport 8000 -j DNAT --to-destination 172.17.0.2:9999规则匹配,此时数据包的源地址、源端口与目的地址、目的端口变为:127.0.0.1.44818 > 172.17.0.2.9999。紧接着数据包在经过POSTROUTING链时被-A POSTROUTING -o docker0 -m addrtype --src-type LOCAL -j MASQUERADE规则匹配,此时数据包的源地址、端口与目的地址、端口变为:172.17.0.1.44818 > 172.17.0.2.9999。数据包被发往docker0,然后发往容器设备vethcbcc4e9,最后被程序接收,程序的输出结果为:addr: ('172.17.0.1', 44818), data: mac

    四、通过容器ip地址发送数据包

    1. 192.168.84.75主机上容器python的ip地址为172.17.0.2,可通过下面方式获得:

      在主机上执行 echo mac | nc -s 172.17.0.1 -p 54877 -u 172.17.0.2 9999,分别对主机的lo、物理网卡(eno16777984)、docker0以及容器网卡进行抓包处理:

    tcpdump -i lo -nn -vv
    tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
    无内容
    
    tcpdump -i eno16777984 udp port 9999 -nn -vv
    tcpdump: listening on eno16777984, link-type EN10MB (Ethernet), capture size 262144 bytes
    无内容
    
    tcpdump -i docker0 -nn -vv
    tcpdump: listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
    14:10:58.373599 IP (tos 0x0, ttl 64, id 35536, offset 0, flags [DF], proto UDP (17), length 32)
        172.17.0.1.54877 > 172.17.0.2.9999: [bad udp cksum 0x5843 -> 0xd9d7!] UDP, length 4
    14:11:03.387315 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    14:11:03.387349 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    tcpdump -i vethcbcc4e9 -nn -vv
    tcpdump: listening on vethcbcc4e9, link-type EN10MB (Ethernet), capture size 262144 bytes
    14:10:58.373617 IP (tos 0x0, ttl 64, id 35536, offset 0, flags [DF], proto UDP (17), length 32)
        172.17.0.1.54877 > 172.17.0.2.9999: [bad udp cksum 0x5843 -> 0xd9d7!] UDP, length 4
    14:11:03.387325 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    14:11:03.387349 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    数据包经过的设备依次为:docker0 ---> vethcbcc4e9
    

    数据包发送前与发送后主机nat对比:

    分析:echo mac | nc -s 172.17.0.1 -p 54877 -u 172.17.0.2 9999 产生的数据包的源地址、源端口与目的地址、目的端口为:172.17.0.1.54877 > 172.17.0.2.9999。实践发现,数据包从用户空间发出仅经过OUTPUT链与POSTROUTING链。数据包从OUTPUT链流向POSTROUTING链时,被-A POSTROUTING -o docker0 -m addrtype --src-type LOCAL -j MASQUERADE规则匹配,将源地址类型为LOCAL、从docker0设备流出的数据包的源地址伪装成docker0设备的地址172.17.0.1,此时数据包的源地址、源端口与目的地址、目的端口变为:172.17.0.1.54877 > 172.17.0.2.9999。紧接着数据包经过docker0,经过容器设备vethcbcc4e9被程序接收,程序的输出结果为:addr: ('172.17.0.1', 54877), data: mac
    1. echo mac | nc -s 172.17.0.2 -p 54877 -u 172.17.0.2 9999
      libnsock mksock_bind_addr(): Bind to 172.17.0.2:54877 failed (IOD #1): Cannot assign requested address (99)
    docker0与容器网卡抓包结果:
    tcpdump -i docker0 -nn -vv
    tcpdump: listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
    14:13:04.045159 IP (tos 0x0, ttl 64, id 35537, offset 0, flags [DF], proto UDP (17), length 32)
        172.17.0.1.35950 > 172.17.0.2.9999: [bad udp cksum 0x5843 -> 0x23c7!] UDP, length 4
    14:13:09.051330 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    14:13:09.051367 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    tcpdump -i vethcbcc4e9 -nn -vv
    tcpdump: listening on vethcbcc4e9, link-type EN10MB (Ethernet), capture size 262144 bytes
    14:13:04.045176 IP (tos 0x0, ttl 64, id 35537, offset 0, flags [DF], proto UDP (17), length 32)
        172.17.0.1.35950 > 172.17.0.2.9999: [bad udp cksum 0x5843 -> 0x23c7!] UDP, length 4
    14:13:09.051340 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    14:13:09.051367 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    程序结果:addr: ('172.17.0.1', 35950), data: mac
    
    1. echo mac | nc -s 127.0.0.1 -p 54877 -u 172.17.0.2 9999
    docker0与容器网卡抓包结果:
    tcpdump -i docker0 -nn -vv(数据包的源地址被规则 -A POSTROUTING -o docker0 -m addrtype --src-type LOCAL -j MASQUERADE 修改为docker0地址172.17.0.1)
    tcpdump: listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
    14:15:08.717851 IP (tos 0x0, ttl 64, id 35538, offset 0, flags [DF], proto UDP (17), length 32)
        172.17.0.1.54877 > 172.17.0.2.9999: [bad udp cksum 0x5843 -> 0xd9d7!] UDP, length 4
    14:15:13.723315 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    14:15:13.723350 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    tcpdump -i vethcbcc4e9 -nn -vv
    tcpdump: listening on vethcbcc4e9, link-type EN10MB (Ethernet), capture size 262144 bytes
    14:15:08.717868 IP (tos 0x0, ttl 64, id 35538, offset 0, flags [DF], proto UDP (17), length 32)
        172.17.0.1.54877 > 172.17.0.2.9999: [bad udp cksum 0x5843 -> 0xd9d7!] UDP, length 4
    14:15:13.723326 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    14:15:13.723350 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    程序结果:addr: ('172.17.0.1', 54877), data: mac
    
    1. 去除POSTROUTING链的规则 -A POSTROUTING -o docker0 -m addrtype --src-type LOCAL -j MASQUERADE

      执行 echo mac | nc -s 127.0.0.1 -p 54877 -u 172.17.0.2 9999
      docker0与容器网卡抓包结果:

    tcpdump -i docker0 -nn -vv
    tcpdump: listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
    14:17:35.597021 IP (tos 0x0, ttl 64, id 35539, offset 0, flags [DF], proto UDP (17), length 32)
        127.0.0.1.54877 > 172.17.0.2.9999: [bad udp cksum 0x2b32 -> 0x06e9!] UDP, length 4
    14:17:40.603314 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    14:17:40.603346 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    tcpdump -i vethcbcc4e9 -nn -vv
    tcpdump: listening on vethcbcc4e9, link-type EN10MB (Ethernet), capture size 262144 bytes
    14:17:35.597050 IP (tos 0x0, ttl 64, id 35539, offset 0, flags [DF], proto UDP (17), length 32)
        127.0.0.1.54877 > 172.17.0.2.9999: [bad udp cksum 0x2b32 -> 0x06e9!] UDP, length 4
    14:17:40.603323 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    14:17:40.603346 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    程序结果:空,即程序没有接收到数据包
    
    1. 添加将udp协议、源地址类型为LOCAL的数据包的源地址修改为主机地址192.168.84.75

      执行 echo mac | nc -s 127.0.0.1 -p 54877 -u 172.17.0.2 9999
      docker0与容器网卡veth1a4e47a抓包结果:

    tcpdump -i docker0 -nn -vv
    tcpdump: listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
    14:20:10.098644 IP (tos 0x0, ttl 64, id 35540, offset 0, flags [DF], proto UDP (17), length 32)
        192.168.84.75.54877 > 172.17.0.2.9999: [bad udp cksum 0xc124 -> 0x70f6!] UDP, length 4
    14:20:15.115338 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    14:20:15.115377 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    tcpdump -i vethcbcc4e9 -nn -vv
    tcpdump: listening on vethcbcc4e9, link-type EN10MB (Ethernet), capture size 262144 bytes
    14:20:10.098661 IP (tos 0x0, ttl 64, id 35540, offset 0, flags [DF], proto UDP (17), length 32)
        192.168.84.75.54877 > 172.17.0.2.9999: [bad udp cksum 0xc124 -> 0x70f6!] UDP, length 4
    14:20:15.115350 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28
    14:20:15.115377 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28
    
    程序结果:addr: ('192.168.84.75', 54877), data: mac
    
    总结

    Centos7中更改数据包源地址的操作即SNAT可以在INPUT链与POSTROUTING链两个地方操作。Docker在INPUT链中没有定义SNAT规则,全部的SNAT操作均定义在POSTROUTING链中。所以对Docker数据包的SNAT的研究只需关注POSTROUTING链即可。我们自己产品对docker的使用属于上述实验一的范围,即是将userland设置为true,包括以下几种情况:

    1. 如果机器的iptables规则没有做额外的配置,通过外主机向本机访问或在本机上通过ip地址访问,数据包的源地址不会被修改

    2. 本机上通过127.0.0.1访问的数据包的源地址会被docker-proxy进程修改,因此本机访问若想要数据包的源地址不被修改,需使用本机ip地址访问

    3. 通过上面实践可发现,源地址为127.0.0.1的数据包在多数场景下不能成功发送出去,能够发送出去且能被容器内程序接收到的数据包在容器网卡或docker0那一层其源地址127.0.0.1已经被修改了;即容器内程序接收到的数据包的源地址不可能是127.0.0.1。源地址为127.0.0.1的数据包在传输过程中意义不大,因为无法根据源地址127.0.0.1找到发包的设备

    4. 数据包在主机与主机或主机与容器之间传输时,数据包的源地址多数情况下会发生改变,如果某功能是以数据包源IP地址作为来源主机的判断是不可靠的;因为可能取的数据包是处于中间状态的包,这类包的源IP地址可能已被修改,这样就会导致来源主机判断的错误。

    相关文章

      网友评论

        本文标题:发往Docker容器内的数据包源地址被修改的研究

        本文链接:https://www.haomeiwen.com/subject/whgjrqtx.html