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 "仅支持一层代理。"