Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion applications/luci-app-passwall/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
include $(TOPDIR)/rules.mk

PKG_NAME:=luci-app-passwall
PKG_VERSION:=26.4.1
PKG_VERSION:=26.4.6
PKG_RELEASE:=1

PKG_CONFIG_DEPENDS:= \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,111 @@ o = s:option(Value, "remarks", translate("Remarks"))
o.default = arg[1]
o.rmempty = false

o = s:option(ListValue, "interface", translate("Source Interface"))
o = s:option(Value, "interface", translate("Source Interface"))
o:value("", translate("All"))
local wa = require "luci.tools.webadmin"
wa.cbi_add_networks(o)
-- Populate with actual kernel network devices instead of UCI interface names,
-- because the backend (nftables iifname / iptables -i) matches kernel device names.
do
local nfs = require "nixio.fs"
local _cursor = require("luci.model.uci").cursor()
local _sysnet = "/sys/class/net/"

-- Map UCI interface names to their device names and vice versa
local _iface_to_dev = {}
local _dev_to_ifaces = {}
local _iface_proto = {}
_cursor:foreach("network", "interface", function(sec)
local name = sec[".name"]
if name ~= "loopback" then
_iface_proto[name] = sec.proto
if sec.device then
_iface_to_dev[name] = sec.device
_dev_to_ifaces[sec.device] = _dev_to_ifaces[sec.device] or {}
table.insert(_dev_to_ifaces[sec.device], name)
end
end
end)

-- Classify device type using sysfs attributes
local function classify_sysfs(dev)
if nfs.stat(_sysnet .. dev .. "/bridge", "type") == "dir" then
return translate("Bridge")
elseif nfs.stat(_sysnet .. dev .. "/wireless", "type") == "dir" then
return translate("Wireless Adapter")
elseif dev:match("^tun") or dev:match("^tap") or dev:match("^wg") or dev:match("^ppp") then
return translate("Tunnel Interface")
else
return translate("Ethernet Adapter")
end
end

-- Classify offline UCI interfaces by config hints
local function classify_uci(dev_name, proto)
if dev_name and dev_name:match("^br%-") then
return translate("Bridge")
elseif proto == "wireguard" or proto == "pppoe" or proto == "pptp" or proto == "l2tp" then
return translate("Tunnel Interface")
else
return translate("Interface")
end
end

local _seen = {}
local _devices = {}

