情景
在校期间(或其他存在需要多人共用网络的情况),私人路由器大概率成为公交车,被所有人连。由于生物多样性,我们会遇到许多需要维护网络主权的情景,包括但不限于:
- 有人长时间下载占满带宽
- 半夜三更打游戏影响其他人
- 大声喊叫
- 打几小时的电话
- 弱智设备请求 DHCP 时不发送设备名称,影响管理
在这里,以我本人成功部署的分流方案为例,探讨如何维护自己的网络主权。
Immortalwrt
最佳的实现方法是使用 Hyper-V
或其他虚拟化技术实现 SDN
,但是这种实现方式很明显对硬件要求过高,基本需要一个 X86 的 PC 或服务器才能够实现。因此退而求其次,不追求细粒度的控制,保证路由的性能和稳定性。
为了控制成本,使用成熟完善的 Openwrt
社区资源能够简化很多复杂的环境配置,其中 LEDE is bullshit,原版又不适应中国国情,所以选择较新较稳定的 Immortalwrt1。为了处理 WiFi 的问题,可以直接找个能刷机的硬路由来实现本文中的功能。
虽然基于 ARM 的设备多比较起夜级,但是能玩的操作也是很多的(OSPF,多层隧道嵌套,深度包检测等等)。
方案选择
- 单设备流量控制
- 流量优先级控制
- 针对网段网络质量控制
- 针对 APP 阻断
- 上网时间控制
虽然很想说我全都要,但是受限于硬件条件,这里选择只实现针对网段网络质量控制
和单设备流量控制
,这些已经能够达到很好的效果了。
如果你有较强性能的软路由,可以选择直接部署 OpenGFW 来实现 APP 阻断和上网限制,但是 ARM 设备性能羸弱,一开会直接拉爆 CPU。
另一个 APP 访问控制,性能要求更低,但是灵活性不强。
注意这两个应用都要求关闭硬件NAT才能够正常工作,所以对于 ARM 来说,可能也会造成较大的性能负担。同时,这些规则多是直接阻断链接,特征明显,可能导致邻里关系不和。
nftables
全家桶
在 21 之后(例如23.05),op全线默认使用 nftables
。相比于 iptables
,nftables
的规则能够让我们更加方便的编写相关规则。
针对某一 ip 进行百分比丢包
首先新建一个blocked_ips.txt
来存储需要特定的ip。
add.sh
#!/bin/sh
BLOCKED_IPS_FILE="/root/blocked_ips.txt"
IPS=""
while IFS= read -r line; do
if [[ -n "$line" && "$line" != \#* ]]; then
if [[ -z "$IPS" ]]; then
IPS="$line"
else
IPS="$IPS, $line"
fi
fi
done < "$BLOCKED_IPS_FILE"
TEMP_FILE=$(mktemp)
cat <<EOF > $TEMP_FILE
table ip filter {
chain input {
type filter hook input priority 0; policy accept;
iif "lo" accept
ip saddr { $IPS } numgen random mod 100 < 50 drop
}
}
EOF
# 执行nftables规则
/usr/sbin/nft -f $TEMP_FILE
# 删除临时文件
rm $TEMP_FILE
remove.sh
#!/bin/sh
RULE_HANDLES=$(nft --handle list table ip filter | grep -E 'ip saddr ([{} 0-9.,]+) numgen random mod 100 < [0-9]+ drop' | grep handle | awk '{print $NF}')
if [ -z "$RULE_HANDLES" ]; then
echo "cannot find any rules"
else
# 删除每个找到的规则句柄
for HANDLE in $RULE_HANDLES; do
sudo nft delete rule ip filter input handle $HANDLE
echo "del: $HANDLE"
done
fi
从这里2可以知道,numgen
已经在 nftables
中实现,但是没有出现在文档中。
通过 nfttables 实现了对指定 ip 的百分比丢包。
注意由于现今网络协议优秀的可靠性(草),这个办法仅对 ICMP 产生显著效果,对于游戏、视频、语音等应用没有明显效果。该方案已废弃,改用其他方案。
控制 TCP 连接数
如果你的路由性能实在太差,可能连接数一多网络就开始作妖,这里可以使用以下规则添加连接数限制(示例为一分钟10个)
nft add table ip filter
nft add chain ip filter input { type filter hook input priority 0 \; }
nft add rule ip filter input ip protocol tcp ct state new, untracked meter ratemeter { ip saddr timeout 5m limit rate over 10/minute } drop
给来自某 ip 的流量打标
#!/usr/sbin/nft -f
table ip my_table {
chain prerouting {
type filter hook prerouting priority 0; policy accept;
ip saddr 192.168.1.100 meta mark set 1
}
}
虽然这边使用了更加简单粗暴的方法,但是使用标记对流量进行精细控制可能能够更符合需求。
QoS
可以直接使用 luci-app-nft-qos
来进行对流量的优先级控制,保证下载的时候不卡。
注意要求关闭硬件NAT才能够正常工作。
针对网段网络质量控制
这里需要你安装 kmod-veth
,因为我的方案需要用到虚拟以太网。你也可以使用上文的标记流量来解决分类这个问题。
我们在小学一年级就学过,CIDR 掩码可以玩出很多花活。在这里我们以正常网络为例:
- 192.168.31.0/24 - 192.168.31.0-192.168.31.255
- 192.168.30.0/24 - 192.168.30.0-192.168.30.255
但是,我们可以将掩码改为 23,让其合二为一
- 192.168.30.0/23 - 192.168.30.0-192.168.31.255
现在我们有了两个表面看起来互相隔离的网段:D
新建一个虚拟以太网网络设备,并填上正确的 MAC 和其他参数,无需新建对端。例如 veth0
的对端是 veth1
,你不用新建 veth1
,veth1
将会在 veth0
UP 后自动出现。
通过将 veth1
声明成另一网关(192.168.31.1)并将 veth0
正确配置完成(网关指向 veth1
的设备能够正常上网),使用 DHCP 设置(tag)将需要管理的设置网关发配到这一虚拟网卡,就完成了第一步。
接下来,我们要用到 tc
进行网络环境模拟,记得安装 kmod-netem
。
使用tc qdisc del dev veth1 root netem delay 50ms 500ms 40% loss 5% duplicate 1% corrupt 0.2%
来模拟一个较差的网络环境,针对长视频和正常网页浏览影响不大,而短视频则会卡顿,游戏则会不能自理。有效控制了网络的使用场景。
使用 tc qdisc del dev veth1 root
清除设置。
结语
在移动网络越来越便利的时代,一刀切的禁止方式通常会适得其反,精准抓住痛点才能够成功解决问题。
使用 DHCP 的方式分发网关仅会在 renew 时生效,能够降低操作与网络环境变化的关联性,而使用 nftables
配合 tc
的流量控制是立即生效的,有好有坏。