diff --git a/luci-app-passwall2/Makefile b/luci-app-passwall2/Makefile
index 95b9a9c46..40b6b169d 100644
--- a/luci-app-passwall2/Makefile
+++ b/luci-app-passwall2/Makefile
@@ -5,7 +5,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall2
-PKG_VERSION:=1.26-3
+PKG_VERSION:=1.27-1
PKG_RELEASE:=
PKG_CONFIG_DEPENDS:= \
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua
index 4953985d7..19afae3ab 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua
@@ -54,7 +54,8 @@ for k, e in ipairs(api.get_valid_nodes()) do
if e.node_type == "normal" then
nodes_table[#nodes_table + 1] = {
id = e[".name"],
- remarks = e["remark"]
+ remarks = e["remark"],
+ type = e["type"]
}
end
if e.protocol == "_balancing" then
@@ -80,11 +81,13 @@ local o = s:option(ListValue, option_name("balancingStrategy"), translate("Balan
o:depends({ [option_name("protocol")] = "_balancing" })
o:value("random")
o:value("leastPing")
-o.default = "random"
+o:value("leastLoad")
+o.default = "leastLoad"
-- 探测地址
local o = s:option(Flag, option_name("useCustomProbeUrl"), translate("Use Custome Probe URL"), translate("By default the built-in probe URL will be used, enable this option to use a custom probe URL."))
o:depends({ [option_name("balancingStrategy")] = "leastPing" })
+o:depends({ [option_name("balancingStrategy")] = "leastLoad" })
local o = s:option(Value, option_name("probeUrl"), translate("Probe URL"))
o:depends({ [option_name("useCustomProbeUrl")] = true })
@@ -94,6 +97,7 @@ o.description = translate("The URL used to detect the connection status.")
-- 探测间隔
local o = s:option(Value, option_name("probeInterval"), translate("Probe Interval"))
o:depends({ [option_name("balancingStrategy")] = "leastPing" })
+o:depends({ [option_name("balancingStrategy")] = "leastLoad" })
o.default = "1m"
o.description = translate("The interval between initiating probes. Every time this time elapses, a server status check is performed on a server. The time format is numbers + units, such as '10s', '2h45m', and the supported time units are ns
, us
, ms
, s
, m
, h
, which correspond to nanoseconds, microseconds, milliseconds, seconds, minutes, and hours, respectively.")
@@ -523,4 +527,21 @@ o.default = 0
o = s:option(Flag, option_name("tcpNoDelay"), "tcpNoDelay")
o.default = 0
+o = s:option(ListValue, option_name("to_node"), translate("Landing node"), translate("Only support a layer of proxy."))
+o.default = ""
+o:value("", translate("Close(Not use)"))
+for k, v in pairs(nodes_table) do
+ if v.type == "Xray" then
+ o:value(v.id, v.remarks)
+ end
+end
+
+for i, v in ipairs(s.fields[option_name("protocol")].keylist) do
+ if not v:find("_") then
+ s.fields[option_name("tcpMptcp")]:depends({ [option_name("protocol")] = v })
+ s.fields[option_name("tcpNoDelay")]:depends({ [option_name("protocol")] = v })
+ s.fields[option_name("to_node")]:depends({ [option_name("protocol")] = v })
+ end
+end
+
api.luci_types(arg[1], m, s, type_name, option_prefix)
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua
index 89c0c0b3a..611a884a7 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua
@@ -65,13 +65,13 @@ o.default = "eth1"
o:depends({ [option_name("protocol")] = "_iface" })
local nodes_table = {}
-local balancers_table = {}
local iface_table = {}
for k, e in ipairs(api.get_valid_nodes()) do
if e.node_type == "normal" then
nodes_table[#nodes_table + 1] = {
id = e[".name"],
- remarks = e["remark"]
+ remarks = e["remark"],
+ type = e["type"]
}
end
if e.protocol == "_iface" then
@@ -89,9 +89,6 @@ if #nodes_table > 0 then
o = s:option(Value, option_name("main_node"), string.format('%s', translate("Preproxy Node")), translate("Set the node to be used as a pre-proxy. Each rule (including Default
) has a separate switch that controls whether this rule uses the pre-proxy or not."))
o:depends({ [option_name("protocol")] = "_shunt", [option_name("preproxy_enabled")] = true })
- for k, v in pairs(balancers_table) do
- o:value(v.id, v.remarks)
- end
for k, v in pairs(iface_table) do
o:value(v.id, v.remarks)
end
@@ -110,9 +107,6 @@ uci:foreach(appname, "shunt_rules", function(e)
o:depends({ [option_name("protocol")] = "_shunt" })
if #nodes_table > 0 then
- for k, v in pairs(balancers_table) do
- o:value(v.id, v.remarks)
- end
for k, v in pairs(iface_table) do
o:value(v.id, v.remarks)
end
@@ -142,9 +136,6 @@ o:value("_direct", translate("Direct Connection"))
o:value("_blackhole", translate("Blackhole"))
if #nodes_table > 0 then
- for k, v in pairs(balancers_table) do
- o:value(v.id, v.remarks)
- end
for k, v in pairs(iface_table) do
o:value(v.id, v.remarks)
end
@@ -631,4 +622,18 @@ o:value("prefer_ipv6")
o:value("ipv4_only")
o:value("ipv6_only")
+o = s:option(ListValue, option_name("to_node"), translate("Landing node"), translate("Only support a layer of proxy."))
+o.default = ""
+o:value("", translate("Close(Not use)"))
+for k, v in pairs(nodes_table) do
+ if v.type == "sing-box" then
+ o:value(v.id, v.remarks)
+ end
+end
+for i, v in ipairs(s.fields[option_name("protocol")].keylist) do
+ if not v:find("_") then
+ o:depends({ [option_name("protocol")] = v })
+ end
+end
+
api.luci_types(arg[1], m, s, type_name, option_prefix)
diff --git a/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua b/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua
index 32371a478..77c1e220c 100644
--- a/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua
+++ b/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua
@@ -872,6 +872,54 @@ function gen_config(var)
node.address = server_host
node.port = server_port
end
+
+ local function set_outbound_detour(node, outbound, outbounds_table, shunt_rule_name)
+ if not node or not outbound or not outbounds_table then return nil end
+ local default_outTag = outbound.tag
+
+ if node.shadowtls == "1" then
+ local _node = {
+ type = "sing-box",
+ protocol = "shadowtls",
+ shadowtls_version = node.shadowtls_version,
+ password = (node.shadowtls_version == "2" or node.shadowtls_version == "3") and node.shadowtls_password or nil,
+ address = node.address,
+ port = node.port,
+ tls = "1",
+ tls_serverName = node.shadowtls_serverName,
+ utls = node.shadowtls_utls,
+ fingerprint = node.shadowtls_fingerprint
+ }
+ local shadowtls_outbound = gen_outbound(nil, _node, outbound.tag .. "_shadowtls")
+ if shadowtls_outbound then
+ table.insert(outbounds_table, shadowtls_outbound)
+ outbound.detour = outbound.tag .. "_shadowtls"
+ outbound.server = nil
+ outbound.server_port = nil
+ end
+ end
+
+ if node.to_node then
+ local to_node = uci:get_all(appname, node.to_node)
+ if to_node then
+ local to_outbound = gen_outbound(nil, to_node)
+ if to_outbound then
+ if shunt_rule_name then
+ to_outbound.tag = outbound.tag
+ outbound.tag = node[".name"]
+ else
+ to_outbound.tag = outbound.tag .. " -> " .. to_outbound.tag
+ end
+
+ to_outbound.detour = outbound.tag
+ table.insert(outbounds_table, to_outbound)
+ default_outTag = to_outbound.tag
+ end
+ end
+ end
+ return default_outTag
+ end
+
if node.protocol == "_shunt" then
local rules = {}
@@ -900,34 +948,14 @@ function gen_config(var)
elseif preproxy_node and api.is_normal_node(preproxy_node) then
local preproxy_outbound = gen_outbound(flag, preproxy_node, preproxy_tag)
if preproxy_outbound then
- if preproxy_node.shadowtls == "1" then
- local _node = {
- type = "sing-box",
- protocol = "shadowtls",
- shadowtls_version = preproxy_node.shadowtls_version,
- password = (preproxy_node.shadowtls_version == "2" or preproxy_node.shadowtls_version == "3") and preproxy_node.shadowtls_password or nil,
- address = preproxy_node.address,
- port = preproxy_node.port,
- tls = "1",
- tls_serverName = preproxy_node.shadowtls_serverName,
- utls = preproxy_node.shadowtls_utls,
- fingerprint = preproxy_node.shadowtls_fingerprint
- }
- local shadowtls_outbound = gen_outbound(flag, _node, preproxy_tag .. "_shadowtls")
- if shadowtls_outbound then
- table.insert(outbounds, shadowtls_outbound)
- preproxy_outbound.detour = preproxy_outbound.tag .. "_shadowtls"
- preproxy_outbound.server = nil
- preproxy_outbound.server_port = nil
- end
- end
+ set_outbound_detour(preproxy_node, preproxy_outbound, outbounds, preproxy_tag)
table.insert(outbounds, preproxy_outbound)
else
preproxy_enabled = false
end
end
- local function gen_shunt_node(rule_name, _node_id, as_proxy)
+ local function gen_shunt_node(rule_name, _node_id)
if not rule_name then return nil, nil end
if not _node_id then _node_id = node[rule_name] or "nil" end
local rule_outboundTag
@@ -999,27 +1027,7 @@ function gen_config(var)
end
local _outbound = gen_outbound(flag, _node, rule_name, { proxy = proxy and 1 or 0, tag = proxy and preproxy_tag or nil })
if _outbound then
- if _node.shadowtls == "1" then
- local shadowtls_node = {
- type = "sing-box",
- protocol = "shadowtls",
- shadowtls_version = _node.shadowtls_version,
- password = (_node.shadowtls_version == "2" or _node.shadowtls_version == "3") and _node.shadowtls_password or nil,
- address = _node.address,
- port = _node.port,
- tls = "1",
- tls_serverName = _node.shadowtls_serverName,
- utls = _node.shadowtls_utls,
- fingerprint = _node.shadowtls_fingerprint
- }
- local shadowtls_outbound = gen_outbound(flag, shadowtls_node, rule_name .. "_shadowtls", { proxy = proxy and 1 or 0, tag = proxy and preproxy_tag or nil })
- if shadowtls_outbound then
- table.insert(outbounds, shadowtls_outbound)
- _outbound.detour = _outbound.tag .. "_shadowtls"
- _outbound.server = nil
- _outbound.server_port = nil
- end
- end
+ set_outbound_detour(_node, _outbound, outbounds, rule_name)
table.insert(outbounds, _outbound)
rule_outboundTag = rule_name
end
@@ -1196,46 +1204,23 @@ function gen_config(var)
for index, value in ipairs(rules) do
table.insert(route.rules, rules[index])
end
- else
- local outbound = nil
- if node.protocol == "_iface" then
- if node.iface then
- outbound = {
- type = "direct",
- tag = node_id,
- bind_interface = node.iface,
- routing_mark = 255,
- }
- sys.call("touch /tmp/etc/passwall2/iface/" .. node.iface)
- end
- else
- outbound = gen_outbound(flag, node)
- if outbound then
- if node.shadowtls == "1" then
- local shadowtls_node = {
- type = "sing-box",
- protocol = "shadowtls",
- shadowtls_version = node.shadowtls_version,
- password = (node.shadowtls_version == "2" or node.shadowtls_version == "3") and node.shadowtls_password or nil,
- address = node.address,
- port = node.port,
- tls = "1",
- tls_serverName = node.shadowtls_serverName,
- utls = node.shadowtls_utls,
- fingerprint = node.shadowtls_fingerprint
- }
- local shadowtls_outbound = gen_outbound(flag, shadowtls_node, outbound.tag .. "_shadowtls")
- if shadowtls_outbound then
- table.insert(outbounds, shadowtls_outbound)
- outbound.detour = outbound.tag .. "_shadowtls"
- outbound.server = nil
- outbound.server_port = nil
- end
- end
- end
- end
- if outbound then
+ elseif node.protocol == "_iface" then
+ if node.iface then
+ local outbound = {
+ type = "direct",
+ tag = node_id,
+ bind_interface = node.iface,
+ routing_mark = 255,
+ }
+ table.insert(outbounds, outbound)
default_outTag = outbound.tag
+ route.final = default_outTag
+ sys.call("touch /tmp/etc/passwall2/iface/" .. node.iface)
+ end
+ else
+ local outbound = gen_outbound(flag, node)
+ if outbound then
+ default_outTag = set_outbound_detour(node, outbound, outbounds)
table.insert(outbounds, outbound)
route.final = default_outTag
end
diff --git a/luci-app-passwall2/luasrc/passwall2/util_xray.lua b/luci-app-passwall2/luasrc/passwall2/util_xray.lua
index 676271308..8004731c3 100644
--- a/luci-app-passwall2/luasrc/passwall2/util_xray.lua
+++ b/luci-app-passwall2/luasrc/passwall2/util_xray.lua
@@ -245,6 +245,11 @@ function gen_outbound(flag, node, tag, proxy_table)
reserved = (node.protocol == "wireguard" and node.wireguard_reserved) and node.wireguard_reserved or nil
}
}
+
+ if node.protocol == "wireguard" then
+ result.settings.kernelMode = false
+ end
+
local alpn = {}
if node.alpn and node.alpn ~= "default" then
string.gsub(node.alpn, '[^' .. "," .. ']+', function(w)
@@ -658,7 +663,7 @@ function gen_config(var)
selector = valid_nodes,
strategy = { type = _node.balancingStrategy or "random" }
}
- if _node.balancingStrategy == "leastPing" then
+ if _node.balancingStrategy == "leastPing" or _node.balancingStrategy == "leastLoad" then
if not observatory then
observatory = {
subjectSelector = { "blc-" },
@@ -685,6 +690,34 @@ function gen_config(var)
return balancer, rule
end
+ local function set_outbound_detour(node, outbound, outbounds_table, shunt_rule_name)
+ if not node or not outbound or not outbounds_table then return nil end
+ local default_outTag = outbound.tag
+
+ if node.to_node then
+ local to_node = uci:get_all(appname, node.to_node)
+ if to_node then
+ local to_outbound = gen_outbound(nil, to_node)
+ if to_outbound then
+ if shunt_rule_name then
+ to_outbound.tag = outbound.tag
+ outbound.tag = node[".name"]
+ else
+ to_outbound.tag = outbound.tag .. " -> " .. to_outbound.tag
+ end
+
+ to_outbound.proxySettings = {
+ tag = outbound.tag,
+ transportLayer = true
+ }
+ table.insert(outbounds_table, to_outbound)
+ default_outTag = to_outbound.tag
+ end
+ end
+ end
+ return default_outTag
+ end
+
for k, v in pairs(nodes) do
if server_host and server_port then
v.address = server_host
@@ -722,6 +755,7 @@ function gen_config(var)
elseif preproxy_node and api.is_normal_node(preproxy_node) then
local preproxy_outbound = gen_outbound(flag, preproxy_node, preproxy_tag, { fragment = xray_settings.fragment == "1" or nil })
if preproxy_outbound then
+ set_outbound_detour(preproxy_node, preproxy_outbound, outbounds, preproxy_tag)
table.insert(outbounds, preproxy_outbound)
else
preproxy_enabled = false
@@ -737,7 +771,7 @@ function gen_config(var)
end
end
- local function gen_shunt_node(rule_name, _node_id, as_proxy)
+ local function gen_shunt_node(rule_name, _node_id)
if not rule_name then return nil, nil end
if not _node_id then _node_id = node[rule_name] or "nil" end
local rule_outboundTag
@@ -831,6 +865,7 @@ function gen_config(var)
end
local _outbound = gen_outbound(flag, _node, rule_name, proxy_table)
if _outbound then
+ set_outbound_detour(_node, _outbound, outbounds, rule_name)
table.insert(outbounds, _outbound)
if proxy then preproxy_used = true end
rule_outboundTag = rule_name
@@ -991,39 +1026,39 @@ function gen_config(var)
}
}
end
- else
- local outbound = nil
- if node.protocol == "_iface" then
- if node.iface then
- outbound = {
- protocol = "freedom",
- tag = "outbound",
- streamSettings = {
- sockopt = {
- mark = 255,
- interface = node.iface
- }
+ elseif node.protocol == "_iface" then
+ if node.iface then
+ local outbound = {
+ protocol = "freedom",
+ tag = "outbound",
+ streamSettings = {
+ sockopt = {
+ mark = 255,
+ interface = node.iface
}
}
- sys.call("touch /tmp/etc/passwall2/iface/" .. node.iface)
- end
- else
- outbound = gen_outbound(flag, node, nil, { fragment = xray_settings.fragment == "1" or nil })
+ }
+ table.insert(outbounds, outbound)
+ sys.call("touch /tmp/etc/passwall2/iface/" .. node.iface)
+ end
+ else
+ local outbound = gen_outbound(flag, node, nil, { fragment = xray_settings.fragment == "1" or nil })
+ if outbound then
+ local default_outTag = set_outbound_detour(node, outbound, outbounds)
+ table.insert(outbounds, outbound)
+ routing = {
+ domainStrategy = "AsIs",
+ domainMatcher = "hybrid",
+ rules = {}
+ }
+ table.insert(routing.rules, {
+ _flag = "default",
+ type = "field",
+ outboundTag = default_outTag,
+ network = "tcp,udp"
+ })
end
- if outbound then table.insert(outbounds, outbound) end
- routing = {
- domainStrategy = "AsIs",
- domainMatcher = "hybrid",
- rules = {}
- }
- table.insert(routing.rules, {
- _flag = "default",
- type = "field",
- outboundTag = node_id,
- network = "tcp,udp"
- })
end
-
end
if remote_dns_udp_server then
diff --git a/luci-app-passwall2/po/zh-cn/passwall2.po b/luci-app-passwall2/po/zh-cn/passwall2.po
index 4cec1a69d..7b6d6f0f6 100644
--- a/luci-app-passwall2/po/zh-cn/passwall2.po
+++ b/luci-app-passwall2/po/zh-cn/passwall2.po
@@ -1482,3 +1482,9 @@ msgstr "分片间隔(ms)"
msgid "If is domain name, The requested domain name will be resolved to IP before connect."
msgstr "如果是域名,域名将在请求发出之前解析为 IP。"
+
+msgid "Landing node"
+msgstr "落地节点"
+
+msgid "Only support a layer of proxy."
+msgstr "仅支持一层代理。"