1625 字
8 分钟
宿舍场景下多人共用路由器下流量分流实践

情景#

在校期间(或其他存在需要多人共用网络的情况),私人路由器大概率成为公交车,被所有人连。由于生物多样性,我们会遇到许多需要维护网络主权的情景,包括但不限于:

  • 有人长时间下载占满带宽
  • 半夜三更打游戏影响其他人
  • 大声喊叫
  • 打几小时的电话
  • 弱智设备请求 DHCP 时不发送设备名称,影响管理

在这里,以我本人成功部署的分流方案为例,探讨如何维护自己的网络主权。

Immortalwrt#

最佳的实现方法是使用 Hyper-V 或其他虚拟化技术实现 SDN,但是这种实现方式很明显对硬件要求过高,基本需要一个 X86 的 PC 或服务器才能够实现。因此退而求其次,不追求细粒度的控制,保证路由的性能和稳定性。

为了控制成本,使用成熟完善的 Openwrt 社区资源能够简化很多复杂的环境配置,其中 LEDE is bullshit,原版又不适应中国国情,所以选择较新较稳定的 Immortalwrt1。为了处理 WiFi 的问题,可以直接找个能刷机的硬路由来实现本文中的功能。

虽然基于 ARM 的设备多比较起夜级,但是能玩的操作也是很多的(OSPF,多层隧道嵌套,深度包检测等等)。

方案选择#

  • 单设备流量控制
  • 流量优先级控制
  • 针对网段网络质量控制
  • 针对 APP 阻断
  • 上网时间控制

虽然很想说我全都要,但是受限于硬件条件,这里选择只实现针对网段网络质量控制单设备流量控制,这些已经能够达到很好的效果了。

apernet
/
OpenGFW
Waiting for api.github.com...
00K
0K
0K
Waiting...

如果你有较强性能的软路由,可以选择直接部署 OpenGFW 来实现 APP 阻断和上网限制,但是 ARM 设备性能羸弱,一开会直接拉爆 CPU。

destan19
/
OpenAppFilter
Waiting for api.github.com...
00K
0K
0K
Waiting...

另一个 APP 访问控制,性能要求更低,但是灵活性不强。

注意

这两个应用都要求关闭硬件NAT才能够正常工作,所以对于 ARM 来说,可能也会造成较大的性能负担。同时,这些规则多是直接阻断链接,特征明显,可能导致邻里关系不和。

nftables 全家桶#

在 21 之后(例如23.05),op全线默认使用 nftables。相比于 iptablesnftables 的规则能够让我们更加方便的编写相关规则。

针对某一 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,你不用新建 veth1veth1 将会在 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 的流量控制是立即生效的,有好有坏。

脚注#

  1. https://github.com/immortalwrt/immortalwrt

  2. https://serverfault.com/questions/971826/how-to-drop-packets-based-on-a-probability-with-nftables

宿舍场景下多人共用路由器下流量分流实践
https://nptr.cc/posts/dorm-router-traffic-management/
作者
Nullpinter
发布于
2024-06-14