mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-26 17:32:51 +08:00
🔥 Sync 2026-10-07 17:07:39
This commit is contained in:
1 parent
007d8fe1b0
commit
ecb79884af
23 files changed
+1659
-78
No files matched your search
@@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-honk
|
||||
PKG_VERSION:=1.1.4
|
||||
PKG_RELEASE:=15
|
||||
PKG_RELEASE:=16
|
||||
|
||||
LUCI_TITLE:=LuCI support for honk
|
||||
LUCI_PKGARCH:=all
|
||||
|
||||
@@ -91,9 +91,8 @@ function strip_dae_comments(content) {
|
||||
}
|
||||
|
||||
function parse_native_api(clean_content) {
|
||||
let api_m = match(clean_content, /native_api\s*\{([^}]+)\}/);
|
||||
if (!api_m) return null;
|
||||
let block = api_m[1];
|
||||
let block = extract_bracket_block(clean_content, /native_api\s*\{/);
|
||||
if (!block) return null;
|
||||
|
||||
let listen_m = match(block, /listen\s*:\s*['"]?([^'" \t\r\n]+)['"]?/);
|
||||
let ui_m = match(block, /ui\s*:\s*['"]?([^'" \t\r\n]+)['"]?/);
|
||||
@@ -167,36 +166,71 @@ function get_api_config() {
|
||||
};
|
||||
}
|
||||
|
||||
function remove_bracket_block(content, header_regex) {
|
||||
function find_bracket_block(content, header_regex) {
|
||||
let lines = split(content, "\n");
|
||||
let new_lines = [];
|
||||
let start_idx = -1;
|
||||
let end_idx = -1;
|
||||
let in_block = false;
|
||||
let depth = 0;
|
||||
let in_single = false;
|
||||
let in_double = false;
|
||||
|
||||
for (let idx, line in lines) {
|
||||
let trimmed = trim(line);
|
||||
if (!in_block && match(trimmed, header_regex)) {
|
||||
in_block = true;
|
||||
let open_cnt = length(split(trimmed, "{")) - 1;
|
||||
let close_cnt = length(split(trimmed, "}")) - 1;
|
||||
depth = open_cnt - close_cnt;
|
||||
if (open_cnt > 0 && depth <= 0) {
|
||||
in_block = false;
|
||||
}
|
||||
continue;
|
||||
start_idx = idx;
|
||||
}
|
||||
|
||||
if (in_block) {
|
||||
let open_cnt = length(split(trimmed, "{")) - 1;
|
||||
let close_cnt = length(split(trimmed, "}")) - 1;
|
||||
depth += (open_cnt - close_cnt);
|
||||
if (depth <= 0) {
|
||||
in_block = false;
|
||||
let len = length(line);
|
||||
for (let i = 0; i < len; i++) {
|
||||
let c = substr(line, i, 1);
|
||||
let next_c = (i + 1 < len) ? substr(line, i + 1, 1) : "";
|
||||
if (c == "'" && !in_double) {
|
||||
in_single = !in_single;
|
||||
} else if (c == '"' && !in_single) {
|
||||
in_double = !in_double;
|
||||
} else if (!in_single && !in_double) {
|
||||
if (c == '#' || (c == '/' && next_c == '/')) {
|
||||
break;
|
||||
} else if (c == '{') {
|
||||
depth++;
|
||||
} else if (c == '}') {
|
||||
depth--;
|
||||
if (depth <= 0) {
|
||||
end_idx = idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
if (end_idx != -1) break;
|
||||
}
|
||||
}
|
||||
return { start: start_idx, end: end_idx };
|
||||
}
|
||||
|
||||
push(new_lines, line);
|
||||
function extract_bracket_block(content, header_regex) {
|
||||
let bounds = find_bracket_block(content, header_regex);
|
||||
if (bounds.start == -1 || bounds.end == -1) return null;
|
||||
let lines = split(content, "\n");
|
||||
let block_lines = [];
|
||||
for (let i = bounds.start; i <= bounds.end; i++) {
|
||||
push(block_lines, lines[i]);
|
||||
}
|
||||
return join("\n", block_lines);
|
||||
}
|
||||
|
||||
function remove_bracket_block(content, header_regex) {
|
||||
let bounds = find_bracket_block(content, header_regex);
|
||||
if (bounds.start == -1 || bounds.end == -1) return content;
|
||||
let lines = split(content, "\n");
|
||||
let new_lines = [];
|
||||
for (let i = 0; i < length(lines); i++) {
|
||||
if (i < bounds.start || i > bounds.end) {
|
||||
push(new_lines, lines[i]);
|
||||
}
|
||||
}
|
||||
return join("\n", new_lines);
|
||||
}
|
||||
@@ -221,9 +255,12 @@ function clean_legacy_api_from_config(config_file) {
|
||||
if (!main_content) return;
|
||||
let cleaned_main = remove_bracket_block(main_content, /^[#\/]*\s*clash_api\s*\{/);
|
||||
cleaned_main = remove_bracket_block(cleaned_main, /^[#\/]*\s*native_api\s*\{/);
|
||||
let exp_m = match(cleaned_main, /experimental\s*\{([^}]+)\}/);
|
||||
if (exp_m) {
|
||||
let inner_clean = strip_dae_comments(exp_m[1]);
|
||||
|
||||
let exp_block = extract_bracket_block(cleaned_main, /experimental\s*\{/);
|
||||
if (exp_block) {
|
||||
let inner_clean = strip_dae_comments(exp_block);
|
||||
inner_clean = replace(inner_clean, /experimental\s*\{/, "");
|
||||
inner_clean = replace(inner_clean, /\}[ \t\r\n]*$/, "");
|
||||
if (!match(inner_clean, /\S/)) {
|
||||
cleaned_main = remove_bracket_block(cleaned_main, /^[#\/]*\s*experimental\s*\{/);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-open-box
|
||||
PKG_VERSION:=0.1.292
|
||||
PKG_RELEASE:=15
|
||||
PKG_VERSION:=0.1.293
|
||||
PKG_RELEASE:=16
|
||||
|
||||
PKG_MAINTAINER:=liandu2024
|
||||
PKG_LICENSE:=GPL-3.0-or-later
|
||||
|
||||
@@ -4,7 +4,7 @@ LUCI_TITLE:=luci-app-ssr-plus
|
||||
LUCI_PKGARCH:=all
|
||||
PKG_NAME:=luci-app-ssr-plus
|
||||
PKG_VERSION:=196
|
||||
PKG_RELEASE:=84
|
||||
PKG_RELEASE:=85
|
||||
|
||||
PKG_CONFIG_DEPENDS:= \
|
||||
CONFIG_PACKAGE_$(PKG_NAME)_Iptables_Transparent_Proxy \
|
||||
|
||||
@@ -136,6 +136,9 @@ o:value("https://ispip.clang.cn/all_cn_cidr.txt", translate("Clang.CN.CIDR"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/gaoyifan/china-operator-ip@ip-lists/china.txt", translate("china-operator-ip"))
|
||||
o.default = "https://ispip.clang.cn/all_cn.txt"
|
||||
|
||||
o = s:option(Value, "chnroute6_url", translate("China IPv6 list update URL"))
|
||||
o.default = "https://raw.githubusercontent.com/1715173329/IPCIDR-CHINA/master/ipv6.txt"
|
||||
|
||||
o = s:option(Flag, "apple_optimization", translate("Apple domains optimization"), translate("For Apple domains equipped with Chinese mainland CDN, always responsive to Chinese CDN IP addresses"))
|
||||
o.rmempty = false
|
||||
o.default = "1"
|
||||
|
||||
@@ -108,6 +108,17 @@ o:value("router", translate("IP Route Mode"))
|
||||
o:value("all", translate("Global Mode"))
|
||||
o.default = gfw
|
||||
|
||||
o = s:option(Flag, "ipv6_support", translate("IPv6 Traffic Proxy"))
|
||||
o.description = translate("Proxy IPv6 traffic using the selected running mode (requires nftables). Allow AAAA replies while enabled and restore the DNS filter when stopped.")
|
||||
o.default = "0"
|
||||
o.rmempty = false
|
||||
function o.validate(self, value, section)
|
||||
if value == "1" and not is_finded("nft") then
|
||||
return nil, translate("IPv6 proxy requires nftables.")
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
o = s:option(ListValue, "dports", translate("Proxy Ports"))
|
||||
o:value("1", translate("All Ports"))
|
||||
o:value("2", translate("Only Common Ports"))
|
||||
@@ -161,12 +172,16 @@ o.description = translate("Custom DNS Server format as tcp://IP:PORT or tls://DO
|
||||
o.default = "tcp://8.8.4.4:53,tcp://8.8.8.8:53"
|
||||
|
||||
o = s:option(Flag, "filter_aaaa", translate("Disable IPv6 for Overseas FQDN"))
|
||||
o:depends("pdnsd_enable", "1")
|
||||
o:depends("pdnsd_enable", "4")
|
||||
o:depends("pdnsd_enable", "7")
|
||||
o:depends({pdnsd_enable = "1", ipv6_support = false})
|
||||
o:depends({pdnsd_enable = "4", ipv6_support = false})
|
||||
o:depends({pdnsd_enable = "7", ipv6_support = false})
|
||||
o.rmempty = false
|
||||
o.default = "1"
|
||||
|
||||
-- Dependency hiding must not delete the saved IPv4-mode preference.
|
||||
function o.remove(self, section)
|
||||
end
|
||||
|
||||
if is_finded("chinadns-ng") then
|
||||
o = s:option(Value, "chinadns_ng_tunnel_forward", translate("Anti-pollution DNS Server"))
|
||||
o:value("8.8.4.4:53", translate("Google Public DNS (8.8.4.4)"))
|
||||
|
||||
@@ -21,10 +21,10 @@ o.description = translate("Listen only on the given interface or, if unspecified
|
||||
s:tab("wan_ac", translate("WAN IP AC"))
|
||||
|
||||
o = s:taboption("wan_ac", DynamicList, "wan_bp_ips", translate("WAN White List IP"))
|
||||
o.datatype = "ip4addr"
|
||||
o.datatype = "ipaddr"
|
||||
|
||||
o = s:taboption("wan_ac", DynamicList, "wan_fw_ips", translate("WAN Force Proxy IP"))
|
||||
o.datatype = "ip4addr"
|
||||
o.datatype = "ipaddr"
|
||||
|
||||
-- Part of LAN
|
||||
s:tab("lan_ac", translate("LAN IP AC"))
|
||||
@@ -37,7 +37,7 @@ o.rmempty = false
|
||||
|
||||
o = s:taboption("lan_ac", DynamicList, "lan_ac_ips", translate("LAN Host List"))
|
||||
o.datatype = "ipaddr"
|
||||
luci.ip.neighbors({family = 4}, function(entry)
|
||||
luci.ip.neighbors({}, function(entry)
|
||||
if entry.reachable then
|
||||
o:value(entry.dest:string())
|
||||
end
|
||||
@@ -47,7 +47,7 @@ o:depends("lan_ac_mode", "b")
|
||||
|
||||
o = s:taboption("lan_ac", DynamicList, "lan_bp_ips", translate("LAN Bypassed Host List"))
|
||||
o.datatype = "ipaddr"
|
||||
luci.ip.neighbors({family = 4}, function(entry)
|
||||
luci.ip.neighbors({}, function(entry)
|
||||
if entry.reachable then
|
||||
o:value(entry.dest:string())
|
||||
end
|
||||
@@ -55,7 +55,7 @@ end)
|
||||
|
||||
o = s:taboption("lan_ac", DynamicList, "lan_fp_ips", translate("LAN Force Proxy Host List"))
|
||||
o.datatype = "ipaddr"
|
||||
luci.ip.neighbors({family = 4}, function(entry)
|
||||
luci.ip.neighbors({}, function(entry)
|
||||
if entry.reachable then
|
||||
o:value(entry.dest:string())
|
||||
end
|
||||
@@ -63,7 +63,7 @@ end)
|
||||
|
||||
o = s:taboption("lan_ac", DynamicList, "lan_gm_ips", translate("Game Mode Host List"))
|
||||
o.datatype = "ipaddr"
|
||||
luci.ip.neighbors({family = 4}, function(entry)
|
||||
luci.ip.neighbors({}, function(entry)
|
||||
if entry.reachable then
|
||||
o:value(entry.dest:string())
|
||||
end
|
||||
|
||||
@@ -266,6 +266,11 @@ s.rawhtml = true
|
||||
s.template = "shadowsocksr/refresh"
|
||||
s.value = ip_count .. " " .. translate("Records")
|
||||
|
||||
s = m:field(DummyValue, "ip6_data", translate("China IPv6 Data"))
|
||||
s.rawhtml = true
|
||||
s.template = "shadowsocksr/refresh"
|
||||
s.value = (tonumber(luci.sys.exec("wc -l < /etc/ssrplus/china6_ssr.txt 2>/dev/null")) or 0) .. " " .. translate("Records")
|
||||
|
||||
if uci:get_first("shadowsocksr", 'global', 'apple_optimization', '0') ~= '0' then
|
||||
s = m:field(DummyValue, "apple_data", translate("Apple Domains Data"))
|
||||
s.rawhtml = true
|
||||
|
||||
@@ -3870,3 +3870,18 @@ msgstr "空闲会话超时(秒)"
|
||||
|
||||
msgid "Minimum idle sessions"
|
||||
msgstr "最小空闲会话数"
|
||||
|
||||
msgid "IPv6 Traffic Proxy"
|
||||
msgstr "IPv6 流量代理"
|
||||
|
||||
msgid "Proxy IPv6 traffic using the selected running mode (requires nftables). Allow AAAA replies while enabled and restore the DNS filter when stopped."
|
||||
msgstr "按照所选运行模式代理 IPv6 流量(需要 nftables)。启用时允许 AAAA 解析,停止后恢复原有 DNS 过滤设置。"
|
||||
|
||||
msgid "IPv6 proxy requires nftables."
|
||||
msgstr "IPv6 代理需要 nftables。"
|
||||
|
||||
msgid "China IPv6 list update URL"
|
||||
msgstr "国内 IPv6 地址库更新地址"
|
||||
|
||||
msgid "China IPv6 Data"
|
||||
msgstr "国内 IPv6 地址库"
|
||||
@@ -127,7 +127,39 @@ uci_get_by_cfgid() {
|
||||
echo "${ret:=$3}"
|
||||
}
|
||||
|
||||
ipv6_enabled() {
|
||||
[ "$(uci_get_by_type global ipv6_support 0)" = "1" ] && [ "$USE_TABLES" = "nftables" ]
|
||||
}
|
||||
|
||||
ipt2socks_listen_args() {
|
||||
if ipv6_enabled; then
|
||||
echo "-b 0.0.0.0 -B ::"
|
||||
else
|
||||
echo "-b 0.0.0.0 -4"
|
||||
fi
|
||||
}
|
||||
|
||||
# Restore the user's global AAAA filter when IPv6 proxying is stopped.
|
||||
set_ipv6_dns_filter() {
|
||||
local saved
|
||||
saved="$(uci -q get 'dhcp.@dnsmasq[0]._ssrp_filter_aaaa')"
|
||||
if [ "$1" = "start" ] && ipv6_enabled; then
|
||||
if [ "$(uci -q get 'dhcp.@dnsmasq[0].filter_aaaa')" = "1" ]; then
|
||||
uci set 'dhcp.@dnsmasq[0]._ssrp_filter_aaaa=1'
|
||||
uci set 'dhcp.@dnsmasq[0].filter_aaaa=0'
|
||||
uci commit dhcp
|
||||
fi
|
||||
elif [ -n "$saved" ]; then
|
||||
if [ "$(uci -q get 'dhcp.@dnsmasq[0].filter_aaaa')" = "0" ]; then
|
||||
uci set "dhcp.@dnsmasq[0].filter_aaaa=$saved"
|
||||
fi
|
||||
uci delete 'dhcp.@dnsmasq[0]._ssrp_filter_aaaa'
|
||||
uci commit dhcp
|
||||
fi
|
||||
}
|
||||
|
||||
get_filter_aaaa() {
|
||||
ipv6_enabled && { echo 0; return; }
|
||||
local ret
|
||||
ret="$(uci get "$NAME.@global[0].filter_aaaa" 2>/dev/null)"
|
||||
[ -z "$ret" ] && ret="$(uci get "$NAME.@global[0].mosdns_ipv6" 2>/dev/null)"
|
||||
@@ -636,7 +668,8 @@ prepare_clash_runtime_config() {
|
||||
external-controller: 127.0.0.1:$CLASH_API_PORT
|
||||
secret: $clash_secret
|
||||
allow-lan: true
|
||||
bind-address: 0.0.0.0
|
||||
bind-address: "$(ipv6_enabled && echo '*' || echo '0.0.0.0')"
|
||||
ipv6: $(ipv6_enabled && echo true || echo false)
|
||||
routing-mark: $CLASH_OUTBOUND_MARK
|
||||
profile:
|
||||
store-selected: true
|
||||
@@ -780,7 +813,12 @@ get_host_ip() {
|
||||
ip="$host"
|
||||
elif ! echo "$host" | grep -q ':'; then
|
||||
ip=$(resolveip -4 -t 3 "$host" | awk 'NR==1{print}')
|
||||
if [ -z "$ip" ] && ipv6_enabled; then
|
||||
ip=$(resolveip -6 -t 3 "$host" | awk 'NR==1{print}')
|
||||
fi
|
||||
[ -z "$ip" ] && ip=$(curl -sSL "http://119.29.29.29/d?dn=$host" | awk -F ';' '{print $1}')
|
||||
elif ipv6_enabled; then
|
||||
ip="$host"
|
||||
fi
|
||||
[ -z "$ip" ] || uci_set_by_name "$1" ip "$ip"
|
||||
[ -n "$ip" ] || ip=""
|
||||
@@ -1000,7 +1038,7 @@ start_socks5_udp_with_ipt2socks() {
|
||||
-k "$(uci_get_by_name "$sid" password)"
|
||||
fi
|
||||
for _i in $(seq 1 "$threads"); do
|
||||
ln_start_bin "$ipt2socks_bin" ipt2socks -U -r -b 0.0.0.0 -4 \
|
||||
ln_start_bin "$ipt2socks_bin" ipt2socks -U -r $(ipt2socks_listen_args) \
|
||||
-s "$server_ip" \
|
||||
-p "$(uci_get_by_name "$sid" server_port)" \
|
||||
-l "$local_port" "$@"
|
||||
@@ -1032,7 +1070,7 @@ start_socks5_redir_with_ipt2socks() {
|
||||
fi
|
||||
for _i in $(seq 1 "$threads"); do
|
||||
ln_start_bin "$ipt2socks_bin" ipt2socks \
|
||||
-T -R -r -b 0.0.0.0 -4 \
|
||||
-T -R -r $(ipt2socks_listen_args) \
|
||||
-s "$server_ip" \
|
||||
-p "$(uci_get_by_name "$sid" server_port)" \
|
||||
-l "$local_port" "$@"
|
||||
@@ -1224,6 +1262,12 @@ normalize_xray_protocol_nodes() {
|
||||
}
|
||||
|
||||
add_dns_into_ipset() {
|
||||
if [ "$USE_TABLES" = "nftables" ]; then
|
||||
# GFW mode must proxy the resolver before any domain can populate the set.
|
||||
# Other nft modes already proxy destinations outside the bypass sets.
|
||||
[ "$1" = "gfw" ] && nft add element inet ss_spec gfwlist "{ ${2%:*} }" 2>/dev/null
|
||||
return 0
|
||||
fi
|
||||
case "$1" in
|
||||
gfw) ipset add gfwlist "${2%:*}" 2>/dev/null ;;
|
||||
*) ipset add ss_spec_wan_ac "${2%:*}" nomatch 2>/dev/null ;;
|
||||
@@ -1281,10 +1325,8 @@ start_dns() {
|
||||
if [ "$builtin_dns_enabled" = "1" ]; then
|
||||
pdnsd_enable_flag=7
|
||||
elif [ "$ssrplus_dns" != "0" ]; then
|
||||
if [ "$USE_TABLES" = "iptables" ]; then
|
||||
if [ -n "$dnsserver" ]; then
|
||||
add_dns_into_ipset "$run_mode" "$dnsserver"
|
||||
fi
|
||||
if [ -n "$dnsserver" ]; then
|
||||
add_dns_into_ipset "$run_mode" "$dnsserver"
|
||||
fi
|
||||
case "$ssrplus_dns" in
|
||||
1)
|
||||
@@ -1297,9 +1339,7 @@ start_dns() {
|
||||
output=$(for i in $(echo "$mosdns_dnsserver" | sed "s/,/ /g"); do
|
||||
dnsserver="${i%:*}"
|
||||
dnsserver="${i##*/}"
|
||||
if [ "$USE_TABLES" = "iptables" ]; then
|
||||
add_dns_into_ipset "$run_mode" "$dnsserver"
|
||||
fi
|
||||
add_dns_into_ipset "$run_mode" "$dnsserver"
|
||||
echo " - addr: $i"
|
||||
echo " enable_pipeline: true"
|
||||
done)
|
||||
@@ -1783,7 +1823,7 @@ start_udp() {
|
||||
trojan) #client
|
||||
gen_config_file "$UDP_RELAY_SERVER" "$type" 2 "$tmp_udp_local_port"
|
||||
ln_start_bin "$(first_type trojan)" "$type" --config "$udp_config_file"
|
||||
ln_start_bin "$(first_type ipt2socks)" ipt2socks -U -b 0.0.0.0 -4 -s 127.0.0.1 -p "$tmp_udp_local_port" -l "$tmp_udp_port"
|
||||
ln_start_bin "$(first_type ipt2socks)" ipt2socks -U $(ipt2socks_listen_args) -s 127.0.0.1 -p "$tmp_udp_local_port" -l "$tmp_udp_port"
|
||||
echolog "UDP TPROXY Relay:$($(first_type trojan) --version 2>&1 | head -1) Started!"
|
||||
;;
|
||||
naiveproxy)
|
||||
@@ -2994,6 +3034,11 @@ start() {
|
||||
mkdir -p /var/run /var/lock /var/log "$DNSMASQ_CONF_DIR" "$TMP_BIN_PATH" "$TMP_DNSMASQ_PATH" "$CLASH_CONFIG_DIR"
|
||||
echo "conf-dir=${TMP_DNSMASQ_PATH}" >"$DNSMASQ_CONF_DIR/dnsmasq-ssrplus.conf"
|
||||
check_run_environment
|
||||
if [ "$(uci_get_by_type global ipv6_support 0)" = "1" ] && [ "$USE_TABLES" != "nftables" ]; then
|
||||
echolog "ERROR: IPv6 透明代理需要 nftables 后端。"
|
||||
unset_lock
|
||||
return 1
|
||||
fi
|
||||
normalize_run_mode >/dev/null
|
||||
normalize_xray_protocol_nodes
|
||||
|
||||
@@ -3003,8 +3048,18 @@ start() {
|
||||
Start_Run
|
||||
start_http_proxy
|
||||
start_xhttp_addr
|
||||
start_rules
|
||||
if ! start_rules; then
|
||||
echolog "ERROR: 无法安装透明代理规则,启动中止。"
|
||||
unset_lock
|
||||
stop
|
||||
return 1
|
||||
fi
|
||||
set_ipv6_dns_filter start
|
||||
start_dns
|
||||
# Include resolver bootstrap addresses in the firewall reload snapshot.
|
||||
if [ "$USE_TABLES" = "nftables" ]; then
|
||||
/usr/bin/ssr-rules -P >/dev/null 2>&1
|
||||
fi
|
||||
# Restore ipsets after rules creation
|
||||
if [ "$USE_TABLES" = "iptables" ]; then
|
||||
for setname in gfwlist china blacklist whitelist; do
|
||||
@@ -3186,6 +3241,7 @@ stop() {
|
||||
uci -q del "dhcp.@dnsmasq[0]._unused_ssrp_changed"
|
||||
uci -q commit "dhcp"
|
||||
fi
|
||||
set_ipv6_dns_filter stop
|
||||
if [ -f "$DNSMASQ_CONF_DIR/dnsmasq-ssrplus.conf" ]; then
|
||||
rm -rf "$DNSMASQ_CONF_DIR/dnsmasq-ssrplus.conf" \
|
||||
"$TMP_DNSMASQ_PATH" \
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -8,6 +8,7 @@
|
||||
#
|
||||
|
||||
. $IPKG_INSTROOT/etc/init.d/shadowsocksr
|
||||
. $IPKG_INSTROOT/usr/share/shadowsocksr/ipv6.sh
|
||||
|
||||
# Detect firewall version and set appropriate tools
|
||||
detect_firewall() {
|
||||
@@ -246,6 +247,7 @@ flush_r() {
|
||||
}
|
||||
|
||||
flush_nftables() {
|
||||
ipv6_policy_cleanup
|
||||
# Delete inet ss_spec table
|
||||
if $NFT list table inet ss_spec >/dev/null 2>&1; then
|
||||
# Delete all chains
|
||||
@@ -618,6 +620,7 @@ ac_rule_nft() {
|
||||
$NFT add chain inet ss_spec ss_spec_prerouting '{ type nat hook prerouting priority 0; policy accept; }'
|
||||
fi
|
||||
$NFT flush chain inet ss_spec ss_spec_prerouting 2>/dev/null
|
||||
$NFT add rule inet ss_spec ss_spec_prerouting meta nfproto ipv6 return
|
||||
|
||||
# Exclude special local addresses
|
||||
if $NFT list chain inet ss_spec ss_spec_prerouting >/dev/null 2>&1; then
|
||||
@@ -695,6 +698,7 @@ ac_rule_nft() {
|
||||
$NFT add chain inet ss_spec ss_spec_output '{ type nat hook output priority 0; policy accept; }'
|
||||
fi
|
||||
$NFT flush chain inet ss_spec ss_spec_output 2>/dev/null
|
||||
$NFT add rule inet ss_spec ss_spec_output meta nfproto ipv6 return
|
||||
$NFT add rule inet ss_spec ss_spec_output meta mark 255 return 2>/dev/null
|
||||
|
||||
# Exclude special local addresses
|
||||
@@ -720,6 +724,7 @@ ac_rule_nft() {
|
||||
$NFT add chain inet ss_spec ss_spec_output '{ type nat hook output priority 0; policy accept; }'
|
||||
fi
|
||||
$NFT flush chain inet ss_spec ss_spec_output 2>/dev/null
|
||||
$NFT add rule inet ss_spec ss_spec_output meta nfproto ipv6 return
|
||||
$NFT add rule inet ss_spec ss_spec_output meta mark 255 return 2>/dev/null
|
||||
|
||||
# Exclude special local addresses
|
||||
@@ -1309,7 +1314,9 @@ persist_nftables_rules() {
|
||||
EOF
|
||||
|
||||
echo "# Auto-updated: $(date)" >> "$NFTABLES_RULES_FILE"
|
||||
echo "# Runmode: ${RUNMODE:-router}" >> "$NFTABLES_RULES_FILE"
|
||||
echo "# IPv6 schema: 1" >> "$NFTABLES_RULES_FILE"
|
||||
echo "# IPv6 support: $(uci_get_by_type global ipv6_support 0)" >> "$NFTABLES_RULES_FILE"
|
||||
echo "# Runmode: ${RUNMODE:-$(normalize_run_mode)}" >> "$NFTABLES_RULES_FILE"
|
||||
echo "# Server: $server, Port: $local_port" >> "$NFTABLES_RULES_FILE"
|
||||
append_nftables_comment "WAN_BP_IP" "$WAN_BP_IP"
|
||||
append_nftables_comment "LAN_AC_IP" "$LAN_AC_IP"
|
||||
@@ -1464,6 +1471,13 @@ restore_from_persistence() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
if grep -q 'set gfwlist6 {' "$NFTABLES_RULES_FILE" ||
|
||||
! grep -q '^# IPv6 schema: 1$' "$NFTABLES_RULES_FILE" ||
|
||||
! grep -q "^# IPv6 support: $(uci_get_by_type global ipv6_support 0)$" "$NFTABLES_RULES_FILE" || ipv6_enabled; then
|
||||
loger 6 "Rebuilding IPv6 rules and policy routes"
|
||||
return 1
|
||||
fi
|
||||
|
||||
loger 6 "Restoring rules from persistence file"
|
||||
|
||||
# Cleanup existing rules
|
||||
@@ -1471,6 +1485,10 @@ restore_from_persistence() {
|
||||
|
||||
# Restore rules from file
|
||||
if $NFT -f "$NFTABLES_RULES_FILE" 2>/dev/null; then
|
||||
if $NFT list chain ip ss_spec_mangle ss_spec_tproxy >/dev/null 2>&1; then
|
||||
ip rule add fwmark "$FWMARK" table 999 priority 999 2>/dev/null
|
||||
ip route replace local 0.0.0.0/0 dev lo table 999
|
||||
fi
|
||||
loger 5 "Rules restored successfully from persistence file"
|
||||
return 0
|
||||
else
|
||||
@@ -1937,7 +1955,7 @@ if [ -n "$local_port" ]; then
|
||||
# STEP 7: FORCE_RECREATE priority → must rebuild rules
|
||||
if [ "$FORCE_RECREATE" = "1" ]; then
|
||||
loger 5 "Forced regeneration of NFTables rules"
|
||||
if flush_r && ipset_r && fw_rule && ac_rule && tp_rule && gen_include; then
|
||||
if flush_r && ipset_r && fw_rule && ac_rule && tp_rule && ipv6_rules_apply && gen_include; then
|
||||
loger 5 "NFT rules applied successfully (forced rebuild)"
|
||||
persist_nftables_rules
|
||||
[ "$ENABLE_AUTO_UPDATE" = "1" ] && start_auto_update_daemon
|
||||
@@ -1963,7 +1981,7 @@ if [ -n "$local_port" ]; then
|
||||
fi
|
||||
|
||||
# STEP 9: Persistence doesn't exist or restore failed → generate new rules
|
||||
if flush_r && ipset_r && fw_rule && ac_rule && tp_rule && gen_include; then
|
||||
if flush_r && ipset_r && fw_rule && ac_rule && tp_rule && ipv6_rules_apply && gen_include; then
|
||||
loger 5 "NFTables rules applied successfully"
|
||||
persist_nftables_rules
|
||||
[ "$ENABLE_AUTO_UPDATE" = "1" ] && start_auto_update_daemon
|
||||
@@ -1974,7 +1992,7 @@ if [ -n "$local_port" ]; then
|
||||
fi
|
||||
else
|
||||
# iptables
|
||||
if flush_r && fw_rule && ipset_r && ac_rule && tp_rule && gen_include; then
|
||||
if flush_r && fw_rule && ipset_r && ac_rule && tp_rule && ipv6_rules_apply && gen_include; then
|
||||
loger 5 "iptables rules applied successfully"
|
||||
exit 0
|
||||
else
|
||||
|
||||
@@ -8,6 +8,18 @@ check_run_environment
|
||||
china_ip="${1:-${china_ip:-/etc/ssrplus/china_ssr.txt}}"
|
||||
[ -f "$china_ip" ] || exit 1
|
||||
|
||||
if [ "$2" = "6" ]; then
|
||||
[ "$USE_TABLES" = "nftables" ] || exit 0
|
||||
nft list set inet ss_spec china6 >/dev/null 2>&1 || exit 0
|
||||
{
|
||||
echo 'flush set inet ss_spec china6'
|
||||
echo 'add element inet ss_spec china6 {'
|
||||
sed -e 's/#.*//' -e '/^[[:space:]]*$/d' -e 's/$/,/' "$china_ip"
|
||||
echo '}'
|
||||
} | nft -f -
|
||||
exit $?
|
||||
fi
|
||||
|
||||
case "$USE_TABLES" in
|
||||
nftables)
|
||||
skip_inet="${SKIP_INET:-0}"
|
||||
|
||||
@@ -419,7 +419,12 @@ local function get_server_field(sid, option, default)
|
||||
return value
|
||||
end
|
||||
|
||||
local function ipv6_enabled()
|
||||
return uci:get_first("shadowsocksr", "global", "ipv6_support", "0") == "1"
|
||||
end
|
||||
|
||||
local function get_filter_aaaa()
|
||||
if ipv6_enabled() then return "0" end
|
||||
local value = uci:get_first("shadowsocksr", "global", "filter_aaaa", "1")
|
||||
if value == nil or value == "" then
|
||||
value = uci:get_first("shadowsocksr", "global", "mosdns_ipv6", "1")
|
||||
@@ -485,7 +490,8 @@ local function build_tuic_runtime_doc(sid, local_port, socks_port, mode)
|
||||
|
||||
local doc = {
|
||||
["allow-lan"] = true,
|
||||
["bind-address"] = "0.0.0.0",
|
||||
["bind-address"] = ipv6_enabled() and "*" or "0.0.0.0",
|
||||
ipv6 = ipv6_enabled(),
|
||||
mode = "rule",
|
||||
["log-level"] = "silent",
|
||||
["find-process-mode"] = "off",
|
||||
@@ -950,7 +956,8 @@ local function build_single_proxy_runtime_doc(proxy, local_port, socks_port, mod
|
||||
local socks_listen = tonumber(socks_port)
|
||||
local doc = {
|
||||
["allow-lan"] = true,
|
||||
["bind-address"] = "0.0.0.0",
|
||||
["bind-address"] = ipv6_enabled() and "*" or "0.0.0.0",
|
||||
ipv6 = ipv6_enabled(),
|
||||
mode = "rule",
|
||||
["log-level"] = "silent",
|
||||
["find-process-mode"] = "off",
|
||||
@@ -1242,7 +1249,8 @@ local function build_shadowsocks_runtime_doc(sid, local_port, socks_port, mode)
|
||||
|
||||
local doc = {
|
||||
["allow-lan"] = true,
|
||||
["bind-address"] = "0.0.0.0",
|
||||
["bind-address"] = ipv6_enabled() and "*" or "0.0.0.0",
|
||||
ipv6 = ipv6_enabled(),
|
||||
mode = "rule",
|
||||
["log-level"] = "silent",
|
||||
["find-process-mode"] = "off",
|
||||
|
||||
@@ -60,10 +60,14 @@ local xray_fragment = ucursor:get_all("shadowsocksr", "@global_xray_fragment[0]"
|
||||
local xray_noise = ucursor:get_all("shadowsocksr", "@xray_noise_packets[0]") or {}
|
||||
local default_node_local_port = ucursor:get_first("shadowsocksr", "global", "default_node_local_port", "1234")
|
||||
local dns_mode = ucursor:get_first("shadowsocksr", "global", "pdnsd_enable", "0")
|
||||
local ipv6_support = ucursor:get_first("shadowsocksr", "global", "ipv6_support", "0") == "1"
|
||||
local redir_address = ipv6_support and "::" or "0.0.0.0"
|
||||
local redir_host = ipv6_support and "[::]" or "0.0.0.0"
|
||||
local dns_ipv4_only = ucursor:get_first("shadowsocksr", "global", "filter_aaaa")
|
||||
if not dns_ipv4_only or dns_ipv4_only == "" then
|
||||
dns_ipv4_only = ucursor:get_first("shadowsocksr", "global", "mosdns_ipv6", "1")
|
||||
end
|
||||
if ipv6_support then dns_ipv4_only = "0" end
|
||||
local builtin_dns_server = ucursor:get_first("shadowsocksr", "global", "tunnel_forward", "8.8.4.4:53")
|
||||
local outbound_settings = nil
|
||||
local xray_version = nil
|
||||
@@ -970,13 +974,13 @@ local trojan = {
|
||||
}
|
||||
local naiveproxy = {
|
||||
proxy = (server.username and server.password and server.server and server.server_port) and "https://" .. server.username .. ":" .. server.password .. "@" .. format_host_port(server.server, server.server_port),
|
||||
listen = (proto == "redir") and "redir" .. "://0.0.0.0:" .. tonumber(local_port) or "socks" .. "://0.0.0.0:" .. tonumber(local_port),
|
||||
listen = (proto == "redir") and "redir" .. "://" .. redir_host .. ":" .. tonumber(local_port) or "socks" .. "://0.0.0.0:" .. tonumber(local_port),
|
||||
["insecure-concurrency"] = tonumber(server.concurrency) or 1
|
||||
}
|
||||
local ss = {
|
||||
server = (server.kcp_enable == "1") and "127.0.0.1" or server.server,
|
||||
server_port = tonumber(server.server_port),
|
||||
local_address = "0.0.0.0",
|
||||
local_address = redir_address,
|
||||
local_port = tonumber(local_port),
|
||||
mode = (proto == "tcp,udp") and "tcp_and_udp" or (proto .. "_only"),
|
||||
password = server.password,
|
||||
@@ -1039,14 +1043,14 @@ local hysteria2 = {
|
||||
},
|
||||
--[[
|
||||
tcpTProxy = (proto:find("tcp") and local_port ~= "0") and {
|
||||
listen = "0.0.0.0:" .. tonumber(local_port)
|
||||
listen = redir_host .. ":" .. tonumber(local_port)
|
||||
} or nil,
|
||||
]]--
|
||||
tcpRedirect = (proto:find("tcp") and local_port ~= "0") and {
|
||||
listen = "0.0.0.0:" .. tonumber(local_port)
|
||||
listen = redir_host .. ":" .. tonumber(local_port)
|
||||
} or nil,
|
||||
udpTProxy = (proto:find("udp") and local_port ~= "0") and {
|
||||
listen = "0.0.0.0:" .. tonumber(local_port)
|
||||
listen = redir_host .. ":" .. tonumber(local_port)
|
||||
} or nil,
|
||||
obfs = (server.flag_obfs == "1") and {
|
||||
type = server.obfs_type,
|
||||
@@ -1124,7 +1128,7 @@ local shadowtls = {
|
||||
local chain_sslocal = {
|
||||
locals = local_port ~= "0" and {
|
||||
{
|
||||
local_address = "0.0.0.0",
|
||||
local_address = redir_address,
|
||||
local_port = (chain_local_port == "0" and effective_node_local_port or tonumber(chain_local_port)),
|
||||
mode = (proto:find("tcp,udp") and "tcp_and_udp") or proto .. "_only",
|
||||
protocol = "redir",
|
||||
@@ -1134,12 +1138,12 @@ local chain_sslocal = {
|
||||
},
|
||||
socks_port ~= "0" and {
|
||||
protocol = "socks",
|
||||
local_address = "0.0.0.0",
|
||||
local_address = redir_address,
|
||||
local_port = tonumber(socks_port)
|
||||
} or nil
|
||||
} or {{
|
||||
protocol = "socks",
|
||||
local_address = "0.0.0.0",
|
||||
local_address = redir_address,
|
||||
local_port = tonumber(socks_port)
|
||||
}},
|
||||
servers = {
|
||||
|
||||
@@ -21,6 +21,14 @@ esac
|
||||
mkdir -p $TMP_DNSMASQ_PATH
|
||||
|
||||
run_mode=$(normalize_run_mode)
|
||||
gfw_set="4#inet#ss_spec#gfwlist"
|
||||
black_set="4#inet#ss_spec#blacklist"
|
||||
white_set="4#inet#ss_spec#whitelist_domain"
|
||||
if ipv6_enabled; then
|
||||
gfw_set="$gfw_set,6#inet#ss_spec#gfwlist6"
|
||||
black_set="$black_set,6#inet#ss_spec#blacklist6"
|
||||
white_set="$white_set,6#inet#ss_spec#whitelist_domain6"
|
||||
fi
|
||||
|
||||
cp -rf /etc/ssrplus/gfw_list.conf $TMP_DNSMASQ_PATH/
|
||||
cp -rf /etc/ssrplus/gfw_base.conf $TMP_DNSMASQ_PATH/
|
||||
@@ -32,12 +40,11 @@ for conf_file in gfw_base.conf gfw_list.conf; do
|
||||
if [ "$run_mode" = "gfw" ]; then
|
||||
if [ "$nft_support" = "1" ]; then
|
||||
# gfw + nft:ipset → nftset
|
||||
sed -i 's|ipset=/\([^/]*\)/\([^[:space:]]*\)|nftset=/\1/inet#ss_spec#\2|g' "$conf"
|
||||
sed -i "s|^ipset=/\\([^/]*\\)/gfwlist$|nftset=/\\1/$gfw_set|" "$conf"
|
||||
fi
|
||||
else
|
||||
# 非 gfw:无条件清理所有分流引用
|
||||
# sed -i '/^[[:space:]]*\(ipset=\|nftset=\)/d' "$conf"
|
||||
sed -i '/^[[:space:]]*ipset=/d' "$conf"
|
||||
sed -i '/^[[:space:]]*\(ipset=\|nftset=\)/d' "$conf"
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -65,12 +72,14 @@ for list_file in /etc/ssrplus/black.list /etc/ssrplus/white.list /etc/ssrplus/de
|
||||
}
|
||||
{
|
||||
# 提取 server=/domain/xxx
|
||||
if (match($0, /^server=\/([^\/]+)\//, m)) {
|
||||
if (m[1] in domain) next
|
||||
if ($0 ~ /^server=\//) {
|
||||
split($0, m, "/")
|
||||
if (m[2] in domain) next
|
||||
}
|
||||
# 提取 ipset=/domain/xxx
|
||||
if (match($0, /^ipset=\/([^\/]+)\//, m)) {
|
||||
if (m[1] in domain) next
|
||||
if ($0 ~ /^(ipset|nftset)=\//) {
|
||||
split($0, m, "/")
|
||||
if (m[2] in domain) next
|
||||
}
|
||||
print
|
||||
}
|
||||
@@ -84,8 +93,8 @@ done
|
||||
|
||||
# 此处直接使用 cat 因为有 sed '/#/d' 删除了 数据
|
||||
if [ "$nft_support" = "1" ]; then
|
||||
sed -e 's/[[:space:]]*#.*$//' -e '/^[[:space:]]*$/d' -e "s/.*/server=\/&\/127.0.0.1#$dns_port\nnftset=\/&\/inet#ss_spec#blacklist/" /etc/ssrplus/black.list > "$TMP_DNSMASQ_PATH/blacklist_forward.conf"
|
||||
sed -e 's/[[:space:]]*#.*$//' -e '/^[[:space:]]*$/d' -e "s/.*/nftset=\/&\/4#inet#ss_spec#whitelist_domain/" /etc/ssrplus/white.list > "$TMP_DNSMASQ_PATH/whitelist_forward.conf"
|
||||
sed -e 's/[[:space:]]*#.*$//' -e '/^[[:space:]]*$/d' -e "s/.*/server=\/&\/127.0.0.1#$dns_port\nnftset=\/&\/$black_set/" /etc/ssrplus/black.list > "$TMP_DNSMASQ_PATH/blacklist_forward.conf"
|
||||
sed -e 's/[[:space:]]*#.*$//' -e '/^[[:space:]]*$/d' -e "s/.*/nftset=\/&\/$white_set/" /etc/ssrplus/white.list > "$TMP_DNSMASQ_PATH/whitelist_forward.conf"
|
||||
elif [ "$nft_support" = "0" ]; then
|
||||
sed -e 's/[[:space:]]*#.*$//' -e '/^[[:space:]]*$/d' -e "s/.*/server=\/&\/127.0.0.1#$dns_port\nipset=\/&\/blacklist/" /etc/ssrplus/black.list > "$TMP_DNSMASQ_PATH/blacklist_forward.conf"
|
||||
sed -e 's/[[:space:]]*#.*$//' -e '/^[[:space:]]*$/d' -e "s/.*/ipset=\/&\/whitelist/" /etc/ssrplus/white.list > "$TMP_DNSMASQ_PATH/whitelist_forward.conf"
|
||||
@@ -117,12 +126,14 @@ if [ "$(uci_get_by_type global adblock 0)" == "1" ]; then
|
||||
{
|
||||
keep = 1
|
||||
# 精确匹配 server=/domain/
|
||||
if (match($0, /^server=\/([^\/]+)\//, m)) {
|
||||
if (m[1] in domain) keep = 0
|
||||
if ($0 ~ /^server=\//) {
|
||||
split($0, m, "/")
|
||||
if (m[2] in domain) keep = 0
|
||||
}
|
||||
# 精确匹配 ipset=/domain/
|
||||
if (match($0, /^ipset=\/([^\/]+)\//, m)) {
|
||||
if (m[1] in domain) keep = 0
|
||||
if ($0 ~ /^(ipset|nftset)=\//) {
|
||||
split($0, m, "/")
|
||||
if (m[2] in domain) keep = 0
|
||||
}
|
||||
if (keep) print
|
||||
}
|
||||
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
#!/bin/sh
|
||||
# IPv6 nftables rules, sourced by ssr-rules. DNS sets are shared by TCP and UDP.
|
||||
|
||||
# br_netfilter can run inet prerouting before ipv6_rcv() orphans the skb,
|
||||
# losing the TPROXY socket on bridged LAN ingress. Routed fw4 filtering still
|
||||
# runs when this bridge-only compatibility hook is disabled.
|
||||
ipv6_bridge_nf() {
|
||||
local knob=/proc/sys/net/bridge/bridge-nf-call-ip6tables
|
||||
local saved=/var/run/ssrplus-ipv6-bridge-nf
|
||||
[ -f "$knob" ] || return 0
|
||||
if [ "$1" = "start" ]; then
|
||||
[ -f "$saved" ] || cat "$knob" > "$saved"
|
||||
echo 0 > "$knob"
|
||||
elif [ -f "$saved" ]; then
|
||||
[ "$(cat "$knob")" != "0" ] || cat "$saved" > "$knob"
|
||||
rm -f "$saved"
|
||||
fi
|
||||
}
|
||||
|
||||
ipv6_policy_route() {
|
||||
ipv6_bridge_nf start || return 1
|
||||
ip -6 rule show | grep -q "fwmark ${FWMARK}.*lookup 999" ||
|
||||
ip -6 rule add fwmark "$FWMARK" table 999 priority 999 || return 1
|
||||
ip -6 route replace local ::/0 dev lo table 999
|
||||
}
|
||||
|
||||
ipv6_policy_cleanup() {
|
||||
ipv6_bridge_nf stop
|
||||
while ip -6 rule del fwmark "$FWMARK" table 999 2>/dev/null; do :; done
|
||||
ip -6 route del local ::/0 dev lo table 999 2>/dev/null
|
||||
return 0
|
||||
}
|
||||
|
||||
ipv6_set_elements() {
|
||||
local setname="$1" address
|
||||
shift
|
||||
for address in "$@"; do
|
||||
case "$address" in
|
||||
*:*) printf 'add element inet ss_spec %s { %s }\n' "$setname" "$address" ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
ipv6_policy_rules() {
|
||||
local chain="$1" target="$2"
|
||||
cat <<-RULES
|
||||
add rule inet ss_spec $chain meta mark 255 return
|
||||
add rule inet ss_spec $chain fib daddr type local return
|
||||
add rule inet ss_spec $chain ip6 daddr @local6 return
|
||||
add rule inet ss_spec $chain ip6 daddr @servers6 return
|
||||
add rule inet ss_spec $chain ip6 daddr @blacklist6 goto $target
|
||||
add rule inet ss_spec $chain ip6 daddr @whitelist6 return
|
||||
add rule inet ss_spec $chain ip6 daddr @whitelist_domain6 return
|
||||
add rule inet ss_spec $chain ip6 saddr @fplan6 goto $target
|
||||
add rule inet ss_spec $chain ip6 saddr @bplan6 return
|
||||
RULES
|
||||
case "$RUNMODE" in
|
||||
gfw)
|
||||
echo "add rule inet ss_spec $chain ip6 daddr @china6 return"
|
||||
echo "add rule inet ss_spec $chain ip6 daddr @gfwlist6 goto $target"
|
||||
echo "add rule inet ss_spec $chain ip6 saddr @gmlan6 goto $target"
|
||||
;;
|
||||
router)
|
||||
echo "add rule inet ss_spec $chain ip6 daddr @china6 return"
|
||||
echo "add rule inet ss_spec $chain goto $target"
|
||||
;;
|
||||
all) echo "add rule inet ss_spec $chain goto $target" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
ipv6_lan_hook() {
|
||||
local chain="$1" proto="$2" target="$3" match="" name ifname
|
||||
case "${LAN_AC_IP%%${LAN_AC_IP#?}}" in
|
||||
w|W) match="ip6 saddr @lan_ac6" ;;
|
||||
b|B) match="ip6 saddr != @lan_ac6" ;;
|
||||
esac
|
||||
if [ -z "$Interface" ]; then
|
||||
echo "add rule inet ss_spec $chain meta nfproto ipv6 meta l4proto $proto $match jump $target"
|
||||
else
|
||||
for name in $Interface; do
|
||||
ifname=$(uci -P /var/state -q get "network.$name.device")
|
||||
[ -n "$ifname" ] || ifname=$(uci -P /var/state -q get "network.$name.device")
|
||||
[ -n "$ifname" ] || continue
|
||||
echo "add rule inet ss_spec $chain iifname \"$ifname\" meta nfproto ipv6 meta l4proto $proto $match jump $target"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
ipv6_rules_generate() {
|
||||
local setname sid host ports
|
||||
for setname in china6 whitelist6 blacklist6 fplan6 bplan6 gmlan6 lan_ac6 servers6; do
|
||||
echo "add set inet ss_spec $setname { type ipv6_addr; flags interval; auto-merge; }"
|
||||
done
|
||||
for setname in gfwlist6 whitelist_domain6; do
|
||||
echo "add set inet ss_spec $setname { type ipv6_addr; }"
|
||||
done
|
||||
cat <<-'RULES'
|
||||
add set inet ss_spec local6 { type ipv6_addr; flags interval; auto-merge; elements = { ::/128, ::1/128, ::ffff:0:0/96, 64:ff9b:1::/48, 100::/64, 2001:db8::/32, fc00::/7, fe80::/10, ff00::/8 }; }
|
||||
RULES
|
||||
if [ -s /etc/ssrplus/china6_ssr.txt ]; then
|
||||
echo 'add element inet ss_spec china6 {'
|
||||
sed -e 's/#.*//' -e '/^[[:space:]]*$/d' -e 's/$/,/' /etc/ssrplus/china6_ssr.txt
|
||||
echo '}'
|
||||
fi
|
||||
ipv6_set_elements whitelist6 $WAN_BP_IP
|
||||
ipv6_set_elements blacklist6 $WAN_FW_IP
|
||||
ipv6_set_elements fplan6 $LAN_FP_IP
|
||||
ipv6_set_elements bplan6 $LAN_BP_IP
|
||||
ipv6_set_elements gmlan6 $LAN_GM_IP
|
||||
ipv6_set_elements lan_ac6 ${LAN_AC_IP#?}
|
||||
for sid in "$(uci_get_by_type global global_server)" "$(uci_get_by_type global udp_relay_server)"; do
|
||||
host=$(uci_get_by_name "$sid" server)
|
||||
host="${host#\[}"
|
||||
host="${host%\]}"
|
||||
[ -n "$host" ] || continue
|
||||
case "$host" in
|
||||
*:*) ipv6_set_elements servers6 "$host" ;;
|
||||
*) ipv6_set_elements servers6 $(resolveip -6 -t 3 "$host" 2>/dev/null) ;;
|
||||
esac
|
||||
done
|
||||
ports=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //; s/:/-/g')
|
||||
cat <<-'RULES'
|
||||
add chain inet ss_spec ss_spec6_tcp
|
||||
add chain inet ss_spec ss_spec6_redirect
|
||||
add chain inet ss_spec ss_spec6_prerouting { type nat hook prerouting priority -1; policy accept; }
|
||||
RULES
|
||||
[ -z "$ports" ] || echo "add rule inet ss_spec ss_spec6_redirect tcp dport != { $ports } return"
|
||||
echo "add rule inet ss_spec ss_spec6_redirect meta l4proto tcp counter redirect to :$local_port"
|
||||
ipv6_policy_rules ss_spec6_tcp ss_spec6_redirect
|
||||
ipv6_lan_hook ss_spec6_prerouting tcp ss_spec6_tcp
|
||||
if [ "$OUTPUT" = "1" ] || [ "$OUTPUT" = "2" ]; then
|
||||
echo 'add chain inet ss_spec ss_spec6_output { type nat hook output priority -1; policy accept; }'
|
||||
if [ "$OUTPUT" = "2" ]; then
|
||||
cat <<-'RULES'
|
||||
add rule inet ss_spec ss_spec6_output meta mark 255 return
|
||||
add rule inet ss_spec ss_spec6_output fib daddr type local return
|
||||
add rule inet ss_spec ss_spec6_output ip6 daddr @local6 return
|
||||
add rule inet ss_spec ss_spec6_output ip6 daddr @servers6 return
|
||||
add rule inet ss_spec ss_spec6_output meta nfproto ipv6 meta l4proto tcp jump ss_spec6_redirect
|
||||
RULES
|
||||
else
|
||||
echo 'add rule inet ss_spec ss_spec6_output meta nfproto ipv6 meta l4proto tcp jump ss_spec6_tcp'
|
||||
fi
|
||||
fi
|
||||
|
||||
[ "$DISABLE_UDP_RULES" = "1" ] && return 0
|
||||
cat <<-'RULES'
|
||||
add chain inet ss_spec ss_spec6_udp
|
||||
add chain inet ss_spec ss_spec6_udp_proxy
|
||||
add chain inet ss_spec ss_spec6_mangle { type filter hook prerouting priority mangle; policy accept; }
|
||||
add rule inet ss_spec ss_spec6_mangle udp dport 53 return
|
||||
RULES
|
||||
if [ -n "$TPROXY" ]; then
|
||||
[ -z "$ports" ] || echo "add rule inet ss_spec ss_spec6_udp_proxy udp dport != { $ports } return"
|
||||
echo "add rule inet ss_spec ss_spec6_udp_proxy meta l4proto udp counter meta mark set $FWMARK tproxy ip6 to :$LOCAL_PORT accept"
|
||||
echo "add rule inet ss_spec ss_spec6_mangle meta nfproto ipv6 meta l4proto udp meta mark $FWMARK jump ss_spec6_udp_proxy"
|
||||
else
|
||||
echo 'add rule inet ss_spec ss_spec6_udp_proxy udp dport 443 counter drop'
|
||||
fi
|
||||
ipv6_policy_rules ss_spec6_udp ss_spec6_udp_proxy
|
||||
ipv6_lan_hook ss_spec6_mangle udp ss_spec6_udp
|
||||
if [ -n "$TPROXY" ] && { [ "$OUTPUT" = "1" ] || [ "$OUTPUT" = "2" ]; }; then
|
||||
cat <<-'RULES'
|
||||
add chain inet ss_spec ss_spec6_udp_output { type route hook output priority mangle; policy accept; }
|
||||
add chain inet ss_spec ss_spec6_udp_mark
|
||||
add chain inet ss_spec ss_spec6_udp_local
|
||||
add rule inet ss_spec ss_spec6_udp_output udp dport 53 return
|
||||
add rule inet ss_spec ss_spec6_udp_output meta nfproto ipv6 meta l4proto udp jump ss_spec6_udp_local
|
||||
RULES
|
||||
[ -z "$ports" ] || echo "add rule inet ss_spec ss_spec6_udp_mark udp dport != { $ports } return"
|
||||
echo "add rule inet ss_spec ss_spec6_udp_mark counter meta mark set $FWMARK return"
|
||||
if [ "$OUTPUT" = "2" ]; then
|
||||
local RUNMODE=all
|
||||
fi
|
||||
ipv6_policy_rules ss_spec6_udp_local ss_spec6_udp_mark
|
||||
fi
|
||||
}
|
||||
|
||||
ipv6_rules_apply() {
|
||||
[ "$USE_NFT" = "1" ] && ipv6_enabled || return 0
|
||||
local rules_file
|
||||
rules_file=$(mktemp /tmp/ssr-ipv6.XXXXXX) || return 1
|
||||
if ! ipv6_rules_generate > "$rules_file" || ! $NFT -f "$rules_file"; then
|
||||
rm -f "$rules_file"
|
||||
loger 3 "Failed to install IPv6 proxy rules"
|
||||
return 1
|
||||
fi
|
||||
rm -f "$rules_file"
|
||||
if [ -n "$TPROXY" ] && [ "$DISABLE_UDP_RULES" != "1" ]; then
|
||||
ipv6_policy_route
|
||||
fi
|
||||
}
|
||||
@@ -7,6 +7,7 @@ config global
|
||||
option custom_ports '80,443'
|
||||
option pdnsd_enable '1'
|
||||
option filter_aaaa '1'
|
||||
option ipv6_support '0'
|
||||
option tunnel_forward '8.8.4.4:53'
|
||||
option monitor_enable '1'
|
||||
option enable_switch '1'
|
||||
@@ -16,6 +17,7 @@ config global
|
||||
option default_node_local_port '1234'
|
||||
option component_mirror 'direct'
|
||||
option gfwlist_url 'https://fastly.jsdelivr.net/gh/YW5vbnltb3Vz/domain-list-community@release/gfwlist.txt'
|
||||
option chnroute6_url 'https://raw.githubusercontent.com/1715173329/IPCIDR-CHINA/master/ipv6.txt'
|
||||
option chnroute_url 'https://ispip.clang.cn/all_cn.txt'
|
||||
option adblock_url 'https://anti-ad.net/anti-ad-for-dnsmasq.conf'
|
||||
|
||||
|
||||
@@ -151,6 +151,29 @@ local function update(url, file, type, file2)
|
||||
local refresh_cmd = "curl -sSL --insecure -o /tmp/ssr-update." .. type .. " " .. url
|
||||
local sret = luci.sys.call(refresh_cmd)
|
||||
if sret == 0 then
|
||||
if type == "ip6_data" then
|
||||
local ip = require "luci.ip"
|
||||
local entries = {}
|
||||
for line in io.lines("/tmp/ssr-update." .. type) do
|
||||
line = line:gsub("#.*$", ""):match("^%s*(.-)%s*$")
|
||||
if line ~= "" then
|
||||
if not ip.IPv6(line) then
|
||||
os.remove("/tmp/ssr-update." .. type)
|
||||
log(-1)
|
||||
return
|
||||
end
|
||||
table.insert(entries, line)
|
||||
end
|
||||
end
|
||||
if #entries == 0 then
|
||||
os.remove("/tmp/ssr-update." .. type)
|
||||
log(-1)
|
||||
return
|
||||
end
|
||||
local out = io.open("/tmp/ssr-update." .. type, "w")
|
||||
out:write(table.concat(entries, "\n"), "\n")
|
||||
out:close()
|
||||
end
|
||||
if type == "gfw_data" then
|
||||
local gfwlist = io.open("/tmp/ssr-update." .. type, "r")
|
||||
local decode = gfwlist:read("*a")
|
||||
@@ -210,7 +233,9 @@ local function update(url, file, type, file2)
|
||||
if file2 then
|
||||
luci.sys.exec("cp -f /tmp/ssr-update." .. type .. " " .. file2)
|
||||
end
|
||||
if type == "gfw_data" or type == "ad_data" then
|
||||
if type == "ip6_data" then
|
||||
luci.sys.call("/usr/share/shadowsocksr/chinaipset.sh /etc/ssrplus/china6_ssr.txt 6")
|
||||
elseif type == "gfw_data" or type == "ad_data" then
|
||||
luci.sys.call("/usr/share/shadowsocksr/gfw2ipset.sh")
|
||||
else
|
||||
if luci.sys.call("command -v ipset >/dev/null 2>&1") == 0 then
|
||||
@@ -233,7 +258,17 @@ local function update(url, file, type, file2)
|
||||
os.remove("/tmp/ssr-update." .. type)
|
||||
end
|
||||
|
||||
local function update_ipv6()
|
||||
update(uci:get_first("shadowsocksr", "global", "chnroute6_url",
|
||||
"https://raw.githubusercontent.com/1715173329/IPCIDR-CHINA/master/ipv6.txt"),
|
||||
"/etc/ssrplus/china6_ssr.txt", "ip6_data")
|
||||
end
|
||||
|
||||
if args then
|
||||
if args == "ip6_data" then
|
||||
update_ipv6()
|
||||
os.exit(0)
|
||||
end
|
||||
if args == "gfw_data" then
|
||||
update(uci:get_first("shadowsocksr", "global", "gfwlist_url"), "/etc/ssrplus/gfw_list.conf", args, TMP_DNSMASQ_PATH .. "/gfw_list.conf")
|
||||
os.exit(0)
|
||||
@@ -253,6 +288,10 @@ if args then
|
||||
else
|
||||
log("正在更新【GFW列表】数据库")
|
||||
update(uci:get_first("shadowsocksr", "global", "gfwlist_url"), "/etc/ssrplus/gfw_list.conf", "gfw_data", TMP_DNSMASQ_PATH .. "/gfw_list.conf")
|
||||
if uci:get_first("shadowsocksr", "global", "ipv6_support", "0") == "1" then
|
||||
log("正在更新【国内IPv6段】数据库")
|
||||
update_ipv6()
|
||||
end
|
||||
log("正在更新【国内IP段】数据库")
|
||||
update(uci:get_first("shadowsocksr", "global", "chnroute_url"), "/etc/ssrplus/china_ssr.txt", "ip_data", TMP_PATH .. "/china_ssr.txt")
|
||||
if uci:get_first("shadowsocksr", "global", "apple_optimization", "0") == "1" then
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env node
|
||||
// Use the real LuCI CBI dependency evaluator, including checkbox empty values.
|
||||
const fs = require('fs');
|
||||
const vm = require('vm');
|
||||
const assert = require('assert');
|
||||
const path = require('path');
|
||||
const cbi = fs.readFileSync(process.argv[2], 'utf8');
|
||||
const client = fs.readFileSync(path.join(__dirname, '../luasrc/model/cbi/shadowsocksr/client.lua'), 'utf8');
|
||||
const filter = client.slice(client.indexOf('o = s:option(Flag, "filter_aaaa"'), client.indexOf('if is_finded("chinadns-ng") then', client.indexOf('o = s:option(Flag, "filter_aaaa"')));
|
||||
const deps = [...filter.matchAll(/o:depends\(\{pdnsd_enable = "(\d+)", ipv6_support = (false|"0")\}\)/g)]
|
||||
.map(m => ({ pdnsd_enable: m[1], ipv6_support: m[2] === 'false' ? false : '0' }));
|
||||
assert.strictEqual(deps.length, 3);
|
||||
const fields = { pdnsd_enable: { type: 'select', value: '1' }, ipv6_support: { type: 'checkbox', value: '1', checked: false } };
|
||||
const ctx = { document: { querySelectorAll: query => [fields[query.match(/id="([^"]+)"/)[1]]] } };
|
||||
vm.createContext(ctx);
|
||||
vm.runInContext(cbi.slice(cbi.indexOf('function cbi_d_checkvalue('), cbi.indexOf('function cbi_d_update(')), ctx);
|
||||
for (const mode of ['0', '1', '4', '6', '7']) {
|
||||
fields.pdnsd_enable.value = mode;
|
||||
for (const checked of [false, true, false]) {
|
||||
fields.ipv6_support.checked = checked;
|
||||
assert.strictEqual(ctx.cbi_d_check(deps), !checked && ['1', '4', '7'].includes(mode));
|
||||
}
|
||||
}
|
||||
assert.match(filter, /function o\.remove\(self, section\)\s*end/);
|
||||
console.log('PASS: actual CBI checkbox dependencies toggle immediately and preserve the saved filter');
|
||||
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Exercise DNS generation with BusyBox awk, both backends and all run modes."""
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
source = Path(__file__).resolve().parents[1] / 'root/usr/share/shadowsocksr/gfw2ipset.sh'
|
||||
with tempfile.TemporaryDirectory(prefix='ssr6-dns-') as tmp:
|
||||
root = Path(tmp)
|
||||
config = root / 'config'
|
||||
output = root / 'output'
|
||||
init = root / 'etc/init.d'
|
||||
for directory in (config, output, init, root / 'bin'):
|
||||
directory.mkdir(parents=True)
|
||||
for name in ('gfw_base.conf', 'gfw_list.conf'):
|
||||
(config / name).write_text(''.join(
|
||||
f'server=/{domain}/127.0.0.1#5335\nipset=/{domain}/gfwlist\n'
|
||||
for domain in ('proxy.example', 'white.example', 'black.example', 'deny.example')))
|
||||
for name, domain in [('white', 'white'), ('black', 'black'), ('deny', 'deny')]:
|
||||
(config / f'{name}.list').write_text(f'# comment\n\n{domain}.example\n')
|
||||
(init / 'shadowsocksr').write_text('''
|
||||
check_run_environment() { USE_TABLES="$BACKEND"; }
|
||||
echolog() { :; }
|
||||
normalize_run_mode() { echo "$MODE"; }
|
||||
ipv6_enabled() { [ "$V6" = 1 ] && [ "$USE_TABLES" = nftables ]; }
|
||||
uci_get_by_type() { echo 0; }
|
||||
TMP_DNSMASQ_PATH="$TEST_OUTPUT"
|
||||
dns_port=5335
|
||||
''')
|
||||
script = root / 'generate.sh'
|
||||
script.write_text(source.read_text().replace('/etc/ssrplus/', str(config) + '/'))
|
||||
busybox = shutil.which('busybox')
|
||||
if not busybox:
|
||||
raise SystemExit('BusyBox is required to test the OpenWrt awk dialect')
|
||||
(root / 'bin/awk').symlink_to(busybox)
|
||||
count = 0
|
||||
for backend in ('nftables', 'iptables'):
|
||||
for mode in ('gfw', 'router', 'all'):
|
||||
for ipv6 in ('0', '1'):
|
||||
env = dict(os.environ, BACKEND=backend, MODE=mode, V6=ipv6,
|
||||
IPKG_INSTROOT=tmp, TEST_OUTPUT=str(output),
|
||||
PATH=str(root / 'bin') + ':' + os.environ['PATH'])
|
||||
subprocess.run([busybox, 'sh', str(script)], env=env, check=True)
|
||||
gfw = (output / 'gfw_list.conf').read_text()
|
||||
white = (output / 'whitelist_forward.conf').read_text()
|
||||
black = (output / 'blacklist_forward.conf').read_text()
|
||||
assert 'proxy.example' in gfw
|
||||
for domain in ('white.example', 'black.example', 'deny.example'):
|
||||
assert domain not in gfw, (backend, mode, ipv6, domain)
|
||||
assert 'server=' not in white
|
||||
assert 'server=/black.example/127.0.0.1#5335' in black
|
||||
if backend == 'nftables':
|
||||
assert '4#inet#ss_spec#whitelist_domain' in white
|
||||
assert ('6#inet#ss_spec#whitelist_domain6' in white) == (ipv6 == '1')
|
||||
assert ('6#inet#ss_spec#blacklist6' in black) == (ipv6 == '1')
|
||||
if mode == 'gfw':
|
||||
assert '4#inet#ss_spec#gfwlist' in gfw
|
||||
assert ('6#inet#ss_spec#gfwlist6' in gfw) == (ipv6 == '1')
|
||||
else:
|
||||
assert 'nftset=' not in gfw and 'ipset=' not in gfw
|
||||
else:
|
||||
assert 'nftset=' not in gfw + white + black
|
||||
assert ('ipset=/proxy.example/gfwlist' in gfw) == (mode == 'gfw')
|
||||
count += 1
|
||||
print(f'PASS: {count} DNS generation combinations, domain overrides and AAAA sets')
|
||||
Executable
+49
@@ -0,0 +1,49 @@
|
||||
#!/bin/sh
|
||||
# Run as root on OpenWrt with the package installed. Rules stay in a new netns.
|
||||
set -eu
|
||||
ns="ssr6-check-$$"
|
||||
tmp=$(mktemp -d)
|
||||
cleanup() {
|
||||
ip netns del "$ns" 2>/dev/null || true
|
||||
rm -rf "$tmp"
|
||||
}
|
||||
trap cleanup EXIT INT TERM
|
||||
ip netns add "$ns"
|
||||
. /usr/share/shadowsocksr/ipv6.sh
|
||||
uci_get_by_type() { :; }
|
||||
uci_get_by_name() { :; }
|
||||
FWMARK=0x53535250
|
||||
local_port=1234
|
||||
LOCAL_PORT=301
|
||||
Interface=""
|
||||
LAN_AC_IP="w192.0.2.1 2001:db8::1"
|
||||
WAN_BP_IP="192.0.2.2 2001:db8:1::/64"
|
||||
WAN_FW_IP="192.0.2.3 2001:db8:2::/64"
|
||||
LAN_BP_IP="2001:db8::2"
|
||||
LAN_FP_IP="2001:db8::3"
|
||||
LAN_GM_IP="2001:db8::4"
|
||||
count=0
|
||||
for RUNMODE in gfw router all; do
|
||||
for OUTPUT in 0 1 2; do
|
||||
for udp in tproxy quic disabled; do
|
||||
TPROXY=""
|
||||
DISABLE_UDP_RULES=0
|
||||
case "$udp" in
|
||||
tproxy) TPROXY=2 ;;
|
||||
disabled) DISABLE_UDP_RULES=1 ;;
|
||||
esac
|
||||
for PROXY_PORTS in "" "-m multiport --dports 53,80,443,8000:8100"; do
|
||||
ip netns exec "$ns" nft flush ruleset
|
||||
ip netns exec "$ns" nft add table inet ss_spec
|
||||
ipv6_rules_generate > "$tmp/rules"
|
||||
ip netns exec "$ns" nft -f "$tmp/rules"
|
||||
# Validate the serialized form used during a firewall reload.
|
||||
ip netns exec "$ns" nft list ruleset > "$tmp/snapshot"
|
||||
ip netns exec "$ns" nft flush ruleset
|
||||
ip netns exec "$ns" nft -f "$tmp/snapshot"
|
||||
count=$((count + 1))
|
||||
done
|
||||
done
|
||||
done
|
||||
done
|
||||
printf 'PASS: %s IPv6 mode/OUTPUT/UDP/port combinations and snapshot reloads\n' "$count"
|
||||
@@ -583,8 +583,8 @@ function getConfigCipherValue(section_id, hwtype) {
|
||||
function getDisplayEncryption(radioNet) {
|
||||
const hwtype = uci.get('wireless', radioNet.getWifiDeviceName(), 'type');
|
||||
const configured = getConfigEncryptionValue(radioNet.getName(), hwtype);
|
||||
// The QCA iwinfo backend reports OWE as WPA2-PSK.
|
||||
if (isQcaWifiHwtype(hwtype) && configured == 'owe')
|
||||
// The QCA iwinfo backend reports OWE and SAE as WPA2-PSK.
|
||||
if (isQcaWifiHwtype(hwtype) && ['owe', 'sae', 'sae-mixed'].includes(configured))
|
||||
return formatConfigEncryption(configured);
|
||||
|
||||
const encryption = radioNet.getActiveEncryption();
|
||||
@@ -3301,6 +3301,10 @@ return view.extend({
|
||||
}
|
||||
else if (e == 'sae') {
|
||||
uci.set('wireless', section_id, 'sae', '1');
|
||||
if (getSecurityBand() == '6g') {
|
||||
uci.set('wireless', section_id, 'sae_pwe', '1');
|
||||
uci.set('wireless', section_id, 'ieee80211w', '2');
|
||||
}
|
||||
}
|
||||
else {
|
||||
uci.unset('wireless', section_id, 'sae');
|
||||
|
||||
Reference in new issue
Block a user