【神器发布】ImmortalWrt/OpenWrt 局域网设备管控脚本 V9 旗舰版 (支持批量操作/防绕过)
你是否遇到过以下烦恼?
- 想给孩子的手机断网,却发现他依然能看 YouTube/刷 TikTok(因为流量走了 Passwall/OpenClash 代理,绕过了普通防火墙规则)?
- 路由器后台的访问控制功能太弱,操作繁琐,还得一个个点击应用?
- 市面上的脚本界面简陋,功能单一,甚至拉黑了还能继续通过现有连接下载?
今天为大家分享一个专为 ImmortalWrt 和 FW4 (nftables) 打造的终端级设备管控神器 —— DevCtrl V9 全能旗舰版。
它不仅界面精美、操作丝滑,更重要的是它采用了内核级优先阻断技术,专治各种“关不掉”的顽固流量。
✨ V9 旗舰版核心特性
- 🛡️ 天网模式 (Priority -450):这是本脚本最核心的黑科技。它将拦截关卡设在 Prerouting 链,且优先级高达 -450。这意味着数据包刚接触路由器网卡就会被拦截,比 Passwall、OpenClash 等科学插件的优先级更高。彻底解决“拉黑了还能看外网视频”的痛点。
- 🚀 批量极速操作:支持批量指令!想同时拉黑第 1、3、5 台设备?只需输入 1 3 5 回车即可,无需重复操作。
- 🎨 现代化 UI 界面:重构的竖向操作面板,配备状态仪表盘。在线设备亮绿灯 🟢,离线设备显灰色 ⚫,状态一目了然。
- ⚡ 实时连接熔断:拉黑瞬间自动执行 conntrack flush,强制切断该设备当前正在进行的视频流或游戏连接,秒级生效。
- 🔰 安全防误触:智能识别当前 SSH 管理终端 IP,禁止拉黑自己,防止把自己关在门外。
📸 界面预览

