一、方案概述
- 限速工具:Linux 内核
tc(Traffic Control)+iptables标记 - 限速对象:指定网卡的上行流量(出口方向)
- 限速粒度:全局上行带宽,可排除特定端口(如 SSH、Web 管理等)
- 持久化:通过 systemd 服务实现开机自动限速
- 定时控制:通过 cron 实现按需开启/关闭或切换限速值
- 适用系统:Ubuntu 20.04 LTS(也兼容 Debian 11/12、飞牛 fnOS)
二、安装必要软件包
Ubuntu 20.04 默认已安装 iproute2(提供 tc)和 iptables,但为了确保环境完整,执行:
bash
sudo apt update sudo apt install -y iproute2 iptables
三、部署限速脚本
1. 创建脚本目录并编写主脚本
bash
sudo mkdir -p /usr/local/bin sudo nano /usr/local/bin/speedlimit.sh
将以下完整脚本内容粘贴进去(已修复 Illegal "match" 错误,使用 fw 分类器):
bash
#!/bin/bash
# ============================================
# Ubuntu 20.04 上行带宽限速脚本
# 功能:对指定网卡上行总带宽进行限速,支持排除端口
# 用法:speedlimit.sh {start|stop|status|restart}
# ============================================
# ========== 用户配置区域 ==========
INTERFACE="eth0" # 你的出口网卡,用 ip link show 查看
LIMIT_RATE="10mbit" # 限速值,例如 2mbit / 10mbit / 100mbit
EXCLUDE_PORTS="22,443,8080" # 排除不限速的端口,逗号分隔,无则留空 ""
# =================================
IPMARK="100"
TC_CLSID="1:${IPMARK}"
start_limit() {
echo "正在为网卡 $INTERFACE 设置上行限速: $LIMIT_RATE ..."
# 清除旧规则
tc qdisc del dev $INTERFACE root 2>/dev/null
# 绑定 HTB 队列树
tc qdisc add dev $INTERFACE root handle 1: htb default 20
tc class add dev $INTERFACE parent 1: classid 1:1 htb rate $LIMIT_RATE
tc class add dev $INTERFACE parent 1:1 classid 1:20 htb rate $LIMIT_RATE
tc class add dev $INTERFACE parent 1:1 classid $TC_CLSID htb rate 10000mbit
# 使用 fw 分类器匹配防火墙标记(避免 u32 match 语法问题)
tc filter add dev $INTERFACE parent 1: protocol ip prio 1 handle $IPMARK fw flowid $TC_CLSID
# 配置 iptables 标记链
iptables -t mangle -D OUTPUT -j SPEEDLIMIT 2>/dev/null
iptables -t mangle -F SPEEDLIMIT 2>/dev/null
iptables -t mangle -X SPEEDLIMIT 2>/dev/null
iptables -t mangle -N SPEEDLIMIT
iptables -t mangle -A OUTPUT -j SPEEDLIMIT
# 排除端口:打上特殊标记 $IPMARK,绕过限速
IFS=',' read -ra PORTS <<< "$EXCLUDE_PORTS"
for PORT in "${PORTS[@]}"; do
if [[ -n "$PORT" ]]; then
iptables -t mangle -A SPEEDLIMIT -p tcp --sport $PORT -j MARK --set-mark $IPMARK
iptables -t mangle -A SPEEDLIMIT -p udp --sport $PORT -j MARK --set-mark $IPMARK
echo "已添加排除端口: $PORT"
fi
done
# 其余流量默认标记 0,被限速
iptables -t mangle -A SPEEDLIMIT -j MARK --set-mark 0
echo "限速规则已生效。"
}
stop_limit() {
echo "正在停止所有限速规则..."
tc qdisc del dev $INTERFACE root 2>/dev/null
iptables -t mangle -D OUTPUT -j SPEEDLIMIT 2>/dev/null
iptables -t mangle -F SPEEDLIMIT 2>/dev/null
iptables -t mangle -X SPEEDLIMIT 2>/dev/null
echo "限速规则已清除。"
}
status_check() {
echo "========== 限速状态检查 =========="
if tc qdisc show dev $INTERFACE | grep -q "htb 1:"; then
echo -e "[状态] 限速: 已启用"
echo "当前限速设置:"
tc class show dev $INTERFACE | grep "htb rate"
else
echo "[状态] 限速: 已停止"
fi
echo "排除端口: $EXCLUDE_PORTS"
echo "================================="
}
case "$1" in
start)
start_limit
;;
stop)
stop_limit
;;
status)
status_check
;;
restart)
stop_limit
start_limit
;;
*)
echo "使用方法: $0 {start|stop|status|restart}"
exit 1
;;
esac
exit 02. 保存并赋予执行权限
bash
sudo chmod +x /usr/local/bin/speedlimit.sh
3. 测试脚本
先修改脚本开头的 INTERFACE 为你的真实网卡名(用 ip link show 查看),例如 enp0s3、ens33、eth0 等。
bash
# 查看网卡名称 ip link show # 编辑脚本改 INTERFACE sudo nano /usr/local/bin/speedlimit.sh
然后测试:
bash
sudo /usr/local/bin/speedlimit.sh start sudo /usr/local/bin/speedlimit.sh status sudo /usr/local/bin/speedlimit.sh stop
确保没有 Illegal "match" 错误,且 status 显示正常。
四、设置开机自动限速(systemd 服务)
创建 systemd 服务文件:
bash
sudo nano /etc/systemd/system/speedlimit.service
粘贴以下内容:
ini
[Unit] Description=Ubuntu Bandwidth Limiter After=network-online.target Wants=network-online.target [Service] Type=oneshot RemainAfterExit=yes ExecStart=/usr/local/bin/speedlimit.sh start ExecStop=/usr/local/bin/speedlimit.sh stop StandardOutput=journal [Install] WantedBy=multi-user.target
启用并启动服务:
bash
sudo systemctl daemon-reload sudo systemctl enable speedlimit.service sudo systemctl start speedlimit.service
检查状态:
bash
sudo systemctl status speedlimit.service sudo /usr/local/bin/speedlimit.sh status
重启系统验证:sudo reboot 后再执行 status 应依然为“已启用”。
五、配置定时开关限速(cron)
如果你需要每天固定时间开启/关闭限速,或者在不同时段使用不同限速值,使用 cron。
1. 编辑 root 用户的 crontab
bash
sudo crontab -e
2. 添加 PATH 和定时任务
在文件开头添加(确保 tc/iptables 命令可被找到):
cron
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
然后在下方添加你的调度规则,例如:
cron
# 每天 22:30 开启限速 30 22 * * * /usr/local/bin/speedlimit.sh start # 每天 08:00 关闭限速 0 8 * * * /usr/local/bin/speedlimit.sh stop
保存退出。
3. 高级用法:不同时段不同限速值
如果需要白天限速较宽松、夜间严格限制,可以复制多个脚本并修改 LIMIT_RATE:
bash
sudo cp /usr/local/bin/speedlimit.sh /usr/local/bin/speedlimit-night.sh sudo nano /usr/local/bin/speedlimit-night.sh # 修改 LIMIT_RATE="2mbit"
然后在 crontab 中:
cron
# 白天 8:00 使用 10mbit 0 8 * * * /usr/local/bin/speedlimit.sh start # 夜间 23:00 切换为 2mbit 0 23 * * * /usr/local/bin/speedlimit-night.sh start # 凌晨 2:00 完全取消限速 0 2 * * * /usr/local/bin/speedlimit.sh stop
注意切换不同脚本前最好先执行 stop,或者直接调用新脚本的 start(它会自动清除旧规则重建)。
六、验证限速是否真实生效
- 本地查看队列统计:bashtc -s qdisc show dev eth0 tc -s class show dev eth0观察
Sent字节数是否增长,dropped是否出现。 - 通过另一台主机进行 iperf3 上行测试:
- 在限速服务器上启动 iperf3 服务端:
iperf3 -s - 在外网客户端(或同一局域网的另一台机器)执行:
iperf3 -c <服务器IP> -R(-R 表示反向测试,即测试服务器上行) - 观察带宽是否被限制到设定值。
- 在限速服务器上启动 iperf3 服务端:
- 检查排除端口效果:
- 从服务器访问外网某服务,源端口为排除端口(如 443),应不受限速影响。
- 可以使用
curl --local-port 443 //example.com测试。
七、常见问题与解决
Q1:执行 start 时出现 RTNETLINK answers: No such file or directory
原因:网卡名称错误或 tc 模块未加载。
解决:确认 INTERFACE 名称正确;执行 modprobe sch_htb 加载 HTB 模块。
Q2:限速不生效,查看 tc -s class 显示 rate 为 0 或巨大
原因:tc filter 未能正确匹配流量。
解决:检查 iptables -t mangle -L -v 是否有包计数;确保使用 fw 分类器而非 u32 match。
Q3:重启后限速规则丢失(systemd 未启动)
原因:服务未启用或网络依赖未解决。
解决:执行 sudo systemctl enable speedlimit.service;检查 After=network-online.target 是否生效,必要时改为 After=network.target 并增加 Restart=on-failure。
Q4:cron 任务未执行
原因:环境变量缺失或脚本权限问题。
解决:在 crontab 中显式设置 PATH;使用绝对路径调用脚本;查看日志:grep CRON /var/log/syslog。
Q5:排除端口无效
原因:iptables 链顺序或标记被覆盖。
解决:确认标记链在所有 OUTPUT 规则之前插入;可以添加 iptables -t mangle -L -n -v 检查计数器。
八、完整部署脚本(一键安装)
为了方便,你也可以将以上所有步骤整合为一个自动化安装脚本,保存为 deploy_speedlimit.sh:
bash
#!/bin/bash # Ubuntu 20.04 限速方案一键部署脚本 set -e # 1. 安装依赖 apt update apt install -y iproute2 iptables # 2. 创建主脚本 cat > /usr/local/bin/speedlimit.sh << 'EOF' (这里粘贴上面完整脚本内容) EOF chmod +x /usr/local/bin/speedlimit.sh # 3. 提示修改网卡名称 echo "请编辑 /usr/local/bin/speedlimit.sh,设置 INTERFACE 为你的出口网卡名($(ip link show | grep -oP '^[0-9]+: \K[^:]+' | grep -v lo | head -1))和 LIMIT_RATE。" echo "完成后执行:sudo /usr/local/bin/speedlimit.sh start" # 4. 创建 systemd 服务 cat > /etc/systemd/system/speedlimit.service << 'EOF' [Unit] Description=Ubuntu Bandwidth Limiter After=network-online.target Wants=network-online.target [Service] Type=oneshot RemainAfterExit=yes ExecStart=/usr/local/bin/speedlimit.sh start ExecStop=/usr/local/bin/speedlimit.sh stop StandardOutput=journal [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable speedlimit.service echo "部署完成。请编辑 /usr/local/bin/speedlimit.sh 后执行 systemctl start speedlimit.service"
执行此一键脚本前请先修改其中的脚本内容占位符为实际完整脚本。
九、总结
通过以上步骤,你可以在 Ubuntu 20.04 系统上获得一个功能完整、持久化、定时可调的 上行带宽限速方案,核心特点:
- 使用
tc+iptables实现精准上行限速 - 支持排除任意端口(如管理端口、P2P 端口)
- 开机自动恢复限速规则(systemd)
- 支持 crontab 灵活调度(按时间开启/关闭或切换限速值)
- 兼容飞牛 fnOS(基于 Debian 12)及其他 Debian 系系统