mirror of
https://github.com/caiwx86/small-packages.git
synced 2026-09-12 11:24:28 +08:00
update 2026-08-08 00:47:38
This commit is contained in:
@@ -89,9 +89,8 @@ if has_xray or has_singbox then
|
||||
o.rmempty = false
|
||||
o.description = "<ul>"
|
||||
.. "<li>" .. translate("Experimental feature.") .. "</li>"
|
||||
.. "<li>" .. "1." .. translate("Analyzes and preloads GeoIP/Geosite data to enhance the shunt performance of Sing-box/Xray.") .. "</li>"
|
||||
.. "<li>" .. "1." .. translate("Parses and preloads GeoIP/Geosite data to improve Sing-box/Xray routing performance.") .. "</li>"
|
||||
.. "<li>" .. "2." .. translate("Once enabled, the rule list can support GeoIP/Geosite rules.") .. "</li>"
|
||||
.. "<li>" .. translate("Note: Increases resource usage; Geosite analysis is only supported in ChinaDNS-NG and SmartDNS modes.") .. "</li>"
|
||||
.. "</ul>"
|
||||
function o.write(self, section, value)
|
||||
local old = m:get(section, self.option) or "0"
|
||||
|
||||
@@ -1085,15 +1085,12 @@ msgstr "启用此选项后手动更新时,即使没有新版本也会从现有 G
|
||||
msgid "Enable Geo Data Parsing"
|
||||
msgstr "开启 Geo 数据解析"
|
||||
|
||||
msgid "Analyzes and preloads GeoIP/Geosite data to enhance the shunt performance of Sing-box/Xray."
|
||||
msgstr "分析和预加载 GeoIP/Geosite 数据,以增强 Sing-box/Xray 的分流效果。"
|
||||
msgid "Parses and preloads GeoIP/Geosite data to improve Sing-box/Xray routing performance."
|
||||
msgstr "解析和预加载 GeoIP/Geosite 数据,以增强 Sing-box/Xray 的分流效果。"
|
||||
|
||||
msgid "Once enabled, the rule list can support GeoIP/Geosite rules."
|
||||
msgstr "启用后,规则列表可以支持 GeoIP/Geosite 规则。"
|
||||
|
||||
msgid "Note: Increases resource usage; Geosite analysis is only supported in ChinaDNS-NG and SmartDNS modes."
|
||||
msgstr "注:会增加一些系统资源的开销,仅在 ChinaDNS-NG 和 SmartDNS 模式下支持分析 Geosite 。"
|
||||
|
||||
msgid "Shunt Rule"
|
||||
msgstr "分流规则"
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ if USE_DIRECT_LIST == "1" and not fs.access(file_direct_host) then
|
||||
geosite_arg = geosite_arg .. (geosite_arg ~= "" and "," or "") .. line
|
||||
else
|
||||
line = api.get_std_domain(line)
|
||||
if line ~= "" and not line:find("#") then
|
||||
if line ~= "" and not line:find("#") and not line:find(":") then
|
||||
insert_unique(direct_domain, line, lookup_direct_domain)
|
||||
end
|
||||
end
|
||||
@@ -277,7 +277,7 @@ if USE_PROXY_LIST == "1" and not fs.access(file_proxy_host) then
|
||||
geosite_arg = geosite_arg .. (geosite_arg ~= "" and "," or "") .. line
|
||||
else
|
||||
line = api.get_std_domain(line)
|
||||
if line ~= "" and not line:find("#") then
|
||||
if line ~= "" and not line:find("#") and not line:find(":") then
|
||||
insert_unique(proxy_domain, line, lookup_proxy_domain)
|
||||
end
|
||||
end
|
||||
@@ -398,7 +398,7 @@ if IS_SHUNT_NODE then
|
||||
|
||||
local domain_list = s.domain_list or ""
|
||||
for line in string.gmatch(domain_list, "[^\r\n]+") do
|
||||
if line ~= "" and not line:find("#") and not line:find("regexp:") and not line:find("ext:") then
|
||||
if line ~= "" and not line:find("#") and not line:find("regexp:") and not line:find("ext:") and not line:find("rule-set:") and not line:find("rs:") then
|
||||
if line:find("geosite:") then
|
||||
line = string.match(line, ":([^:]+)$")
|
||||
if _node_id == "_direct" then
|
||||
|
||||
@@ -181,10 +181,15 @@ function add_rule(var)
|
||||
local CACHE_TEXT_FILE = CACHE_DNS_PATH .. ".txt"
|
||||
local USE_CHINADNS_NG = "0"
|
||||
local IS_SHUNT_NODE = uci:get(appname, TCP_NODE, "protocol") == "_shunt"
|
||||
local USE_GEOVIEW = uci:get(appname, "@global_rules[0]", "enable_geoview")
|
||||
|
||||
local list1 = {}
|
||||
local excluded_domain = {}
|
||||
|
||||
if not api.is_finded("geoview") then
|
||||
USE_GEOVIEW = "0"
|
||||
end
|
||||
|
||||
local function log(...)
|
||||
if NO_LOGIC_LOG == "1" then
|
||||
return
|
||||
@@ -301,6 +306,21 @@ function add_rule(var)
|
||||
return false
|
||||
end
|
||||
|
||||
local function foreach_geosite(list_arg, callback)
|
||||
local geosite_path = uci:get(appname, "@global_rules[0]", "v2ray_location_asset") or "/usr/share/v2ray/"
|
||||
geosite_path = geosite_path:match("^(.*)/") .. "/geosite.dat"
|
||||
if not fs.access(geosite_path) then return end
|
||||
local bin = api.finded_com("geoview")
|
||||
if not (bin and list_arg) then return end
|
||||
local cmd = string.format("%q -type geosite -action extract -input %q -list %q -lowmem=true", bin, geosite_path, list_arg)
|
||||
local pipe = io.popen(cmd)
|
||||
if not pipe then return end
|
||||
for line in pipe:lines() do
|
||||
if line ~= "" then callback(line) end
|
||||
end
|
||||
pipe:close()
|
||||
end
|
||||
|
||||
local cache_text = ""
|
||||
local nodes_address_md5 = sys.exec("echo -n $(uci show passwall | grep '\\.address') | md5sum")
|
||||
local new_rules = sys.exec("echo -n $(find /usr/share/passwall/rules -type f | xargs md5sum)")
|
||||
@@ -347,16 +367,28 @@ function add_rule(var)
|
||||
|
||||
--屏蔽列表
|
||||
if USE_CHINADNS_NG == "0" and USE_BLOCK_LIST == "1" then
|
||||
local geosite_arg = ""
|
||||
local f = io.open("/usr/share/passwall/rules/block_host")
|
||||
if f then
|
||||
for line in f:lines() do
|
||||
line = api.get_std_domain(line)
|
||||
if line ~= "" and not line:find("#") and not line:find(":") then
|
||||
set_domain_address(line, "")
|
||||
if not line:find("#") and line:find("geosite:") then
|
||||
line = string.match(line, ":([^:]+)$")
|
||||
geosite_arg = geosite_arg .. (geosite_arg ~= "" and "," or "") .. line
|
||||
else
|
||||
line = api.get_std_domain(line)
|
||||
if line ~= "" and not line:find("#") and not line:find(":") then
|
||||
set_domain_address(line, "")
|
||||
end
|
||||
end
|
||||
end
|
||||
f:close()
|
||||
end
|
||||
if USE_GEOVIEW == "1" and geosite_arg ~= "" then
|
||||
foreach_geosite(geosite_arg, function(line)
|
||||
set_domain_address(line, "")
|
||||
end)
|
||||
log(" - 解析[屏蔽列表] Geosite 到屏蔽域名表(blocklist)完成")
|
||||
end
|
||||
end
|
||||
|
||||
local fwd_dns
|
||||
@@ -400,88 +432,124 @@ function add_rule(var)
|
||||
|
||||
--直连(白名单)列表
|
||||
if USE_DIRECT_LIST == "1" then
|
||||
if fs.access("/usr/share/passwall/rules/direct_host") then
|
||||
fwd_dns = LOCAL_DNS
|
||||
if USE_CHINADNS_NG == "1" then
|
||||
fwd_dns = nil
|
||||
end
|
||||
if fwd_dns then
|
||||
local sets = {
|
||||
setflag_4 .. "psw_white",
|
||||
setflag_6 .. "psw_white6"
|
||||
}
|
||||
--始终用国内DNS解析直连(白名单)列表
|
||||
for line in io.lines("/usr/share/passwall/rules/direct_host") do
|
||||
line = api.get_std_domain(line)
|
||||
if line ~= "" and not line:find("#") and not line:find(":") then
|
||||
add_excluded_domain(line)
|
||||
set_domain_dns(line, fwd_dns)
|
||||
set_domain_ipset(line, table.concat(sets, ","))
|
||||
fwd_dns = LOCAL_DNS
|
||||
if USE_CHINADNS_NG == "1" then
|
||||
fwd_dns = nil
|
||||
end
|
||||
if fwd_dns then
|
||||
local sets = {
|
||||
setflag_4 .. "psw_white",
|
||||
setflag_6 .. "psw_white6"
|
||||
}
|
||||
--始终用国内DNS解析直连(白名单)列表
|
||||
local geosite_arg = ""
|
||||
local f = io.open("/usr/share/passwall/rules/direct_host")
|
||||
if f then
|
||||
for line in f:lines() do
|
||||
if not line:find("#") and line:find("geosite:") then
|
||||
line = string.match(line, ":([^:]+)$")
|
||||
geosite_arg = geosite_arg .. (geosite_arg ~= "" and "," or "") .. line
|
||||
else
|
||||
line = api.get_std_domain(line)
|
||||
if line ~= "" and not line:find("#") and not line:find(":") then
|
||||
add_excluded_domain(line)
|
||||
set_domain_dns(line, fwd_dns)
|
||||
set_domain_ipset(line, table.concat(sets, ","))
|
||||
end
|
||||
end
|
||||
end
|
||||
f:close()
|
||||
log(string.format(" - 域名白名单(whitelist):%s", fwd_dns or "默认"))
|
||||
end
|
||||
if USE_GEOVIEW == "1" and geosite_arg ~= "" then
|
||||
foreach_geosite(geosite_arg, function(line)
|
||||
add_excluded_domain(line)
|
||||
set_domain_dns(line, fwd_dns)
|
||||
set_domain_ipset(line, table.concat(sets, ","))
|
||||
end)
|
||||
log(" - 解析[直连列表] Geosite 到域名白名单(whitelist)完成")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--代理(黑名单)列表
|
||||
if USE_PROXY_LIST == "1" then
|
||||
if fs.access("/usr/share/passwall/rules/proxy_host") then
|
||||
fwd_dns = TUN_DNS
|
||||
if USE_CHINADNS_NG == "1" then
|
||||
fwd_dns = nil
|
||||
fwd_dns = TUN_DNS
|
||||
if USE_CHINADNS_NG == "1" then
|
||||
fwd_dns = nil
|
||||
end
|
||||
if fwd_dns then
|
||||
local set_name = "psw_black"
|
||||
local set6_name = "psw_black6"
|
||||
if FLAG ~= "default" then
|
||||
set_name = "psw_" .. FLAG .. "_black"
|
||||
set6_name = "psw_" .. FLAG .. "_black6"
|
||||
end
|
||||
if fwd_dns then
|
||||
local set_name = "psw_black"
|
||||
local set6_name = "psw_black6"
|
||||
if FLAG ~= "default" then
|
||||
set_name = "psw_" .. FLAG .. "_black"
|
||||
set6_name = "psw_" .. FLAG .. "_black6"
|
||||
end
|
||||
local sets = {
|
||||
setflag_4 .. set_name
|
||||
}
|
||||
if NO_PROXY_IPV6 ~= "1" then
|
||||
table.insert(sets, setflag_6 .. set6_name)
|
||||
end
|
||||
--始终使用远程DNS解析代理(黑名单)列表
|
||||
for line in io.lines("/usr/share/passwall/rules/proxy_host") do
|
||||
line = api.get_std_domain(line)
|
||||
if line ~= "" and not line:find("#") and not line:find(":") then
|
||||
add_excluded_domain(line)
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
set_domain_address(line, "::")
|
||||
local sets = {
|
||||
setflag_4 .. set_name
|
||||
}
|
||||
if NO_PROXY_IPV6 ~= "1" then
|
||||
table.insert(sets, setflag_6 .. set6_name)
|
||||
end
|
||||
--始终使用远程DNS解析代理(黑名单)列表
|
||||
local geosite_arg = ""
|
||||
local f = io.open("/usr/share/passwall/rules/proxy_host")
|
||||
if f then
|
||||
for line in f:lines() do
|
||||
if not line:find("#") and line:find("geosite:") then
|
||||
line = string.match(line, ":([^:]+)$")
|
||||
geosite_arg = geosite_arg .. (geosite_arg ~= "" and "," or "") .. line
|
||||
else
|
||||
line = api.get_std_domain(line)
|
||||
if line ~= "" and not line:find("#") and not line:find(":") then
|
||||
add_excluded_domain(line)
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
set_domain_address(line, "::")
|
||||
end
|
||||
set_domain_dns(line, fwd_dns)
|
||||
set_domain_ipset(line, table.concat(sets, ","))
|
||||
end
|
||||
set_domain_dns(line, fwd_dns)
|
||||
set_domain_ipset(line, table.concat(sets, ","))
|
||||
end
|
||||
end
|
||||
f:close()
|
||||
log(string.format(" - 代理域名表(blacklist):%s", fwd_dns or "默认"))
|
||||
end
|
||||
if USE_GEOVIEW == "1" and geosite_arg ~= "" then
|
||||
foreach_geosite(geosite_arg, function(line)
|
||||
add_excluded_domain(line)
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
set_domain_address(line, "::")
|
||||
end
|
||||
set_domain_dns(line, fwd_dns)
|
||||
set_domain_ipset(line, table.concat(sets, ","))
|
||||
end)
|
||||
log(" - 解析[代理列表] Geosite 到代理域名表(blacklist)完成")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--GFW列表
|
||||
if USE_GFW_LIST == "1" then
|
||||
if fs.access("/usr/share/passwall/rules/gfwlist") then
|
||||
fwd_dns = TUN_DNS
|
||||
if USE_CHINADNS_NG == "1" then
|
||||
fwd_dns = nil
|
||||
fwd_dns = TUN_DNS
|
||||
if USE_CHINADNS_NG == "1" then
|
||||
fwd_dns = nil
|
||||
end
|
||||
if fwd_dns then
|
||||
local set_name = "psw_gfw"
|
||||
local set6_name = "psw_gfw6"
|
||||
if FLAG ~= "default" then
|
||||
set_name = "psw_" .. FLAG .. "_gfw"
|
||||
set6_name = "psw_" .. FLAG .. "_gfw6"
|
||||
end
|
||||
if fwd_dns then
|
||||
local set_name = "psw_gfw"
|
||||
local set6_name = "psw_gfw6"
|
||||
if FLAG ~= "default" then
|
||||
set_name = "psw_" .. FLAG .. "_gfw"
|
||||
set6_name = "psw_" .. FLAG .. "_gfw6"
|
||||
end
|
||||
local sets = {
|
||||
setflag_4 .. set_name
|
||||
}
|
||||
if NO_PROXY_IPV6 ~= "1" then
|
||||
table.insert(sets, setflag_6 .. set6_name)
|
||||
end
|
||||
for line in io.lines("/usr/share/passwall/rules/gfwlist") do
|
||||
local sets = {
|
||||
setflag_4 .. set_name
|
||||
}
|
||||
if NO_PROXY_IPV6 ~= "1" then
|
||||
table.insert(sets, setflag_6 .. set6_name)
|
||||
end
|
||||
local f = io.open("/usr/share/passwall/rules/gfwlist")
|
||||
if f then
|
||||
for line in f:lines() do
|
||||
if line ~= "" and not line:find("#") and not check_excluded_domain(line) then
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
set_domain_address(line, "::")
|
||||
@@ -494,6 +562,7 @@ function add_rule(var)
|
||||
set_domain_ipset(line, table.concat(sets, ","))
|
||||
end
|
||||
end
|
||||
f:close()
|
||||
log(string.format(" - 防火墙域名表(gfwlist):%s", fwd_dns or "默认"))
|
||||
end
|
||||
end
|
||||
@@ -501,30 +570,31 @@ function add_rule(var)
|
||||
|
||||
--中国列表
|
||||
if CHN_LIST ~= "0" then
|
||||
if fs.access("/usr/share/passwall/rules/chnlist") then
|
||||
fwd_dns = nil
|
||||
if CHN_LIST == "direct" then
|
||||
fwd_dns = LOCAL_DNS
|
||||
end
|
||||
if CHN_LIST == "proxy" then
|
||||
fwd_dns = TUN_DNS
|
||||
end
|
||||
if USE_CHINADNS_NG == "1" then
|
||||
fwd_dns = nil
|
||||
if CHN_LIST == "direct" then
|
||||
fwd_dns = LOCAL_DNS
|
||||
end
|
||||
end
|
||||
if fwd_dns then
|
||||
local sets = {
|
||||
setflag_4 .. "psw_chn",
|
||||
setflag_6 .. "psw_chn6"
|
||||
}
|
||||
if CHN_LIST == "proxy" then
|
||||
fwd_dns = TUN_DNS
|
||||
end
|
||||
if USE_CHINADNS_NG == "1" then
|
||||
fwd_dns = nil
|
||||
end
|
||||
if fwd_dns then
|
||||
local sets = {
|
||||
setflag_4 .. "psw_chn",
|
||||
setflag_6 .. "psw_chn6"
|
||||
}
|
||||
if CHN_LIST == "proxy" then
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
sets = {
|
||||
setflag_4 .. "psw_chn"
|
||||
}
|
||||
end
|
||||
if NO_PROXY_IPV6 == "1" then
|
||||
sets = {
|
||||
setflag_4 .. "psw_chn"
|
||||
}
|
||||
end
|
||||
for line in io.lines("/usr/share/passwall/rules/chnlist") do
|
||||
end
|
||||
local f = io.open("/usr/share/passwall/rules/chnlist")
|
||||
if f then
|
||||
for line in f:lines() do
|
||||
if line ~= "" and not line:find("#") and not check_excluded_domain(line) then
|
||||
if CHN_LIST == "proxy" and NO_PROXY_IPV6 == "1" then
|
||||
set_domain_address(line, "::")
|
||||
@@ -537,6 +607,7 @@ function add_rule(var)
|
||||
set_domain_ipset(line, table.concat(sets, ","))
|
||||
end
|
||||
end
|
||||
f:close()
|
||||
log(string.format(" - 中国域名表(chnroute):%s", fwd_dns or "默认"))
|
||||
end
|
||||
end
|
||||
@@ -590,21 +661,40 @@ function add_rule(var)
|
||||
end
|
||||
|
||||
local domain_list = s.domain_list or ""
|
||||
local geosite_arg = ""
|
||||
for line in string.gmatch(domain_list, "[^\r\n]+") do
|
||||
if line ~= "" and not line:find("#") and not line:find("regexp:") and not line:find("geosite:") and not line:find("ext:") then
|
||||
if line:find("domain:") or line:find("full:") then
|
||||
if line ~= "" and not line:find("#") and not line:find("regexp:") and not line:find("ext:") and not line:find("rule-set:") and not line:find("rs:") then
|
||||
if line:find("geosite:") then
|
||||
line = string.match(line, ":([^:]+)$")
|
||||
end
|
||||
line = api.get_std_domain(line)
|
||||
add_excluded_domain(line)
|
||||
geosite_arg = geosite_arg .. (geosite_arg ~= "" and "," or "") .. line
|
||||
else
|
||||
if line:find("domain:") or line:find("full:") then
|
||||
line = string.match(line, ":([^:]+)$")
|
||||
end
|
||||
line = api.get_std_domain(line)
|
||||
add_excluded_domain(line)
|
||||
|
||||
if no_ipv6 then
|
||||
set_domain_address(line, "::")
|
||||
end
|
||||
set_domain_dns(line, fwd_dns)
|
||||
set_domain_ipset(line, table.concat(sets, ","))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if USE_GFW_LIST == "1" and CHN_LIST == "0" and USE_GEOVIEW == "1" and geosite_arg ~= "" then --仅GFW模式解析geosite
|
||||
foreach_geosite(geosite_arg, function(line)
|
||||
add_excluded_domain(line)
|
||||
if no_ipv6 then
|
||||
set_domain_address(line, "::")
|
||||
end
|
||||
set_domain_dns(line, fwd_dns)
|
||||
set_domain_ipset(line, table.concat(sets, ","))
|
||||
end
|
||||
end)
|
||||
log(string.format(" - 解析分流规则(%s) Geosite 完成", s.remarks))
|
||||
end
|
||||
|
||||
if _node_id ~= "_direct" then
|
||||
log(string.format(" - Sing-Box/Xray分流规则(%s):%s", s.remarks, fwd_dns or "默认"))
|
||||
end
|
||||
|
||||
@@ -388,7 +388,7 @@ if USE_DIRECT_LIST == "1" and not fs.access(file_direct_host) then
|
||||
geosite_arg = geosite_arg .. (geosite_arg ~= "" and "," or "") .. line
|
||||
else
|
||||
line = api.get_std_domain(line)
|
||||
if line ~= "" and not line:find("#") then
|
||||
if line ~= "" and not line:find("#") and not line:find(":") then
|
||||
insert_unique(direct_domain, line, lookup_direct_domain)
|
||||
end
|
||||
end
|
||||
@@ -440,7 +440,7 @@ if USE_PROXY_LIST == "1" and not fs.access(file_proxy_host) then
|
||||
geosite_arg = geosite_arg .. (geosite_arg ~= "" and "," or "") .. line
|
||||
else
|
||||
line = api.get_std_domain(line)
|
||||
if line ~= "" and not line:find("#") then
|
||||
if line ~= "" and not line:find("#") and not line:find(":") then
|
||||
insert_unique(proxy_domain, line, lookup_proxy_domain)
|
||||
end
|
||||
end
|
||||
@@ -566,7 +566,7 @@ if IS_SHUNT_NODE then
|
||||
|
||||
local domain_list = s.domain_list or ""
|
||||
for line in string.gmatch(domain_list, "[^\r\n]+") do
|
||||
if line ~= "" and not line:find("#") and not line:find("regexp:") and not line:find("ext:") then
|
||||
if line ~= "" and not line:find("#") and not line:find("regexp:") and not line:find("ext:") and not line:find("rule-set:") and not line:find("rs:") then
|
||||
if line:find("geosite:") then
|
||||
line = string.match(line, ":([^:]+)$")
|
||||
if _node_id == "_direct" then
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-passwall2
|
||||
PKG_VERSION:=26.7.16
|
||||
PKG_VERSION:=26.8.7
|
||||
PKG_RELEASE:=1
|
||||
PKG_PO_VERSION:=$(PKG_VERSION)
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
function arraysEqual(a, b) {
|
||||
if (a === b) return true;
|
||||
if (a == null || b == null) return false;
|
||||
if (a.length !== b.length) return false;
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function waitForElement(selector, callback) {
|
||||
const el = document.querySelector(selector);
|
||||
if (el) return callback(el);
|
||||
const observer = new MutationObserver(() => {
|
||||
const el = document.querySelector(selector);
|
||||
if (el) {
|
||||
observer.disconnect();
|
||||
callback(el);
|
||||
}
|
||||
});
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
}
|
||||
|
||||
function get_current_url() {
|
||||
return window.location.origin + window.location.pathname;
|
||||
}
|
||||
|
||||
function getOption(config, section, opt) {
|
||||
let obj;
|
||||
const id = `cbid.${config}.${section}.${opt}`;
|
||||
obj = document.getElementsByName(id)[0] || document.getElementById(id);
|
||||
if (obj) {
|
||||
const combobox = document.getElementById('cbi.combobox.' + id);
|
||||
if (combobox) {
|
||||
obj.combobox = combobox;
|
||||
}
|
||||
const div = document.getElementById(id);
|
||||
if (div && div.getElementsByTagName("li").length > 0) {
|
||||
obj = div;
|
||||
}
|
||||
return obj;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -79,8 +79,8 @@ function index()
|
||||
entry({"admin", "services", appname, "connect_status"}, call("connect_status")).leaf = true
|
||||
entry({"admin", "services", appname, "ping_node"}, call("ping_node")).leaf = true
|
||||
entry({"admin", "services", appname, "urltest_node"}, call("urltest_node")).leaf = true
|
||||
entry({"admin", "services", appname, "update_config"}, call("update_config")).leaf = true
|
||||
entry({"admin", "services", appname, "add_node"}, call("add_node")).leaf = true
|
||||
entry({"admin", "services", appname, "update_node"}, call("update_node")).leaf = true
|
||||
entry({"admin", "services", appname, "set_node"}, call("set_node")).leaf = true
|
||||
entry({"admin", "services", appname, "copy_node"}, call("copy_node")).leaf = true
|
||||
entry({"admin", "services", appname, "clear_all_nodes"}, call("clear_all_nodes")).leaf = true
|
||||
@@ -96,6 +96,10 @@ function index()
|
||||
entry({"admin", "services", appname, "subscribe_manual"}, call("subscribe_manual")).leaf = true
|
||||
entry({"admin", "services", appname, "subscribe_manual_all"}, call("subscribe_manual_all")).leaf = true
|
||||
entry({"admin", "services", appname, "flush_set"}, call("flush_set")).leaf = true
|
||||
entry({"admin", "services", appname, "get_shunt_rules"}, call("get_shunt_rules")).leaf = true
|
||||
entry({"admin", "services", appname, "add_shunt_rule"}, call("add_shunt_rule")).leaf = true
|
||||
entry({"admin", "services", appname, "delete_select_shunt_rules"}, call("delete_select_shunt_rules")).leaf = true
|
||||
entry({"admin", "services", appname, "save_shunt_rule_order"}, call("save_shunt_rule_order")).leaf = true
|
||||
|
||||
--[[Components update]]
|
||||
entry({"admin", "services", appname, "check_passwall2"}, call("app_check")).leaf = true
|
||||
@@ -369,6 +373,23 @@ function urltest_node()
|
||||
http_write_json(e)
|
||||
end
|
||||
|
||||
function update_config()
|
||||
local id = http.formvalue("id") -- Node id
|
||||
local data = http.formvalue("data") -- json new Data
|
||||
if id and data then
|
||||
local data_t = jsonParse(data) or {}
|
||||
if next(data_t) then
|
||||
for k, v in pairs(data_t) do
|
||||
uci:set(appname, id, k, v)
|
||||
end
|
||||
api.uci_save(uci, appname)
|
||||
http_write_json_ok()
|
||||
return
|
||||
end
|
||||
end
|
||||
http_write_json_error()
|
||||
end
|
||||
|
||||
function add_node()
|
||||
local redirect = http.formvalue("redirect")
|
||||
|
||||
@@ -391,23 +412,6 @@ function add_node()
|
||||
end
|
||||
end
|
||||
|
||||
function update_node()
|
||||
local id = http.formvalue("id") -- Node id
|
||||
local data = http.formvalue("data") -- json new Data
|
||||
if id and data then
|
||||
local data_t = jsonParse(data) or {}
|
||||
if next(data_t) then
|
||||
for k, v in pairs(data_t) do
|
||||
uci:set(appname, id, k, v)
|
||||
end
|
||||
api.uci_save(uci, appname)
|
||||
http_write_json_ok()
|
||||
return
|
||||
end
|
||||
end
|
||||
http_write_json_error()
|
||||
end
|
||||
|
||||
function set_node()
|
||||
local type = http.formvalue("type")
|
||||
local config = http.formvalue("config")
|
||||
@@ -968,3 +972,87 @@ function fetch_certsha256()
|
||||
local data = api.fetch_cert_sha256(address, port, sni, 5)
|
||||
http_write_json(data ~= "" and { code = 1, data = data } or { code = 0 })
|
||||
end
|
||||
|
||||
function get_shunt_rules()
|
||||
local id = http.formvalue("id")
|
||||
local result = {}
|
||||
|
||||
if id then
|
||||
result = uci:get_all(appname, id)
|
||||
else
|
||||
local default_items = {}
|
||||
local other_items = {}
|
||||
uci:foreach(appname, "shunt_rules", function(t)
|
||||
if not t.group or t.group == "" then
|
||||
default_items[#default_items + 1] = t
|
||||
else
|
||||
other_items[#other_items + 1] = t
|
||||
end
|
||||
end)
|
||||
for i = 1, #default_items do result[#result + 1] = default_items[i] end
|
||||
for i = 1, #other_items do result[#result + 1] = other_items[i] end
|
||||
end
|
||||
http_write_json(result)
|
||||
end
|
||||
|
||||
function add_shunt_rule()
|
||||
local add_name = http.formvalue("add_name")
|
||||
local redirect = http.formvalue("redirect")
|
||||
|
||||
local uuid = add_name
|
||||
if add_name then
|
||||
local has = uci:get(appname, uuid)
|
||||
if has then
|
||||
http_write_json_error({ message = "This ID already exists." })
|
||||
return
|
||||
end
|
||||
else
|
||||
uuid = api.gen_short_uuid()
|
||||
end
|
||||
uci:section(appname, "shunt_rules", uuid)
|
||||
|
||||
local group = http.formvalue("group")
|
||||
if group and group ~= "default" then
|
||||
uci:set(appname, uuid, "group", group)
|
||||
end
|
||||
|
||||
if redirect == "1" then
|
||||
api.uci_save(uci, appname)
|
||||
http.redirect(api.url("shunt_rules", uuid))
|
||||
else
|
||||
api.uci_save(uci, appname)
|
||||
http_write_json_ok({uuid = uuid, redirect_url = api.url("shunt_rules", uuid)})
|
||||
end
|
||||
end
|
||||
|
||||
function delete_select_shunt_rules()
|
||||
local ids = http.formvalue("ids")
|
||||
local redirect = http.formvalue("redirect")
|
||||
string.gsub(ids, '[^' .. "," .. ']+', function(w)
|
||||
uci:foreach(appname, "nodes", function(s)
|
||||
if s["protocol"] and s["protocol"] == "_shunt" then
|
||||
uci:delete(appname, s[".name"], w)
|
||||
end
|
||||
end)
|
||||
uci:delete(appname, w)
|
||||
end)
|
||||
if redirect == "1" then
|
||||
api.uci_save(uci, appname)
|
||||
http.redirect(api.url("rule"))
|
||||
else
|
||||
api.uci_save(uci, appname, true, true)
|
||||
end
|
||||
end
|
||||
|
||||
function save_shunt_rule_order()
|
||||
local ids = http.formvalue("ids") or ""
|
||||
local new_order = {}
|
||||
for id in ids:gmatch("([^,]+)") do
|
||||
new_order[#new_order + 1] = id
|
||||
end
|
||||
for idx, name in ipairs(new_order) do
|
||||
luci.sys.call(string.format("uci -q reorder %s.%s=%d", appname, name, idx - 1))
|
||||
end
|
||||
api.sh_uci_commit(appname)
|
||||
http_write_json({ status = "ok" })
|
||||
end
|
||||
|
||||
@@ -101,6 +101,7 @@ if (has_singbox or has_xray) and #nodes_table > 0 then
|
||||
if current_node.protocol == "_shunt" then
|
||||
local shunt_lua = loadfile("/usr/lib/lua/luci/model/cbi/passwall2/client/include/shunt_options.lua")
|
||||
setfenv(shunt_lua, getfenv(1))(m, s, {
|
||||
s_cfgid = s:cfgsections()[1],
|
||||
node_id = current_node_id,
|
||||
node = current_node,
|
||||
socks_list = socks_list,
|
||||
|
||||
@@ -4,9 +4,17 @@ if not data.node_id or not data.node then
|
||||
return
|
||||
end
|
||||
|
||||
local s_cfgid = data.s_cfgid
|
||||
local current_node_id = data.node_id
|
||||
local node_list = data.node_list or api.get_node_list()
|
||||
|
||||
local groups = {}
|
||||
m.uci:foreach(appname, "shunt_rules", function(s)
|
||||
if s.group and s.group ~= "" then
|
||||
groups[s.group] = true
|
||||
end
|
||||
end)
|
||||
|
||||
local function get_cfgvalue()
|
||||
return function(self, section)
|
||||
return m:get(current_node_id, self.option)
|
||||
@@ -106,15 +114,27 @@ if api.is_finded("geoview") then
|
||||
.. "</ul>"
|
||||
end
|
||||
|
||||
shunt_group = add_option(ListValue, "shunt_group", translate("Group Name"))
|
||||
shunt_group:value("", translate("default"))
|
||||
for k, v in pairs(groups) do
|
||||
shunt_group:value(k)
|
||||
end
|
||||
|
||||
local shunt_group_val = m:get(current_node_id, "shunt_group") or ""
|
||||
shunt_group_val = shunt_group_val:lower()
|
||||
local shunt_rules = {}
|
||||
m.uci:foreach(appname, "shunt_rules", function(e)
|
||||
e.id = e[".name"]
|
||||
e.remarks = e.remarks or e[".name"]
|
||||
e["_node_option"] = e[".name"]
|
||||
e["_node_default"] = ""
|
||||
e["_fakedns_option"] = e[".name"] .. "_fakedns"
|
||||
e["_proxy_tag_option"] = e[".name"] .. "_proxy_tag"
|
||||
table.insert(shunt_rules, e)
|
||||
local group = e.group or ""
|
||||
group = group:lower()
|
||||
if group == shunt_group_val then
|
||||
e.id = e[".name"]
|
||||
e.remarks = e.remarks or e[".name"]
|
||||
e["_node_option"] = e[".name"]
|
||||
e["_node_default"] = ""
|
||||
e["_fakedns_option"] = e[".name"] .. "_fakedns"
|
||||
e["_proxy_tag_option"] = e[".name"] .. "_proxy_tag"
|
||||
table.insert(shunt_rules, e)
|
||||
end
|
||||
end)
|
||||
table.insert(shunt_rules, {
|
||||
id = ".default",
|
||||
@@ -199,6 +219,8 @@ end
|
||||
|
||||
local footer = Template(appname .. "/include/shunt_options")
|
||||
footer.api = api
|
||||
footer.config = m.config
|
||||
footer.id = current_node_id
|
||||
footer.s_cfgid = s_cfgid or current_node_id
|
||||
footer.normal_list = api.jsonc.stringify(node_list.normal_list)
|
||||
m:append(footer)
|
||||
|
||||
@@ -135,6 +135,9 @@ s.template = "cbi/tblsection"
|
||||
s.extedit = api.url("node_subscribe_config", "%s")
|
||||
function s.create(e, t)
|
||||
local id = TypedSection.create(e, t)
|
||||
uci:set(appname, id, "hysteria_up_mbps", "100")
|
||||
uci:set(appname, id, "hysteria_down_mbps", "100")
|
||||
api.uci_save(uci, appname)
|
||||
luci.http.redirect(e.extedit:format(id))
|
||||
end
|
||||
|
||||
|
||||
@@ -223,11 +223,9 @@ if #hysteria2_type > 0 then
|
||||
|
||||
o = s:option(Value, "hysteria_up_mbps", "Hy/Hy2 " .. translate("Max upload Mbps"))
|
||||
o.datatype = "uinteger"
|
||||
o.default = "100"
|
||||
|
||||
o = s:option(Value, "hysteria_down_mbps", "Hy/Hy2 " .. translate("Max download Mbps"))
|
||||
o.datatype = "uinteger"
|
||||
o.default = "100"
|
||||
end
|
||||
|
||||
o = s:option(Flag, "boot_update", translate("Update Once on Boot"), translate("Updates the subscription the first time runs automatically after each system boot."))
|
||||
|
||||
@@ -88,31 +88,6 @@ end
|
||||
|
||||
s:append(Template(appname .. "/rule/rule_version"))
|
||||
|
||||
local cfgname = "shunt_rules"
|
||||
s = m:section(TypedSection, cfgname, "Sing-Box/Xray " .. translate("Shunt Rule"), "<a style='color: red'>" .. translate("Please note attention to the priority, the higher the order, the higher the priority.") .. "</a>")
|
||||
s.template = "cbi/tblsection"
|
||||
s.anonymous = false
|
||||
s.addremove = true
|
||||
s.sortable = true
|
||||
s.extedit = api.url("shunt_rules", "%s")
|
||||
function s.create(e, t)
|
||||
TypedSection.create(e, t)
|
||||
luci.http.redirect(e.extedit:format(t))
|
||||
end
|
||||
function s.remove(e, t)
|
||||
m.uci:foreach(appname, "nodes", function(s)
|
||||
if s["protocol"] and s["protocol"] == "_shunt" then
|
||||
m:del(s[".name"], t)
|
||||
end
|
||||
end)
|
||||
TypedSection.remove(e, t)
|
||||
end
|
||||
|
||||
o = s:option(DummyValue, "remarks", translate("Remarks"))
|
||||
|
||||
local sortable = Template(appname .. "/cbi/sortable")
|
||||
sortable.api = api
|
||||
sortable.target_cfgname = cfgname
|
||||
m:append(sortable)
|
||||
s:append(Template(appname .. "/rule/shunt_rule_list"))
|
||||
|
||||
return api.return_map(m)
|
||||
|
||||
@@ -45,6 +45,19 @@ function clean_text(text)
|
||||
:gsub("[ \t]*\n[ \t]*", "\n")
|
||||
end
|
||||
|
||||
local remarks_lookup = {}
|
||||
local groups = {}
|
||||
m.uci:foreach(appname, "shunt_rules", function(s)
|
||||
if s[".name"] ~= arg[1] then
|
||||
if s.remarks then
|
||||
remarks_lookup[s.remarks] = s[".name"]
|
||||
end
|
||||
if s.group and s.group ~= "" then
|
||||
groups[s.group] = true
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
s = m:section(NamedSection, arg[1], "shunt_rules", "")
|
||||
s.addremove = false
|
||||
s.dynamic = false
|
||||
@@ -57,9 +70,34 @@ remarks.validate = function(self, value, section)
|
||||
if value == "" then
|
||||
return nil, translate("Remark cannot be empty.")
|
||||
end
|
||||
if remarks_lookup[value] then
|
||||
return nil, translate("This remark already exists, please change a new remark.")
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
o = s:option(Value, "group", translate("Group Name"))
|
||||
o.default = ""
|
||||
o:value("", translate("default"))
|
||||
for k, v in pairs(groups) do
|
||||
o:value(k)
|
||||
end
|
||||
o.write = function(self, section, value)
|
||||
value = api.trim(value)
|
||||
local lower = value:lower()
|
||||
|
||||
if lower == "" or lower == "default" then
|
||||
return m:del(section, self.option)
|
||||
end
|
||||
|
||||
for _, v in ipairs(self.keylist or {}) do
|
||||
if v:lower() == lower then
|
||||
return m:set(section, self.option, v)
|
||||
end
|
||||
end
|
||||
m:set(section, self.option, value)
|
||||
end
|
||||
|
||||
protocol = s:option(MultiValue, "protocol", translate("Protocol"))
|
||||
protocol:value("http")
|
||||
protocol:value("tls")
|
||||
|
||||
@@ -109,10 +109,12 @@ o.rewrite_option = o.option
|
||||
o = s:option(Value, _n("recv_window_conn"), translate("QUIC connection receive window"))
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, _n("idle_timeout"), translate("Idle Timeout"), translate("Example:") .. "30s (4s~120s)")
|
||||
o = s:option(Value, _n("idle_timeout"), translate("Idle Timeout"), translate("Units:seconds") .. " (4~120)")
|
||||
o.datatype = "range(4,120)"
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Value, _n("keep_alive_period"), translate("QUIC KeepAlive interval"), translate("Example:") .. "10s (2s~60s)")
|
||||
o = s:option(Value, _n("keep_alive_period"), translate("QUIC KeepAlive interval"), translate("Units:seconds") .. " (2~60)")
|
||||
o.datatype = "range(2,60)"
|
||||
o.rewrite_option = o.option
|
||||
|
||||
o = s:option(Flag, _n("disable_mtu_discovery"), translate("Disable MTU detection"))
|
||||
|
||||
@@ -394,10 +394,12 @@ o:depends({ [_n("protocol")] = "hysteria2" })
|
||||
o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps"))
|
||||
o:depends({ [_n("protocol")] = "hysteria2" })
|
||||
|
||||
o = s:option(Value, _n("hysteria2_idle_timeout"), translate("Idle Timeout"), translate("Example:") .. "30s (4s~120s)")
|
||||
o = s:option(Value, _n("hysteria2_idle_timeout"), translate("Idle Timeout"), translate("Units:seconds") .. " (4~120)")
|
||||
o.datatype = "range(4,120)"
|
||||
o:depends({ [_n("protocol")] = "hysteria2"})
|
||||
|
||||
o = s:option(Value, _n("hysteria2_keep_alive_period"), translate("QUIC KeepAlive interval"), translate("Example:") .. "10s (2s~60s)")
|
||||
o = s:option(Value, _n("hysteria2_keep_alive_period"), translate("QUIC KeepAlive interval"), translate("Units:seconds") .. " (2~60)")
|
||||
o.datatype = "range(2,60)"
|
||||
o:depends({ [_n("protocol")] = "hysteria2"})
|
||||
|
||||
o = s:option(Flag, _n("hysteria2_disable_mtu_discovery"), translate("Disable MTU detection"))
|
||||
@@ -803,6 +805,9 @@ o:value("", translate("Auto"))
|
||||
o:value("UseIPv4", translate("IPv4 Only"))
|
||||
o:value("UseIPv6", translate("IPv6 Only"))
|
||||
|
||||
o = s:option(Flag, _n("happy_eyeballs"), translate("Enable Happy Eyeballs"), translate("Attempts IPv4 and IPv6 simultaneously; automatically uses the faster connection."))
|
||||
o.default = 0
|
||||
|
||||
local protocols = s.fields[_n("protocol")].keylist
|
||||
if #protocols > 0 then
|
||||
for i, v in ipairs(protocols) do
|
||||
@@ -814,7 +819,11 @@ if #protocols > 0 then
|
||||
s.fields[_n("address")]:depends(depends_condition)
|
||||
s.fields[_n("port")]:depends(depends_condition)
|
||||
s.fields[_n("domain_resolver")]:depends(depends_condition)
|
||||
s.fields[_n("domain_strategy")]:depends(depends_condition)
|
||||
s.fields[_n("happy_eyeballs")]:depends(depends_condition)
|
||||
|
||||
local strategy_depends = api.clone(depends_condition)
|
||||
strategy_depends[_n("happy_eyeballs")] = false
|
||||
s.fields[_n("domain_strategy")]:depends(strategy_depends)
|
||||
|
||||
if v ~= "hysteria2" then
|
||||
s.fields[_n("tcp_fast_open")]:depends({ [_n("protocol")] = v })
|
||||
|
||||
@@ -457,10 +457,12 @@ if singbox_tags:find("with_quic") then
|
||||
o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps"))
|
||||
o:depends({ [_n("protocol")] = "hysteria2" })
|
||||
|
||||
o = s:option(Value, _n("hysteria2_idle_timeout"), translate("Idle Timeout"), translate("Example:") .. "30s (4s~120s)")
|
||||
o = s:option(Value, _n("hysteria2_idle_timeout"), translate("Idle Timeout"), translate("Units:seconds") .. " (4~120)")
|
||||
o.datatype = "range(4,120)"
|
||||
o:depends({ [_n("protocol")] = "hysteria2"})
|
||||
|
||||
o = s:option(Value, _n("hysteria2_keep_alive_period"), translate("QUIC KeepAlive interval"), translate("Example:") .. "10s (2s~60s)")
|
||||
o = s:option(Value, _n("hysteria2_keep_alive_period"), translate("QUIC KeepAlive interval"), translate("Units:seconds") .. " (2~60)")
|
||||
o.datatype = "range(2,60)"
|
||||
o:depends({ [_n("protocol")] = "hysteria2"})
|
||||
|
||||
o = s:option(Flag, _n("hysteria2_disable_mtu_discovery"), translate("Disable MTU detection"))
|
||||
@@ -629,6 +631,10 @@ if singbox_tags:find("with_utls") then
|
||||
o:depends({ [_n("reality")] = true })
|
||||
end
|
||||
|
||||
o = s:option(Flag, _n("anytls_disable_reuse"), translate("Disable TLS Reuse"))
|
||||
o.default = 0
|
||||
o:depends({ [_n("protocol")] = "anytls" })
|
||||
|
||||
o = s:option(ListValue, _n("transport"), translate("Transport"))
|
||||
o:value("tcp", "TCP")
|
||||
o:value("http", "HTTP")
|
||||
|
||||
@@ -1170,7 +1170,7 @@ function to_extract(app_name, file, subfix)
|
||||
exec("/bin/rm", {"-f", file})
|
||||
return {
|
||||
code = 1,
|
||||
error = i18n.translate("Not installed %s, Can't unzip!" % { tools_name })
|
||||
error = i18n.translatef("Not installed %s, Can't unzip!", tools_name)
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -1383,8 +1383,6 @@ function set_apply_on_parse(map)
|
||||
if old then old(self) end
|
||||
map:set("@global[0]", "timestamp", os.time())
|
||||
end
|
||||
local cbi = require "luci.cbi"
|
||||
map:append(cbi.Template(appname .. "/cbi/optimize_cbi_ui"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -647,6 +647,8 @@ function gen_outbound(flag, node, tag, proxy_table)
|
||||
idle_session_check_interval = "30s",
|
||||
idle_session_timeout = "30s",
|
||||
min_idle_session = 5,
|
||||
disable_reuse = (node.anytls_disable_reuse == "1") and true or nil,
|
||||
client_metadata = api.compare_versions(local_version, ">=", "1.13.16") and "anytls/0.0.13" or nil,
|
||||
tls = tls
|
||||
}
|
||||
end
|
||||
@@ -1599,6 +1601,9 @@ function gen_config(var)
|
||||
|
||||
--shunt rule
|
||||
uci:foreach(appname, "shunt_rules", function(e)
|
||||
if node["shunt_group"] ~= e.group then
|
||||
return
|
||||
end
|
||||
local outboundTag = gen_shunt_node(e[".name"])
|
||||
if outboundTag and e.remarks then
|
||||
if outboundTag == "default" then
|
||||
|
||||
@@ -170,9 +170,15 @@ function gen_outbound(flag, node, tag, proxy_table)
|
||||
mark = 255,
|
||||
domainStrategy = node.domain_strategy or "UseIP",
|
||||
tcpFastOpen = (node.tcp_fast_open == "1") and true or nil,
|
||||
tcpMptcp = (node.tcpMptcp == "1") and true or nil
|
||||
tcpMptcp = (node.tcpMptcp == "1") and true or nil,
|
||||
happyEyeballs = (node.happy_eyeballs == "1") and {
|
||||
TryDelayMs = 250,
|
||||
PrioritizeIPv6 = false,
|
||||
Interleave = 1,
|
||||
MaxConcurrentTry = 4
|
||||
} or nil
|
||||
},
|
||||
network = node.transport,
|
||||
[(api.compare_versions(xray_version, "<", "26.7.11")) and "network" or "method"] = node.transport, -- Todo: Remove version check and "network"
|
||||
security = node.stream_security,
|
||||
tlsSettings = (node.stream_security == "tls") and {
|
||||
serverName = node.tls_serverName,
|
||||
@@ -659,7 +665,7 @@ function gen_config_server(node)
|
||||
protocol = node.protocol,
|
||||
settings = settings,
|
||||
streamSettings = {
|
||||
network = node.transport,
|
||||
[(api.compare_versions(xray_version, "<", "26.7.11")) and "network" or "method"] = node.transport, -- Todo: Remove version check and "network"
|
||||
security = "none",
|
||||
tlsSettings = ("1" == node.tls) and {
|
||||
disableSystemRoot = false,
|
||||
@@ -1073,6 +1079,10 @@ function gen_config(var)
|
||||
if outboundTag then
|
||||
valid_nodes[#valid_nodes + 1] = outboundTag
|
||||
end
|
||||
-- Check if balancing node duplicates fallback node
|
||||
if _node.fallback_node == blc_node_id then
|
||||
_node.fallback_node = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
if #valid_nodes == 0 then return nil end
|
||||
@@ -1392,6 +1402,9 @@ function gen_config(var)
|
||||
|
||||
--shunt rule
|
||||
uci:foreach(appname, "shunt_rules", function(e)
|
||||
if node["shunt_group"] ~= e.group then
|
||||
return
|
||||
end
|
||||
local outboundTag = gen_shunt_node(e[".name"])
|
||||
if outboundTag and e.remarks then
|
||||
if outboundTag == "default" then
|
||||
|
||||
@@ -1 +1,18 @@
|
||||
<!-- map footer -->
|
||||
<script type="text/javascript">
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
setTimeout(updateElements, 200);
|
||||
function updateElements() {
|
||||
// Remove extra line breaks between the value control and its description.
|
||||
document.querySelectorAll("div.cbi-value-description").forEach(function(descDiv) {
|
||||
var prev = descDiv.previousSibling;
|
||||
while (prev && prev.nodeType === Node.TEXT_NODE && prev.textContent.trim() === "") {
|
||||
prev = prev.previousSibling;
|
||||
}
|
||||
if (prev && prev.nodeType === Node.ELEMENT_NODE && prev.tagName === "BR") {
|
||||
prev.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -1,2 +1,24 @@
|
||||
<!-- map header -->
|
||||
<script src="<%=resource%>/view/<%=self.api.appname%>/cbi.js"></script>
|
||||
<script src="<%=resource%>/view/<%=self.api.appname%>/func.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
function update_config(cfgid, data, redirect_url) {
|
||||
XHR.get('<%=self.api.url("update_config")%>', {
|
||||
id: cfgid,
|
||||
data: JSON.stringify(data)
|
||||
},
|
||||
function(x, data) {
|
||||
if (x && x.status == 200 && data.code == 1) {
|
||||
if (redirect_url) {
|
||||
window.location.href = redirect_url;
|
||||
}
|
||||
}
|
||||
else {
|
||||
alert("<%:Error%>");
|
||||
}
|
||||
});
|
||||
}
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
<script type="text/javascript">
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
setTimeout(updateElements, 200);
|
||||
function updateElements() {
|
||||
document.querySelectorAll("input.btn.cbi-button.cbi-button-up").forEach(function(btn) {
|
||||
btn.value = "<%:Move up%>";
|
||||
});
|
||||
document.querySelectorAll("input.btn.cbi-button.cbi-button-down").forEach(function(btn) {
|
||||
btn.value = "<%:Move down%>";
|
||||
});
|
||||
document.querySelectorAll("div.cbi-value-description").forEach(function(descDiv) {
|
||||
var prev = descDiv.previousSibling;
|
||||
while (prev && prev.nodeType === Node.TEXT_NODE && prev.textContent.trim() === "") {
|
||||
prev = prev.previousSibling;
|
||||
}
|
||||
if (prev && prev.nodeType === Node.ELEMENT_NODE && prev.tagName === "BR") {
|
||||
prev.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -207,7 +207,7 @@ local appname = api.appname
|
||||
let new_val = el.target.value
|
||||
const new_hasItem = shunt_list.find(element => element.id == new_val);
|
||||
if (new_hasItem) {
|
||||
XHR.get('<%=api.url("update_node")%>', {
|
||||
XHR.get('<%=api.url("update_config")%>', {
|
||||
id: "<%=self.global_cfgid%>",
|
||||
data: JSON.stringify({
|
||||
node: new_val
|
||||
|
||||
@@ -53,6 +53,22 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
waitForElement('select[name*="<%=self.config%>"][name*="shunt_group"]', function(el) {
|
||||
let o_val = el.value;
|
||||
el.addEventListener("change", () => {
|
||||
el.blur();
|
||||
if (o_val != el.value) {
|
||||
let save = true;
|
||||
if (save) {
|
||||
update_config("<%=self.id%>", {
|
||||
shunt_group: getOption("<%=self.config%>", "<%=self.s_cfgid%>", "shunt_group").value,
|
||||
}, get_current_url());
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
document.addEventListener("DOMContentLoaded", () => setTimeout(() => {
|
||||
refresh_depends();
|
||||
const table_dom = document.getElementById("cbi-passwall2-shunt_option_list");
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<%
|
||||
local api = self.api
|
||||
-%>
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
let node_id = "<%=self.section%>";
|
||||
let node_config_url = '<%=self.api.url("node_config")%>/' + node_id;
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
waitForElement('select[name*="<%=self.config%>"][name*="type"]', function(el) {
|
||||
let o_val = el.value;
|
||||
@@ -11,13 +11,18 @@ local api = self.api
|
||||
if (o_val != el.value) {
|
||||
let save = true;
|
||||
if (save) {
|
||||
//document.getElementsByClassName('cbi-button-save')[0].click();
|
||||
update_node({
|
||||
remarks: getOption("remarks").value,
|
||||
group: getOption("group").value,
|
||||
type: getOption("type").value,
|
||||
/*
|
||||
const saveBtn = document.getElementsByClassName('cbi-button-save');
|
||||
if (saveBtn && saveBtn.length > 0) {
|
||||
saveBtn[saveBtn.length - 1].click();
|
||||
}
|
||||
*/
|
||||
update_config(node_id, {
|
||||
remarks: getOption("<%=self.config%>", "<%=self.section%>", "remarks").value,
|
||||
group: getOption("<%=self.config%>", "<%=self.section%>", "group").value,
|
||||
type: getOption("<%=self.config%>", "<%=self.section%>", "type").value,
|
||||
protocol: ''
|
||||
});
|
||||
}, node_config_url);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -31,12 +36,17 @@ local api = self.api
|
||||
let name = name_split[name_split.length - 1];
|
||||
let save = true;
|
||||
if (save) {
|
||||
//document.getElementsByClassName('cbi-button-save')[0].click();
|
||||
update_node({
|
||||
remarks: getOption("remarks").value,
|
||||
group: getOption("group").value,
|
||||
protocol: getOption(name).value
|
||||
});
|
||||
/*
|
||||
const saveBtn = document.getElementsByClassName('cbi-button-save');
|
||||
if (saveBtn && saveBtn.length > 0) {
|
||||
saveBtn[saveBtn.length - 1].click();
|
||||
}
|
||||
*/
|
||||
update_config(node_id, {
|
||||
remarks: getOption("<%=self.config%>", "<%=self.section%>", "remarks").value,
|
||||
group: getOption("<%=self.config%>", "<%=self.section%>", "group").value,
|
||||
protocol: getOption("<%=self.config%>", "<%=self.section%>", name).value,
|
||||
}, node_config_url);
|
||||
} else {
|
||||
window.location.href = node_config_url + "?select_proto=" + el.value;
|
||||
}
|
||||
|
||||
@@ -1,64 +1,11 @@
|
||||
<%
|
||||
local api = self.api
|
||||
-%>
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
let node_id = "<%=self.section%>";
|
||||
let node_config_url = '<%=api.url("node_config")%>/' + node_id;
|
||||
|
||||
function waitForElement(selector, callback) {
|
||||
const el = document.querySelector(selector);
|
||||
if (el) return callback(el);
|
||||
const observer = new MutationObserver(() => {
|
||||
const el = document.querySelector(selector);
|
||||
if (el) {
|
||||
observer.disconnect();
|
||||
callback(el);
|
||||
}
|
||||
});
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
}
|
||||
|
||||
function getOption(opt) {
|
||||
var obj;
|
||||
var id = 'cbid.<%=self.config%>.' + node_id + '.' + opt;
|
||||
obj = document.getElementsByName(id)[0] || document.getElementById(id);
|
||||
if (obj) {
|
||||
var combobox = document.getElementById('cbi.combobox.' + id);
|
||||
if (combobox) {
|
||||
obj.combobox = combobox;
|
||||
}
|
||||
var div = document.getElementById(id);
|
||||
if (div && div.getElementsByTagName("li").length > 0) {
|
||||
obj = div;
|
||||
}
|
||||
return obj;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function update_node(data) {
|
||||
XHR.get('<%=api.url("update_node")%>', {
|
||||
id: node_id,
|
||||
data: JSON.stringify(data)
|
||||
},
|
||||
function(x, data) {
|
||||
if (x && x.status == 200 && data.code == 1) {
|
||||
window.location.href = node_config_url;
|
||||
}
|
||||
else {
|
||||
alert("<%:Error%>");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function fetchCertSha256(el) {
|
||||
if (el.getAttribute("data-loading") === "1") return;
|
||||
el.setAttribute("data-loading", "1");
|
||||
el.style.pointerEvents = "none";
|
||||
el.style.color = "red";
|
||||
XHR.get('<%=api.url("fetch_certsha256")%>', { id: node_id }, function(x, res) {
|
||||
XHR.get('<%=self.api.url("fetch_certsha256")%>', { id: "<%=self.section%>" }, function(x, res) {
|
||||
el.setAttribute("data-loading", "0");
|
||||
el.style.pointerEvents = "";
|
||||
el.style.color = "";
|
||||
|
||||
@@ -1750,7 +1750,7 @@ local current_node = map:get(section)
|
||||
opt.set(dom_prefix + 'tls', true);
|
||||
opt.set(dom_prefix + 'reality', false);
|
||||
opt.set(dom_prefix + 'alpn', queryParam.alpn || 'default');
|
||||
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || '');
|
||||
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || queryParam.peer || '');
|
||||
opt.set(dom_prefix + 'tls_allowInsecure', !((queryParam.allowinsecure ?? '0') === '0' && (queryParam.insecure ?? '0') === '0'));
|
||||
if (queryParam.fp && queryParam.fp.trim() != "") {
|
||||
opt.set(dom_prefix + 'utls', true);
|
||||
@@ -1761,7 +1761,7 @@ local current_node = map:get(section)
|
||||
opt.set(dom_prefix + 'tls', true);
|
||||
opt.set(dom_prefix + 'reality', true);
|
||||
opt.set(dom_prefix + 'alpn', queryParam.alpn || 'default');
|
||||
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || '');
|
||||
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || queryParam.peer || '');
|
||||
if (queryParam.fp && queryParam.fp.trim() != "") {
|
||||
opt.set(dom_prefix + 'utls', true);
|
||||
opt.set(dom_prefix + 'fingerprint', queryParam.fp);
|
||||
|
||||
@@ -0,0 +1,475 @@
|
||||
<%
|
||||
local api = require "luci.passwall2.api"
|
||||
local appname = api.appname
|
||||
local uci = api.uci
|
||||
-%>
|
||||
<script src="<%=resource%>/view/<%=appname%>/Sortable.min.js"></script>
|
||||
|
||||
<style>
|
||||
table th, .table .th {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table td, .table .td {
|
||||
text-align: center;
|
||||
/* white-space: nowrap; */
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
.td.cbi-section-actions {
|
||||
text-align: right !important;
|
||||
}
|
||||
|
||||
.shunt_rule-wrapper {
|
||||
align-items: center;
|
||||
display: inline-flex !important;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.cbi-tabmenu > li {
|
||||
margin-right: 2px !important;
|
||||
}
|
||||
|
||||
.cbi-tabmenu > li:last-child {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
.shunt_rule-wrapper .drag-handle {
|
||||
cursor: grab !important;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
font-weight: 100;
|
||||
padding: 0 !important;
|
||||
line-height: inherit;
|
||||
user-select: none;
|
||||
align-self: stretch;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.shunt_rule-wrapper .drag-handle:hover {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* enable flex for small screens*/
|
||||
@media screen and (max-width: 1152px) {
|
||||
.cbi-section-table-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.cbi-section-table-titles {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.td-actions {
|
||||
padding-top: 0 !important;
|
||||
border-top-width: 0 !important;
|
||||
flex: 1 1 350px;
|
||||
}
|
||||
}
|
||||
|
||||
/* shrink actionbar even further for mobile devices */
|
||||
@media screen and (max-width: 500px) {
|
||||
.shunt_rule-wrapper {
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.cbi-button {
|
||||
margin-left: 0 !important;
|
||||
margin-right: 1px !important;
|
||||
}
|
||||
|
||||
.td-actions {
|
||||
padding-left: 5px!important;
|
||||
padding-right: 5px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.sortable-chosen {
|
||||
background-color: rgba(220, 235, 245, 0.4) !important;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.sortable-ghost {
|
||||
background: #cce5ff !important;
|
||||
height: 3px !important;
|
||||
}
|
||||
|
||||
.dragging-row {
|
||||
background-color: rgba(131, 191, 255, 0.7) !important;
|
||||
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||
}
|
||||
</style>
|
||||
|
||||
<% if api.is_js_luci() then -%>
|
||||
<script type="text/javascript">
|
||||
var cbi_t = [];
|
||||
function cbi_t_add(section, tab) {
|
||||
var t = document.getElementById('tab.' + section + '.' + tab);
|
||||
var c = document.getElementById('container.' + section + '.' + tab);
|
||||
|
||||
if( t && c ) {
|
||||
cbi_t[section] = (cbi_t[section] || [ ]);
|
||||
cbi_t[section][tab] = { 'tab': t, 'container': c, 'cid': c.id };
|
||||
}
|
||||
}
|
||||
|
||||
function cbi_t_switch(section, tab) {
|
||||
if( cbi_t[section] && cbi_t[section][tab] ) {
|
||||
// Before switching tabs, first deselect all currently active tabs.
|
||||
var o = cbi_t[section][tab];
|
||||
var h = document.getElementById('tab.' + section);
|
||||
for( var tid in cbi_t[section] ) {
|
||||
var o2 = cbi_t[section][tid];
|
||||
if( o.tab.id != o2.tab.id ) {
|
||||
o2.tab.className = o2.tab.className.replace(/(^| )cbi-tab( |$)/, " cbi-tab-disabled ");
|
||||
o2.container.style.display = 'none';
|
||||
}
|
||||
else {
|
||||
if(h) h.value = tab;
|
||||
o2.tab.className = o2.tab.className.replace(/(^| )cbi-tab-disabled( |$)/, " cbi-tab ");
|
||||
o2.container.style.display = 'block';
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
</script>
|
||||
<%- end %>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
function toPage(action, cbi_id) {
|
||||
if (action == "add") {
|
||||
const dom = document.getElementsByClassName("cbi-tab")[0];
|
||||
const current_group = dom ? (dom.getAttribute("group_name") || "default") : "default";
|
||||
const add_name_val = document.getElementById("add_name_" + current_group).value;
|
||||
if (add_name_val.length > 1) {
|
||||
XHR.get('<%=api.url("add_shunt_rule")%>', {
|
||||
add_name: add_name_val,
|
||||
group: current_group
|
||||
},
|
||||
function(x, result) {
|
||||
if (x && x.status === 200) {
|
||||
if (result.code == 1) {
|
||||
window.location.href = result.data.redirect_url;
|
||||
} else {
|
||||
alert(result.data.message);
|
||||
}
|
||||
} else {
|
||||
alert("<%:Save failed!%>");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
if (action == "edit") {
|
||||
window.location.href='<%=api.url("shunt_rules")%>/' + cbi_id;
|
||||
}
|
||||
if (action == "del") {
|
||||
window.location.href = '<%=api.url("delete_select_shunt_rules")%>' + "?redirect=1&ids=" + cbi_id;
|
||||
}
|
||||
}
|
||||
|
||||
function row_top(btn, group) {
|
||||
const row = btn.closest("tr");
|
||||
if (!row) return;
|
||||
const parent = row.parentNode;
|
||||
let firstDataRow = parent.querySelector("tr:not(.cbi-section-table-titles)");
|
||||
if (firstDataRow && firstDataRow !== row) {
|
||||
parent.insertBefore(row, firstDataRow);
|
||||
let save_order_btn = document.getElementById("save_order_btn_" + group);
|
||||
if (save_order_btn) {
|
||||
const new_order = get_shunt_rule_order(group);
|
||||
if (!arraysEqual(new_order, origin_group_shunt_rule_order[group])) {
|
||||
save_order_btn.style.display = null;
|
||||
} else {
|
||||
save_order_btn.style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function get_shunt_rule_order(group) {
|
||||
let table = document.getElementById("cbi-<%=appname%>-shunt_rules-" + group + "-table");
|
||||
if (!table) {
|
||||
return;
|
||||
}
|
||||
let rows = table.querySelectorAll("tr.cbi-section-table-row");
|
||||
if (!rows || rows.length === 0) {
|
||||
return;
|
||||
}
|
||||
var ids = [];
|
||||
rows.forEach(function(row) {
|
||||
var id = row.id.replace("cbi-<%=appname%>-", "");
|
||||
ids.push(id);
|
||||
});
|
||||
return ids;
|
||||
}
|
||||
|
||||
function save_current_page_order(group) {
|
||||
var table = document.getElementById("cbi-<%=appname%>-shunt_rules-" + group + "-table");
|
||||
if (!table) {
|
||||
alert("<%:No table!%>");
|
||||
return;
|
||||
}
|
||||
var rows = table.querySelectorAll("tr.cbi-section-table-row");
|
||||
if (!rows || rows.length === 0) {
|
||||
alert("<%:No shunt_rules!%>");
|
||||
return;
|
||||
}
|
||||
var btn = document.getElementById("save_order_btn_" + group);
|
||||
if (btn) {
|
||||
btn.style.display = "none";
|
||||
btn.disabled = true;
|
||||
}
|
||||
var ids = [];
|
||||
rows.forEach(function(row) {
|
||||
var id = row.id.replace("cbi-<%=appname%>-", "");
|
||||
ids.push(id);
|
||||
});
|
||||
XHR.get('<%=api.url("save_shunt_rule_order")%>', {
|
||||
group: group,
|
||||
ids: ids.join(",")
|
||||
},
|
||||
function(x, result) {
|
||||
if (btn) {
|
||||
btn.style.display = null;
|
||||
btn.disabled = false;
|
||||
}
|
||||
if (x && x.status === 200) {
|
||||
origin_group_shunt_rule_order[group] = get_shunt_rule_order(group);
|
||||
alert("<%:Saved current page order successfully.%>");
|
||||
if (btn) {
|
||||
btn.style.display = "none";
|
||||
}
|
||||
} else {
|
||||
alert("<%:Save failed!%>");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function arraysEqual(a, b) {
|
||||
if (a === b) return true;
|
||||
if (a == null || b == null) return false;
|
||||
if (a.length !== b.length) return false;
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// List drag and rearrange
|
||||
function initSortableForTable(table) {
|
||||
if (!table) return null;
|
||||
let group = table.id.replace("cbi-<%=appname%>-shunt_rules-", "").replace("-table", "")
|
||||
var root = table.querySelector('tbody') || table;
|
||||
if (root._sortable_initialized) return root._sortable_instance;
|
||||
root._sortable_initialized = true;
|
||||
var opts = {
|
||||
handle: ".drag-handle",
|
||||
draggable: "tr.cbi-section-table-row",
|
||||
animation: 150,
|
||||
ghostClass: "dragging-row",
|
||||
fallbackOnBody: true,
|
||||
forceFallback: false,
|
||||
swapThreshold: 0.65,
|
||||
onEnd: function (evt) {
|
||||
//save_current_page_order(group); // Auto save
|
||||
let save_order_btn = document.getElementById("save_order_btn_" + group);
|
||||
if (save_order_btn) {
|
||||
const new_order = get_shunt_rule_order(group);
|
||||
if (!arraysEqual(new_order, origin_group_shunt_rule_order[group])) {
|
||||
save_order_btn.style.display = null;
|
||||
} else {
|
||||
save_order_btn.style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
try {
|
||||
var instance = Sortable.create(root, opts);
|
||||
root._sortable_instance = instance;
|
||||
return instance;
|
||||
} catch (err) {
|
||||
root._sortable_initialized = false;
|
||||
console.error("Sortable init failed:", err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function initAllSortable(group_shunt_rules) {
|
||||
if (typeof Sortable === 'undefined') {
|
||||
var retries = 0;
|
||||
var maxRetries = 25;
|
||||
var t = setInterval(function () {
|
||||
retries++;
|
||||
if (typeof Sortable !== 'undefined') {
|
||||
clearInterval(t);
|
||||
for (var group in group_shunt_rules) {
|
||||
var table = document.getElementById("cbi-<%=appname%>-shunt_rules-" + group + "-table");
|
||||
initSortableForTable(table);
|
||||
}
|
||||
} else if (retries >= maxRetries) {
|
||||
clearInterval(t);
|
||||
}
|
||||
}, 200);
|
||||
} else {
|
||||
for (var group in group_shunt_rules) {
|
||||
var table = document.getElementById("cbi-<%=appname%>-shunt_rules-" + group + "-table");
|
||||
initSortableForTable(table);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function escape_html(s) {
|
||||
return s.replace(/[&<>"']/g, c => ({
|
||||
"&":"&", "<":"<", ">":">", '"':""", "'":"'"
|
||||
}[c]));
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<script type="text/template" id="shunt_rules-table-template">
|
||||
<fieldset class="cbi-section cbi-tblsection" id="cbi-<%=appname%>-shunt_rules-{{group}}-fieldset">
|
||||
<table class="table cbi-section-table" id="cbi-<%=appname%>-shunt_rules-{{group}}-table" style="">
|
||||
<tr class="tr cbi-section-table-titles anonymous">
|
||||
<th class="th cbi-section-table-cell" style="width:30%">ID</th>
|
||||
<th class="th cbi-section-table-cell" style="width:30%"><%:Remarks%></th>
|
||||
<th class="th cbi-section-table-cell cbi-section-actions"></th>
|
||||
</tr>
|
||||
{{shunt_rule-tr}}
|
||||
</table>
|
||||
<div class="cbi-section-create cbi-tblsection-create">
|
||||
<input type="text" id="add_name_{{group}}">
|
||||
<input class="cbi-button cbi-button-add" type="button" value="<%:Add%>" onclick="toPage('add')">
|
||||
<input class="cbi-button cbi-button-apply" style="display: none" type="button" id="save_order_btn_{{group}}" value="<%:Save Order%>" onclick="save_current_page_order('{{group}}')">
|
||||
</div>
|
||||
</fieldset>
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="shunt_rule-tr-template">
|
||||
<tr class="tr cbi-section-table-row" id="cbi-<%=appname%>-{{id}}">
|
||||
<td class="td cbi-value-field"><h3>{{id}}</h3></td>
|
||||
<td class="td cbi-value-field">{{remarks}}</td>
|
||||
<td class="td cbi-section-table-cell nowrap cbi-section-actions td-actions">
|
||||
<div class="shunt_rule-wrapper">
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" value="<%:To Top%>" onclick="row_top(this, '{{group}}')" title="<%:To Top%>"/>
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" value="<%:Edit%>" onclick="toPage('edit', '{{id}}')" alt="<%:Edit%>" title="<%:Edit%>">
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" value="<%:Delete%>" onclick="toPage('del', '{{id}}')" alt="<%:Delete%>" title="<%:Delete%>">
|
||||
<span class="drag-handle center" title="<%:Drag to reorder%>">⠿</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</script>
|
||||
|
||||
<fieldset class="cbi-section">
|
||||
<legend>Sing-Box/Xray <%:Shunt Rule%></legend>
|
||||
<div class="cbi-section-descr"><a style="color: red"><%:Please note attention to the priority, the higher the order, the higher the priority.%></a></div>
|
||||
<div id="shunt_rule_list"></div>
|
||||
</fieldset>
|
||||
|
||||
<script type="text/javascript">
|
||||
function loadNodeList() {
|
||||
XHR.get('<%=api.url("get_shunt_rules")%>', null, function(x, result) {
|
||||
var shunt_rule_list = result
|
||||
|
||||
var group_shunt_rules = {}
|
||||
for (let i = 0; i < shunt_rule_list.length; i++) {
|
||||
let _shunt_rule = shunt_rule_list[i]
|
||||
if (!_shunt_rule.group || _shunt_rule.group === "") {
|
||||
_shunt_rule.group = "default"
|
||||
}
|
||||
if (!group_shunt_rules[_shunt_rule.group]) {
|
||||
group_shunt_rules[_shunt_rule.group] = []
|
||||
}
|
||||
group_shunt_rules[_shunt_rule.group].push(_shunt_rule)
|
||||
}
|
||||
|
||||
var tab_ul_li_html = '';
|
||||
var tab_content_html = '';
|
||||
var shunt_rules_table_template = document.getElementById("shunt_rules-table-template");
|
||||
var shunt_rule_template = document.getElementById("shunt_rule-tr-template");
|
||||
var default_group = null
|
||||
for (let group in group_shunt_rules) {
|
||||
if (default_group == null)
|
||||
default_group = group
|
||||
|
||||
var table_html = "";
|
||||
if (true) {
|
||||
var new_shunt_rules_table_dom = shunt_rules_table_template.cloneNode(true);
|
||||
var _html = new_shunt_rules_table_dom.innerHTML;
|
||||
_html = _html.split("{{group}}").join(group);
|
||||
var shunt_rule_tr_html = "";
|
||||
for (var i = 0; i < group_shunt_rules[group].length; i++) {
|
||||
let o = group_shunt_rules[group][i]
|
||||
var newDom = shunt_rule_template.cloneNode(true);
|
||||
newDom.classList.add("cbi-rowstyle-" + (i % 2 + 1));
|
||||
var innerHTML = newDom.innerHTML;
|
||||
innerHTML = innerHTML.split("{{id}}").join(o[".name"]);
|
||||
innerHTML = innerHTML.split("{{group}}").join(o["group"] || "");
|
||||
innerHTML = innerHTML.split("{{remarks}}").join(o["remarks"] || "");
|
||||
|
||||
shunt_rule_tr_html += innerHTML
|
||||
}
|
||||
_html = _html.split("{{shunt_rule-tr}}").join(shunt_rule_tr_html);
|
||||
table_html = _html;
|
||||
}
|
||||
|
||||
var group_name = group
|
||||
if (group === "default") {
|
||||
group_name = "<%:default%>"
|
||||
}
|
||||
|
||||
tab_ul_li_html +=
|
||||
'<li group_name="' + group + '" id="tab.<%=appname%>.shunt_rules.' + group + '" class="cbi-tab">' +
|
||||
'<a onclick="this.blur(); return cbi_t_switch(\'<%=appname%>.shunt_rules\', \'' + group + '\')" href="<%=REQUEST_URI%>?tab.<%=appname%>.shunt_rules=' + group + '">' + escape_html(group_name) + " | " + "<font style='color: red'>" + group_shunt_rules[group].length + '</font></a>' +
|
||||
'</li>'
|
||||
tab_content_html +=
|
||||
'<div class="cbi-tabcontainer" id="container.<%=appname%>.shunt_rules.' + group + '" style="display: none;">' +
|
||||
'' + table_html +
|
||||
'</div>'
|
||||
}
|
||||
|
||||
function addTabDOM(li, content) {
|
||||
let tab_ul_html = '<ul class="cbi-tabmenu">';
|
||||
let tab_content_html = '<fieldset class="cbi-section-shunt_rule cbi-section-shunt_rule-tabbed" id="cbi-<%=appname%>-shunt_rules">';
|
||||
if (li) {
|
||||
tab_ul_html += li;
|
||||
}
|
||||
if (content) {
|
||||
tab_content_html += content;
|
||||
}
|
||||
tab_ul_html += '</ul>';
|
||||
tab_content_html += '</fieldset>';
|
||||
return tab_ul_html + tab_content_html;
|
||||
}
|
||||
|
||||
document.getElementById("shunt_rule_list").innerHTML = addTabDOM(tab_ul_li_html, tab_content_html);
|
||||
|
||||
for (let group in group_shunt_rules) {
|
||||
cbi_t_add("<%=appname%>.shunt_rules", group)
|
||||
}
|
||||
|
||||
if (default_group) {
|
||||
cbi_t_switch("<%=appname%>.shunt_rules", default_group)
|
||||
}
|
||||
|
||||
origin_group_shunt_rule_order = {};
|
||||
for (let group in group_shunt_rules) {
|
||||
origin_group_shunt_rule_order[group] = get_shunt_rule_order(group);
|
||||
}
|
||||
|
||||
initAllSortable(group_shunt_rules);
|
||||
});
|
||||
}
|
||||
|
||||
loadNodeList();
|
||||
|
||||
//]]>
|
||||
</script>
|
||||
@@ -1750,6 +1750,12 @@ msgstr "حل مشکل DNS دامنه"
|
||||
msgid "If the node address is a domain name, this DNS will be used for resolution."
|
||||
msgstr "اگر آدرس گره یک نام دامنه باشد، از این DNS برای حل مسئله استفاده خواهد شد."
|
||||
|
||||
msgid "Enable Happy Eyeballs"
|
||||
msgstr "فعال کردن چشمهای خوشحال"
|
||||
|
||||
msgid "Attempts IPv4 and IPv6 simultaneously; automatically uses the faster connection."
|
||||
msgstr "IPv4 و IPv6 را همزمان امتحان میکند؛ بهطور خودکار از اتصال سریعتر استفاده میکند."
|
||||
|
||||
msgid "Supports only Xray or Sing-box node types."
|
||||
msgstr "فقط گرههای نوع Xray یا Sing-box پشتیبانی میشوند."
|
||||
|
||||
@@ -2413,3 +2419,9 @@ msgstr "اندازه بسته Gecko (حداکثر)"
|
||||
|
||||
msgid "valid time (hh:mm)"
|
||||
msgstr "زمان معتبر (ساعت:میلیمتر)"
|
||||
|
||||
msgid "Disable TLS Reuse"
|
||||
msgstr "غیرفعال کردن استفاده مجدد از TLS"
|
||||
|
||||
msgid "Not Set"
|
||||
msgstr "تنظیم نشده"
|
||||
|
||||
@@ -1751,6 +1751,12 @@ msgstr "Разрешение DNS-запросов для домена"
|
||||
msgid "If the node address is a domain name, this DNS will be used for resolution."
|
||||
msgstr "Если адрес узла является доменным именем, для разрешения будет использоваться этот DNS-сервер."
|
||||
|
||||
msgid "Enable Happy Eyeballs"
|
||||
msgstr "Включить счастливые глаза"
|
||||
|
||||
msgid "Attempts IPv4 and IPv6 simultaneously; automatically uses the faster connection."
|
||||
msgstr "Одновременно пытается использовать IPv4 и IPv6; автоматически выбирает более быстрое соединение."
|
||||
|
||||
msgid "Supports only Xray or Sing-box node types."
|
||||
msgstr "Поддерживаются только узлы типа Xray или Sing-box."
|
||||
|
||||
@@ -2411,3 +2417,9 @@ msgstr "Размер упаковки Gecko (макс)"
|
||||
|
||||
msgid "valid time (hh:mm)"
|
||||
msgstr "Время действия (чч:мм)"
|
||||
|
||||
msgid "Disable TLS Reuse"
|
||||
msgstr "Отключить повторное использование TLS"
|
||||
|
||||
msgid "Not Set"
|
||||
msgstr "Не задано"
|
||||
|
||||
@@ -1736,6 +1736,12 @@ msgstr "域名 DNS 解析"
|
||||
msgid "If the node address is a domain name, this DNS will be used for resolution."
|
||||
msgstr "如果节点地址是域名,则将使用此 DNS 进行解析。"
|
||||
|
||||
msgid "Enable Happy Eyeballs"
|
||||
msgstr "启用 Happy Eyeballs"
|
||||
|
||||
msgid "Attempts IPv4 and IPv6 simultaneously; automatically uses the faster connection."
|
||||
msgstr "同时尝试 IPv4 和 IPv6 连接,自动使用更快的连接。"
|
||||
|
||||
msgid "Supports only Xray or Sing-box node types."
|
||||
msgstr "仅支持 Xray 或 Sing-box 类型节点。"
|
||||
|
||||
@@ -2399,3 +2405,9 @@ msgstr "Gecko 包大小(最大)"
|
||||
|
||||
msgid "valid time (hh:mm)"
|
||||
msgstr "有效时间(hh:mm)"
|
||||
|
||||
msgid "Disable TLS Reuse"
|
||||
msgstr "禁用 TLS 连接复用"
|
||||
|
||||
msgid "Not Set"
|
||||
msgstr "未设置"
|
||||
|
||||
@@ -1742,6 +1742,12 @@ msgstr "域名 DNS 解析"
|
||||
msgid "If the node address is a domain name, this DNS will be used for resolution."
|
||||
msgstr "如果節點地址是域名,則將使用此 DNS 進行解析。"
|
||||
|
||||
msgid "Enable Happy Eyeballs"
|
||||
msgstr "啓用 Happy Eyeballs"
|
||||
|
||||
msgid "Attempts IPv4 and IPv6 simultaneously; automatically uses the faster connection."
|
||||
msgstr "同時嘗試 IPv4 和 IPv6 鏈接,自動使用更快的鏈接。"
|
||||
|
||||
msgid "Supports only Xray or Sing-box node types."
|
||||
msgstr "僅支援 Xray 或 Sing-box 類型節點。"
|
||||
|
||||
@@ -2405,3 +2411,9 @@ msgstr "Gecko 包大小(最大)"
|
||||
|
||||
msgid "valid time (hh:mm)"
|
||||
msgstr "有效時間(hh:mm)"
|
||||
|
||||
msgid "Disable TLS Reuse"
|
||||
msgstr "禁用 TLS 連接復用"
|
||||
|
||||
msgid "Not Set"
|
||||
msgstr "未設置"
|
||||
|
||||
@@ -59,51 +59,6 @@ config global_subscribe
|
||||
|
||||
config global_singbox
|
||||
|
||||
config shunt_rules 'China'
|
||||
option remarks 'China'
|
||||
option network 'tcp,udp'
|
||||
option domain_list 'geosite:cn'
|
||||
option ip_list 'geoip:cn'
|
||||
|
||||
config shunt_rules 'Iran'
|
||||
option remarks 'Iran'
|
||||
option network 'tcp,udp'
|
||||
option domain_list 'geosite:ir
|
||||
#rule-set:remote:https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-ir.srs'
|
||||
option ip_list 'geoip:ir
|
||||
#rule-set:remote:https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geoip-ir.srs'
|
||||
|
||||
config shunt_rules 'Russia_Block'
|
||||
option remarks 'Russia_Block'
|
||||
option network 'tcp,udp'
|
||||
option domain_list 'geosite:ru-blocked
|
||||
#rule-set:remote:https://raw.githubusercontent.com/runetfreedom/russia-v2ray-rules-dat/refs/heads/release/sing-box/rule-set-geosite/geosite-ru-blocked.srs'
|
||||
option ip_list 'geoip:ru-blocked
|
||||
#rule-set:remote:https://raw.githubusercontent.com/runetfreedom/russia-v2ray-rules-dat/refs/heads/release/sing-box/rule-set-geoip/geoip-ru-blocked.srs'
|
||||
|
||||
config shunt_rules 'Russia'
|
||||
option remarks 'Russia'
|
||||
option network 'tcp,udp'
|
||||
option ip_list 'geoip:ru
|
||||
#rule-set:remote:https://raw.githubusercontent.com/runetfreedom/russia-v2ray-rules-dat/refs/heads/release/sing-box/rule-set-geoip/geoip-ru.srs'
|
||||
|
||||
config shunt_rules 'PrivateIP'
|
||||
option remarks 'PrivateIP'
|
||||
option network 'tcp,udp'
|
||||
option ip_list '10.0.0.0/8
|
||||
127.0.0.0/8
|
||||
198.51.100.0/24
|
||||
192.0.0.0/24
|
||||
169.254.0.0/16
|
||||
224.0.0.0/3
|
||||
192.168.0.0/16
|
||||
192.88.99.0/24
|
||||
100.64.0.0/10
|
||||
172.16.0.0/12
|
||||
0.0.0.0/8
|
||||
192.0.2.0/24
|
||||
203.0.113.0/24'
|
||||
|
||||
config nodes 'examplenode'
|
||||
option remarks 'Example'
|
||||
option type 'Xray'
|
||||
@@ -126,3 +81,151 @@ config nodes 'rulenode'
|
||||
option domainMatcher 'hybrid'
|
||||
option write_ipset_direct '1'
|
||||
option enable_geoview_ip '1'
|
||||
option shunt_group 'CN'
|
||||
|
||||
config shunt_rules 'DirectFront'
|
||||
option remarks 'DirectFront'
|
||||
option network 'tcp,udp'
|
||||
option domain_list 'domain:ip233.cn'
|
||||
option group 'CN'
|
||||
|
||||
config shunt_rules 'ProxyFront'
|
||||
option remarks 'ProxyFront'
|
||||
option network 'tcp,udp'
|
||||
option domain_list 'domain:www.gstatic.com
|
||||
domain:ssl.gstatic.com
|
||||
domain:fonts.gstatic.com'
|
||||
option group 'CN'
|
||||
|
||||
config shunt_rules 'DirectGame'
|
||||
option remarks 'DirectGame'
|
||||
option domain_list '# steam直连域名获取国内CDN走国内线路下载
|
||||
cm.steampowered.com
|
||||
steamserver.net
|
||||
|
||||
# steam国内CDN华为云
|
||||
steampipe.steamcontent.tnkjmec.com
|
||||
# steam国内CDN白山云
|
||||
st.dl.eccdnx.com
|
||||
st.dl.bscstorage.net
|
||||
st.dl.pinyuncloud.com
|
||||
# steam国内CDN新流云(原金山云)(支持ipv6)
|
||||
dl.steam.clngaa.com
|
||||
# steam国内CDN网宿
|
||||
cdn.mileweb.cs.steampowered.com.8686c.com
|
||||
cdn-ws.content.steamchina.com
|
||||
# steam国内CDN腾讯云 (蒸汽中国独占)
|
||||
cdn-qc.content.steamchina.com
|
||||
# steam国内CDN阿里云(支持ipv6)
|
||||
cdn-ali.content.steamchina.com
|
||||
xz.pphimalayanrt.com
|
||||
lv.queniujq.cn
|
||||
alibaba.cdn.steampipe.steamcontent.com
|
||||
|
||||
# 国内游戏geosite域名
|
||||
geosite:category-games@cn'
|
||||
option ip_list '# steam直连IP
|
||||
45.121.184.0/24
|
||||
103.10.124.0/23
|
||||
103.28.54.0/24
|
||||
146.66.152.0/24
|
||||
146.66.155.0/24
|
||||
153.254.86.0/24
|
||||
155.133.224.0/22
|
||||
155.133.230.0/24
|
||||
155.133.232.0/23
|
||||
155.133.234.0/24
|
||||
155.133.236.0/22
|
||||
155.133.240.0/23
|
||||
155.133.244.0/23
|
||||
155.133.246.0/24
|
||||
155.133.248.0/21
|
||||
162.254.192.0/21
|
||||
185.25.182.0/23
|
||||
190.217.32.0/22
|
||||
192.69.96.0/22
|
||||
205.196.6.0/24
|
||||
208.64.200.0/22
|
||||
208.78.164.0/22
|
||||
205.185.194.0/24'
|
||||
option network 'tcp,udp'
|
||||
option group 'CN'
|
||||
|
||||
config shunt_rules 'ProxyGame'
|
||||
option remarks 'ProxyGame'
|
||||
option domain_list '# steam 商店/客服/聊天/网页布局/API/二维码/Google云同步 代理URL
|
||||
steamcommunity.com
|
||||
www.steamcommunity.com
|
||||
store.steampowered.com
|
||||
checkout.steampowered.com
|
||||
api.steampowered.com
|
||||
help.steampowered.com
|
||||
login.steampowered.com
|
||||
store.akamai.steamstatic.com
|
||||
steambroadcast.akamaized.net
|
||||
steamvideo-a.akamaihd.net
|
||||
steamusercontent-a.akamaihd.net
|
||||
steamstore-a.akamaihd.net
|
||||
steamcommunity-a.akamaihd.net
|
||||
steamcdn-a.akamaihd.net
|
||||
steamuserimages-a.akamaihd.net
|
||||
community.akamai.steamstatic.com
|
||||
avatars.akamai.steamstatic.com
|
||||
community.steamstatic.com
|
||||
cdn.akamai.steamstatic.com
|
||||
avatars.steamstatic.com
|
||||
shared.akamai.steamstatic.com
|
||||
clan.akamai.steamstatic.com
|
||||
cdn.cloudflare.steamstatic.com
|
||||
community.cloudflare.steamstatic.com
|
||||
store.cloudflare.steamstatic.com
|
||||
avatars.cloudflare.steamstatic.com
|
||||
clan.cloudflare.steamstatic.com
|
||||
shared.cloudflare.steamstatic.com
|
||||
steam-chat.com
|
||||
steamcloud-ugc.storage.googleapis.com
|
||||
steamcloud-eu-ams.storage.googleapis.com
|
||||
steamcloud-eu-fra.storage.googleapis.com
|
||||
steamcloud-finland.storage.googleapis.com
|
||||
steamcloud-saopaulo.storage.googleapis.com
|
||||
steamcloud-singapore.storage.googleapis.com
|
||||
steamcloud-sydney.storage.googleapis.com
|
||||
steamcloud-taiwan.storage.googleapis.com
|
||||
steamcloud-eu.storage.googleapis.com
|
||||
domain:blizzard.com
|
||||
geosite:category-games'
|
||||
option network 'tcp,udp'
|
||||
option group 'CN'
|
||||
|
||||
config shunt_rules 'CN'
|
||||
option remarks 'CN'
|
||||
option domain_list 'geosite:cn'
|
||||
option ip_list 'geoip:cn'
|
||||
option network 'tcp,udp'
|
||||
option group 'CN'
|
||||
|
||||
config shunt_rules 'Iran'
|
||||
option remarks 'Iran'
|
||||
option network 'tcp,udp'
|
||||
option domain_list 'geosite:ir
|
||||
#rule-set:remote:https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-ir.srs'
|
||||
option ip_list 'geoip:ir
|
||||
#rule-set:remote:https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geoip-ir.srs'
|
||||
option group 'IR'
|
||||
|
||||
config shunt_rules 'Russia_Block'
|
||||
option remarks 'Russia_Block'
|
||||
option network 'tcp,udp'
|
||||
option domain_list 'geosite:ru-blocked
|
||||
#rule-set:remote:https://raw.githubusercontent.com/runetfreedom/russia-v2ray-rules-dat/refs/heads/release/sing-box/rule-set-geosite/geosite-ru-blocked.srs'
|
||||
option ip_list 'geoip:ru-blocked
|
||||
#rule-set:remote:https://raw.githubusercontent.com/runetfreedom/russia-v2ray-rules-dat/refs/heads/release/sing-box/rule-set-geoip/geoip-ru-blocked.srs'
|
||||
option group 'RU'
|
||||
|
||||
config shunt_rules 'Russia'
|
||||
option remarks 'Russia'
|
||||
option network 'tcp,udp'
|
||||
option ip_list 'geoip:ru
|
||||
#rule-set:remote:https://raw.githubusercontent.com/runetfreedom/russia-v2ray-rules-dat/refs/heads/release/sing-box/rule-set-geoip/geoip-ru.srs'
|
||||
option group 'RU'
|
||||
|
||||
|
||||
@@ -189,7 +189,9 @@ gen_shunt_list() {
|
||||
local default_outbound="redirect"
|
||||
[ "$default_node" = "_direct" ] && default_outbound="direct"
|
||||
local shunt_ids=$(uci show $CONFIG | grep "=shunt_rules" | awk -F '.' '{print $2}' | awk -F '=' '{print $1}')
|
||||
local shunt_group=$(config_n_get $node shunt_group)
|
||||
for shunt_id in $shunt_ids; do
|
||||
[ "${shunt_group}" != "$(config_n_get ${shunt_id} group)" ] && continue
|
||||
local shunt_node=$(config_n_get ${node} "${shunt_id}")
|
||||
[ -n "$shunt_node" ] && {
|
||||
local ipset_v4="psw2_${node}_${shunt_id}"
|
||||
@@ -702,15 +704,13 @@ add_firewall_rule() {
|
||||
fi
|
||||
|
||||
$ipt_n -N PSW2
|
||||
$ipt_n -A PSW2 $(dst $IPSET_DIRECT) -j RETURN
|
||||
$ipt_n -A PSW2 $(dst $IPSET_VPS) -j RETURN
|
||||
$ipt_n -A PSW2 $(comment "WAN_IP_RETURN") $(dst $IPSET_WAN) -j RETURN
|
||||
|
||||
[ "$accept_icmp" = "1" ] && insert_rule_after "$ipt_n" "PREROUTING" "prerouting_rule" "-p icmp -j PSW2"
|
||||
[ -z "${is_tproxy}" ] && insert_rule_after "$ipt_n" "PREROUTING" "prerouting_rule" "-p tcp -j PSW2"
|
||||
[ "$accept_icmp" = "1" ] && insert_rule_after "$ipt_n" "PREROUTING" "prerouting_rule" "$(dst $IPSET_DIRECT !) -p icmp -j PSW2"
|
||||
[ -z "${is_tproxy}" ] && insert_rule_after "$ipt_n" "PREROUTING" "prerouting_rule" "$(dst $IPSET_DIRECT !) -p tcp -j PSW2"
|
||||
|
||||
$ipt_n -N PSW2_OUTPUT
|
||||
$ipt_n -A PSW2_OUTPUT $(dst $IPSET_DIRECT) -j RETURN
|
||||
$ipt_n -A PSW2_OUTPUT $(dst $IPSET_VPS) -j RETURN
|
||||
$ipt_n -A PSW2_OUTPUT -m mark --mark 255 -j RETURN
|
||||
|
||||
@@ -722,10 +722,6 @@ add_firewall_rule() {
|
||||
$ipt_n -I PREROUTING -m set --match-set $IPSET_DIRECT src -j PSW2_DNS
|
||||
fi
|
||||
|
||||
$ipt_m -N PSW2_DIVERT
|
||||
$ipt_m -A PSW2_DIVERT -j MARK --set-mark ${FWMARK}
|
||||
$ipt_m -A PSW2_DIVERT -j ACCEPT
|
||||
|
||||
$ipt_m -N PSW2_RULE
|
||||
$ipt_m -A PSW2_RULE -j CONNMARK --restore-mark
|
||||
$ipt_m -A PSW2_RULE -m mark --mark ${FWMARK} -j RETURN
|
||||
@@ -734,17 +730,16 @@ add_firewall_rule() {
|
||||
$ipt_m -A PSW2_RULE -j CONNMARK --save-mark
|
||||
|
||||
$ipt_m -N PSW2
|
||||
$ipt_m -A PSW2 $(dst $IPSET_DIRECT) -j RETURN
|
||||
# Socket Only TCP, UDP Invalid.
|
||||
$ipt_m -A PSW2 -p tcp -m socket -j MARK --set-mark ${FWMARK}
|
||||
$ipt_m -A PSW2 -p tcp -m socket -j ACCEPT
|
||||
$ipt_m -A PSW2 $(dst $IPSET_VPS) -j RETURN
|
||||
$ipt_m -A PSW2 $(comment "WAN_IP_RETURN") $(dst $IPSET_WAN) -j RETURN
|
||||
$ipt_m -A PSW2 -m conntrack --ctdir REPLY -j RETURN
|
||||
|
||||
insert_rule_before "$ipt_m" "PREROUTING" "mwan3" "-j PSW2"
|
||||
# Only TCP, UDP Invalid.
|
||||
insert_rule_before "$ipt_m" "PREROUTING" "PSW2" "-p tcp -m socket -j PSW2_DIVERT"
|
||||
insert_rule_before "$ipt_m" "PREROUTING" "mwan3" "$(dst $IPSET_DIRECT !) -j PSW2"
|
||||
|
||||
$ipt_m -N PSW2_OUTPUT
|
||||
$ipt_m -A PSW2_OUTPUT $(dst $IPSET_DIRECT) -j RETURN
|
||||
$ipt_m -A PSW2_OUTPUT $(dst $IPSET_VPS) -j RETURN
|
||||
$ipt_m -A PSW2_OUTPUT -m conntrack --ctdir REPLY -j RETURN
|
||||
$ipt_m -A PSW2_OUTPUT -m mark --mark 255 -j RETURN
|
||||
@@ -754,12 +749,10 @@ add_firewall_rule() {
|
||||
|
||||
[ "$accept_icmpv6" = "1" ] && {
|
||||
$ip6t_n -N PSW2
|
||||
$ip6t_n -A PSW2 $(dst $IPSET_DIRECT6) -j RETURN
|
||||
$ip6t_n -A PSW2 $(dst $IPSET_VPS6) -j RETURN
|
||||
$ip6t_n -A PREROUTING -p ipv6-icmp -j PSW2
|
||||
$ip6t_n -A PREROUTING $(dst $IPSET_DIRECT6 !) -p ipv6-icmp -j PSW2
|
||||
|
||||
$ip6t_n -N PSW2_OUTPUT
|
||||
$ip6t_n -A PSW2_OUTPUT $(dst $IPSET_DIRECT6) -j RETURN
|
||||
$ip6t_n -A PSW2_OUTPUT $(dst $IPSET_VPS6) -j RETURN
|
||||
$ip6t_n -A PSW2_OUTPUT -m mark --mark 255 -j RETURN
|
||||
}
|
||||
@@ -772,10 +765,6 @@ add_firewall_rule() {
|
||||
$ip6t_n -I PREROUTING -m set --match-set $IPSET_DIRECT6 src -j PSW2_DNS
|
||||
fi
|
||||
|
||||
$ip6t_m -N PSW2_DIVERT
|
||||
$ip6t_m -A PSW2_DIVERT -j MARK --set-mark ${FWMARK}
|
||||
$ip6t_m -A PSW2_DIVERT -j ACCEPT
|
||||
|
||||
$ip6t_m -N PSW2_RULE
|
||||
$ip6t_m -A PSW2_RULE -j CONNMARK --restore-mark
|
||||
$ip6t_m -A PSW2_RULE -m mark --mark ${FWMARK} -j RETURN
|
||||
@@ -784,18 +773,17 @@ add_firewall_rule() {
|
||||
$ip6t_m -A PSW2_RULE -j CONNMARK --save-mark
|
||||
|
||||
$ip6t_m -N PSW2
|
||||
$ip6t_m -A PSW2 $(dst $IPSET_DIRECT6) -j RETURN
|
||||
# Socket Only TCP, UDP Invalid.
|
||||
$ip6t_m -A PSW2 -p tcp -m socket -j MARK --set-mark ${FWMARK}
|
||||
$ip6t_m -A PSW2 -p tcp -m socket -j ACCEPT
|
||||
$ip6t_m -A PSW2 $(dst $IPSET_VPS6) -j RETURN
|
||||
$ip6t_m -A PSW2 $(comment "WAN6_IP_RETURN") $(dst $IPSET_WAN6) -j RETURN
|
||||
$ip6t_m -A PSW2 -m conntrack --ctdir REPLY -j RETURN
|
||||
|
||||
insert_rule_before "$ip6t_m" "PREROUTING" "mwan3" "-j PSW2"
|
||||
# Only TCP, UDP Invalid.
|
||||
insert_rule_before "$ip6t_m" "PREROUTING" "PSW2" "-p tcp -m socket -j PSW2_DIVERT"
|
||||
insert_rule_before "$ip6t_m" "PREROUTING" "mwan3" "$(dst $IPSET_DIRECT6 !) -j PSW2"
|
||||
|
||||
$ip6t_m -N PSW2_OUTPUT
|
||||
$ip6t_m -A PSW2_OUTPUT -m mark --mark 255 -j RETURN
|
||||
$ip6t_m -A PSW2_OUTPUT $(dst $IPSET_DIRECT6) -j RETURN
|
||||
$ip6t_m -A PSW2_OUTPUT $(dst $IPSET_VPS6) -j RETURN
|
||||
$ip6t_m -A PSW2_OUTPUT -m conntrack --ctdir REPLY -j RETURN
|
||||
|
||||
@@ -860,14 +848,14 @@ add_firewall_rule() {
|
||||
# Loading local router proxy TCP
|
||||
if [ -n "$NODE" ] && [ "$TCP_LOCALHOST_PROXY" = "1" ]; then
|
||||
[ "$accept_icmp" = "1" ] && {
|
||||
$ipt_n -A OUTPUT -p icmp -j PSW2_OUTPUT
|
||||
$ipt_n -A OUTPUT $(dst $IPSET_DIRECT !) -p icmp -j PSW2_OUTPUT
|
||||
$ipt_n -A PSW2_OUTPUT -p icmp -d $FAKE_IP $(REDIRECT)
|
||||
add_shunt_t_rule "${SHUNT_LIST4}" "$ipt_n -A PSW2_OUTPUT -p icmp" "$(REDIRECT)"
|
||||
$ipt_n -A PSW2_OUTPUT -p icmp $(REDIRECT)
|
||||
}
|
||||
|
||||
[ "$accept_icmpv6" = "1" ] && {
|
||||
$ip6t_n -A OUTPUT -p ipv6-icmp -j PSW2_OUTPUT
|
||||
$ip6t_n -A OUTPUT $(dst $IPSET_DIRECT6 !) -p ipv6-icmp -j PSW2_OUTPUT
|
||||
$ip6t_n -A PSW2_OUTPUT -p ipv6-icmp -d $FAKE_IP_6 $(REDIRECT)
|
||||
add_shunt_t_rule "${SHUNT_LIST6}" "$ip6t_n -A PSW2_OUTPUT -p ipv6-icmp" "$(REDIRECT)"
|
||||
$ip6t_n -A PSW2_OUTPUT -p ipv6-icmp $(REDIRECT)
|
||||
@@ -882,11 +870,11 @@ add_firewall_rule() {
|
||||
$ipt_tmp -A PSW2_OUTPUT -p tcp -d $FAKE_IP ${ipt_j}
|
||||
add_shunt_t_rule "${SHUNT_LIST4}" "$ipt_tmp -A PSW2_OUTPUT -p tcp" "${ipt_j}" $TCP_REDIR_PORTS
|
||||
add_port_rules "$ipt_tmp -A PSW2_OUTPUT -p tcp" $TCP_REDIR_PORTS "${ipt_j}"
|
||||
[ -z "${is_tproxy}" ] && $ipt_n -A OUTPUT -p tcp -j PSW2_OUTPUT
|
||||
[ -z "${is_tproxy}" ] && $ipt_n -A OUTPUT $(dst $IPSET_DIRECT !) -p tcp -j PSW2_OUTPUT
|
||||
[ -n "${is_tproxy}" ] && {
|
||||
$ipt_m -A PSW2 $(comment "${comment_l}") -p tcp -i lo $(REDIRECT $REDIR_PORT TPROXY)
|
||||
$ipt_m -A PSW2 $(comment "${comment_l}") -p tcp -i lo -j RETURN
|
||||
insert_rule_before "$ipt_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) -p tcp -j PSW2_OUTPUT"
|
||||
insert_rule_before "$ipt_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) $(dst $IPSET_DIRECT !) -p tcp -j PSW2_OUTPUT"
|
||||
}
|
||||
|
||||
[ "$PROXY_IPV6" == "1" ] && {
|
||||
@@ -895,7 +883,7 @@ add_firewall_rule() {
|
||||
add_port_rules "$ip6t_m -A PSW2_OUTPUT -p tcp" $TCP_REDIR_PORTS "-j PSW2_RULE"
|
||||
$ip6t_m -A PSW2 $(comment "${comment_l}") -p tcp -i lo $(REDIRECT $REDIR_PORT TPROXY)
|
||||
$ip6t_m -A PSW2 $(comment "${comment_l}") -p tcp -i lo -j RETURN
|
||||
insert_rule_before "$ip6t_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) -p tcp -j PSW2_OUTPUT"
|
||||
insert_rule_before "$ip6t_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) $(dst $IPSET_DIRECT6 !) -p tcp -j PSW2_OUTPUT"
|
||||
}
|
||||
|
||||
[ -d "${TMP_IFACE_PATH}" ] && {
|
||||
@@ -913,7 +901,7 @@ add_firewall_rule() {
|
||||
add_port_rules "$ipt_m -A PSW2_OUTPUT -p udp" $UDP_REDIR_PORTS "-j PSW2_RULE"
|
||||
$ipt_m -A PSW2 $(comment "${comment_l}") -p udp -i lo $(REDIRECT $REDIR_PORT TPROXY)
|
||||
$ipt_m -A PSW2 $(comment "${comment_l}") -p udp -i lo -j RETURN
|
||||
insert_rule_before "$ipt_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) -p udp -j PSW2_OUTPUT"
|
||||
insert_rule_before "$ipt_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) $(dst $IPSET_DIRECT !) -p udp -j PSW2_OUTPUT"
|
||||
|
||||
[ "$PROXY_IPV6" == "1" ] && {
|
||||
$ip6t_m -A PSW2_OUTPUT -p udp -d $FAKE_IP_6 -j PSW2_RULE
|
||||
@@ -921,7 +909,7 @@ add_firewall_rule() {
|
||||
add_port_rules "$ip6t_m -A PSW2_OUTPUT -p udp" $UDP_REDIR_PORTS "-j PSW2_RULE"
|
||||
$ip6t_m -A PSW2 $(comment "${comment_l}") -p udp -i lo $(REDIRECT $REDIR_PORT TPROXY)
|
||||
$ip6t_m -A PSW2 $(comment "${comment_l}") -p udp -i lo -j RETURN
|
||||
insert_rule_before "$ip6t_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) -p udp -j PSW2_OUTPUT"
|
||||
insert_rule_before "$ip6t_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) $(dst $IPSET_DIRECT6 !) -p udp -j PSW2_OUTPUT"
|
||||
}
|
||||
|
||||
[ -d "${TMP_IFACE_PATH}" ] && {
|
||||
@@ -938,8 +926,10 @@ add_firewall_rule() {
|
||||
$ip6t_m -I OUTPUT $(comment "mangle-OUTPUT-PSW2") -o lo -j RETURN
|
||||
insert_rule_before "$ip6t_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) -m mark --mark ${FWMARK} -j RETURN"
|
||||
|
||||
$ipt_m -A PSW2 -p udp --dport 53 -j RETURN
|
||||
$ip6t_m -A PSW2 -p udp --dport 53 -j RETURN
|
||||
$ipt_m -A PSW2 -p tcp --dport 53 -j ACCEPT
|
||||
$ipt_m -A PSW2 -p udp --dport 53 -j ACCEPT
|
||||
$ip6t_m -A PSW2 -p tcp --dport 53 -j ACCEPT
|
||||
$ip6t_m -A PSW2 -p udp --dport 53 -j ACCEPT
|
||||
}
|
||||
|
||||
load_acl
|
||||
@@ -970,7 +960,7 @@ del_firewall_rule() {
|
||||
$ipt -D $chain $index 2>/dev/null
|
||||
done
|
||||
done
|
||||
for chain in "PSW2" "PSW2_OUTPUT" "PSW2_DIVERT" "PSW2_DNS" "PSW2_RULE"; do
|
||||
for chain in "PSW2" "PSW2_OUTPUT" "PSW2_DNS" "PSW2_RULE"; do
|
||||
$ipt -F $chain 2>/dev/null
|
||||
$ipt -X $chain 2>/dev/null
|
||||
done
|
||||
@@ -1004,7 +994,7 @@ gen_include() {
|
||||
[ -z "${_ipt}" ] && return
|
||||
|
||||
echo "*$2"
|
||||
${_ipt}-save -t $2 | grep "PSW2" | grep -v "\-j PSW2$" | grep -v "socket \-j PSW2_DIVERT$" | sed -e "s/^-A \(OUTPUT\|PREROUTING\)/-I \1 1/"
|
||||
${_ipt}-save -t $2 | grep "PSW2" | grep -v "\-j PSW2$" | sed -e "s/^-A \(OUTPUT\|PREROUTING\)/-I \1 1/"
|
||||
echo 'COMMIT'
|
||||
}
|
||||
local __ipt=""
|
||||
@@ -1017,11 +1007,10 @@ gen_include() {
|
||||
$(extract_rules 4 mangle)
|
||||
EOT
|
||||
|
||||
[ "$accept_icmp" = "1" ] && \$(${MY_PATH} insert_rule_after "$ipt_n" "PREROUTING" "prerouting_rule" "-p icmp -j PSW2")
|
||||
[ -z "${is_tproxy}" ] && \$(${MY_PATH} insert_rule_after "$ipt_n" "PREROUTING" "prerouting_rule" "-p tcp -j PSW2")
|
||||
[ "$accept_icmp" = "1" ] && \$(${MY_PATH} insert_rule_after "$ipt_n" "PREROUTING" "prerouting_rule" "$(dst $IPSET_DIRECT !) -p icmp -j PSW2")
|
||||
[ -z "${is_tproxy}" ] && \$(${MY_PATH} insert_rule_after "$ipt_n" "PREROUTING" "prerouting_rule" "$(dst $IPSET_DIRECT !) -p tcp -j PSW2")
|
||||
|
||||
\$(${MY_PATH} insert_rule_before "$ipt_m" "PREROUTING" "mwan3" "-j PSW2")
|
||||
\$(${MY_PATH} insert_rule_before "$ipt_m" "PREROUTING" "PSW2" "-p tcp -m socket -j PSW2_DIVERT")
|
||||
\$(${MY_PATH} insert_rule_before "$ipt_m" "PREROUTING" "mwan3" "$(dst $IPSET_DIRECT !) -j PSW2")
|
||||
EOF
|
||||
)
|
||||
}
|
||||
@@ -1035,10 +1024,9 @@ gen_include() {
|
||||
$(extract_rules 6 mangle)
|
||||
EOT
|
||||
|
||||
[ "$accept_icmpv6" = "1" ] && $ip6t_n -A PREROUTING -p ipv6-icmp -j PSW2
|
||||
[ "$accept_icmpv6" = "1" ] && $ip6t_n -A PREROUTING $(dst $IPSET_DIRECT6 !) -p ipv6-icmp -j PSW2
|
||||
|
||||
\$(${MY_PATH} insert_rule_before "$ip6t_m" "PREROUTING" "mwan3" "-j PSW2")
|
||||
\$(${MY_PATH} insert_rule_before "$ip6t_m" "PREROUTING" "PSW2" "-p tcp -m socket -j PSW2_DIVERT")
|
||||
\$(${MY_PATH} insert_rule_before "$ip6t_m" "PREROUTING" "mwan3" "$(dst $IPSET_DIRECT6 !) -j PSW2")
|
||||
EOF
|
||||
)
|
||||
}
|
||||
|
||||
@@ -231,7 +231,9 @@ gen_shunt_list() {
|
||||
local default_outbound="redirect"
|
||||
[ "$default_node" = "_direct" ] && default_outbound="direct"
|
||||
local shunt_ids=$(uci show $CONFIG | grep "=shunt_rules" | awk -F '.' '{print $2}' | awk -F '=' '{print $1}')
|
||||
local shunt_group=$(config_n_get $node shunt_group)
|
||||
for shunt_id in $shunt_ids; do
|
||||
[ "${shunt_group}" != "$(config_n_get ${shunt_id} group)" ] && continue
|
||||
local shunt_node=$(config_n_get ${node} "${shunt_id}")
|
||||
[ -n "$shunt_node" ] && {
|
||||
local nftset_v4="psw2_${node}_${shunt_id}"
|
||||
@@ -700,13 +702,13 @@ add_firewall_rule() {
|
||||
|
||||
for ip in $(cat /usr/share/passwall2/direct_ip | tr -s "\r\n" "\n" | grep -v "^#" | sed -e "/^$/d"); do
|
||||
if [[ "$ip" == *::* ]]; then
|
||||
echo "$ip" | insert_nftset $NFTSET_DIRECT6 0
|
||||
echo "$ip" | insert_nftset $NFTSET_DIRECT6 "-1"
|
||||
elif [[ "$ip" == "geoip:"* ]]; then
|
||||
local _geoip_code=$(echo $ip | awk -F ':' '{print $2}')
|
||||
get_geoip $_geoip_code ipv4 | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | insert_nftset $NFTSET_DIRECT "0"
|
||||
get_geoip $_geoip_code ipv6 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | insert_nftset $NFTSET_DIRECT6 "0"
|
||||
get_geoip $_geoip_code ipv4 | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | insert_nftset $NFTSET_DIRECT "-1"
|
||||
get_geoip $_geoip_code ipv6 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | insert_nftset $NFTSET_DIRECT6 "-1"
|
||||
else
|
||||
echo "$ip" | insert_nftset $NFTSET_DIRECT 0
|
||||
echo "$ip" | insert_nftset $NFTSET_DIRECT "-1"
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -726,14 +728,14 @@ add_firewall_rule() {
|
||||
update_wan_sets
|
||||
|
||||
[ -n "$ISP_DNS" ] && {
|
||||
echo "$ISP_DNS" | insert_nftset $NFTSET_DIRECT 0
|
||||
echo "$ISP_DNS" | insert_nftset $NFTSET_DIRECT "-1"
|
||||
for ispip in $ISP_DNS; do
|
||||
log_i18n 1 "$(i18n "Add ISP %s DNS to the whitelist: %s" "IPv4" "${ispip}")"
|
||||
done
|
||||
}
|
||||
|
||||
[ -n "$ISP_DNS6" ] && {
|
||||
echo $ISP_DNS6 | insert_nftset $NFTSET_DIRECT6 0
|
||||
echo $ISP_DNS6 | insert_nftset $NFTSET_DIRECT6 "-1"
|
||||
for ispip6 in $ISP_DNS6; do
|
||||
log_i18n 1 "$(i18n "Add ISP %s DNS to the whitelist: %s" "IPv6" "${ispip6}")"
|
||||
done
|
||||
@@ -762,11 +764,6 @@ add_firewall_rule() {
|
||||
nft_output_chain="PSW2_OUTPUT_MANGLE"
|
||||
fi
|
||||
|
||||
nft "add chain $NFTABLE_NAME PSW2_DIVERT"
|
||||
nft "flush chain $NFTABLE_NAME PSW2_DIVERT"
|
||||
# Only TCP, UDP Invalid.
|
||||
nft "add rule $NFTABLE_NAME PSW2_DIVERT meta l4proto tcp socket transparent 1 mark set ${FWMARK} counter accept"
|
||||
|
||||
nft "add chain $NFTABLE_NAME PSW2_DNS"
|
||||
nft "flush chain $NFTABLE_NAME PSW2_DNS"
|
||||
if [ $(config_t_get global dns_redirect "1") = "0" ]; then
|
||||
@@ -790,33 +787,29 @@ add_firewall_rule() {
|
||||
#ipv4 tproxy mode and udp
|
||||
nft "add chain $NFTABLE_NAME PSW2_MANGLE"
|
||||
nft "flush chain $NFTABLE_NAME PSW2_MANGLE"
|
||||
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip daddr @$NFTSET_DIRECT counter return"
|
||||
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip daddr @$NFTSET_VPS counter return"
|
||||
nft "add rule $NFTABLE_NAME PSW2_MANGLE ct direction reply counter return"
|
||||
|
||||
nft "add chain $NFTABLE_NAME PSW2_OUTPUT_MANGLE"
|
||||
nft "flush chain $NFTABLE_NAME PSW2_OUTPUT_MANGLE"
|
||||
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE ip daddr @$NFTSET_DIRECT counter return"
|
||||
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE ip daddr @$NFTSET_VPS counter return"
|
||||
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE ct direction reply counter return"
|
||||
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE meta mark 255 counter return"
|
||||
|
||||
# jump chains
|
||||
nft "add rule $NFTABLE_NAME mangle_prerouting counter jump PSW2_DIVERT"
|
||||
nft "add rule $NFTABLE_NAME mangle_prerouting ip protocol udp counter jump PSW2_MANGLE"
|
||||
[ -n "${is_tproxy}" ] && nft "add rule $NFTABLE_NAME mangle_prerouting ip protocol tcp counter jump PSW2_MANGLE"
|
||||
# Only TCP, UDP Invalid.
|
||||
nft "add rule $NFTABLE_NAME mangle_prerouting meta nfproto ipv4 meta l4proto tcp socket transparent 1 mark set ${FWMARK} counter accept"
|
||||
nft "add rule $NFTABLE_NAME mangle_prerouting ip daddr != @$NFTSET_DIRECT ip protocol udp counter jump PSW2_MANGLE"
|
||||
[ -n "${is_tproxy}" ] && nft "add rule $NFTABLE_NAME mangle_prerouting ip daddr != @$NFTSET_DIRECT ip protocol tcp counter jump PSW2_MANGLE"
|
||||
|
||||
#ipv4 tcp redirect mode
|
||||
[ -z "${is_tproxy}" ] && {
|
||||
nft "add chain $NFTABLE_NAME PSW2_NAT"
|
||||
nft "flush chain $NFTABLE_NAME PSW2_NAT"
|
||||
nft "add rule $NFTABLE_NAME PSW2_NAT ip daddr @$NFTSET_DIRECT counter return"
|
||||
nft "add rule $NFTABLE_NAME PSW2_NAT ip daddr @$NFTSET_VPS counter return"
|
||||
nft "add rule $NFTABLE_NAME dstnat ip protocol tcp counter jump PSW2_NAT"
|
||||
nft "add rule $NFTABLE_NAME dstnat ip daddr != @$NFTSET_DIRECT ip protocol tcp counter jump PSW2_NAT"
|
||||
|
||||
nft "add chain $NFTABLE_NAME PSW2_OUTPUT_NAT"
|
||||
nft "flush chain $NFTABLE_NAME PSW2_OUTPUT_NAT"
|
||||
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_NAT ip daddr @$NFTSET_DIRECT counter return"
|
||||
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_NAT ip daddr @$NFTSET_VPS counter return"
|
||||
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_NAT meta mark 255 counter return"
|
||||
}
|
||||
@@ -825,16 +818,16 @@ add_firewall_rule() {
|
||||
if [ "$accept_icmp" = "1" ]; then
|
||||
nft "add chain $NFTABLE_NAME PSW2_ICMP_REDIRECT"
|
||||
nft "flush chain $NFTABLE_NAME PSW2_ICMP_REDIRECT"
|
||||
nft "add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT ip daddr @$NFTSET_DIRECT counter return"
|
||||
nft "add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT ip daddr @$NFTSET_VPS counter return"
|
||||
|
||||
[ "$accept_icmpv6" = "1" ] && {
|
||||
nft "add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT ip6 daddr @$NFTSET_DIRECT6 counter return"
|
||||
nft "add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT ip6 daddr @$NFTSET_VPS6 counter return"
|
||||
}
|
||||
|
||||
nft "add rule $NFTABLE_NAME dstnat meta l4proto {icmp,icmpv6} counter jump PSW2_ICMP_REDIRECT"
|
||||
nft "add rule $NFTABLE_NAME nat_output meta l4proto {icmp,icmpv6} counter jump PSW2_ICMP_REDIRECT"
|
||||
nft "add rule $NFTABLE_NAME dstnat ip daddr != @$NFTSET_DIRECT meta l4proto icmp counter jump PSW2_ICMP_REDIRECT"
|
||||
nft "add rule $NFTABLE_NAME dstnat ip6 daddr != @$NFTSET_DIRECT6 meta l4proto icmpv6 counter jump PSW2_ICMP_REDIRECT"
|
||||
nft "add rule $NFTABLE_NAME nat_output ip daddr != @$NFTSET_DIRECT meta l4proto icmp counter jump PSW2_ICMP_REDIRECT"
|
||||
nft "add rule $NFTABLE_NAME nat_output ip6 daddr != @$NFTSET_DIRECT6 meta l4proto icmpv6 counter jump PSW2_ICMP_REDIRECT"
|
||||
fi
|
||||
|
||||
#ipv4 wan_ip
|
||||
@@ -847,13 +840,11 @@ add_firewall_rule() {
|
||||
#ipv6 tproxy mode and udp
|
||||
nft "add chain $NFTABLE_NAME PSW2_MANGLE_V6"
|
||||
nft "flush chain $NFTABLE_NAME PSW2_MANGLE_V6"
|
||||
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 ip6 daddr @$NFTSET_DIRECT6 counter return"
|
||||
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 ip6 daddr @$NFTSET_VPS6 counter return"
|
||||
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 ct direction reply counter return"
|
||||
|
||||
nft "add chain $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6"
|
||||
nft "flush chain $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6"
|
||||
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 ip6 daddr @$NFTSET_DIRECT6 counter return"
|
||||
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 ip6 daddr @$NFTSET_VPS6 counter return"
|
||||
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 ct direction reply counter return"
|
||||
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 meta mark 255 counter return"
|
||||
@@ -874,8 +865,10 @@ add_firewall_rule() {
|
||||
|
||||
# jump chains
|
||||
[ "$PROXY_IPV6" == "1" ] && {
|
||||
nft "add rule $NFTABLE_NAME mangle_prerouting meta nfproto {ipv6} counter jump PSW2_MANGLE_V6"
|
||||
nft "add rule $NFTABLE_NAME mangle_output meta nfproto {ipv6} counter jump PSW2_OUTPUT_MANGLE_V6 comment \"PSW2_OUTPUT_MANGLE\""
|
||||
# Only TCP, UDP Invalid.
|
||||
nft "add rule $NFTABLE_NAME mangle_prerouting meta nfproto ipv6 meta l4proto tcp socket transparent 1 mark set ${FWMARK} counter accept"
|
||||
nft "add rule $NFTABLE_NAME mangle_prerouting ip6 daddr != @$NFTSET_DIRECT6 meta nfproto {ipv6} counter jump PSW2_MANGLE_V6"
|
||||
nft "add rule $NFTABLE_NAME mangle_output ip6 daddr != @$NFTSET_DIRECT6 meta nfproto {ipv6} counter jump PSW2_OUTPUT_MANGLE_V6 comment \"PSW2_OUTPUT_MANGLE\""
|
||||
|
||||
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 ip6 daddr @$NFTSET_WAN6 counter return comment \"WAN6_IP_RETURN\""
|
||||
|
||||
@@ -948,11 +941,11 @@ add_firewall_rule() {
|
||||
nft "add rule $NFTABLE_NAME $nft_chain ip protocol tcp ip daddr $FAKE_IP ${nft_j}"
|
||||
add_shunt_t_rule "${SHUNT_LIST4}" "nft add rule $NFTABLE_NAME $nft_chain ip protocol tcp $(factor $TCP_REDIR_PORTS "tcp dport") ip daddr" "${nft_j}"
|
||||
nft "add rule $NFTABLE_NAME $nft_chain ip protocol tcp $(factor $TCP_REDIR_PORTS "tcp dport") ${nft_j}"
|
||||
[ -z "${is_tproxy}" ] && nft "add rule $NFTABLE_NAME nat_output ip protocol tcp counter jump PSW2_OUTPUT_NAT"
|
||||
[ -z "${is_tproxy}" ] && nft "add rule $NFTABLE_NAME nat_output ip daddr != @$NFTSET_DIRECT ip protocol tcp counter jump PSW2_OUTPUT_NAT"
|
||||
[ -n "${is_tproxy}" ] && {
|
||||
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol tcp iif lo $(REDIRECT $REDIR_PORT TPROXY4) comment \"${comment_l}\""
|
||||
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol tcp iif lo counter return comment \"${comment_l}\""
|
||||
nft "add rule $NFTABLE_NAME mangle_output ip protocol tcp counter jump PSW2_OUTPUT_MANGLE comment \"PSW2_OUTPUT_MANGLE\""
|
||||
nft "add rule $NFTABLE_NAME mangle_output ip daddr != @$NFTSET_DIRECT ip protocol tcp counter jump PSW2_OUTPUT_MANGLE comment \"PSW2_OUTPUT_MANGLE\""
|
||||
}
|
||||
|
||||
[ "$PROXY_IPV6" == "1" ] && {
|
||||
@@ -978,7 +971,7 @@ add_firewall_rule() {
|
||||
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE ip protocol udp $(factor $UDP_REDIR_PORTS "udp dport") counter jump PSW2_RULE"
|
||||
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp iif lo $(REDIRECT $REDIR_PORT TPROXY4) comment \"${comment_l}\""
|
||||
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp iif lo counter return comment \"${comment_l}\""
|
||||
nft "add rule $NFTABLE_NAME mangle_output ip protocol udp counter jump PSW2_OUTPUT_MANGLE comment \"PSW2_OUTPUT_MANGLE\""
|
||||
nft "add rule $NFTABLE_NAME mangle_output ip daddr != @$NFTSET_DIRECT ip protocol udp counter jump PSW2_OUTPUT_MANGLE comment \"PSW2_OUTPUT_MANGLE\""
|
||||
|
||||
[ "$PROXY_IPV6" == "1" ] && {
|
||||
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 meta l4proto udp ip6 daddr $FAKE_IP_6 jump PSW2_RULE"
|
||||
@@ -996,11 +989,13 @@ add_firewall_rule() {
|
||||
}
|
||||
fi
|
||||
|
||||
nft "add rule $NFTABLE_NAME mangle_output oif lo counter return comment \"PSW2_OUTPUT_MANGLE\""
|
||||
nft "add rule $NFTABLE_NAME mangle_output meta mark ${FWMARK} counter return comment \"PSW2_OUTPUT_MANGLE\""
|
||||
nft "add rule $NFTABLE_NAME mangle_output ip daddr != @$NFTSET_DIRECT oif lo counter return comment \"PSW2_OUTPUT_MANGLE\""
|
||||
nft "add rule $NFTABLE_NAME mangle_output ip daddr != @$NFTSET_DIRECT meta mark ${FWMARK} counter return comment \"PSW2_OUTPUT_MANGLE\""
|
||||
|
||||
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp udp dport 53 counter return"
|
||||
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp udp dport 53 counter return"
|
||||
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol tcp tcp dport 53 counter accept"
|
||||
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp udp dport 53 counter accept"
|
||||
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto tcp tcp dport 53 counter accept"
|
||||
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp udp dport 53 counter accept"
|
||||
}
|
||||
|
||||
load_acl
|
||||
|
||||
@@ -126,7 +126,11 @@ test_auto_switch() {
|
||||
# If the current node is not found, or if the current node is the last node, then take the first node.
|
||||
[ -z "$new_node" ] && new_node="$first_node"
|
||||
local msg2="$(i18n "next backup node")"
|
||||
[ "$now_node" = "$main_node" ] && msg2="$(i18n "backup node")"
|
||||
if [ "$new_node" = "$main_node" ]; then
|
||||
msg2="$(i18n "main node")"
|
||||
else
|
||||
[ "$now_node" = "$main_node" ] && msg2="$(i18n "backup node")"
|
||||
fi
|
||||
msg="$(i18n "switch to %s test detect!" "${msg2}")"
|
||||
else
|
||||
# When there is only one backup node, poll with the primary node.
|
||||
@@ -138,11 +142,6 @@ test_auto_switch() {
|
||||
log_i18n 0 "Socks switch detection: %s 【%s:[%s]】 abnormal, %s" "${id}" "$(config_n_get $now_node type)" "$(config_n_get $now_node remarks)" "${msg}"
|
||||
test_node ${new_node}
|
||||
if [ $? -eq 0 ]; then
|
||||
# [ "$restore_switch" = "0" ] && {
|
||||
# uci set $CONFIG.${id}.node=$new_node
|
||||
# [ -z "$(echo $b_nodes | grep $main_node)" ] && uci add_list $CONFIG.${id}.autoswitch_backup_node=$main_node
|
||||
# uci commit $CONFIG
|
||||
# }
|
||||
check_process
|
||||
log_i18n 0 "Socks switch detection: %s 【%s:[%s]】 normal, switch to this node!" "${id}" "$(config_n_get $new_node type)" "$(config_n_get $new_node remarks)"
|
||||
$APP_FILE socks_node_switch flag=${id} new_node=${new_node}
|
||||
@@ -171,6 +170,10 @@ start() {
|
||||
backup_node_num=$(printf "%s\n" "$backup_node" | wc -w)
|
||||
if [ "$backup_node_num" -eq 1 ]; then
|
||||
[ "$main_node" = "$backup_node" ] && return
|
||||
elif [ "$backup_node_num" -gt 1 ]; then
|
||||
[ "$restore_switch" != "1" ] && {
|
||||
[ -z "$(echo $backup_node | grep -F "$main_node")" ] && backup_node="${backup_node} ${main_node}"
|
||||
}
|
||||
fi
|
||||
else
|
||||
return
|
||||
|
||||
@@ -768,6 +768,9 @@ local function parseClashNode(node, add_mode, group, sub_cfg)
|
||||
result.reality_publicKey = node["reality-opts"]["public-key"]
|
||||
result.reality_shortId = node["reality-opts"]["short-id"]
|
||||
end
|
||||
if node["disable-reuse"] then
|
||||
result.anytls_disable_reuse = "1"
|
||||
end
|
||||
end
|
||||
if not result.remarks or result.remarks == "" then
|
||||
if result.address and result.port then
|
||||
@@ -1865,7 +1868,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
|
||||
end
|
||||
if params.security == "tls" or params.security == "reality" then
|
||||
result.tls = "1"
|
||||
result.tls_serverName = params.sni
|
||||
result.tls_serverName = params.sni or params.peer
|
||||
result.alpn = params.alpn
|
||||
if params.fp and params.fp ~= "" then
|
||||
result.utls = "1"
|
||||
|
||||
Reference in New Issue
Block a user