背景
学校最近把三家运营商都并入校园网了,使用一套名为深澜的系统对绑定不同运营商的用户自动加载不同的路由实现网络流量走不同的 ISP 出口。同时,大概是为了更大地盈利,加入了 DPI 和缓存服务器,并同时屏蔽了外部网络的 53 端口。
至此,接入校园网的用户无法使用默认使用 53 端口的 DNS(TCP/UDP),同时只允许接入三台上网设备。
本文于 2017-03-26 最后更新,内容已大幅改动。
摸索
印象里以往检测上网设备数量方法似乎都抗不过两个路由级联的威力。
但这一次有所不同:
- 上网使用 WebAuth,不需要客户端;
- 上网设备接入时没有上网行为不会被检测(比如 Android 只是打开了,没有开启程序或后台);
- 上网使用的电脑安装使用非 Windows 操作系统不会被认为是一台设备。
故根据网上的资料综合各种奇怪的现象猜测黑盒子里是用上了 深度包检测 即 DPI
某乎上是这么写的:
通过 DPI 设备厂商的一些行为分析经验库来识别,即便是同一个源 IP 地址,可能会在传输层甚至应用层的某一些字段可判断出这是一台单独的终端。
于是这么猜测:每台设备上都有什么不会重复的服务,并且有类似于心跳包的发送规律,可以用来识别用户设备数量。但是类似的服务在干净的 Linux 系统(Android 除外)上不存在。
出路
根据 DPI 的原理,这东西是要拆数据包的。
那么在不存在 MITM 的情况下,经过 TLS 加密的数据包以及 DPI 不认识的协议应该是拆不出来的。
根据这一原理,可以得出 Plan A
在路由端使用 Shadowsocks 或者其他 VPN 程序,配合一台至少和寝室带宽对等的服务器,将流量完全加密。
当然,众所周知,国内的大带宽服务器比寝室的校园网月租还要高的,国外的对内速度又不理想,所以这一方案必须放弃。 (买个沟里云国际的 HK B区机器就好了嘛)
其次是,观察到当 DPI 检测不到上网设备时就会自动切断流量,完全加密大概是不可行的(不要全部机器都走代理就好了嘛)。
使用这一方法的要点就是不能所有机器流量都被加密,所以选择路由器配置 DHCP,将默认分配的网关改成树莓派的 IP。在 OpenWRT/PandoraBox 上,可以在 LAN 接口的 DHCP 高级设置中加入(其中 192.168.1.8 即为树莓派的 IP):
3,192.168.1.8
然后手工把三台电脑改成路由器本体网关(这样就默认检测到三台设备了)。
接下来配置好树莓派的静态 IP 及网关,然后把树莓派的转发开起来。
开启转发可以把下边这行加进 /etc/sysctl.d/99-sysctl.conf (咱的树莓派安装的是 ArchLinuxARM,其他机器的配置文件在 /etc/sysctl.conf)然后重启来实现:
net.ipv4.ip_forward = 1
然后安装配置 ss-redir:
pacman -S shadowsocks-libev
systemctl enable shadowsocks-libev-redir@redir
似乎要改一下 systemctl 里这个 ss-redir 的启动配置(具体位置 /usr/lib/systemd/system/[email protected]),找到 ExecStart 这一行改成这样:
ExecStart=/usr/bin/ss-redir -b 0.0.0.0 -u -c /etc/shadowsocks/%i.json
ss-dir 配置文件在 /etc/shadowsocks/redir.json 内容大概这样:
{
"server": "xx.xx.xx.xx",
"server_port": 808,
"local_adress": "0.0.0.0",
"local_port": 59872,
"password": "NyaaNyaaNyaa",
"timeout": 5,
"method":"chacha20"
}
注意,下边的这份 iptables 配置文档是支持 UDP 流量转发的,所以请配合使用支持 UDP 转发的服务器。
然后配置 iptables 转发流量:
# Reset
iptables -F
iptables -X
iptables -Z
iptables -t nat -F
iptables -t nat -X
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
# Mask in forward
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j MASQUERADE
# Create SS TCP redir chain
iptables -t nat -N SS
# Bypass private address
iptables -t nat -A SS -d 0.0.0.0/8 -j RETURN
iptables -t nat -A SS -d 10.0.0.0/8 -j RETURN
iptables -t nat -A SS -d 127.0.0.0/8 -j RETURN
iptables -t nat -A SS -d 169.254.0.0/16 -j RETURN
iptables -t nat -A SS -d 172.16.0.0/12 -j RETURN
iptables -t nat -A SS -d 192.168.0.0/16 -j RETURN
iptables -t nat -A SS -d 224.0.0.0/4 -j RETURN
iptables -t nat -A SS -d 240.0.0.0/4 -j RETURN
# Bypass SS address
iptables -t nat -A SS -d xx.xx.xx.xx -j RETURN
# Redir to SS-Redir
iptables -t nat -A SS -p tcp -j REDIRECT --to-port 59872
# Apply to PREROUTING
iptables -t nat -A PREROUTING -p tcp -j SS
# Mark UDP stream
ip rule add fwmark 0x01/0x01 table 100
ip route add local 0.0.0.0/0 dev lo table 100
# Create SS UDP redir chain
iptables -t mangle -N SSUDP
# Bypass private address
iptables -t mangle -I PREROUTING -d 127.0.0.0/24 -j RETURN
iptables -t mangle -I PREROUTING -d 192.168.0.0/16 -j RETURN
iptables -t mangle -I PREROUTING -d 10.42.0.0/16 -j RETURN
iptables -t mangle -I PREROUTING -d 0.0.0.0/8 -j RETURN
iptables -t mangle -I PREROUTING -d 10.0.0.0/8 -j RETURN
iptables -t mangle -I PREROUTING -d 172.16.0.0/12 -j RETURN
iptables -t mangle -I PREROUTING -d 224.0.0.0/4 -j RETURN
iptables -t mangle -I PREROUTING -d 240.0.0.0/4 -j RETURN
iptables -t mangle -I PREROUTING -d 169.254.0.0/16 -j RETURN
iptables -t mangle -I PREROUTING -d 255.255.0.0/8 -j RETURN
# Redir to SS-Redir
iptables -t mangle -A SSUDP -p udp -j TPROXY --on-port 59872 --tproxy-mark 0x01/0x01
iptables -t mangle -A PREROUTING -j SSUDP
# Add PI to OUTPUT chain
iptables -t nat -A OUTPUT -p tcp -j SS
没问题就保存下配置:
iptables-save > /etc/iptables/iptables.rules
systemctl enable iptables.service
systemctl start iptables.service
因为部分情况下,DPI检测不会肛 80 443 端口下的流量,所以可以屏蔽掉其他端口流量。以下是 Plan B
将全寝室流量分开,只允许三台设备使用完整的网络,其他设备数据经过协议过滤,只允许 DPI 解不开(或者没碰)的协议和 HTTP(S)。
从这一思路出发,有两种方案:
- 使用内网 VPN 将设备流量完全转移到过滤流量使用的设备上,
使用了 树莓派2 配合 SoftEtherVPN 实现这一方案;- 使用 iptables 直接在 NAT 转发中阻断流量。
方案1 的步骤:
- 设置 DHCP 使设备默认分配到无效的网关 (参考 PlanA);
- 为树莓派设置静态IP,手动指定网关 安装 SoftEtherVPN;
- 设置 舍弃 全部 TCP/UDP 流量。
- 同时放行 80、443、53 以及 Shadowsocks 使用到的服务器端口。
- 为了能够正常启动 Steam 建议加入这些端口(你可以按自己的需求开启)。
- 为 SoftEtherVPN 配置用户,并启用 L2TP VPN。
- 接下来就可以嗨了。
使用提示:
- 连接上 VPN 不能玩网游。所以需要玩的机器改成路由器网关;
- 其他客户端在 DHCP 默认分配网关下必须连接 VPN 才能使用。
方案2 的步骤:
- 设置 DHCP 使设备默认分配到无效的网关 (参考 PlanA);
- 为树莓派设置静态IP,手动指定网关;
- 开启转发(参考 PlanA);
- 运行以下脚本;
- 保存配置(参考 PlanA)。
# Base iptables -P FORWARD ACCEPT # Mask in forward iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j MASQUERADE # Allow DNS iptables -A FORWARD -p udp --dport 53 -j ACCEPT iptables -A FORWARD -p udp --sport 53 -j ACCEPT # Allow SSH HTTP(S) CIFS iptables -A FORWARD -p tcp -m multiport --dport 80,443,445,8800 -j ACCEPT iptables -A FORWARD -p tcp -m multiport --sport 80,443,445,8800 -j ACCEPT # Allow Shadowsocks # TODO -> Add your shadowsocks port iptables -A FORWARD -p tcp -m multiport --dport 7000,808 -j ACCEPT iptables -A FORWARD -p tcp -m multiport --sport 7000,808 -j ACCEPT # Allow Steam game iptables -A FORWARD -p tcp --dport 27015:27030 -j ACCEPT iptables -A FORWARD -p tcp --sport 27015:27030 -j ACCEPT iptables -A FORWARD -p udp --dport 27000:27036 -j ACCEPT iptables -A FORWARD -p udp --sport 27000:27036 -j ACCEPT iptables -A FORWARD -p tcp -m multiport --dport 27036,27037 -j ACCEPT iptables -A FORWARD -p tcp -m multiport --sport 27036,27037 -j ACCEPT iptables -A FORWARD -p udp --dport 4380 -j ACCEPT iptables -A FORWARD -p udp --sport 4380 -j ACCEPT # Drop others iptables -A FORWARD -j DROP
使用提示:
- 连接上 VPN 不能玩网游。所以需要玩的机器改成路由器网关;
- 其他客户端在 DHCP 默认分配网关下直接可以使用网络。
大概的框图(手残请不要吐槽):
增强
由于无法使用默认 53 端口的外部 DNS,故使用自建 DNS 绕开限制。
以下是 dnsmasq 使用的配置档,其中 192.168.1.1 对应的是 ChinaDNS 程序提供的无污染 DNS 解析。
剩下的是 USTC 提供的 DNS 服务。
port=53
listen-address=127.0.0.1,192.168.1.2
no-resolv all-servers
server=202.141.176.93#5353
server=202.141.162.123#5353
server=192.168.1.1#53
至于 DPI 下的强制智障缓存(只看 URL 的暴力缓存),有两种方法:
- 全局代理(这个就不说了);
- 路由器上 DROP 掉 302 包,似乎是这样的 iptables 命令:
iptables -I FORWARD -m string –string “Location: http://xx.xx.xx.xx" –algo bm -j DROP