🛠️ 安装教程
使用 SSH 连接到您的路由器(工具推荐:Putty, Xshell, 或 Windows 终端),复制下方所有代码,在终端内粘贴并回车即可一键安装。
系统要求:ImmortalWrt / OpenWrt 21.02+ (基于 FW4/nftables)
Bash
cat << 'INSTALL_EOF' > /tmp/install_devctrl_v9_fix.sh
#!/bin/bash
# ================= 变量定义 =================
APP_PATH="/root/devctrl"
PY_FILE="$APP_PATH/core.py"
MGR_FILE="$APP_PATH/manager.sh"
BAN_LIST="/etc/devctrl_blacklist.txt"
WHT_LIST="/etc/devctrl_whitelist.txt"
NFT_CONFIG="/tmp/devctrl_rules.nft"
LINK_BIN="/usr/bin/dev"
mkdir -p "$APP_PATH"
echo "-----------------------------------------------"
echo "正在安装 设备管控 V9 (BusyBox修正版 + 白/黑名单编辑 + Nano)..."
echo "特性: 修复日志grep报错 / 批量拉黑 / UI微调 / 编辑白名单&黑名单(强制nano)"
echo "-----------------------------------------------"
# 1. 依赖安装(方案1:强制安装 nano)
opkg update 2>/dev/null
opkg install python3 python3-base bash conntrack nano 2>/dev/null
# 预创建名单文件
touch "$BAN_LIST" "$WHT_LIST"
chmod 600 "$BAN_LIST" "$WHT_LIST" 2>/dev/null
# 2. 部署 V9 核心引擎
cat << 'EOF' > "$PY_FILE"
import subprocess, os, sys, time, re
BLACKLIST_FILE = "/etc/devctrl_blacklist.txt"
WHITELIST_FILE = "/etc/devctrl_whitelist.txt"
NFT_CONFIG_FILE = "/tmp/devctrl_rules.nft"
MAC_RE = re.compile(r"^[0-9A-F]{2}(:[0-9A-F]{2}){5}$")
# === 颜色与图标定义 ===
C_RESET = "\033[0m"
C_BOLD = "\033[1m"
C_RED = "\033[38;5;196m"
C_GREEN = "\033[38;5;46m"
C_YELLOW = "\033[38;5;226m"
C_BLUE = "\033[38;5;39m"
C_PURPLE = "\033[38;5;135m"
C_CYAN = "\033[38;5;51m"
C_WHITE = "\033[38;5;255m"
C_GRAY = "\033[38;5;243m"
C_DARK = "\033[38;5;237m"
ICON_ON = f"{C_GREEN}🟢{C_RESET}"
ICON_OFF = f"{C_GRAY}⚫{C_RESET}"
ICON_BAN = f"{C_RED}🚫{C_RESET}"
ICON_OK = f"{C_GREEN}✅{C_RESET}"
ICON_PAUSE = f"{C_YELLOW}⏸️ {C_RESET}"
ICON_ME = f"{C_PURPLE}💻{C_RESET}"
ICON_WHT = f"{C_CYAN}⭐{C_RESET}"
def run_cmd(cmd):
try:
subprocess.run(cmd, shell=True, check=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
except:
pass
def run_cmd_output(cmd):
try:
res = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
return res.stdout.strip()
except:
return ""
def _load_set(path):
if not os.path.exists(path):
return set()
out = set()
try:
with open(path, "r") as f:
for line in f:
s = line.strip()
if not s or s.startswith("#"):
continue
s = s.upper()
if MAC_RE.match(s):
out.add(s)
except:
pass
return out
def _save_set(path, mac_set):
try:
with open(path, "w") as f:
for mac in sorted(mac_set):
f.write(mac + "\n")
except:
pass
def load_blacklist():
return _load_set(BLACKLIST_FILE)
def save_blacklist(mac_set):
_save_set(BLACKLIST_FILE, mac_set)
def load_whitelist():
return _load_set(WHITELIST_FILE)
def save_whitelist(mac_set):
_save_set(WHITELIST_FILE, mac_set)
def is_firewall_active():
res = run_cmd_output("nft list table inet devctrl_v6")
return "enforce_block_early" in res
def apply_firewall():
blacklist = load_blacklist()
whitelist = load_whitelist()
# 白名单优先:即使同时在黑名单,也视为白名单放行
blacklist = set(m for m in blacklist if m not in whitelist)
w_elements = ""
b_elements = ""
if whitelist:
w_elements = ", ".join([f"{mac}" for mac in sorted(whitelist)])
w_elements = f"elements = {{ {w_elements} }}"
if blacklist:
b_elements = ", ".join([f"{mac}" for mac in sorted(blacklist)])
b_elements = f"elements = {{ {b_elements} }}"
nft_content = f"""
table inet devctrl_v6 {{
set whitelisted_macs {{
type ether_addr
flags interval
{w_elements}
}}
set blacklisted_macs {{
type ether_addr
flags interval
{b_elements}
}}
chain enforce_block_early {{
type filter hook prerouting priority -450; policy accept;
# 白名单优先放行
ether saddr @whitelisted_macs accept
# 黑名单拦截
ether saddr @blacklisted_macs log prefix "BAN_ALL: " drop
}}
}}
"""
with open(NFT_CONFIG_FILE, "w") as f:
f.write(nft_content)
run_cmd("nft delete table inet devctrl_v4")
run_cmd("nft delete table inet devctrl_v6")
run_cmd(f"nft -f {NFT_CONFIG_FILE}")
def stop_firewall():
run_cmd("nft delete table inet devctrl_v6")
def flush_conntrack(target_mac, dev_list):
for dev in dev_list:
if dev["mac"] == target_mac:
ip = dev.get("ip")
if ip:
run_cmd(f"conntrack -D -s {ip}")
run_cmd(f"conntrack -D -d {ip}")
def get_current_ssh_ip():
val = run_cmd_output("echo $SSH_CLIENT")
return val.split()[0] if val else "未知"
def get_devices():
leases = {}
try:
with open("/tmp/dhcp.leases", "r") as f:
for line in f:
parts = line.split()
if len(parts) >= 4:
leases[parts[1].upper()] = {"ip": parts[2], "name": parts[3]}
except:
pass
devices = []
res = run_cmd_output("ip -4 neigh show | grep -v FAILED")
seen_macs = set()
for line in res.splitlines():
parts = line.split()
if len(parts) < 5:
continue
try:
mac_idx = parts.index("lladdr") + 1
mac = parts[mac_idx].upper()
except:
continue
if len(mac) != 17:
continue
seen_macs.add(mac)
name = leases.get(mac, {}).get("name", "*")
devices.append({"mac": mac, "ip": parts[0], "name": name, "online": True})
for mac, info in leases.items():
if mac not in seen_macs:
devices.append({"mac": mac, "ip": info["ip"], "name": info["name"], "online": False})
return devices
# === V9 新版界面渲染 ===
def show_menu():
if not is_firewall_active():
apply_firewall()
my_ip = get_current_ssh_ip()
dev_list_temp = get_devices()
my_mac = next((d["mac"] for d in dev_list_temp if d["ip"] == my_ip), "")
while True:
whitelist = load_whitelist()
blacklist = load_blacklist()
active = is_firewall_active()
os.system("clear")
# === 顶部仪表盘 ===
print(f"{C_BLUE}╭{'─'*62}╮{C_RESET}")
print(f"{C_BLUE}│{C_RESET} {C_BOLD}{C_CYAN}🛡️ 设备管控终端 V9{C_RESET} {C_GRAY}(BusyBox版){C_RESET}".ljust(74) + f"{C_BLUE}│{C_RESET}")
status_txt = f"{C_GREEN}保护中{C_RESET}" if active else f"{C_RED}已停止{C_RESET}"
ban_txt = f"{C_RED}{len(blacklist)}{C_RESET}" if blacklist else f"{C_GREEN}0{C_RESET}"
wht_txt = f"{C_CYAN}{len(whitelist)}{C_RESET}" if whitelist else f"{C_GREEN}0{C_RESET}"
print(f"{C_BLUE}│{C_RESET} 引擎: [{status_txt}] | 拉黑: [{ban_txt}] | 白名单: [{wht_txt}] | 终端: {C_YELLOW}{my_ip}{C_RESET}".ljust(102) + f"{C_BLUE}│{C_RESET}")
print(f"{C_BLUE}╰{'─'*62}╯{C_RESET}")
# === 设备列表 ===
print(f"{C_DARK} {'ID':<4} {'状态':<9} {'IP地址':<16} {'MAC地址':<18} {'设备名称'}{C_RESET}")
print(f"{C_DARK} {'─'*62}{C_RESET}")
dev_list = get_devices()
dev_list.sort(key=lambda x: (not x["online"], x["ip"]))
for idx, dev in enumerate(dev_list):
mac = dev["mac"]
if mac in whitelist:
status = f"{ICON_WHT} {C_CYAN}白名单{C_RESET}"
elif mac in blacklist:
status = f"{ICON_BAN} {C_RED}拦截{C_RESET}" if active else f"{ICON_PAUSE}{C_YELLOW}待拦{C_RESET}"
else:
status = f"{ICON_OK} {C_GREEN}允许{C_RESET}"
online_icon = ICON_ON if dev["online"] else ICON_OFF
is_me_icon = ICON_ME if mac == my_mac else ""
name = dev["name"] if dev["name"] != "*" else f"{C_GRAY}未知设备{C_RESET}"
name = name[:18]
row_color = C_WHITE if dev["online"] else C_GRAY
print(f" {C_BOLD}{idx+1:<4}{C_RESET} {status} {row_color}{dev['ip']:<16} {mac:<18} {online_icon} {name} {is_me_icon}{C_RESET}")
# === 底部竖向操作面板 ===
print(f"\n{C_BLUE}╭─ 功能面板 {'─'*48}╮{C_RESET}")
print(f"{C_BLUE}│{C_RESET} {C_BOLD}{C_CYAN}[序号]{C_RESET} 批量切换(如1 3) {C_DARK}|{C_RESET} {C_BOLD}{C_GREEN}[S]{C_RESET} 重载引擎 {C_BLUE}│{C_RESET}")
print(f"{C_BLUE}│{C_RESET} {C_BOLD}{C_YELLOW}[R]{C_RESET} 刷新列表 {C_DARK}|{C_RESET} {C_BOLD}{C_RED}[P]{C_RESET} 暂停防护 {C_BLUE}│{C_RESET}")
print(f"{C_BLUE}│{C_RESET} {C_BOLD}{C_BLUE}[L]{C_RESET} 实时日志 {C_DARK}|{C_RESET} {C_BOLD}{C_RED}[U]{C_RESET} 卸载脚本 {C_BLUE}│{C_RESET}")
print(f"{C_BLUE}│{C_RESET} {C_BOLD}{C_CYAN}[W]{C_RESET} 编辑白名单 {C_DARK}|{C_RESET} {C_BOLD}{C_CYAN}[B]{C_RESET} 编辑黑名单 {C_BLUE}│{C_RESET}")
print(f"{C_BLUE}╰{'─'*62}╯{C_RESET}")
print(f"{C_GRAY} 退出请按 [0]{C_RESET}")
choice = input(f"\n👉 {C_BOLD}请输入指令:{C_RESET} ").strip().lower()
# === 指令处理 ===
if choice == "0":
sys.exit(0)
elif choice == "l":
sys.exit(99)
elif choice == "u":
sys.exit(88)
elif choice == "w":
sys.exit(77) # manager.sh 负责 nano 编辑
elif choice == "b":
sys.exit(66) # manager.sh 负责 nano 编辑
elif choice == "r":
print("正在刷新...")
time.sleep(0.1)
continue
elif choice == "s":
print("正在重载...")
apply_firewall()
time.sleep(0.3)
elif choice == "p":
print("正在暂停...")
stop_firewall()
time.sleep(0.3)
else:
# === 批量序号处理逻辑 ===
try:
indexes = [int(x) for x in choice.replace(",", " ").split()]
if not indexes:
continue
changed = False
whitelist = load_whitelist()
blacklist = load_blacklist()
for idx in indexes:
real_idx = idx - 1
if 0 <= real_idx < len(dev_list):
target = dev_list[real_idx]
mac = target["mac"]
if mac == my_mac:
print(f"{C_RED}❌ 跳过本机 (安全保护){C_RESET}")
continue
if mac in whitelist:
print(f"{C_YELLOW}⚠️ 该设备在白名单中,禁止拉黑: {mac}{C_RESET}")
continue
if mac in blacklist:
blacklist.remove(mac)
else:
blacklist.add(mac)
if active:
flush_conntrack(mac, dev_list)
changed = True
if changed:
save_blacklist(blacklist)
if active:
apply_firewall()
print(f"{C_GREEN}✅ 批量操作已执行{C_RESET}")
time.sleep(0.5)
except ValueError:
print(f"{C_YELLOW}⚠️ 无效指令,请输入序号或功能键{C_RESET}")
time.sleep(0.5)
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "boot":
apply_firewall()
else:
try:
show_menu()
except KeyboardInterrupt:
sys.exit(0)
EOF
chmod +x "$PY_FILE"
# 3. 配置服务
cat << 'EOF' > "/etc/init.d/devctrl"
#!/bin/sh /etc/rc.common
START=99
USE_PROCD=1
SCRIPT_PATH="/root/devctrl/core.py"
PYTHON_BIN="/usr/bin/python3"
start_service() { $PYTHON_BIN $SCRIPT_PATH boot; }
EOF
chmod +x "/etc/init.d/devctrl"
/etc/init.d/devctrl enable
/etc/init.d/devctrl restart >/dev/null 2>&1
# 4. 管理脚本 (修正 logread | grep 问题 + 白/黑名单编辑:强制 nano)
cat << 'EOF' > "$MGR_FILE"
#!/bin/bash
PY_CORE="/root/devctrl/core.py"
BAN_LIST="/etc/devctrl_blacklist.txt"
WHT_LIST="/etc/devctrl_whitelist.txt"
sanitize_list() {
# 清洗: 去空行/注释,转大写,去重,仅保留合法 MAC
# BusyBox awk 兼容写法
local f="$1"
[ -f "$f" ] || return 0
awk '
function ord(ch){ return index("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F !\"#$%&'\''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", ch)-1 }
{
gsub(/\r/,"",$0)
line=$0
sub(/^[ \t]+/,"",line); sub(/[ \t]+$/,"",line)
if(line=="" || substr(line,1,1)=="#") next
# 转大写
out=""
for(i=1;i<=length(line);i++){
c=substr(line,i,1)
if(c>="a"&&c<="z") c=sprintf("%c", ord(c)-32)
out=out c
}
line=out
if(line ~ /^[0-9A-F]{2}(:[0-9A-F]{2}){5}$/){
if(!seen[line]++){ print line }
}
}
' "$f" > "${f}.tmp" && mv "${f}.tmp" "$f"
}
edit_list() {
local f="$1"
local title="$2"
clear
echo -e "\033[36m=== $title ===\033[0m"
echo "说明:"
echo "1) 每行一个 MAC,例如: AA:BB:CC:DD:EE:FF"
echo "2) 支持以 # 开头的注释行"
echo "3) 保存退出后会自动清洗格式并重载引擎"
echo
touch "$f" 2>/dev/null
chmod 600 "$f" 2>/dev/null
# 方案1:强制使用 nano
if ! command -v nano >/dev/null 2>&1; then
echo -e "\033[31m未找到 nano。请安装: opkg install nano\033[0m"
read -p "按回车返回..." _
return
fi
nano "$f"
sanitize_list "$f"
# 编辑完成后重载规则
/usr/bin/python3 /root/devctrl/core.py boot >/dev/null 2>&1
echo -e "\033[32m已保存并重载。\033[0m"
read -p "按回车返回..." _
}
show_log() {
clear
echo -e "\033[36m=== 拦截日志 (按 Ctrl+C 返回) ===\033[0m"
trap 'return' SIGINT
# 修正: 去掉 BusyBox 不支持的 --line-buffered 参数
logread -f | grep "BAN_ALL" | while read line; do
echo -e "\033[31m$line\033[0m"
done
trap - SIGINT
}
do_uninstall() {
clear
read -p "确定要卸载吗? (y/n): " c
if [[ "$c" == "y" ]]; then
/etc/init.d/devctrl stop 2>/dev/null
/etc/init.d/devctrl disable 2>/dev/null
nft delete table inet devctrl_v6 2>/dev/null
rm -rf /root/devctrl /etc/init.d/devctrl /usr/bin/dev
echo -e "\033[32m已卸载。\033[0m"; exit 0
fi
}
while true; do
python3 "$PY_CORE"
c=$?
if [ $c -eq 99 ]; then show_log;
elif [ $c -eq 88 ]; then do_uninstall;
elif [ $c -eq 77 ]; then edit_list "$WHT_LIST" "编辑白名单";
elif [ $c -eq 66 ]; then edit_list "$BAN_LIST" "编辑黑名单";
elif [ $c -eq 0 ]; then clear; exit 0;
else read; fi
done
EOF
chmod +x "$MGR_FILE"
ln -sf "$MGR_FILE" "$LINK_BIN"
echo "-----------------------------------------------"
echo "✅ V9 (修正版 + 白/黑名单编辑 + 强制nano) 安装完成!"
echo "输入 dev 即可使用"
echo "白名单: /etc/devctrl_whitelist.txt"
echo "黑名单: /etc/devctrl_blacklist.txt"
echo "-----------------------------------------------"
INSTALL_EOF
bash /tmp/install_devctrl_v9_fix.sh && rm -f /tmp/install_devctrl_v9_fix.sh
🕹️ 使用指南
- 启动管理:在终端输入 dev 即可进入图形化管理界面。
- 基本操作:
- 拉黑/解封:输入设备对应的 序号(例如
2)并回车。 - 批量操作:输入多个序号并用空格隔开,例如
1 3 5,一次搞定。
- 拉黑/解封:输入设备对应的 序号(例如
- 功能按键:
- [S] 重载引擎:强制重新应用防火墙规则(通常不仅不需要,脚本自动处理,但可用于手动纠错)。
- [R] 刷新列表:重新扫描局域网设备,更新在线状态。
- [P] 暂停防护:临时允许所有人上网(例如全家聚会时),再次按 S 可恢复拦截。
- [L] 实时日志:查看被拦截设备的连接请求,看看谁在悄悄上网。
- [U] 卸载脚本:一键清理所有文件和规则,还你一个干净的系统。
⚠️ 免责声明:本脚本仅用于个人家庭网络管理,请勿用于非法用途或公共网络环境。操作前请确保您对 SSH 和 OpenWrt 有基础了解。