🐤 Sync 2026-10-03 00:12:36

This commit is contained in:
github-actions[bot] committed 2026-10-03 00:12:36 +08:00
1 parent ce3df55b24
commit cb1d978497
58 files changed
+1627 -1018

No files matched your search

@@ -102,3 +102,4 @@ config nftset 'no_proxy_dst_tcp_ports'
config nftset 'no_proxy_dst_udp_ports'
option enabled '1'
option type 'inet_service'
list elements '123'
+13
View File
@@ -0,0 +1,13 @@
#!/bin/sh
# Hotplug trigger for flowproxy to restore policy routing on interface reload
[ "$ACTION" = "ifup" ] || exit 0
enabled=$(uci -q get flowproxy.global.enabled || echo 0)
[ "$enabled" = "1" ] || exit 0
target_iface=$(uci -q get flowproxy.global.interface || echo "br-lan")
if [ "$DEVICE" = "$target_iface" ] || [ "$INTERFACE" = "$target_iface" ]; then
[ -x /etc/init.d/flowproxy ] && /etc/init.d/flowproxy add_route
fi
+135 -60
View File
@@ -4,6 +4,8 @@
START=99
STOP=10
USE_PROCD=1
EXTRA_COMMANDS="add_route"
EXTRA_HELP=" add_route Add routing rules and policy"
NAME="flowproxy"
UCI_CONFIG="flowproxy"
@@ -57,9 +59,62 @@ check_env() {
check_config() {
local proxy_server_ip_addr=$(uci -q get "$UCI_CONFIG.global.proxy_server_ip_addr")
[ -z "$proxy_server_ip_addr" ] && { log_msg error "proxy_server_ip_addr not configured"; return 1; }
if [ "$proxy_server_ip_addr" = "127.0.0.1" ] || [ "$proxy_server_ip_addr" = "0.0.0.0" ]; then
log_msg error "Invalid proxy_server_ip_addr: $proxy_server_ip_addr"; return 1;
fi
# 纵深防御:严禁将代理服务器设为本路由器自身的任一 IP,防止策略路由自指死循环导致网络崩溃
if ip -4 -o addr show 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | grep -Fxq "$proxy_server_ip_addr"; then
log_msg error "proxy_server_ip_addr ($proxy_server_ip_addr) is a local IP of this router, aborting to prevent routing loop"
return 1
fi
# 检查路由表 ID 是否为系统保留表 (0, 253, 254, 255)
local routing_table=$(uci -q get "$UCI_CONFIG.global.routing_table" || echo "888")
if [ "$routing_table" = "0" ] || [ "$routing_table" = "253" ] || [ "$routing_table" = "254" ] || [ "$routing_table" = "255" ]; then
log_msg error "routing_table ($routing_table) is a reserved system table (0, 253, 254, 255), aborting"
return 1
fi
return 0
}
probe_proxy_server() {
local proxy_ip="$1"
local proxy_dns_port="$2"
local interface="$3"
log_msg info "Probing proxy server $proxy_ip on interface $interface..."
# 1. 检查 ARP 缓存或快速唤醒以获取 MAC 地址
local mac=""
if [ -f "/proc/net/arp" ]; then
mac=$(awk -v ip="$proxy_ip" '$1 == ip { print $4; exit }' /proc/net/arp)
fi
if [ -n "$mac" ] && [ "$mac" != "00:00:00:00:00:00" ]; then
log_msg info "Proxy server is REACHABLE: IP=$proxy_ip, MAC=$mac (found in ARP cache)"
else
# 快速单发 ping 1 秒唤醒 ARP 学习
ping -c 1 -W 1 "$proxy_ip" >/dev/null 2>&1
if [ -f "/proc/net/arp" ]; then
mac=$(awk -v ip="$proxy_ip" '$1 == ip { print $4; exit }' /proc/net/arp)
fi
if [ -n "$mac" ] && [ "$mac" != "00:00:00:00:00:00" ]; then
log_msg info "Proxy server is REACHABLE: IP=$proxy_ip, MAC=$mac"
else
log_msg warn "Proxy server $proxy_ip is NOT responding to ARP yet (it may be booting or offline, traffic will still be routed via policy table)"
fi
fi
# 2. 若启用了上游 DNS 代理,在日志中记录目标地址与端口
local dns_proxy=$(uci -q get "$UCI_CONFIG.global.dns_proxy_enabled" || echo 0)
if [ "$dns_proxy" = "1" ]; then
log_msg info "Upstream DNS redirection target configured: $proxy_ip#$proxy_dns_port"
fi
}
apply_nft_rules() {
log_msg info "Generating nftables configuration..."
local conf_level=$(uci -q get "$UCI_CONFIG.global.log_level" || echo "info")
@@ -100,11 +155,17 @@ apply_nft_rules() {
}
add_route() {
check_config || return 1
local proxy_server_ip_addr=$(uci -q get "$UCI_CONFIG.global.proxy_server_ip_addr")
local interface=$(uci -q get "$UCI_CONFIG.global.interface" || echo "br-lan")
local traffic_mark=$(uci -q get "$UCI_CONFIG.global.traffic_mark" || echo "0x666")
local routing_table=$(uci -q get "$UCI_CONFIG.global.routing_table" || echo "888")
local rule_priority=$(uci -q get "$UCI_CONFIG.global.rule_priority" || echo "1000")
local interface=$(uci -q get "$UCI_CONFIG.global.interface")
interface="${interface:-br-lan}"
local traffic_mark=$(uci -q get "$UCI_CONFIG.global.traffic_mark")
traffic_mark="${traffic_mark:-0x666}"
local routing_table=$(uci -q get "$UCI_CONFIG.global.routing_table")
routing_table="${routing_table:-888}"
local rule_priority=$(uci -q get "$UCI_CONFIG.global.rule_priority")
rule_priority="${rule_priority:-1000}"
# 1. 先尝试用旧记录清理一次,防止由于配置变更导致的残留
delete_route "quiet"
@@ -127,6 +188,12 @@ add_route() {
else
log_msg error "Failed to add ip rule: $err"
fi
# 5. 禁用目标网卡的 ICMP 重定向通告,防止客户端绕行或 TCP 连接挂起
if [ -d "/proc/sys/net/ipv4/conf/$interface" ]; then
sysctl -w "net.ipv4.conf.$interface.send_redirects=0" >/dev/null 2>&1
log_msg debug "Disabled send_redirects for $interface"
fi
}
delete_route() {
@@ -143,9 +210,14 @@ delete_route() {
[ "$quiet" != "quiet" ] && rm -f "/var/run/flowproxy.mark"
fi
local curr_mark=$(uci -q get "$UCI_CONFIG.global.traffic_mark" || echo "0x666")
local curr_table=$(uci -q get "$UCI_CONFIG.global.routing_table" || echo "888")
local curr_prio=$(uci -q get "$UCI_CONFIG.global.rule_priority" || echo "1000")
local curr_mark=$(uci -q get "$UCI_CONFIG.global.traffic_mark")
curr_mark="${curr_mark:-0x666}"
local curr_table=$(uci -q get "$UCI_CONFIG.global.routing_table")
curr_table="${curr_table:-888}"
local curr_prio=$(uci -q get "$UCI_CONFIG.global.rule_priority")
curr_prio="${curr_prio:-1000}"
local curr_iface=$(uci -q get "$UCI_CONFIG.global.interface")
curr_iface="${curr_iface:-br-lan}"
[ "$quiet" != "quiet" ] && log_msg info "Cleaning up FlowProxy routes (Atomic Mode)..."
@@ -153,6 +225,10 @@ delete_route() {
safe_del_rule() {
local m="$1" t="$2" p="$3"
[ -z "$m" ] || [ -z "$t" ] && return
if [ "$t" = "0" ] || [ "$t" = "253" ] || [ "$t" = "254" ] || [ "$t" = "255" ]; then
log_msg error "Refusing to flush reserved system routing table $t"
return
fi
log_msg debug "Attempting cleanup: mark=$m, table=$t, priority=${p:-any}"
# 精准清理 (带优先级)
[ -n "$p" ] && while ip rule del fwmark "$m" lookup "$t" priority "$p" 2>/dev/null; do
@@ -171,6 +247,11 @@ delete_route() {
safe_del_rule "$old_mark" "$old_table" "$old_prio"
[ "$old_mark" != "$curr_mark" ] || [ "$old_table" != "$curr_table" ] && \
safe_del_rule "$curr_mark" "$curr_table" "$curr_prio"
# 恢复 ICMP 重定向通告
if [ -d "/proc/sys/net/ipv4/conf/$curr_iface" ]; then
sysctl -w "net.ipv4.conf.$curr_iface.send_redirects=1" >/dev/null 2>&1
fi
}
delete_nft() {
@@ -192,64 +273,46 @@ setup_dns() {
[ -z "$proxy_server_ip_addr" ] && { log_msg error "proxy_server_ip_addr not configured, cannot setup DNS"; return 1; }
log_msg info "Setting up DNS redirection to $proxy_server_ip_addr#$proxy_server_dns_port..."
log_msg info "Setting up DNS redirection to $proxy_server_ip_addr#$proxy_server_dns_port via drop-in config..."
# Persistent backup in UCI
if ! uci -q get "$UCI_CONFIG.dns_backup" >/dev/null; then
local old_noresolv=$(uci -q get dhcp.@dnsmasq[0].noresolv)
local old_dns_redirect=$(uci -q get dhcp.@dnsmasq[0].dns_redirect)
local old_servers=$(uci -q get dhcp.@dnsmasq[0].server | tr '\n' ',' | sed 's/,$//')
log_msg debug "Backing up DNS: noresolv=${old_noresolv:-empty}, redirect=${old_dns_redirect:-empty}, servers=${old_servers:-empty}"
uci set "$UCI_CONFIG.dns_backup=dns_backup"
[ -n "$old_noresolv" ] && uci set "$UCI_CONFIG.dns_backup.noresolv=$old_noresolv"
[ -n "$old_dns_redirect" ] && uci set "$UCI_CONFIG.dns_backup.dns_redirect=$old_dns_redirect"
[ -n "$old_servers" ] && uci set "$UCI_CONFIG.dns_backup.servers=$old_servers"
uci commit "$UCI_CONFIG"
mkdir -p /tmp/dnsmasq.d
cat > /tmp/dnsmasq.d/flowproxy.conf << EOF
# Generated by FlowProxy - DO NOT EDIT MANUALLY
no-resolv
server=$proxy_server_ip_addr#$proxy_server_dns_port
EOF
# 同步分发至各个可能存在的 dnsmasq 实例目录
for dir in /tmp/dnsmasq*.d; do
if [ -d "$dir" ] && [ "$dir" != "/tmp/dnsmasq.d" ]; then
cp -f /tmp/dnsmasq.d/flowproxy.conf "$dir/flowproxy.conf"
fi
done
# 历史遗留自愈:若早期版本存在残留的 dns_backup 节,静默移除避免脏数据
if uci -q get "$UCI_CONFIG.dns_backup" >/dev/null; then
uci -q delete "$UCI_CONFIG.dns_backup"
uci -q commit "$UCI_CONFIG"
fi
log_msg debug "Applying new DNS: noresolv=1, redirect=0, server=$proxy_server_ip_addr#$proxy_server_dns_port"
uci set dhcp.@dnsmasq[0].noresolv='1'
uci set dhcp.@dnsmasq[0].dns_redirect='0'
# Delete current servers and add our proxy
uci -q delete dhcp.@dnsmasq[0].server
uci add_list dhcp.@dnsmasq[0].server="$proxy_server_ip_addr#$proxy_server_dns_port"
uci commit dhcp
/etc/init.d/dnsmasq restart
log_msg info "DNS drop-in configuration applied successfully"
}
cleanup_dns() {
log_msg info "Cleaning up DNS redirection drop-in configuration..."
rm -f /tmp/dnsmasq.d/flowproxy.conf
for dir in /tmp/dnsmasq*.d; do
[ -d "$dir" ] && rm -f "$dir/flowproxy.conf"
done
# 历史遗留自愈
if uci -q get "$UCI_CONFIG.dns_backup" >/dev/null; then
log_msg info "Restoring original DNS configuration..."
local noresolv=$(uci -q get "$UCI_CONFIG.dns_backup.noresolv")
local dns_redirect=$(uci -q get "$UCI_CONFIG.dns_backup.dns_redirect")
local servers=$(uci -q get "$UCI_CONFIG.dns_backup.servers")
log_msg debug "Restoring DNS values: noresolv=${noresolv:-0}, redirect=${dns_redirect:-1}, servers=${servers:-empty}"
# Restore noresolv (default to 0 if not backed up)
[ -n "$noresolv" ] && uci set dhcp.@dnsmasq[0].noresolv="$noresolv" || uci set dhcp.@dnsmasq[0].noresolv="0"
# Restore dns_redirect (default to 1 as requested for OpenWrt typical modded builds)
[ -n "$dns_redirect" ] && uci set dhcp.@dnsmasq[0].dns_redirect="$dns_redirect" || uci set dhcp.@dnsmasq[0].dns_redirect="1"
# Restore servers
uci -q delete dhcp.@dnsmasq[0].server
if [ -n "$servers" ]; then
for s in $servers; do
[ -n "$s" ] && uci add_list dhcp.@dnsmasq[0].server="$s"
done
fi
uci commit dhcp
uci delete "$UCI_CONFIG.dns_backup"
uci commit "$UCI_CONFIG"
/etc/init.d/dnsmasq restart
uci -q delete "$UCI_CONFIG.dns_backup"
uci -q commit "$UCI_CONFIG"
fi
/etc/init.d/dnsmasq restart
}
start_service() {
@@ -265,6 +328,12 @@ start_service() {
check_env || return 1
check_config || return 1
local proxy_server_ip_addr=$(uci -q get "$UCI_CONFIG.global.proxy_server_ip_addr")
local proxy_server_dns_port=$(uci -q get "$UCI_CONFIG.global.proxy_server_dns_port" || echo 5353)
local interface=$(uci -q get "$UCI_CONFIG.global.interface" || echo "br-lan")
probe_proxy_server "$proxy_server_ip_addr" "$proxy_server_dns_port" "$interface"
apply_nft_rules || return 1
add_route
setup_dns
@@ -272,6 +341,7 @@ start_service() {
touch "$STATE_FILE"
log_msg info "FlowProxy service started successfully"
}
stop_service() {
log_msg info "Stopping FlowProxy..."
# 强制先移除标记,防止状态误报
@@ -282,6 +352,12 @@ stop_service() {
log_msg info "FlowProxy service stopped"
}
reload_service() {
log_msg info "Reloading FlowProxy..."
stop_service
start_service
}
restart_service() {
stop_service
start_service
@@ -292,8 +368,7 @@ service_triggers() {
procd_add_reload_trigger "$UCI_CONFIG"
# 监听防火墙重载事件,防止规则被 fw4 清空后无法自动恢复
procd_add_reload_trigger "firewall"
}
reload_service() {
restart
# 监听网络接口事件
local iface=$(uci -q get "$UCI_CONFIG.global.interface" || echo "br-lan")
procd_add_reload_interface_trigger "$iface"
}
@@ -44,20 +44,15 @@ gen_set_definition() {
[ -n "$uci_elems" ] && { [ -n "$elems" ] && elems="${elems}, "; elems="${elems}${uci_elems}"; }
file_path=$(uci -q get "$CONFIG.$section.file_path")
download_url=$(uci -q get "$CONFIG.$section.download_url")
if [ -n "$download_url" ] && [ -n "$file_path" ]; then
if [ ! -f "$file_path" ]; then
echo "Downloading $section data from $download_url to $file_path..." >&2
wget -q -O "$file_path" "$download_url" --timeout=10 --no-check-certificate
[ $? -ne 0 ] && { echo "Failed to download $section data" >&2; rm -f "$file_path"; }
fi
fi
if [ -n "$file_path" ] && [ -f "$file_path" ]; then
local file_elems=$(get_file_elements "$file_path")
if [ -n "$file_elems" ]; then
[ -n "$elems" ] && elems="${elems}, "
elems="${elems}${file_elems}"
if [ -n "$file_path" ]; then
if [ -f "$file_path" ]; then
local file_elems=$(get_file_elements "$file_path")
if [ -n "$file_elems" ]; then
[ -n "$elems" ] && elems="${elems}, "
elems="${elems}${file_elems}"
fi
else
log_debug "Set file $file_path not found, continuing without file elements"
fi
fi
@@ -183,15 +178,19 @@ TCP_ENABLED=$(uci -q get "$CONFIG.global.tcp_enabled" || echo "1")
UDP_ENABLED=$(uci -q get "$CONFIG.global.udp_enabled" || echo "1")
TRAFFIC_MARK=$(uci -q get "$CONFIG.global.traffic_mark" || echo "0x666")
PROXY_SERVER_IP_ADDR=$(uci -q get "$CONFIG.global.proxy_server_ip_addr")
INTERFACE=$(uci -q get "$CONFIG.global.interface")
[ -z "$INTERFACE" ] && INTERFACE="br-lan"
if [ "$TCP_ENABLED" = "1" ]; then
cat >> "$OUTPUT_FILE" << EOF
chain LAN_MARKFLOW_TCP {
type filter hook prerouting priority mangle; policy accept;
iifname != "$INTERFACE" return
fib daddr type { unspec, local, anycast, multicast } counter return
EOF
if [ -n "$PROXY_SERVER_IP_ADDR" ]; then
echo " ip saddr $PROXY_SERVER_IP_ADDR ip protocol tcp counter return" >> "$OUTPUT_FILE"
echo " ip saddr $PROXY_SERVER_IP_ADDR counter return" >> "$OUTPUT_FILE"
echo " ip daddr $PROXY_SERVER_IP_ADDR counter return" >> "$OUTPUT_FILE"
fi
for s in $SECTIONS_TCP; do process_rule "$s" "tcp" >> "$OUTPUT_FILE"; done
echo " ip protocol tcp counter meta mark set $TRAFFIC_MARK" >> "$OUTPUT_FILE"
@@ -202,10 +201,12 @@ if [ "$UDP_ENABLED" = "1" ]; then
cat >> "$OUTPUT_FILE" << EOF
chain LAN_MARKFLOW_UDP {
type filter hook prerouting priority mangle; policy accept;
iifname != "$INTERFACE" return
fib daddr type { unspec, local, anycast, multicast } counter return
EOF
if [ -n "$PROXY_SERVER_IP_ADDR" ]; then
echo " ip saddr $PROXY_SERVER_IP_ADDR ip protocol udp counter return" >> "$OUTPUT_FILE"
echo " ip saddr $PROXY_SERVER_IP_ADDR counter return" >> "$OUTPUT_FILE"
echo " ip daddr $PROXY_SERVER_IP_ADDR counter return" >> "$OUTPUT_FILE"
fi
for s in $SECTIONS_UDP; do process_rule "$s" "udp" >> "$OUTPUT_FILE"; done
echo " ip protocol udp counter meta mark set $TRAFFIC_MARK" >> "$OUTPUT_FILE"
@@ -13,11 +13,6 @@
},
"write": {
"uci": [ "flowproxy" ],
"file": {
"/usr/bin/wget": [ "exec" ],
"/usr/bin/wc": [ "exec" ],
"/usr/share/flowproxy/generate_nft.sh": [ "exec" ]
},
"ubus": {
"luci.flowproxy": [ "*" ]
}
@@ -1,6 +1,6 @@
'use strict';
import { access, readfile, writefile, stat } from 'fs';
import { access, readfile, writefile, stat, popen } from 'fs';
import { cursor } from 'uci';
import { connect } from 'ubus';
@@ -9,6 +9,7 @@ const STATE_FILE = '/var/run/flowproxy.running';
const IP_CACHE = '/tmp/flowproxy/lan_ip.cache';
const LOG_FILE = '/var/log/flowproxy.log';
const NFT_TABLE = 'flowproxy';
const DNS_DROPIN_FILE = '/tmp/dnsmasq.d/flowproxy.conf';
// 辅助函数:安全 trim
function safe_trim(str) {
@@ -18,10 +19,10 @@ function safe_trim(str) {
const methods = {
get_status: {
call: function() {
// 1. 快速探测
// 1. 快速探测标记文件
let is_running = access(STATE_FILE, 'f');
// 2. 自愈:检查内核状态并同步标记文件
// 2. 自愈:检查内核状态 (nft 链与策略路由)
let tcp_active = (system('nft list chain inet ' + NFT_TABLE + ' LAN_MARKFLOW_TCP >/dev/null 2>&1') === 0);
let udp_active = (system('nft list chain inet ' + NFT_TABLE + ' LAN_MARKFLOW_UDP >/dev/null 2>&1') === 0);
let real_running = (tcp_active || udp_active);
@@ -40,7 +41,6 @@ const methods = {
if (ubus_conn) {
let ifaces = ubus_conn.call('network.interface', 'dump');
if (type(ifaces?.interface) === 'array') {
// 优先寻找名为 'lan' 的接口,或者寻找拥有 ipv4 地址的第一个非回环接口
let fallback_ip = '';
for (let iface in ifaces.interface) {
if (iface.interface === 'lan' && iface['ipv4-address']?.[0]?.address) {
@@ -64,9 +64,8 @@ const methods = {
}
let uci_cursor = cursor();
let proxy_server_ip_addr = uci_cursor.get(UCI_CONFIG, 'global', 'proxy_server_ip_addr') || '';
let dns_running = (uci_cursor.get(UCI_CONFIG, 'dns_backup') ? 1 : 0);
let dns_running = access(DNS_DROPIN_FILE, 'f') ? 1 : 0;
return {
running: real_running ? 1 : 0,
@@ -114,6 +113,67 @@ const methods = {
}
},
get_dataset_info: {
args: { file_path: 'string' },
call: function(request) {
let file_path = request.args?.file_path || '/usr/share/flowproxy/chnroute.txt';
if (!match(file_path, /^\/(usr\/share\/flowproxy|tmp\/flowproxy)\/[a-zA-Z0-9_.-]+$/)) {
return { error: 'Invalid path' };
}
if (!access(file_path, 'r')) {
return { exists: false, lines: 0, size: 0 };
}
let s = stat(file_path);
let lines = 0;
let fd = popen(sprintf("wc -l '%s' 2>/dev/null", file_path));
if (fd) {
let out = fd.read('all');
fd.close();
if (out) {
let parts = split(trim(out), /\s+/);
lines = int(parts[0]) || 0;
}
}
return { exists: true, lines: lines, size: s ? s.size : 0 };
}
},
update_dataset: {
args: { url: 'string', file_path: 'string' },
call: function(request) {
let url = request.args?.url;
let file_path = request.args?.file_path || '/usr/share/flowproxy/chnroute.txt';
if (!url || !match(url, /^https?:\/\/[A-Za-z0-9_.\/:~%#?&=+-]+$/)) {
return { success: false, error: 'Invalid URL' };
}
if (!match(file_path, /^\/(usr\/share\/flowproxy|tmp\/flowproxy)\/[a-zA-Z0-9_.-]+$/)) {
return { success: false, error: 'Invalid target file path' };
}
let tmp_file = file_path + '.tmp';
let cmd = sprintf("wget -q -O '%s' '%s' --timeout=15 --no-check-certificate 2>&1", tmp_file, url);
let rc = system(cmd);
if (rc === 0 && access(tmp_file, 'f')) {
let s = stat(tmp_file);
if (s && s.size > 0) {
system(sprintf("mv -f '%s' '%s'", tmp_file, file_path));
let lines = 0;
let fd = popen(sprintf("wc -l '%s' 2>/dev/null", file_path));
if (fd) {
let out = fd.read('all');
fd.close();
if (out) {
let parts = split(trim(out), /\s+/);
lines = int(parts[0]) || 0;
}
}
return { success: true, lines: lines };
}
}
system(sprintf("rm -f '%s'", tmp_file));
return { success: false, error: 'Download failed or file empty' };
}
},
generate_nft_config: {
call: function() {
system('mkdir -p /tmp/flowproxy');
@@ -125,19 +185,18 @@ const methods = {
get_runtime_config: {
call: function() {
let res_file = '/tmp/flowproxy/runtime.tmp';
let dns_file = '/tmp/flowproxy/dns_runtime.tmp';
system('mkdir -p /tmp/flowproxy');
system('/usr/share/flowproxy/generate_nft.sh runtime > ' + res_file + ' 2>&1');
let nft_runtime = readfile(res_file) || "(failed)";
system('rm -f ' + res_file);
system('uci show dhcp.@dnsmasq[0] | grep -E "noresolv|dns_redirect|server" | sed -E "s/^dhcp\\.[^.]*\\.([^=]*)=(.*)/\\1: \\2/; s/\\x27//g; s/#/:/g" > ' + dns_file + ' 2>&1');
let dns_out = readfile(dns_file);
system('rm -f ' + dns_file);
let dns_runtime = "\n--- [ DNSmasq Runtime Configuration ] ---\n";
dns_runtime += (dns_out || "(no active dnsmasq overrides)");
let dns_runtime = "\n--- [ DNSmasq Drop-in Runtime Configuration ] ---\n";
let dns_content = readfile(DNS_DROPIN_FILE);
if (dns_content) {
dns_runtime += dns_content;
} else {
dns_runtime += "(no active dnsmasq drop-in overrides)";
}
return { runtime: safe_trim(nft_runtime + dns_runtime) };
}
@@ -149,7 +208,6 @@ const methods = {
try {
let ubus_conn = connect();
if (ubus_conn) {
// 使用 network.device status 以获取所有物理设备 (包括 eth0 等 bridge 成员)
let dev_status = ubus_conn.call('network.device', 'status');
if (dev_status) {
for (let name in dev_status) {