-- Active kernel devices from /sys/class/net/.
-- Skip bridge member ports (/master) and DSA master devices (/dsa) because
-- nftables iifname matches the parent bridge for routed traffic, not
-- individual member ports. Also skip internal virtual devices.
local _iter = nfs.dir(_sysnet)
if _iter then
for dev in _iter do
if dev ~= "lo"
and not dev:match("^veth")
and not dev:match("^ifb")
and not dev:match("^gre")
and not dev:match("^sit")
and not dev:match("^ip6tnl")
and not dev:match("^erspan")
and not nfs.stat(_sysnet .. dev .. "/master", "type")
and not nfs.stat(_sysnet .. dev .. "/dsa", "type")
then
local dtype = classify_sysfs(dev)
local label = dtype .. ': "' .. dev .. '"'
if _dev_to_ifaces[dev] then
label = label .. " (" .. table.concat(_dev_to_ifaces[dev], ", ") .. ")"
end
_devices[#_devices + 1] = { name = dev, label = label, sort = dtype .. ":" .. dev }
_seen[dev] = true
end
end
end

-- UCI interfaces whose device does not currently exist (down tunnels, VPNs, etc.).
-- Stored by UCI name since the kernel device is not available yet.
-- Dedup by device: if two interfaces share a device, only one is shown.
for iface, dev in pairs(_iface_to_dev) do
if not _seen[dev] then
local dtype = classify_uci(dev, _iface_proto[iface])
local label = dtype .. ': "' .. iface .. '"'
-- Sort offline entries after active devices
_devices[#_devices + 1] = { name = iface, label = label, sort = "zzz:" .. iface }
_seen[dev] = true
end
end

table.sort(_devices, function(a, b) return a.sort < b.sort end)
for _, d in ipairs(_devices) do
o:value(d.name, d.label)
end
end

o.validate = function(self, value, section)
if value == "" or value:match("^[a-zA-Z0-9][a-zA-Z0-9%.%_%-]*$") then
return value
end
return nil, translate("Invalid interface name")
end

local mac_t = {}
sys.net.mac_hints(function(e, t)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ if not arg[1] or not m:get(arg[1]) then
luci.http.redirect(m.redirect)
end

fs = require "nixio.fs"
formvalue_key = "cbid." .. appname .. "." .. arg[1] .. "."

local header = Template(appname .. "/node_config/header")
header.api = api
header.section = arg[1]
Expand Down Expand Up @@ -61,12 +64,18 @@ o.write = function(self, section, value)
m:set(section, self.option, value)
end

local fs = api.fs
local types_dir = "/usr/lib/lua/luci/model/cbi/passwall/client/type/"
s.val = {}
s.val["type"] = m.uci:get(appname, arg[1], "type")
s.val["protocol"] = m.uci:get(appname, arg[1], "protocol")

if luci.http.formvalue("cbi.submit") == "1" then
local formvalue_type = luci.http.formvalue(formvalue_key .. "type")
if formvalue_type then
s.val["type"] = formvalue_type
end
end

o = s:option(ListValue, "type", translate("Type"))

if api.is_finded("ipt2socks") then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ local function _n(name)
return option_prefix .. name
end

local formvalue_key = "cbid." .. appname .. "." .. arg[1] .. "."
local formvalue_proto = luci.http.formvalue(formvalue_key .. _n("protocol"))

if formvalue_proto then s.val["protocol"] = formvalue_proto end
Expand Down Expand Up @@ -298,6 +297,7 @@ o = s:option(ListValue, _n("flow"), translate("flow"))
o.default = ""
o:value("", translate("Disable"))
o:value("xtls-rprx-vision")
o:value("xtls-rprx-vision-udp443")
o:depends({ [_n("protocol")] = "vless" })

---- [[hysteria2]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ local function _n(name)
return option_prefix .. name
end

local formvalue_key = "cbid." .. appname .. "." .. arg[1] .. "."
local formvalue_proto = luci.http.formvalue(formvalue_key .. _n("protocol"))

if formvalue_proto then s.val["protocol"] = formvalue_proto end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ o = s:option(ListValue, _n("flow"), translate("flow"))
o.default = ""
o:value("", translate("Disable"))
o:value("xtls-rprx-vision")
o:value("xtls-rprx-vision-udp443")
o:depends({ [_n("protocol")] = "vless" })

---- [[ hysteria2 ]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ function gen_config(var)
local config = {
server = server,
transport = {
type = node.protocol or "udp",
type = "udp",
udp = {
hopInterval = (function()
local HopIntervalStr = tostring(node.hysteria2_hop_interval or "30s")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,8 @@ function gen_outbound(flag, node, tag, proxy_table)
["User-Agent"] = node.user_agent
} or nil,
quic = node.naive_quic == "1" and true or false,
quic_congestion_control = (node.naive_quic == "1" and node.naive_congestion_control) and node.naive_congestion_control or nil
quic_congestion_control = (node.naive_quic == "1" and node.naive_congestion_control) and node.naive_congestion_control or nil,
tls = tls
}
end

Expand Down Expand Up @@ -1812,7 +1813,9 @@ function gen_config(var)
table.insert(dns.rules, {
query_type = { "A", "AAAA" },
server = fakedns_tag,
disable_cache = true
disable_cache = true,
rewrite_ttl = 30,
strategy = remote_strategy
})
end
end
Expand Down Expand Up @@ -1954,7 +1957,7 @@ function gen_config(var)
}
})
for index, value in ipairs(config.outbounds) do
if not value["_flag_proxy_tag"] and not value.detour and value["_id"] and value.server and value.server_port and not no_run then
if not value["_flag_proxy_tag"] and not value.detour and value["_id"] and value.server and (value.server_port or value.server_ports) and not no_run then
sys.call(string.format("echo '%s' >> %s", value["_id"], api.TMP_PATH .. "/direct_node_list"))
end
for k, v in pairs(config.outbounds[index]) do
Expand Down
67 changes: 44 additions & 23 deletions applications/luci-app-passwall/luasrc/passwall/util_xray.lua
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,7 @@ function gen_config(var)
local fakedns = nil
local routing = nil
local observatory = nil
local burstObservatory = nil
local strategy = nil
local inbounds = {}
local outbounds = {}
Expand Down Expand Up @@ -1003,19 +1004,31 @@ function gen_config(var)
fallbackTag = fallback_node_tag,
strategy = strategy
})
if not observatory and (_node.balancingStrategy == "leastPing" or _node.balancingStrategy == "leastLoad" or fallback_node_tag) then
if _node.balancingStrategy == "leastPing" or _node.balancingStrategy == "leastLoad" or fallback_node_tag then
local t = api.format_go_time(_node.probeInterval)
if t == "0s" then
t = "60s"
elseif not t:find("[hm]") and tonumber(t:match("%d+")) < 10 then
t = "10s"
end
observatory = {
subjectSelector = { "blc-" },
probeUrl = _node.useCustomProbeUrl and _node.probeUrl or "https://www.google.com/generate_204",
probeInterval = t,
enableConcurrency = true
}
if _node.balancingStrategy == "leastLoad" then
burstObservatory = burstObservatory or {
subjectSelector = { "blc-" },
pingConfig = {
destination = _node.useCustomProbeUrl and _node.probeUrl or nil,
interval = t,
sampling = 3,
timeout = "5s"
}
}
else
observatory = observatory or {
subjectSelector = { "blc-" },
probeUrl = _node.useCustomProbeUrl and _node.probeUrl or nil,
probeInterval = t,
enableConcurrency = true
}
end
end
local loopback_outbound = gen_loopback(loopback_tag, loopback_dst)
local inbound_tag = loopback_outbound.settings.inboundTag
Expand All @@ -1034,14 +1047,25 @@ function gen_config(var)
else
local preproxy_node = get_node_by_id(node.preproxy_node)
if preproxy_node then
local preproxy_outbound = gen_outbound(node[".name"], preproxy_node)
local preproxy_outbound, exist
if preproxy_node.protocol == "_balancing" then
local balancer_tag, loopback_outbound = gen_balancer(preproxy_node)
if loopback_outbound then
preproxy_outbound = loopback_outbound
exist = true
end
else
preproxy_outbound = gen_outbound(node[".name"], preproxy_node)
end
if preproxy_outbound then
outbound.tag = preproxy_outbound.tag .. " -> " .. outbound.tag
outbound.proxySettings = {
tag = preproxy_outbound.tag,
transportLayer = true
}
last_insert_outbound = preproxy_outbound
if not exist then
last_insert_outbound = preproxy_outbound
end
default_outTag = outbound.tag
end
end
Expand All @@ -1051,17 +1075,17 @@ function gen_config(var)
local to_node = get_node_by_id(node.to_node)
if to_node then
-- Landing Node not support use special node.
if to_node.protocol:find("^_") then
if to_node.protocol and to_node.protocol:find("^_") then
to_node = nil
end
end
if to_node then
local to_outbound
if to_node.type ~= "Xray" then
local tag = to_node[".name"]
local in_tag = "inbound_" .. to_node[".name"] .. "_" .. tostring(outbound.tag)
local new_port = api.get_new_port()
table.insert(inbounds, {
tag = tag,
tag = in_tag,
listen = "127.0.0.1",
port = new_port,
protocol = "dokodemo-door",
Expand All @@ -1073,11 +1097,11 @@ function gen_config(var)
to_node.address = "127.0.0.1"
to_node.port = new_port
table.insert(rules, 1, {
inboundTag = {tag},
inboundTag = {in_tag},
outboundTag = outbound.tag
})
to_outbound = gen_outbound(node[".name"], to_node, tag, {
tag = tag,
to_outbound = gen_outbound(node[".name"], to_node, to_node[".name"], {
tag = to_node[".name"],
run_socks_instance = not no_run
})
else
Expand Down Expand Up @@ -1670,7 +1694,8 @@ function gen_config(var)
-- 传出连接
outbounds = outbounds,
-- 连接观测
observatory = observatory,
observatory = (not burstObservatory) and observatory or nil,
burstObservatory = burstObservatory,
-- 路由
routing = routing,
-- 本地策略
Expand Down Expand Up @@ -1720,14 +1745,10 @@ function gen_config(var)
else
table.insert(outbounds, blackhole_outbound)
end

for index, value in ipairs(config.outbounds) do
local s = value.settings
if not value["_flag_proxy_tag"] and value["_id"] and s and not no_run and
((s.vnext and s.vnext[1] and s.vnext[1].address and s.vnext[1].port) or
(s.servers and s.servers[1] and s.servers[1].address and s.servers[1].port) or
(s.peers and s.peers[1] and s.peers[1].endpoint) or
(s.address and s.port)) then
local pt = value.protocol
local exclude = { blackhole=1, dns=1, freedom=1, loopback=1 }
if not value["_flag_proxy_tag"] and value["_id"] and pt and not exclude[pt] and not no_run then
sys.call(string.format("echo '%s' >> %s", value["_id"], api.TMP_PATH .. "/direct_node_list"))
end
for k, v in pairs(config.outbounds[index]) do
Expand Down
Loading
Loading