直接在服务器执行以下命令,一键覆盖完整双栈版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
cat > /root/port_limit.sh << 'EOF'
#!/bin/bash

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
BLUE='\033[0;34m'
BOLD='\033[1m'
NC='\033[0m'

IFACE=$(ip route show default 2>/dev/null | awk '/default/ {print $5}' | head -1)
IFACE=${IFACE:-eth0}

check_root() {
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}错误:此脚本需要 root 权限运行!${NC}"
exit 1
fi
}

check_deps() {
local missing=()
for cmd in tc iptables ip6tables ip; do
command -v "$cmd" &>/dev/null || missing+=("$cmd")
done
if [[ ${#missing[@]} -gt 0 ]]; then
echo -e "${RED}缺少依赖命令: ${missing[*]}${NC}"
exit 1
fi
}

ensure_root_qdisc() {
if ! tc qdisc show dev "$IFACE" 2>/dev/null | grep -q "htb"; then
tc qdisc add dev "$IFACE" root handle 1: htb default 999 2>/dev/null
tc class add dev "$IFACE" parent 1: classid 1:999 htb rate 10gbit 2>/dev/null
fi
}

port_to_classid() { printf "1:%x" "$1"; }
port_to_mark() { printf "0x%x" "$1"; }
mbps_to_tc() { echo "${1}mbit"; }

show_limited_ports() {
echo ""
echo -e "${CYAN}${BOLD}══════════════════════════════════════════${NC}"
echo -e "${CYAN}${BOLD} 当前限速端口列表 [接口: $IFACE] ${NC}"
echo -e "${CYAN}${BOLD}══════════════════════════════════════════${NC}"
local found=0
while IFS= read -r line; do
local classid hex_port port rate_info rate_display
classid=$(echo "$line" | awk '{print $3}')
[[ -z "$classid" ]] && continue
[[ "$classid" == "1:1" || "$classid" == "1:999" ]] && continue
hex_port=$(echo "$classid" | cut -d: -f2)
port=$((16#${hex_port}))
[[ "$port" -lt 1 || "$port" -gt 65535 ]] && continue
rate_info=$(echo "$line" | grep -oP 'rate \K[^ ]+')
[[ -z "$rate_info" ]] && continue
rate_display=$(echo "$rate_info" | sed 's/[Mm]bit/Mbps/;s/[Kk]bit/Kbps/;s/[Gg]bit/Gbps/')
echo -e " ${GREEN}${NC} 端口 ${BOLD}${port}${NC} → 限速 ${YELLOW}${rate_display}${NC} ${CYAN}[IPv4+IPv6]${NC}"
found=$((found + 1))
done < <(tc class show dev "$IFACE" 2>/dev/null)
if [[ $found -eq 0 ]]; then
echo -e " ${YELLOW}暂无限速端口${NC}"
fi
echo -e "${CYAN}══════════════════════════════════════════${NC}"
echo ""
}

add_limit() {
local port=$1 rate_mbps=$2
local classid mark tc_rate
classid=$(port_to_classid "$port")
mark=$(port_to_mark "$port")
tc_rate=$(mbps_to_tc "$rate_mbps")

# tc class
if tc class show dev "$IFACE" classid "$classid" 2>/dev/null | grep -q "htb"; then
echo -e "${YELLOW}端口 $port 已存在,正在更新...${NC}"
tc class change dev "$IFACE" parent 1: classid "$classid" htb rate "$tc_rate" burst 15k
else
tc class add dev "$IFACE" parent 1: classid "$classid" htb rate "$tc_rate" burst 15k
# IPv4 filter
tc filter add dev "$IFACE" parent 1: protocol ip handle "$mark" fw flowid "$classid"
# IPv6 filter
tc filter add dev "$IFACE" parent 1: protocol ipv6 handle "$mark" fw flowid "$classid"
fi

# iptables IPv4
for proto in tcp udp; do
iptables -t mangle -C OUTPUT -p "$proto" --sport "$port" \
-m comment --comment "LIMIT_PORT_${port}" -j MARK --set-mark "$mark" 2>/dev/null \
|| iptables -t mangle -A OUTPUT -p "$proto" --sport "$port" \
-m comment --comment "LIMIT_PORT_${port}" -j MARK --set-mark "$mark"
done

# ip6tables IPv6
for proto in tcp udp; do
ip6tables -t mangle -C OUTPUT -p "$proto" --sport "$port" \
-m comment --comment "LIMIT_PORT_${port}" -j MARK --set-mark "$mark" 2>/dev/null \
|| ip6tables -t mangle -A OUTPUT -p "$proto" --sport "$port" \
-m comment --comment "LIMIT_PORT_${port}" -j MARK --set-mark "$mark"
done

echo -e "${GREEN}✔ 端口 ${BOLD}$port${NC}${GREEN} 限速已设置为 ${BOLD}${rate_mbps}Mbps${NC}${GREEN}(IPv4+IPv6)!${NC}"
}

remove_limit() {
local port=$1
local classid mark removed=0
classid=$(port_to_classid "$port")
mark=$(port_to_mark "$port")

# 清 iptables IPv4
for proto in tcp udp; do
while iptables -t mangle -C OUTPUT -p "$proto" --sport "$port" \
-m comment --comment "LIMIT_PORT_${port}" -j MARK --set-mark "$mark" 2>/dev/null; do
iptables -t mangle -D OUTPUT -p "$proto" --sport "$port" \
-m comment --comment "LIMIT_PORT_${port}" -j MARK --set-mark "$mark"
removed=1
done
done

# 清 ip6tables IPv6
for proto in tcp udp; do
while ip6tables -t mangle -C OUTPUT -p "$proto" --sport "$port" \
-m comment --comment "LIMIT_PORT_${port}" -j MARK --set-mark "$mark" 2>/dev/null; do
ip6tables -t mangle -D OUTPUT -p "$proto" --sport "$port" \
-m comment --comment "LIMIT_PORT_${port}" -j MARK --set-mark "$mark"
removed=1
done
done

# 清 tc filter(IPv4 + IPv6)
tc filter del dev "$IFACE" parent 1: protocol ip handle "$mark" fw 2>/dev/null && removed=1
tc filter del dev "$IFACE" parent 1: protocol ipv6 handle "$mark" fw 2>/dev/null && removed=1

# 清 tc class
if tc class show dev "$IFACE" classid "$classid" 2>/dev/null | grep -q "htb"; then
tc class del dev "$IFACE" classid "$classid" 2>/dev/null
removed=1
fi

if [[ $removed -eq 1 ]]; then
echo -e "${GREEN}✔ 端口 ${BOLD}$port${NC}${GREEN} 限速已解除!${NC}"
else
echo -e "${YELLOW}⚠ 未找到端口 $port 的限速规则${NC}"
fi
}

validate_port() {
[[ "$1" =~ ^[0-9]+$ ]] && [[ "$1" -ge 1 && "$1" -le 65535 ]] && return 0
echo -e "${RED}错误:端口号必须为 1~65535${NC}"; return 1
}

validate_rate() {
[[ "$1" =~ ^[0-9]+(\.[0-9]+)?$ ]] && [[ "$1" != "0" ]] && return 0
echo -e "${RED}错误:速率必须为正数(Mbps)${NC}"; return 1
}

print_header() {
clear
echo -e "${BLUE}${BOLD}"
echo " ╔══════════════════════════════════════╗"
echo " ║ 端口限速管理工具 ║"
echo " ║ Port Bandwidth Limiter v1.2 ║"
echo " ╚══════════════════════════════════════╝"
echo -e "${NC}"
echo -e " 网络接口: ${CYAN}${IFACE}${NC}"
echo ""
}

print_menu() {
echo -e "${BOLD} ┌─────────────────────────────────────┐${NC}"
echo -e "${BOLD} │ 主菜单 │${NC}"
echo -e "${BOLD} ├─────────────────────────────────────┤${NC}"
echo -e " │ ${GREEN}1${NC} 查看当前限速端口及速率 │"
echo -e " │ ${GREEN}2${NC} 添加限速端口 │"
echo -e " │ ${GREEN}3${NC} 删除限速端口 │"
echo -e " │ ${RED}9${NC} 退出 │"
echo -e "${BOLD} └─────────────────────────────────────┘${NC}"
echo ""
}

wait_return() {
echo -e " 按 ${YELLOW}0${NC} 返回主菜单..."
while true; do read -r -s -n1 k; [[ "$k" == "0" ]] && break; done
}

menu_view() {
print_header; show_limited_ports; wait_return
}

menu_add() {
print_header
echo -e "${BOLD} ── 添加限速端口 ──${NC}"; echo ""
while true; do
echo -ne " 请输入端口号 (1-65535,0返回): "; read -r port
[[ "$port" == "0" ]] && return
validate_port "$port" && break
done
while true; do
echo -ne " 请输入限速速率 (Mbps,如 100): "; read -r rate
[[ "$rate" == "0" ]] && return
validate_rate "$rate" && break
done
echo ""
ensure_root_qdisc
add_limit "$port" "$rate"
echo ""
show_limited_ports
wait_return
}

menu_remove() {
print_header
echo -e "${BOLD} ── 删除限速端口 ──${NC}"; echo ""
show_limited_ports
while true; do
echo -ne " 请输入要解除限速的端口号 (0返回): "; read -r port
[[ "$port" == "0" ]] && return
if validate_port "$port"; then
echo ""; remove_limit "$port"; echo ""; show_limited_ports
fi
done
}

main() {
check_root
check_deps
ensure_root_qdisc
while true; do
print_header; print_menu
echo -ne " 请选择操作 [1/2/3/9]: "; read -r choice
case "$choice" in
1) menu_view ;;
2) menu_add ;;
3) menu_remove ;;
9) echo -e "\n ${GREEN}再见!${NC}\n"; exit 0 ;;
*) echo -e " ${RED}无效选项${NC}"; sleep 1 ;;
esac
done
}

main
EOF
echo "✔ 写入完成"
chmod +x /root/port_limit.sh

写入后清掉旧规则重新运行:

1
2
3
4
iptables -t mangle -F OUTPUT
ip6tables -t mangle -F OUTPUT
tc qdisc del dev ens192 root 2>/dev/null
bash /root/port_limit.sh

v1.2 新增内容:

  • tc filter 同时注册 protocol ipprotocol ipv6 两条规则
  • ip6tables 同步对 IPv6 的 TCP/UDP 出站流量打 mark
  • 删除限速时同时清理 IPv4 + IPv6 全部规则
  • 限速列表显示 [IPv4+IPv6] 标识