luci: shunt added xray balancer support

- optimized pre-proxy settings (https://github.com/xiaorouji/openwrt-passwall/issues/2234)
This commit is contained in:
Tianhe Y 2023-03-31 13:44:00 +08:00 committed by sbwml
parent eadb44a03a
commit ee7ded80e5
7 changed files with 363 additions and 240 deletions

View File

@ -59,7 +59,7 @@ o = s:option(Flag, "restore_switch", "TCP " .. translate("Restore Switch"), tran
o = s:option(ListValue, "shunt_logic", "TCP " .. translate("If the main node is V2ray/Xray shunt")) o = s:option(ListValue, "shunt_logic", "TCP " .. translate("If the main node is V2ray/Xray shunt"))
o:value("0", translate("Switch it")) o:value("0", translate("Switch it"))
o:value("1", translate("Applying to the default node")) o:value("1", translate("Applying to the default node"))
o:value("2", translate("Applying to the default preproxy node")) o:value("2", translate("Applying to the preproxy node"))
m:append(Template(appname .. "/auto_switch/footer")) m:append(Template(appname .. "/auto_switch/footer"))

View File

@ -100,74 +100,106 @@ if current_node and current_node ~= "" and current_node ~= "nil" then
end end
tcp_node:value("nil", translate("Close")) tcp_node:value("nil", translate("Close"))
---- UDP Node
udp_node = s:taboption("Main", ListValue, "udp_node", "<a style='color: red'>" .. translate("UDP Node") .. "</a>")
udp_node:value("nil", translate("Close"))
udp_node:value("tcp", translate("Same as the tcp node"))
-- 分流 -- 分流
if (has_v2ray or has_xray) and #nodes_table > 0 then if (has_v2ray or has_xray) and #nodes_table > 0 then
local normal_list = {} local normal_list = {}
local shunt_list = {} local shunt_list = {}
for k, v in pairs(nodes_table) do for k, v in pairs(nodes_table) do
if v.node_type == "normal" then if v.node_type == "normal" or v.protocol == "_balancing" then
normal_list[#normal_list + 1] = v normal_list[#normal_list + 1] = v
end end
if v.protocol and v.protocol == "_shunt" then if v.protocol and v.protocol == "_shunt" then
shunt_list[#shunt_list + 1] = v shunt_list[#shunt_list + 1] = v
end end
end end
local function get_cfgvalue(shunt_node_id, rule_id)
return function(self, section)
return m:get(shunt_node_id, rule_id) or "nil"
end
end
local function get_write(shunt_node_id, rule_id)
return function(self, section, value)
m:set(shunt_node_id, rule_id, value)
end
end
for k, v in pairs(shunt_list) do for k, v in pairs(shunt_list) do
local vid = v.id:sub(1, 8)
o = s:taboption("Main", ListValue, vid .. "-main_node", string.format('<a style="color:red">%s</a>', translate("Preproxy Node")), translate("Set the node to be used as a pre-proxy. Each rule (including <code>Default</code>) has a separate switch that controls whether this rule uses the pre-proxy or not."))
o:depends("tcp_node", v.id)
o:value("nil", translate("Close"))
for k1, v1 in pairs(normal_list) do
o:value(v1.id, v1.remark)
end
o.cfgvalue = get_cfgvalue(v.id, "main_node")
o.write = get_write(v.id, "main_node")
local dialerProxy = s:taboption("Main", Flag, vid .. "-dialerProxy", translate("dialerProxy"))
if v.type == "Xray" then
dialerProxy:depends("tcp_node", v.id)
else --主设置界面没有type判断只能判断本分流节点类型是Xray就添加对本分流节点的依赖但不是的话就没有依赖会全部显示所以添加一个不存在的依赖以达到隐藏的目的
dialerProxy:depends("tcp_node", "xray_shunt")
end
dialerProxy.cfgvalue = get_cfgvalue(v.id, "dialerProxy")
dialerProxy.write = get_write(v.id, "dialerProxy")
uci:foreach(appname, "shunt_rules", function(e) uci:foreach(appname, "shunt_rules", function(e)
local id = e[".name"] local id = e[".name"]
local node_option = vid .. "-" .. id .. "_node"
if id and e.remarks then if id and e.remarks then
o = s:taboption("Main", ListValue, v.id .. "." .. id .. "_node", string.format('* <a href="%s" target="_blank">%s</a>', api.url("shunt_rules", id), e.remarks)) o = s:taboption("Main", ListValue, node_option, string.format('* <a href="%s" target="_blank">%s</a>', api.url("shunt_rules", id), e.remarks))
o:depends("tcp_node", v.id) o:depends("tcp_node", v.id)
o:value("nil", translate("Close")) o:value("nil", translate("Close"))
o:value("_default", translate("Default")) o:value("_default", translate("Default"))
o:value("_direct", translate("Direct Connection")) o:value("_direct", translate("Direct Connection"))
o:value("_blackhole", translate("Blackhole")) o:value("_blackhole", translate("Blackhole"))
local pt = s:taboption("Main", ListValue, vid .. "-".. id .. "_proxy_tag", string.format('* <a style="color:red">%s</a>', e.remarks .. " " .. translate("Preproxy")))
pt:value("nil", translate("Close"))
pt:value("main", translate("Preproxy Node"))
pt.default = "nil"
for k1, v1 in pairs(normal_list) do for k1, v1 in pairs(normal_list) do
o:value(v1.id, v1["remark"]) o:value(v1.id, v1.remark)
if v1.protocol ~= "_balancing" then
pt:depends(node_option, v1.id)
end end
o.cfgvalue = function(self, section)
return m:get(v.id, id) or "nil"
end
o.write = function(self, section, value)
m:set(v.id, id, value)
end end
o.cfgvalue = get_cfgvalue(v.id, id)
o.write = get_write(v.id, id)
pt.cfgvalue = get_cfgvalue(v.id, id .. "_proxy_tag")
pt.write = get_write(v.id, id .. "_proxy_tag")
end end
end) end)
local id = "default_node" local id = "default_node"
o = s:taboption("Main", ListValue, v.id .. "." .. id, string.format('* <a style="color:red">%s</a>', translate("Default"))) o = s:taboption("Main", ListValue, vid .. "-" .. id, string.format('* <a style="color:red">%s</a>', translate("Default")))
o:depends("tcp_node", v.id) o:depends("tcp_node", v.id)
o:value("_direct", translate("Direct Connection")) o:value("_direct", translate("Direct Connection"))
o:value("_blackhole", translate("Blackhole")) o:value("_blackhole", translate("Blackhole"))
for k1, v1 in pairs(normal_list) do for k1, v1 in pairs(normal_list) do
o:value(v1.id, v1["remark"]) o:value(v1.id, v1["remark"])
end end
o.cfgvalue = function(self, section) o.cfgvalue = get_cfgvalue(v.id, id)
return m:get(v.id, id) or "nil" o.write = get_write(v.id, id)
end
o.write = function(self, section, value)
m:set(v.id, id, value)
end
local id = "main_node" local id = "default_proxy_tag"
o = s:taboption("Main", ListValue, v.id .. "." .. id, string.format('* <a style="color:red">%s</a>', translate("Default Preproxy")), translate("When using, localhost will connect this node first and then use this node to connect the default node.")) o = s:taboption("Main", ListValue, vid .. "-" .. id, string.format('* <a style="color:red">%s</a>', translate("Default Preproxy")), translate("When using, localhost will connect this node first and then use this node to connect the default node."))
o:depends("tcp_node", v.id)
o:value("nil", translate("Close"))
for k1, v1 in pairs(normal_list) do for k1, v1 in pairs(normal_list) do
o:value(v1.id, v1["remark"]) if v1.protocol ~= "_balancing" then
end o:depends(vid .. "-default_node", v1.id)
o.cfgvalue = function(self, section)
return m:get(v.id, id) or "nil"
end
o.write = function(self, section, value)
m:set(v.id, id, value)
end end
end end
o:value("nil", translate("Close"))
o:value("main", translate("Preproxy Node"))
o.cfgvalue = get_cfgvalue(v.id, id)
o.write = get_write(v.id, id)
end
end end
udp_node = s:taboption("Main", ListValue, "udp_node", "<a style='color: red'>" .. translate("UDP Node") .. "</a>")
udp_node:value("nil", translate("Close"))
udp_node:value("tcp", translate("Same as the tcp node"))
tcp_node_socks_port = s:taboption("Main", Value, "tcp_node_socks_port", translate("TCP Node") .. " Socks " .. translate("Listen Port")) tcp_node_socks_port = s:taboption("Main", Value, "tcp_node_socks_port", translate("TCP Node") .. " Socks " .. translate("Listen Port"))
tcp_node_socks_port.default = 1070 tcp_node_socks_port.default = 1070

View File

@ -133,9 +133,10 @@ iface:depends("protocol", "_iface")
local nodes_table = {} local nodes_table = {}
for k, e in ipairs(api.get_valid_nodes()) do for k, e in ipairs(api.get_valid_nodes()) do
if e.node_type == "normal" then if e.node_type == "normal" or e.protocol == "_balancing" then
nodes_table[#nodes_table + 1] = { nodes_table[#nodes_table + 1] = {
id = e[".name"], id = e[".name"],
protocol = e["protocol"],
remarks = e["remark"] remarks = e["remark"]
} }
end end
@ -145,17 +146,39 @@ end
local balancing_node = s:option(DynamicList, "balancing_node", translate("Load balancing node list"), translate("Load balancing node list, <a target='_blank' href='https://toutyrater.github.io/routing/balance2.html'>document</a>")) local balancing_node = s:option(DynamicList, "balancing_node", translate("Load balancing node list"), translate("Load balancing node list, <a target='_blank' href='https://toutyrater.github.io/routing/balance2.html'>document</a>"))
for k, v in pairs(nodes_table) do balancing_node:value(v.id, v.remarks) end for k, v in pairs(nodes_table) do balancing_node:value(v.id, v.remarks) end
balancing_node:depends("protocol", "_balancing") balancing_node:depends("protocol", "_balancing")
local balancingStrategy = s:option(ListValue, "balancingStrategy", translate("Balancing Strategy")) local balancingStrategy = s:option(ListValue, "balancingStrategy", translate("Balancing Strategy"))
balancingStrategy:depends("protocol", "_balancing") balancingStrategy:depends("protocol", "_balancing")
balancingStrategy:value("random") balancingStrategy:value("random")
balancingStrategy:value("leastPing") balancingStrategy:value("leastPing")
balancingStrategy.default = "random" balancingStrategy.default = "random"
-- 探测地址
local useCustomProbeUrl = s:option(Flag, "useCustomProbeUrl", translate("Use Custome Probe URL"))
useCustomProbeUrl:depends("balancingStrategy", "leastPing")
useCustomProbeUrl.description = "By default the built-in probe URL will be used, enable this option to use a custom probe URL."
local probeUrl = s:option(Value, "probeUrl", translate("Probe URL"))
probeUrl:depends("useCustomProbeUrl", true)
probeUrl.default = "https://www.google.com/generate_204"
probeUrl.description = translate("The URL used to detect the connection status.")
-- 探测间隔
local probeInterval = s:option(Value, "probeInterval", translate("Probe Interval")) local probeInterval = s:option(Value, "probeInterval", translate("Probe Interval"))
probeInterval:depends("balancingStrategy", "leastPing") probeInterval:depends("balancingStrategy", "leastPing")
probeInterval.default = "1m" probeInterval.default = "1m"
probeInterval.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 <code>ns</code>, <code>us</code>, <code>ms</code>, <code>s</code>, <code>m</code>, <code>h</code>, which correspond to nanoseconds, microseconds, milliseconds, seconds, minutes, and hours, respectively.") probeInterval.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 <code>ns</code>, <code>us</code>, <code>ms</code>, <code>s</code>, <code>m</code>, <code>h</code>, which correspond to nanoseconds, microseconds, milliseconds, seconds, minutes, and hours, respectively.")
-- 分流 -- 分流
if #nodes_table > 0 then
o = s:option(ListValue, "main_node", string.format('<a style="color:red">%s</a>', translate("Preproxy Node")), translate("Set the node to be used as a pre-proxy. Each rule (including <code>Default</code>) has a separate switch that controls whether this rule uses the pre-proxy or not."))
o:depends("protocol", "_shunt")
o:value("nil", translate("Close"))
dialerProxy = s:option(Flag, "dialerProxy", translate("dialerProxy"))
dialerProxy:depends({ type = "Xray", protocol = "_shunt" , })
for k, v in pairs(nodes_table) do
o:value(v.id, v.remarks)
--dialerProxy:depends({ type = "Xray", main_node = v.id })
end
o.default = "nil"
end
uci:foreach(appname, "shunt_rules", function(e) uci:foreach(appname, "shunt_rules", function(e)
if e[".name"] and e.remarks then if e[".name"] and e.remarks then
o = s:option(ListValue, e[".name"], string.format('* <a href="%s" target="_blank">%s</a>', api.url("shunt_rules", e[".name"]), e.remarks)) o = s:option(ListValue, e[".name"], string.format('* <a href="%s" target="_blank">%s</a>', api.url("shunt_rules", e[".name"]), e.remarks))
@ -166,15 +189,15 @@ uci:foreach(appname, "shunt_rules", function(e)
o:depends("protocol", "_shunt") o:depends("protocol", "_shunt")
if #nodes_table > 0 then if #nodes_table > 0 then
_proxy_tag = s:option(ListValue, e[".name"] .. "_proxy_tag", string.format('* <a style="color:red">%s</a>', e.remarks .. " " .. translate("Preproxy"))) local pt = s:option(ListValue, e[".name"] .. "_proxy_tag", string.format('* <a style="color:red">%s</a>', e.remarks .. " " .. translate("Preproxy")))
_proxy_tag:value("nil", translate("Close")) pt:value("nil", translate("Close"))
_proxy_tag:value("default", translate("Default")) pt:value("main", translate("Preproxy Node"))
_proxy_tag:value("main", translate("Default Preproxy")) pt.default = "nil"
_proxy_tag.default = "nil"
for k, v in pairs(nodes_table) do for k, v in pairs(nodes_table) do
o:value(v.id, v.remarks) o:value(v.id, v.remarks)
_proxy_tag:depends(e[".name"], v.id) if v.protocol ~= "_balancing" then
pt:depends(e[".name"], v.id)
end
end end
end end
end end
@ -194,16 +217,16 @@ for k, v in pairs(nodes_table) do default_node:value(v.id, v.remarks) end
default_node:depends("protocol", "_shunt") default_node:depends("protocol", "_shunt")
if #nodes_table > 0 then if #nodes_table > 0 then
o = s:option(ListValue, "main_node", string.format('* <a style="color:red">%s</a>', translate("Default Preproxy")), translate("When using, localhost will connect this node first and then use this node to connect the default node.")) local dpt = s:option(ListValue, "default_proxy_tag", string.format('* <a style="color:red">%s</a>', translate("Default Preproxy")), translate("When using, localhost will connect this node first and then use this node to connect the default node."))
o:value("nil", translate("Close")) dpt:value("nil", translate("Close"))
dpt:value("main", translate("Preproxy Node"))
dpt.default = "nil"
for k, v in pairs(nodes_table) do for k, v in pairs(nodes_table) do
o:value(v.id, v.remarks) if v.protocol ~= "_balancing" then
o:depends("default_node", v.id) dpt:depends("default_node", v.id)
end
end end
end end
dialerProxy = s:option(Flag, "dialerProxy", translate("dialerProxy"))
dialerProxy:depends({ type = "Xray", protocol = "_shunt"})
domainStrategy = s:option(ListValue, "domainStrategy", translate("Domain Strategy")) domainStrategy = s:option(ListValue, "domainStrategy", translate("Domain Strategy"))
domainStrategy:value("AsIs") domainStrategy:value("AsIs")

View File

@ -215,7 +215,7 @@ local function stop()
ip6t("-F PSW-SERVER 2>/dev/null") ip6t("-F PSW-SERVER 2>/dev/null")
ip6t("-X PSW-SERVER 2>/dev/null") ip6t("-X PSW-SERVER 2>/dev/null")
else else
nft_cmd="handles=$(nft -a list chain inet fw4 input | grep -E \"PSW-SERVER\" | awk -F '# handle ' '{print$2}')\n for handle in $handles; do\n nft delete rule inet fw4 input handle ${handle} 2>/dev/null\n done" local nft_cmd = "handles=$(nft -a list chain inet fw4 input | grep -E \"PSW-SERVER\" | awk -F '# handle ' '{print$2}')\n for handle in $handles; do\n nft delete rule inet fw4 input handle ${handle} 2>/dev/null\n done"
cmd(nft_cmd) cmd(nft_cmd)
cmd("nft flush chain inet fw4 PSW-SERVER 2>/dev/null") cmd("nft flush chain inet fw4 PSW-SERVER 2>/dev/null")
cmd("nft delete chain inet fw4 PSW-SERVER 2>/dev/null") cmd("nft delete chain inet fw4 PSW-SERVER 2>/dev/null")

View File

@ -91,8 +91,7 @@ function gen_outbound(flag, node, tag, proxy_table)
config_file, --config file config_file, --config file
(proxy == 1 and relay_port) and tostring(relay_port) or "" --relay port (proxy == 1 and relay_port) and tostring(relay_port) or "" --relay port
) )
) ))
)
node = {} node = {}
node.protocol = "socks" node.protocol = "socks"
node.transport = "tcp" node.transport = "tcp"
@ -596,96 +595,124 @@ function gen_config(var)
end end
end end
if node.protocol == "_shunt" then local function get_balancer_tag(_node_id)
local rules = {} return "balancer-" .. _node_id:sub(1, 8)
local default_node_id = node.default_node or "_direct"
local default_outboundTag
if default_node_id == "_direct" then
default_outboundTag = "direct"
elseif default_node_id == "_blackhole" then
default_outboundTag = "blackhole"
else
local default_node = uci:get_all(appname, default_node_id)
local main_node_id = node.main_node or "nil"
local proxy = 0
local proxy_tag
if main_node_id ~= "nil" then
local main_node = uci:get_all(appname, main_node_id)
if main_node and api.is_normal_node(main_node) and main_node_id ~= default_node_id then
local main_node_outbound = gen_outbound(flag, main_node, "main")
if main_node_outbound then
table.insert(outbounds, main_node_outbound)
proxy = 1
proxy_tag = "main"
local pre_proxy = nil
if default_node.type ~= "V2ray" and default_node.type ~= "Xray" then
pre_proxy = true
end
if default_node.type == "Xray" and default_node.tlsflow == "xtls-rprx-vision" then
pre_proxy = true
end
if pre_proxy then
proxy_tag = nil
new_port = get_new_port()
table.insert(inbounds, {
tag = "proxy_default",
listen = "127.0.0.1",
port = new_port,
protocol = "dokodemo-door",
settings = {network = "tcp,udp", address = default_node.address, port = tonumber(default_node.port)}
})
if default_node.tls_serverName == nil then
default_node.tls_serverName = default_node.address
end
default_node.address = "127.0.0.1"
default_node.port = new_port
table.insert(rules, 1, {
type = "field",
inboundTag = {"proxy_default"},
outboundTag = "main"
})
end
end
end
end
if default_node and api.is_normal_node(default_node) then
local default_outbound = gen_outbound(flag, default_node, "default", { proxy = proxy, tag = proxy_tag, dialerProxy = node.dialerProxy })
if default_outbound then
table.insert(outbounds, default_outbound)
default_outboundTag = "default"
end
end
end end
uci:foreach(appname, "shunt_rules", function(e) local function gen_balancer(_node, loopbackTag)
local name = e[".name"] local blc_nodes = _node.balancing_node
if name and e.remarks then local length = #blc_nodes
local _node_id = node[name] or "nil" local valid_nodes = {}
local proxy_tag = node[name .. "_proxy_tag"] or "nil" for i = 1, length do
local outboundTag local blc_node_id = blc_nodes[i]
if _node_id == "_direct" then local blc_node_tag = "blc-" .. blc_node_id:sub(1, 8)
outboundTag = "direct" local is_new_blc_node = true
elseif _node_id == "_blackhole" then for _, outbound in ipairs(outbounds) do
outboundTag = "blackhole" if outbound.tag == blc_node_tag then
elseif _node_id == "_default" then is_new_blc_node = false
outboundTag = "default" valid_nodes[#valid_nodes + 1] = blc_node_tag
else
if _node_id ~= "nil" then
local _node = uci:get_all(appname, _node_id)
if _node and api.is_normal_node(_node) then
local new_outbound
for index, value in ipairs(outbounds) do
if value["_flag_tag"] == _node_id and value["_flag_proxy_tag"] == proxy_tag then
new_outbound = api.clone(value)
break break
end end
end end
if new_outbound then if is_new_blc_node then
new_outbound["tag"] = name local blc_node = uci:get_all(appname, blc_node_id)
table.insert(outbounds, new_outbound) local outbound = gen_outbound(flag, blc_node, blc_node_tag)
outboundTag = name if outbound then
table.insert(outbounds, outbound)
valid_nodes[#valid_nodes + 1] = blc_node_tag
end
end
end
local balancer, rule
if #valid_nodes > 0 then
local balancerTag = get_balancer_tag(_node[".name"])
balancer = {
tag = balancerTag,
selector = valid_nodes,
strategy = { type = _node.balancingStrategy or "random" }
}
if _node.balancingStrategy == "leastPing" then
if not observatory then
observatory = {
subjectSelector = { "blc-" },
probeUrl = _node.useCustomProbeUrl == true and _node.probeUrl or nil,
probeInterval = _node.probeInterval or "1m",
enableConcurrency = node.type == "Xray" and true or nil --这里只判断顶层节点(分流总节点/单独的负载均衡节点)类型为Xray就可以启用并发
}
end
end
if loopbackTag and loopbackTag ~= "" then
local inboundTag = loopbackTag .. "-in"
table.insert(outbounds, {
protocol = "loopback",
tag = loopbackTag,
settings = { inboundTag = inboundTag }
})
rule = {
type = "field",
inboundTag = { inboundTag },
balancerTag = balancerTag
}
end
end
return balancer, rule
end
if node.protocol == "_shunt" then
local rules = {}
local balancers = {}
local preproxy_enabled = false
local preproxy_tag = "main"
local preproxy_node_id = node[preproxy_tag .. "_node"] or "nil"
local function gen_shunt_node(rule_name, _node_id, as_proxy)
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
local rule_balancerTag
if _node_id == "_direct" then
rule_outboundTag = "direct"
elseif _node_id == "_blackhole" then
rule_outboundTag = "blackhole"
elseif _node_id == "_default" and rule_name ~= "default" then
rule_outboundTag = "default"
elseif _node_id ~= "nil" then
local _node = uci:get_all(appname, _node_id)
if not _node then return nil, nil end
if api.is_normal_node(_node) then --这一块根据代理设置的修改方向还需要修改
local proxy_tag = node[rule_name .. "_proxy_tag"] or "nil"
if proxy_tag == preproxy_tag and not preproxy_enabled then proxy_tag = "nil" end
local proxy_node_id = proxy_tag ~= "nil" and node[proxy_tag .. "_node"] or "nil" --为了适配之前默认节点也可用作前置代理的写法只设一个的话直接用preproxy_node_id
if _node_id == proxy_node_id then proxy_tag = "nil" end --规则启用了前置代理,但规则本身节点和前置代理节点是同一个,则前置代理设置无效
local proxy_node = uci:get_all(appname, proxy_node_id) --前置代理节点
local is_balancing_proxy
if proxy_node and proxy_node.protocol == "_balancing" then
is_balancing_proxy = true
local blc_nodes = proxy_node.balancing_node
for _, blc_node_id in ipairs(blc_nodes) do
if _node_id == blc_node_id then
proxy_tag = "nil"
break
end
end
end
local copied_outbound
for index, value in ipairs(outbounds) do
if value["_flag_tag"] == _node_id and value["_flag_proxy_tag"] == proxy_tag then
copied_outbound = api.clone(value)
break
end
end
if copied_outbound then
copied_outbound.tag = rule_name
table.insert(outbounds, copied_outbound)
rule_outboundTag = rule_name
else else
if proxy_tag ~= "nil" then
local pre_proxy = nil local pre_proxy = nil
if _node.type ~= "V2ray" and _node.type ~= "Xray" then if _node.type ~= "V2ray" and _node.type ~= "Xray" then
pre_proxy = true pre_proxy = true
@ -694,10 +721,9 @@ function gen_config(var)
pre_proxy = true pre_proxy = true
end end
if pre_proxy then if pre_proxy then
if proxy_tag ~= "nil" then
new_port = get_new_port() new_port = get_new_port()
table.insert(inbounds, { table.insert(inbounds, {
tag = "proxy_" .. name, tag = "proxy_" .. rule_name,
listen = "127.0.0.1", listen = "127.0.0.1",
port = new_port, port = new_port,
protocol = "dokodemo-door", protocol = "dokodemo-door",
@ -710,21 +736,69 @@ function gen_config(var)
_node.port = new_port _node.port = new_port
table.insert(rules, 1, { table.insert(rules, 1, {
type = "field", type = "field",
inboundTag = {"proxy_" .. name}, inboundTag = {"proxy_" .. rule_name},
outboundTag = proxy_tag outboundTag = is_balancing_proxy and nil or proxy_tag,
balancerTag = is_balancing_proxy and get_balancer_tag(proxy_node_id) or nil
}) })
end end
end end
local _outbound = gen_outbound(flag, _node, name, { proxy = (proxy_tag ~= "nil") and 1 or 0, tag = (proxy_tag ~= "nil") and proxy_tag or nil, dialerProxy = node.dialerProxy }) local _outbound = gen_outbound(flag, _node, rule_name, { proxy = (proxy_tag ~= "nil") and 1 or 0, tag = (proxy_tag ~= "nil") and proxy_tag or nil, dialerProxy = node.dialerProxy })
if _outbound then if _outbound then
table.insert(outbounds, _outbound) table.insert(outbounds, _outbound)
outboundTag = name if proxy_tag == preproxy_tag then preproxy_used = true end
rule_outboundTag = rule_name
end
end
elseif _node.protocol == "_balancing" then
local is_new_balancer = true
for _, v in ipairs(balancers) do
if v["_flag_tag"] == _node_id then
is_new_balancer = false
rule_balancerTag = v.tag
break
end
end
if is_new_balancer then --注释掉的是给需要用作前置代理的balancer生成等效OutboundTagloopback + 规则路由至)的代码,可能用上
--local loopbackTag = as_proxy == true and rule_name or nil
local balancer = gen_balancer(_node) --local balancer, rule = gen_balancer(_node)
if balancer then
table.insert(balancers, balancer)
--if rule then table.insert(rules, rule) end
rule_balancerTag = balancer.tag
end end
end end
end end
end end
return rule_outboundTag, rule_balancerTag
end end
if outboundTag then
--[[此处只要前置代理设置选择了节点即使全部规则都没使用仍会先尝试生成生成有效配置才真正开启功能会造成配置文件里面会有多余未使用的outbound
使
proxy_tag]]
if preproxy_node_id ~= "nil" then
local preproxy_node = uci:get_all(appname, preproxy_node_id)
if preproxy_node and api.is_normal_node(preproxy_node) then
local preproxy_outbound = gen_outbound(flag, preproxy_node, preproxy_tag)
if preproxy_outbound then
table.insert(outbounds, preproxy_outbound)
preproxy_enabled = true
end
elseif preproxy_node and preproxy_node.protocol == "_balancing" then
local preproxy_balancer, preproxy_rule = gen_balancer(preproxy_node, preproxy_tag)
if preproxy_balancer and preproxy_rule then
table.insert(balancers, preproxy_balancer)
table.insert(rules, preproxy_rule)
preproxy_enabled = true
end
end
end
local default_node_id = node.default_node or "_direct"
local default_outboundTag, default_balancerTag = gen_shunt_node("default", default_node_id)
uci:foreach(appname, "shunt_rules", function(e)
local outboundTag, balancerTag = gen_shunt_node(e[".name"])
if outboundTag or balancerTag and e.remarks then
if outboundTag == "default" then if outboundTag == "default" then
outboundTag = default_outboundTag outboundTag = default_outboundTag
end end
@ -735,45 +809,36 @@ function gen_config(var)
table.insert(protocols, w) table.insert(protocols, w)
end) end)
end end
local _domain = nil
if e.domain_list then if e.domain_list then
local _domain = {} _domain = {}
string.gsub(e.domain_list, '[^' .. "\r\n" .. ']+', function(w) string.gsub(e.domain_list, '[^' .. "\r\n" .. ']+', function(w)
table.insert(_domain, w) table.insert(_domain, w)
end) end)
table.insert(rules, {
type = "field",
outboundTag = outboundTag,
domain = _domain,
protocol = protocols
})
end end
local _ip = nil
if e.ip_list then if e.ip_list then
local _ip = {} _ip = {}
string.gsub(e.ip_list, '[^' .. "\r\n" .. ']+', function(w) string.gsub(e.ip_list, '[^' .. "\r\n" .. ']+', function(w)
table.insert(_ip, w) table.insert(_ip, w)
end) end)
end
table.insert(rules, { table.insert(rules, {
type = "field", type = "field",
outboundTag = outboundTag, outboundTag = outboundTag,
balancerTag = balancerTag,
domain = _domain,
ip = _ip, ip = _ip,
protocol = protocols protocol = protocols
}) })
end end
if not e.domain_list and not e.ip_list and protocols then
table.insert(rules, {
type = "field",
outboundTag = outboundTag,
protocol = protocols
})
end
end
end
end) end)
if default_outboundTag then if default_outboundTag or default_balancerTag then
table.insert(rules, { table.insert(rules, {
type = "field", type = "field",
outboundTag = default_outboundTag, outboundTag = default_outboundTag,
balancerTag = default_balancerTag,
network = "tcp,udp" network = "tcp,udp"
}) })
end end
@ -781,31 +846,16 @@ function gen_config(var)
routing = { routing = {
domainStrategy = node.domainStrategy or "AsIs", domainStrategy = node.domainStrategy or "AsIs",
domainMatcher = node.domainMatcher or "hybrid", domainMatcher = node.domainMatcher or "hybrid",
balancers = #balancers > 0 and balancers or nil,
rules = rules rules = rules
} }
elseif node.protocol == "_balancing" then elseif node.protocol == "_balancing" then
if node.balancing_node then if node.balancing_node then
local nodes = node.balancing_node local balancer = gen_balancer(node)
local length = #nodes
for i = 1, length do
local node = uci:get_all(appname, nodes[i])
local outbound = gen_outbound(flag, node)
if outbound then table.insert(outbounds, outbound) end
end
if node.balancingStrategy == "leastPing" then
observatory = {
subjectSelector = nodes,
probeInterval = node.probeInterval or "1m"
}
end
routing = { routing = {
balancers = {{ balancers = { balancer },
tag = "balancer",
selector = nodes,
strategy = {type = node.balancingStrategy or "random"}
}},
rules = { rules = {
{type = "field", network = "tcp,udp", balancerTag = "balancer"} { type = "field", network = "tcp,udp", balancerTag = balancer.tag }
} }
} }
end end

View File

@ -352,6 +352,18 @@ msgstr "V2ray 负载均衡"
msgid "Balancing Strategy" msgid "Balancing Strategy"
msgstr "负载均衡策略" msgstr "负载均衡策略"
msgid "Use Custome Probe URL"
msgstr "使用自定义探测网址"
msgid "By default the built-in probe URL will be used, enable this option to use a custom probe URL."
msgstr "默认使用内置的探测网址,启用此选项以使用自定义探测网址。"
msgid "Probe URL"
msgstr "探测网址"
msgid "The URL used to detect the connection status."
msgstr "用于检测连接状态的网址。"
msgid "Probe Interval" msgid "Probe Interval"
msgstr "探测间隔" msgstr "探测间隔"
@ -370,6 +382,12 @@ msgstr "V2ray 分流"
msgid "Preproxy" msgid "Preproxy"
msgstr "前置代理" msgstr "前置代理"
msgid "Preproxy Node"
msgstr "前置代理节点"
msgid "Set the node to be used as a pre-proxy. Each rule (including <code>Default</code>) has a separate switch that controls whether this rule uses the pre-proxy or not."
msgstr "设置用作前置代理的节点。每条规则(包括<code>默认</code>)都有独立开关控制本规则是否使用前置代理。"
msgid "Direct Connection" msgid "Direct Connection"
msgstr "直连" msgstr "直连"
@ -661,8 +679,8 @@ msgstr "切掉它"
msgid "Applying to the default node" msgid "Applying to the default node"
msgstr "应用于默认节点" msgstr "应用于默认节点"
msgid "Applying to the default preproxy node" msgid "Applying to the preproxy node"
msgstr "应用于默认前置节点" msgstr "应用于前置代理节点"
msgid "Add nodes to the standby node list by keywords" msgid "Add nodes to the standby node list by keywords"
msgstr "通过关键字添加节点到备用节点列表" msgstr "通过关键字添加节点到备用节点列表"