diff --git a/openwrt/files/luci/model/cbi/ua3f.lua b/openwrt/files/luci/model/cbi/ua3f.lua
index c50b9b2..318944d 100644
--- a/openwrt/files/luci/model/cbi/ua3f.lua
+++ b/openwrt/files/luci/model/cbi/ua3f.lua
@@ -11,7 +11,13 @@ local ua3f = cbi.Map("ua3f",
Across the Campus we can reach every corner in the world.
]]
)
-local fields = require("luci.model.cbi.ua3f.fields")
+local status = require("luci.model.cbi.ua3f.status")
+local general = require("luci.model.cbi.ua3f.general")
+local rule = require("luci.model.cbi.ua3f.rule")
+local desync = require("luci.model.cbi.ua3f.desync")
+local others = require("luci.model.cbi.ua3f.others")
+local statistics = require("luci.model.cbi.ua3f.statistics")
+local log = require("luci.model.cbi.ua3f.log")
function create_sections(map)
local sections = {}
@@ -22,10 +28,10 @@ function create_sections(map)
-- General Section with tabs
sections.general = map:section(NamedSection, "main", "ua3f", translate("General"))
sections.general:tab("general", translate("Settings"))
- sections.general:tab("rewrite", translate("Rewrite Rules"))
+ sections.general:tab("rules", translate("Rewrite Rules"))
sections.general:tab("desync", translate("Desync Settings"))
sections.general:tab("others", translate("Others Settings"))
- sections.general:tab("stats", translate("Statistics"))
+ sections.general:tab("statistics", translate("Statistics"))
sections.general:tab("log", translate("Log"))
return sections
@@ -33,12 +39,12 @@ end
local sections = create_sections(ua3f)
-fields.add_status_fields(sections.status)
-fields.add_general_fields(sections.general)
-fields.add_rewrite_fields(sections.general)
-fields.add_desync_fields(sections.general)
-fields.add_others_fields(sections.general)
-fields.add_stats_fields(sections.general)
-fields.add_log_fields(sections.general)
+status.add_status_fields(sections.status)
+general.add_general_fields(sections.general)
+rule.add_rule_fields(sections.general)
+desync.add_desync_fields(sections.general)
+others.add_others_fields(sections.general)
+statistics.add_statistics_fields(sections.general)
+log.add_log_fields(sections.general)
return ua3f
diff --git a/openwrt/files/luci/model/cbi/ua3f/desync.lua b/openwrt/files/luci/model/cbi/ua3f/desync.lua
new file mode 100644
index 0000000..e8026e7
--- /dev/null
+++ b/openwrt/files/luci/model/cbi/ua3f/desync.lua
@@ -0,0 +1,42 @@
+local M = {}
+
+local cbi = require("luci.cbi")
+local i18n = require("luci.i18n")
+local utils = require("luci.model.cbi.ua3f.utils")
+local translate = i18n.translate
+
+local Flag = cbi.Flag
+local Value = cbi.Value
+local DummyValue = cbi.DummyValue
+
+function M.add_desync_fields(section)
+ -- Enable TCP Desync
+ local desync_enabled = section:taboption("desync", Flag, "desync_enabled", translate("Enable TCP Desync"))
+ desync_enabled.description = translate("Enable TCP Desynchronization to evade DPI")
+
+ if not utils.nfqueue_exists() then
+ local nfqueue_warning = section:taboption("desync", DummyValue, "_desync_nfqueue_warning", " ")
+ nfqueue_warning.rawhtml = true
+ nfqueue_warning:depends("desync_enabled", 1)
+ function nfqueue_warning.cfgvalue(self, section)
+ return "" ..
+ translate("Recommend install kmod-nft-queue package for NFQUEUE mode") .. ""
+ end
+ end
+
+ -- CT Byte Setting
+ local ct_byte = section:taboption("desync", Value, "desync_ct_bytes", translate("Desync Bytes"))
+ ct_byte.placeholder = "1500"
+ ct_byte.datatype = "uinteger"
+ ct_byte.description = translate("Number of bytes for fragmented random emission")
+ ct_byte:depends("desync_enabled", "1")
+
+ -- CT Packets Setting
+ local ct_packets = section:taboption("desync", Value, "desync_ct_packets", translate("Desync Packets"))
+ ct_packets.placeholder = "8"
+ ct_packets.datatype = "uinteger"
+ ct_packets.description = translate("Number of packets for fragmented random emission")
+ ct_packets:depends("desync_enabled", "1")
+end
+
+return M
diff --git a/openwrt/files/luci/model/cbi/ua3f/fields.lua b/openwrt/files/luci/model/cbi/ua3f/fields.lua
deleted file mode 100644
index 7f03cff..0000000
--- a/openwrt/files/luci/model/cbi/ua3f/fields.lua
+++ /dev/null
@@ -1,303 +0,0 @@
-local M = {}
-
-local cbi = require("luci.cbi")
-local i18n = require("luci.i18n")
-local sys = require("luci.sys")
-local translate = i18n.translate
-
-local Flag = cbi.Flag
-local Value = cbi.Value
-local ListValue = cbi.ListValue
-local DummyValue = cbi.DummyValue
-local TextValue = cbi.TextValue
-
-function cmd_exists(cmd)
- return sys.call("command -v " .. cmd .. " >/dev/null 2>&1") == 0
-end
-
-function nfqueue_exists()
- local opkg = cmd_exists("opkg") and sys.call("opkg list-installed kmod-nft-queue | grep -q kmod-nft-queue") == 0
- local apk = cmd_exists("apk") and (sys.call("apk info | grep -q kmod-nft-queue") == 0)
- return opkg or apk
-end
-
-function tproxy_exists()
- local opkg = cmd_exists("opkg") and sys.call("opkg list-installed kmod-nft-tproxy | grep -q kmod-nft-tproxy") == 0
- local apk = cmd_exists("apk") and (sys.call("apk info | grep -q kmod-nft-tproxy") == 0)
- return opkg or apk
-end
-
--- Status Section Fields
-function M.add_status_fields(section)
- -- Enabled Flag
- section:option(Flag, "enabled", translate("Enabled"))
-
- -- Running Status Display
- local running = section:option(DummyValue, "running", translate("Status"))
- running.rawhtml = true
- running.cfgvalue = function(self, section)
- local pid = sys.exec("pidof ua3f")
- if pid == "" then
- return ""
- else
- return ""
- end
- end
-end
-
--- General Tab Fields
-function M.add_general_fields(section)
- -- Server Mode
- local server_mode = section:taboption("general", ListValue, "server_mode", translate("Server Mode"))
- server_mode:value("HTTP", "HTTP")
- server_mode:value("SOCKS5", "SOCKS5")
- server_mode:value("TPROXY", "TPROXY")
- server_mode:value("REDIRECT", "REDIRECT")
- server_mode:value("NFQUEUE", "NFQUEUE")
- server_mode.default = "TPROXY"
-
- if not tproxy_exists() then
- local tproxy_warning = section:taboption("general", DummyValue, "_tproxy_warning", " ")
- tproxy_warning.rawhtml = true
- tproxy_warning:depends("server_mode", "TPROXY")
- function tproxy_warning.cfgvalue(self, section)
- return "" ..
- translate("Recommend install kmod-nft-tproxy package for TPROXY mode") .. ""
- end
- end
-
- if not nfqueue_exists() then
- local nfqueue_warning = section:taboption("general", DummyValue, "_nfqueue_warning", " ")
- nfqueue_warning.rawhtml = true
- nfqueue_warning:depends("server_mode", "NFQUEUE")
- function nfqueue_warning.cfgvalue(self, section)
- return "" ..
- translate("Recommend install kmod-nft-queue package for NFQUEUE mode") .. ""
- end
- end
-
- -- Bind Address
- local bind = section:taboption("general", Value, "bind", translate("Bind Address"))
- bind:value("127.0.0.1")
- bind:value("0.0.0.0")
- bind:depends("server_mode", "HTTP")
- bind:depends("server_mode", "SOCKS5")
-
- -- Port
- local port = section:taboption("general", Value, "port", translate("Port"))
- port.placeholder = "1080"
- port:depends("server_mode", "HTTP")
- port:depends("server_mode", "SOCKS5")
- port:depends("server_mode", "TPROXY")
- port:depends("server_mode", "REDIRECT")
-
- -- Rewrite Mode
- local rewrite_mode = section:taboption("general", ListValue, "rewrite_mode", translate("Rewrite Mode"))
- rewrite_mode:value("DIRECT", translate("Direct Forward"))
- rewrite_mode:value("GLOBAL", translate("Global Rewrite"))
- rewrite_mode:value("RULES", translate("Rule Based"))
- rewrite_mode.default = "GLOBAL"
- rewrite_mode.description = translate(
- "Direct Forward: No rewriting. Global Rewrite: Rewrite all User-Agents. Rule Based: Use rewrite rules to determine behavior.")
-
- -- User-Agent (for Global Rewrite)
- local ua = section:taboption("general", Value, "ua", translate("User-Agent"))
- ua.placeholder = "FFF"
- ua.description = translate("User-Agent after rewrite")
- ua:depends("rewrite_mode", "GLOBAL")
- ua:depends("server_mode", "NFQUEUE")
-
- -- User-Agent Regex
- local regex = section:taboption("general", Value, "ua_regex", translate("User-Agent Regex"))
- regex.description = translate("Regular expression pattern for matching User-Agent")
- regex:depends("rewrite_mode", "GLOBAL")
- regex:depends("server_mode", "NFQUEUE")
-
- -- Partial Replace
- local partialReplace = section:taboption("general", Flag, "partial_replace", translate("Partial Replace"))
- partialReplace.description =
- translate(
- "Replace only the matched part of the User-Agent, only works when User-Agent Regex is not empty")
- partialReplace.default = "0"
- partialReplace:depends("rewrite_mode", "GLOBAL")
- partialReplace:depends("server_mode", "NFQUEUE")
-end
-
--- Rewrite Rules Tab Fields
-function M.add_rewrite_fields(section)
- local rules = section:taboption("rewrite", DummyValue, "")
- rules.template = "ua3f/rules"
-end
-
--- Statistics Tab Fields
-function M.add_stats_fields(section)
- local stats = section:taboption("stats", DummyValue, "")
- stats.template = "ua3f/statistics"
-end
-
--- Log Tab Fields
-function M.add_log_fields(section)
- -- Log Display
- local log = section:taboption("log", TextValue, "log")
- log.readonly = true
- log.rows = 30
- log.wrap = "off"
- function log.cfgvalue(self, section)
- local logfile = "/var/log/ua3f/ua3f.log"
- local fs = require("nixio.fs")
- if not fs.access(logfile) then
- return ""
- end
- local n = tonumber(luci.model.uci.cursor():get("ua3f", section, "log_lines")) or 1000
- return luci.sys.exec("tail -n " .. n .. " " .. logfile)
- end
-
- function log.write(self, section, value) end
-
- function log.render(self, section, scope)
- TextValue.render(self, section, scope)
- luci.http.write("")
- end
-
- -- Log Level
- local log_level = section:taboption("log", ListValue, "log_level", translate("Log Level"))
- log_level:value("DEBUG")
- log_level:value("INFO")
- log_level:value("WARN")
- log_level:value("ERROR")
- log_level.default = "WARN"
- log_level.description = translate(
- "Sets the logging level. Do not keep the log level set to DEBUG for an extended period of time.")
-
- -- Log Lines
- local logLines = section:taboption("log", Value, "log_lines", translate("Display Lines"))
- logLines.default = "1000"
- logLines.datatype = "uinteger"
- logLines.rmempty = false
-
- -- Button Container (DummyValue to hold all buttons)
- local button_container = section:taboption("log", DummyValue, "_button_container", translate("Log Actions"))
- button_container.rawhtml = true
-
- function button_container.cfgvalue(self, section)
- return ""
- end
-
- function button_container.render(self, section, scope)
- luci.http.write([[
-
-
- ]])
- end
-
- return log
-end
-
-function M.add_desync_fields(section)
- -- Enable TCP Desync
- local desync_enabled = section:taboption("desync", Flag, "desync_enabled", translate("Enable TCP Desync"))
- desync_enabled.description = translate("Enable TCP Desynchronization to evade DPI")
-
- if not nfqueue_exists() then
- local nfqueue_warning = section:taboption("desync", DummyValue, "_desync_nfqueue_warning", " ")
- nfqueue_warning.rawhtml = true
- nfqueue_warning:depends("desync_enabled", 1)
- function nfqueue_warning.cfgvalue(self, section)
- return "" ..
- translate("Recommend install kmod-nft-queue package for NFQUEUE mode") .. ""
- end
- end
-
- -- CT Byte Setting
- local ct_byte = section:taboption("desync", Value, "desync_ct_bytes", translate("Desync Bytes"))
- ct_byte.placeholder = "1500"
- ct_byte.datatype = "uinteger"
- ct_byte.description = translate("Number of bytes for fragmented random emission")
- ct_byte:depends("desync_enabled", "1")
-
- -- CT Packets Setting
- local ct_packets = section:taboption("desync", Value, "desync_ct_packets", translate("Desync Packets"))
- ct_packets.placeholder = "8"
- ct_packets.datatype = "uinteger"
- ct_packets.description = translate("Number of packets for fragmented random emission")
- ct_packets:depends("desync_enabled", "1")
-end
-
--- Others Tab Fields
-function M.add_others_fields(section)
- -- TTL Setting
- local ttl = section:taboption("others", Flag, "set_ttl", translate("Set TTL"))
- ttl.description = translate("Set the TTL 64 for packets")
-
- -- TCP Timestamp Deletion
- local tcpts = section:taboption("others", Flag, "del_tcpts", translate("Delete TCP Timestamps"))
- tcpts.description = translate("Remove TCP Timestamp option")
-
- -- TCP Initial Window
- local tcp_init_window = section:taboption("others", Flag, "set_tcp_init_window", translate("Set TCP Initial Window"))
- tcp_init_window.description = translate("Set the TCP Initial Window to 65535 for SYN packets")
-
- -- IP ID Setting
- local ipid = section:taboption("others", Flag, "set_ipid", translate("Set IP ID"))
- ipid.description = translate("Set the IP ID to 0 for packets")
-
- if not nfqueue_exists() then
- local nfqueue_warning = section:taboption("others", DummyValue, "_others_nfqueue_warning", " ")
- nfqueue_warning.rawhtml = true
- nfqueue_warning:depends("del_tcpts", 1)
- nfqueue_warning:depends("set_tcp_init_window", 1)
- nfqueue_warning:depends("set_ipid", 1)
- function nfqueue_warning.cfgvalue(self, section)
- return "" ..
- translate("Recommend install kmod-nft-queue package for NFQUEUE mode") .. ""
- end
- end
-end
-
-return M
diff --git a/openwrt/files/luci/model/cbi/ua3f/general.lua b/openwrt/files/luci/model/cbi/ua3f/general.lua
new file mode 100644
index 0000000..aeb643d
--- /dev/null
+++ b/openwrt/files/luci/model/cbi/ua3f/general.lua
@@ -0,0 +1,90 @@
+local M = {}
+
+local cbi = require("luci.cbi")
+local i18n = require("luci.i18n")
+local utils = require("luci.model.cbi.ua3f.utils")
+local translate = i18n.translate
+
+local Flag = cbi.Flag
+local Value = cbi.Value
+local ListValue = cbi.ListValue
+local DummyValue = cbi.DummyValue
+
+function M.add_general_fields(section)
+ -- Server Mode
+ local server_mode = section:taboption("general", ListValue, "server_mode", translate("Server Mode"))
+ server_mode:value("HTTP", "HTTP")
+ server_mode:value("SOCKS5", "SOCKS5")
+ server_mode:value("TPROXY", "TPROXY")
+ server_mode:value("REDIRECT", "REDIRECT")
+ server_mode:value("NFQUEUE", "NFQUEUE")
+ server_mode.default = "TPROXY"
+
+ if not utils.tproxy_exists() then
+ local tproxy_warning = section:taboption("general", DummyValue, "_tproxy_warning", " ")
+ tproxy_warning.rawhtml = true
+ tproxy_warning:depends("server_mode", "TPROXY")
+ function tproxy_warning.cfgvalue(self, section)
+ return "" ..
+ translate("Recommend install kmod-nft-tproxy package for TPROXY mode") .. ""
+ end
+ end
+
+ if not utils.nfqueue_exists() then
+ local nfqueue_warning = section:taboption("general", DummyValue, "_nfqueue_warning", " ")
+ nfqueue_warning.rawhtml = true
+ nfqueue_warning:depends("server_mode", "NFQUEUE")
+ function nfqueue_warning.cfgvalue(self, section)
+ return "" ..
+ translate("Recommend install kmod-nft-queue package for NFQUEUE mode") .. ""
+ end
+ end
+
+ -- Bind Address
+ local bind = section:taboption("general", Value, "bind", translate("Bind Address"))
+ bind:value("127.0.0.1")
+ bind:value("0.0.0.0")
+ bind:depends("server_mode", "HTTP")
+ bind:depends("server_mode", "SOCKS5")
+
+ -- Port
+ local port = section:taboption("general", Value, "port", translate("Port"))
+ port.placeholder = "1080"
+ port:depends("server_mode", "HTTP")
+ port:depends("server_mode", "SOCKS5")
+ port:depends("server_mode", "TPROXY")
+ port:depends("server_mode", "REDIRECT")
+
+ -- Rewrite Mode
+ local rewrite_mode = section:taboption("general", ListValue, "rewrite_mode", translate("Rewrite Mode"))
+ rewrite_mode:value("DIRECT", translate("Direct Forward"))
+ rewrite_mode:value("GLOBAL", translate("Global Rewrite"))
+ rewrite_mode:value("RULES", translate("Rule Based"))
+ rewrite_mode.default = "GLOBAL"
+ rewrite_mode.description = translate(
+ "Direct Forward: No rewriting. Global Rewrite: Rewrite all User-Agents. Rule Based: Use rewrite rules to determine behavior.")
+
+ -- User-Agent (for Global Rewrite)
+ local ua = section:taboption("general", Value, "ua", translate("User-Agent"))
+ ua.placeholder = "FFF"
+ ua.description = translate("User-Agent after rewrite")
+ ua:depends("rewrite_mode", "GLOBAL")
+ ua:depends("server_mode", "NFQUEUE")
+
+ -- User-Agent Regex
+ local regex = section:taboption("general", Value, "ua_regex", translate("User-Agent Regex"))
+ regex.description = translate("Regular expression pattern for matching User-Agent")
+ regex:depends("rewrite_mode", "GLOBAL")
+ regex:depends("server_mode", "NFQUEUE")
+
+ -- Partial Replace
+ local partialReplace = section:taboption("general", Flag, "partial_replace", translate("Partial Replace"))
+ partialReplace.description =
+ translate(
+ "Replace only the matched part of the User-Agent, only works when User-Agent Regex is not empty")
+ partialReplace.default = "0"
+ partialReplace:depends("rewrite_mode", "GLOBAL")
+ partialReplace:depends("server_mode", "NFQUEUE")
+end
+
+return M
diff --git a/openwrt/files/luci/model/cbi/ua3f/log.lua b/openwrt/files/luci/model/cbi/ua3f/log.lua
new file mode 100644
index 0000000..50e95e8
--- /dev/null
+++ b/openwrt/files/luci/model/cbi/ua3f/log.lua
@@ -0,0 +1,113 @@
+local M = {}
+
+local cbi = require("luci.cbi")
+local i18n = require("luci.i18n")
+local translate = i18n.translate
+
+local Value = cbi.Value
+local ListValue = cbi.ListValue
+local DummyValue = cbi.DummyValue
+local TextValue = cbi.TextValue
+
+function M.add_log_fields(section)
+ -- Log Display
+ local log = section:taboption("log", TextValue, "log")
+ log.readonly = true
+ log.rows = 30
+ log.wrap = "off"
+ function log.cfgvalue(self, section)
+ local logfile = "/var/log/ua3f/ua3f.log"
+ local fs = require("nixio.fs")
+ if not fs.access(logfile) then
+ return ""
+ end
+ local n = tonumber(luci.model.uci.cursor():get("ua3f", section, "log_lines")) or 1000
+ return luci.sys.exec("tail -n " .. n .. " " .. logfile)
+ end
+
+ function log.write(self, section, value) end
+
+ function log.render(self, section, scope)
+ TextValue.render(self, section, scope)
+ luci.http.write("")
+ end
+
+ -- Log Level
+ local log_level = section:taboption("log", ListValue, "log_level", translate("Log Level"))
+ log_level:value("DEBUG")
+ log_level:value("INFO")
+ log_level:value("WARN")
+ log_level:value("ERROR")
+ log_level.default = "WARN"
+ log_level.description = translate(
+ "Sets the logging level. Do not keep the log level set to DEBUG for an extended period of time.")
+
+ -- Log Lines
+ local logLines = section:taboption("log", Value, "log_lines", translate("Display Lines"))
+ logLines.default = "1000"
+ logLines.datatype = "uinteger"
+ logLines.rmempty = false
+
+ -- Button Container (DummyValue to hold all buttons)
+ local button_container = section:taboption("log", DummyValue, "_button_container", translate("Log Actions"))
+ button_container.rawhtml = true
+
+ function button_container.cfgvalue(self, section)
+ return ""
+ end
+
+ function button_container.render(self, section, scope)
+ luci.http.write([[
+
+
+ ]])
+ end
+
+ return log
+end
+
+return M
diff --git a/openwrt/files/luci/model/cbi/ua3f/others.lua b/openwrt/files/luci/model/cbi/ua3f/others.lua
new file mode 100644
index 0000000..d374154
--- /dev/null
+++ b/openwrt/files/luci/model/cbi/ua3f/others.lua
@@ -0,0 +1,41 @@
+local M = {}
+
+local cbi = require("luci.cbi")
+local i18n = require("luci.i18n")
+local utils = require("luci.model.cbi.ua3f.utils")
+local translate = i18n.translate
+
+local Flag = cbi.Flag
+local DummyValue = cbi.DummyValue
+
+function M.add_others_fields(section)
+ -- TTL Setting
+ local ttl = section:taboption("others", Flag, "set_ttl", translate("Set TTL"))
+ ttl.description = translate("Set the TTL 64 for packets")
+
+ -- TCP Timestamp Deletion
+ local tcpts = section:taboption("others", Flag, "del_tcpts", translate("Delete TCP Timestamps"))
+ tcpts.description = translate("Remove TCP Timestamp option")
+
+ -- TCP Initial Window
+ local tcp_init_window = section:taboption("others", Flag, "set_tcp_init_window", translate("Set TCP Initial Window"))
+ tcp_init_window.description = translate("Set the TCP Initial Window to 65535 for SYN packets")
+
+ -- IP ID Setting
+ local ipid = section:taboption("others", Flag, "set_ipid", translate("Set IP ID"))
+ ipid.description = translate("Set the IP ID to 0 for packets")
+
+ if not utils.nfqueue_exists() then
+ local nfqueue_warning = section:taboption("others", DummyValue, "_others_nfqueue_warning", " ")
+ nfqueue_warning.rawhtml = true
+ nfqueue_warning:depends("del_tcpts", 1)
+ nfqueue_warning:depends("set_tcp_init_window", 1)
+ nfqueue_warning:depends("set_ipid", 1)
+ function nfqueue_warning.cfgvalue(self, section)
+ return "" ..
+ translate("Recommend install kmod-nft-queue package for NFQUEUE mode") .. ""
+ end
+ end
+end
+
+return M
diff --git a/openwrt/files/luci/model/cbi/ua3f/rule.lua b/openwrt/files/luci/model/cbi/ua3f/rule.lua
new file mode 100644
index 0000000..5c62e24
--- /dev/null
+++ b/openwrt/files/luci/model/cbi/ua3f/rule.lua
@@ -0,0 +1,12 @@
+local M = {}
+
+local cbi = require("luci.cbi")
+
+local DummyValue = cbi.DummyValue
+
+function M.add_rule_fields(section)
+ local rules = section:taboption("rules", DummyValue, "")
+ rules.template = "ua3f/rules"
+end
+
+return M
diff --git a/openwrt/files/luci/model/cbi/ua3f/statistics.lua b/openwrt/files/luci/model/cbi/ua3f/statistics.lua
index 8440ee3..b78770a 100644
--- a/openwrt/files/luci/model/cbi/ua3f/statistics.lua
+++ b/openwrt/files/luci/model/cbi/ua3f/statistics.lua
@@ -1,6 +1,14 @@
--- UA3F Statistics Data Module
local M = {}
+local cbi = require("luci.cbi")
+
+local DummyValue = cbi.DummyValue
+
+function M.add_statistics_fields(section)
+ local stats = section:taboption("statistics", DummyValue, "")
+ stats.template = "ua3f/statistics"
+end
+
-- Read rewrite statistics from file
function M.read_rewrite_stats()
local stats = {}
diff --git a/openwrt/files/luci/model/cbi/ua3f/status.lua b/openwrt/files/luci/model/cbi/ua3f/status.lua
new file mode 100644
index 0000000..ceb3701
--- /dev/null
+++ b/openwrt/files/luci/model/cbi/ua3f/status.lua
@@ -0,0 +1,30 @@
+local M = {}
+
+local cbi = require("luci.cbi")
+local i18n = require("luci.i18n")
+local sys = require("luci.sys")
+local translate = i18n.translate
+
+local Flag = cbi.Flag
+local DummyValue = cbi.DummyValue
+
+function M.add_status_fields(section)
+ -- Enabled Flag
+ section:option(Flag, "enabled", translate("Enabled"))
+
+ -- Running Status Display
+ local running = section:option(DummyValue, "running", translate("Status"))
+ running.rawhtml = true
+ running.cfgvalue = function(self, section)
+ local pid = sys.exec("pidof ua3f")
+ if pid == "" then
+ return ""
+ else
+ return ""
+ end
+ end
+end
+
+return M
diff --git a/openwrt/files/luci/model/cbi/ua3f/utils.lua b/openwrt/files/luci/model/cbi/ua3f/utils.lua
new file mode 100644
index 0000000..ace9fd4
--- /dev/null
+++ b/openwrt/files/luci/model/cbi/ua3f/utils.lua
@@ -0,0 +1,21 @@
+local M = {}
+
+local sys = require("luci.sys")
+
+local function cmd_exists(cmd)
+ return sys.call("command -v " .. cmd .. " >/dev/null 2>&1") == 0
+end
+
+function M.nfqueue_exists()
+ local opkg = cmd_exists("opkg") and sys.call("opkg list-installed kmod-nft-queue | grep -q kmod-nft-queue") == 0
+ local apk = cmd_exists("apk") and (sys.call("apk info | grep -q kmod-nft-queue") == 0)
+ return opkg or apk
+end
+
+function M.tproxy_exists()
+ local opkg = cmd_exists("opkg") and sys.call("opkg list-installed kmod-nft-tproxy | grep -q kmod-nft-tproxy") == 0
+ local apk = cmd_exists("apk") and (sys.call("apk info | grep -q kmod-nft-tproxy") == 0)
+ return opkg or apk
+end
+
+return M