diff --git a/luci-app-passwall/Makefile b/luci-app-passwall/Makefile
index f807b5681..be1051427 100644
--- a/luci-app-passwall/Makefile
+++ b/luci-app-passwall/Makefile
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall
PKG_VERSION:=26.9.16
-PKG_RELEASE:=281
+PKG_RELEASE:=282
PKG_PO_VERSION:=$(PKG_VERSION)
PKG_CONFIG_DEPENDS:= \
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/1_sing-box.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/1_sing-box.lua
index 769e125d9..fe6fd30eb 100644
--- a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/1_sing-box.lua
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/1_sing-box.lua
@@ -564,9 +564,12 @@ o.default = ""
o.rows = 5
o.wrap = "off"
o:depends({ tls_certificate = true })
+o.cfgvalue = function(self, section)
+ return (m:get(section, "tls_certificate_pem") or ""):gsub("\\n", "\n")
+end
o.validate = function(self, value)
- value = api.trim(value):gsub("\r\n", "\n"):gsub("[ \t]*\n[ \t]*", "\n"):gsub("\n+", "\n")
- return value
+ value = api.trim(value):gsub("\r\n", "\n"):gsub("\r", "\n"):gsub("[ \t]*\n[ \t]*", "\n"):gsub("\n+", "\n")
+ return value:gsub("\n", "\\n")
end
o = s:option(Value, "cipherSuites", translate("Cipher Suites"), '***' .. " " .. translate("Configures the list of supported cipher suites, separated by :"))
@@ -585,9 +588,12 @@ o.default = ""
o.rows = 5
o.wrap = "off"
o:depends({ ech = true })
+o.cfgvalue = function(self, section)
+ return (m:get(section, "ech_config") or ""):gsub("\\n", "\n")
+end
o.validate = function(self, value)
- value = api.trim(value):gsub("\r\n", "\n"):gsub("[ \t]*\n[ \t]*", "\n"):gsub("\n+", "\n")
- return value
+ value = api.trim(value):gsub("\r\n", "\n"):gsub("\r", "\n"):gsub("[ \t]*\n[ \t]*", "\n"):gsub("\n+", "\n")
+ return value:gsub("\n", "\\n")
end
o = s:option(Value, "ech_query_server_name", translate("ECH Query Domain"), translate("Overrides the domain name used for ECH HTTPS record queries."))
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/2_xray.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/2_xray.lua
index 52d5df673..c4a564a87 100644
--- a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/2_xray.lua
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/2_xray.lua
@@ -453,9 +453,12 @@ o.default = ""
o.rows = 5
o.wrap = "off"
o:depends({ tls_certificate = true })
+o.cfgvalue = function(self, section)
+ return (m:get(section, "tls_certificate_pem") or ""):gsub("\\n", "\n")
+end
o.validate = function(self, value)
- value = api.trim(value):gsub("\r\n", "\n"):gsub("[ \t]*\n[ \t]*", "\n"):gsub("\n+", "\n")
- return value
+ value = api.trim(value):gsub("\r\n", "\n"):gsub("\r", "\n"):gsub("[ \t]*\n[ \t]*", "\n"):gsub("\n+", "\n")
+ return value:gsub("\n", "\\n")
end
o = s:option(Flag, "ech", translate("ECH"))
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/1_sing-box.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/1_sing-box.lua
index 59a843d47..65e69f993 100644
--- a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/1_sing-box.lua
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/1_sing-box.lua
@@ -253,12 +253,19 @@ o.default = 0
o.validate = function(self, value, t)
if value then
local reality = s.fields["reality"] and s.fields["reality"]:formvalue(t) or nil
+ local use_pem = s.fields["tls_use_pem"] and s.fields["tls_use_pem"]:formvalue(t) or nil
if reality and reality == "1" then return value end
- if value == "1" then
+ if value == "1" and use_pem ~= "1" then
local ca = s.fields["tls_certificateFile"] and s.fields["tls_certificateFile"]:formvalue(t) or ""
local key = s.fields["tls_keyFile"] and s.fields["tls_keyFile"]:formvalue(t) or ""
if ca == "" or key == "" then
- return nil, translate("Certificate or Private key path can not be empty!")
+ return nil, translate("Certificate and Private key path can not be empty!")
+ end
+ elseif value == "1" and use_pem == "1" then
+ local ca = s.fields["tls_certificate"] and s.fields["tls_certificate"]:formvalue(t) or ""
+ local key = s.fields["tls_key"] and s.fields["tls_key"]:formvalue(t) or ""
+ if ca == "" or key == "" then
+ return nil, translate("Certificate and Private key PEM can not be empty!")
end
end
return value
@@ -311,14 +318,21 @@ o:depends({ protocol = "hysteria" })
-- [[ TLS部分 ]] --
-o = s:option(FileUpload, "tls_certificateFile", translate("Path to the certificate file"), translate("as:") .. "/etc/ssl/fullchain.crt")
-o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".crt"
-if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
+o = s:option(Flag, "tls_use_pem", translate("Use PEM"), translate("Use certificate and private key PEM content."))
o:depends({ tls = true, reality = false })
o:depends({ protocol = "naive" })
o:depends({ protocol = "hysteria" })
o:depends({ protocol = "tuic" })
o:depends({ protocol = "hysteria2" })
+
+o = s:option(FileUpload, "tls_certificateFile", translate("Path to the certificate file"), translate("as:") .. "/etc/ssl/fullchain.crt")
+o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".crt"
+if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
+o:depends({ tls = true, reality = false, tls_use_pem = false })
+o:depends({ protocol = "naive", tls_use_pem = false })
+o:depends({ protocol = "hysteria", tls_use_pem = false })
+o:depends({ protocol = "tuic", tls_use_pem = false })
+o:depends({ protocol = "hysteria2", tls_use_pem = false })
o.validate = function(self, value, t)
if value and value ~= "" then
if not api.fs.access(value) then
@@ -333,11 +347,11 @@ end
o = s:option(FileUpload, "tls_keyFile", translate("Path to the private key file"), translate("as:") .. "/etc/ssl/private.key")
o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. arg[1] .. ".key"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
-o:depends({ tls = true, reality = false })
-o:depends({ protocol = "naive" })
-o:depends({ protocol = "hysteria" })
-o:depends({ protocol = "tuic" })
-o:depends({ protocol = "hysteria2" })
+o:depends({ tls = true, reality = false, tls_use_pem = false })
+o:depends({ protocol = "naive", tls_use_pem = false })
+o:depends({ protocol = "hysteria", tls_use_pem = false })
+o:depends({ protocol = "tuic", tls_use_pem = false })
+o:depends({ protocol = "hysteria2", tls_use_pem = false })
o.validate = function(self, value, t)
if value and value ~= "" then
if not api.fs.access(value) then
@@ -349,6 +363,32 @@ o.validate = function(self, value, t)
return nil
end
+o = s:option(TextValue, "tls_certificate", "TLS Certificate (PEM)", translate("Full certificate (chain), PEM format."))
+o.default = ""
+o.rows = 5
+o.wrap = "off"
+o:depends({ tls_use_pem = true })
+o.cfgvalue = function(self, section)
+ return (m:get(section, "tls_certificate") or ""):gsub("\\n", "\n")
+end
+o.validate = function(self, value)
+ value = api.trim(value):gsub("\r\n", "\n"):gsub("\r", "\n"):gsub("[ \t]*\n[ \t]*", "\n"):gsub("\n+", "\n")
+ return value:gsub("\n", "\\n")
+end
+
+o = s:option(TextValue, "tls_key", "TLS Private Key (PEM)", translate("Private key in PEM format."))
+o.default = ""
+o.rows = 5
+o.wrap = "off"
+o:depends({ tls_use_pem = true })
+o.cfgvalue = function(self, section)
+ return (m:get(section, "tls_key") or ""):gsub("\\n", "\n")
+end
+o.validate = function(self, value)
+ value = api.trim(value):gsub("\r\n", "\n"):gsub("\r", "\n"):gsub("[ \t]*\n[ \t]*", "\n"):gsub("\n+", "\n")
+ return value:gsub("\n", "\\n")
+end
+
o = s:option(Flag, "ech", translate("ECH"))
o.default = "0"
o:depends({ tls = true, flow = "", reality = false })
@@ -362,13 +402,12 @@ o.default = ""
o.rows = 5
o.wrap = "off"
o:depends({ ech = true })
+o.cfgvalue = function(self, section)
+ return (m:get(section, "ech_key") or ""):gsub("\\n", "\n")
+end
o.validate = function(self, value)
- value = value:gsub("^%s+", ""):gsub("%s+$","\n"):gsub("\r\n","\n"):gsub("[ \t]*\n[ \t]*", "\n")
- value = value:gsub("^%s*\n", "")
- if value:sub(-1) == "\n" then
- value = value:sub(1, -2)
- end
- return value
+ value = api.trim(value):gsub("\r\n", "\n"):gsub("\r", "\n"):gsub("[ \t]*\n[ \t]*", "\n"):gsub("\n+", "\n")
+ return value:gsub("\n", "\\n")
end
o = s:option(ListValue, "transport", translate("Transport"))
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/2_xray.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/2_xray.lua
index 017814211..b126f0211 100644
--- a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/2_xray.lua
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/2_xray.lua
@@ -193,12 +193,19 @@ o.default = 0
o.validate = function(self, value, t)
if value then
local reality = s.fields["reality"] and s.fields["reality"]:formvalue(t) or nil
+ local use_pem = s.fields["tls_use_pem"] and s.fields["tls_use_pem"]:formvalue(t) or nil
if reality and reality == "1" then return value end
- if value == "1" then
+ if value == "1" and use_pem ~= "1" then
local ca = s.fields["tls_certificateFile"] and s.fields["tls_certificateFile"]:formvalue(t) or ""
local key = s.fields["tls_keyFile"] and s.fields["tls_keyFile"]:formvalue(t) or ""
if ca == "" or key == "" then
- return nil, translate("Certificate or Private key path can not be empty!")
+ return nil, translate("Certificate and Private key path can not be empty!")
+ end
+ elseif value == "1" and use_pem == "1" then
+ local ca = s.fields["tls_certificate"] and s.fields["tls_certificate"]:formvalue(t) or ""
+ local key = s.fields["tls_key"] and s.fields["tls_key"]:formvalue(t) or ""
+ if ca == "" or key == "" then
+ return nil, translate("Certificate and Private key PEM can not be empty!")
end
end
return value
@@ -263,11 +270,15 @@ end
-- [[ TLS部分 ]] --
+o = s:option(Flag, "tls_use_pem", translate("Use PEM"), translate("Use certificate and private key PEM content."))
+o:depends({ tls = true, reality = false })
+o:depends({ protocol = "hysteria2" })
+
o = s:option(FileUpload, "tls_certificateFile", translate("Path to the certificate file"), translate("as:") .. "/etc/ssl/fullchain.crt")
o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. s.section .. ".crt"
if o and o:formvalue(s.section) then o.default = o:formvalue(s.section) end
-o:depends({ tls = true, reality = false })
-o:depends({ protocol = "hysteria2"})
+o:depends({ tls = true, reality = false, tls_use_pem = false })
+o:depends({ protocol = "hysteria2", tls_use_pem = false })
o.validate = function(self, value, t)
if value and value ~= "" then
if not api.fs.access(value) then
@@ -282,8 +293,8 @@ end
o = s:option(FileUpload, "tls_keyFile", translate("Path to the private key file"), translate("as:") .. "/etc/ssl/private.key")
o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. s.section .. ".key"
if o and o:formvalue(s.section) then o.default = o:formvalue(s.section) end
-o:depends({ tls = true, reality = false })
-o:depends({ protocol = "hysteria2"})
+o:depends({ tls = true, reality = false, tls_use_pem = false })
+o:depends({ protocol = "hysteria2", tls_use_pem = false })
o.validate = function(self, value, t)
if value and value ~= "" then
if not api.fs.access(value) then
@@ -295,6 +306,32 @@ o.validate = function(self, value, t)
return nil
end
+o = s:option(TextValue, "tls_certificate", "TLS Certificate (PEM)", translate("Full certificate (chain), PEM format."))
+o.default = ""
+o.rows = 5
+o.wrap = "off"
+o:depends({ tls_use_pem = true })
+o.cfgvalue = function(self, section)
+ return (m:get(section, "tls_certificate") or ""):gsub("\\n", "\n")
+end
+o.validate = function(self, value)
+ value = api.trim(value):gsub("\r\n", "\n"):gsub("\r", "\n"):gsub("[ \t]*\n[ \t]*", "\n"):gsub("\n+", "\n")
+ return value:gsub("\n", "\\n")
+end
+
+o = s:option(TextValue, "tls_key", "TLS Private Key (PEM)", translate("Private key in PEM format."))
+o.default = ""
+o.rows = 5
+o.wrap = "off"
+o:depends({ tls_use_pem = true })
+o.cfgvalue = function(self, section)
+ return (m:get(section, "tls_key") or ""):gsub("\\n", "\n")
+end
+o.validate = function(self, value)
+ value = api.trim(value):gsub("\r\n", "\n"):gsub("\r", "\n"):gsub("[ \t]*\n[ \t]*", "\n"):gsub("\n+", "\n")
+ return value:gsub("\n", "\\n")
+end
+
o = s:option(Flag, "ech", translate("ECH"))
o.default = "0"
o:depends({ tls = true, reality = false })
diff --git a/luci-app-passwall/luasrc/passwall/util_sing-box.lua b/luci-app-passwall/luasrc/passwall/util_sing-box.lua
index b795ed42f..de86ddcb9 100644
--- a/luci-app-passwall/luasrc/passwall/util_sing-box.lua
+++ b/luci-app-passwall/luasrc/passwall/util_sing-box.lua
@@ -248,7 +248,7 @@ function gen_outbound(flag, node, tag, proxy_table)
--max_version = "1.3",
fragment = fragment,
record_fragment = record_fragment,
- certificate = (node.tls_certificate == "1" and node.tls_certificate_pem ~= "") and api.split(node.tls_certificate_pem, "\n") or nil,
+ certificate = (node.tls_certificate == "1" and node.tls_certificate_pem ~= "") and api.split(node.tls_certificate_pem:gsub("\\n", "\n"), "\n") or nil,
cipher_suites = (node.cipherSuites and node.cipherSuites ~= "") and api.split(node.cipherSuites, ":") or nil,
ech = (node.ech == "1") and (function()
local function get_ech_domain(s) --兼容xray "域名+DNS" 格式ech
@@ -273,7 +273,7 @@ function gen_outbound(flag, node, tag, proxy_table)
ech.query_server_name = qname
ech_domain[qname] = true
elseif config then
- ech.config = { config }
+ ech.config = api.split(config:gsub("\\n", "\n"), "\n")
elseif node.tls_serverName and node.tls_serverName ~= "" then
ech_domain[node.tls_serverName] = true
end
@@ -709,8 +709,10 @@ function gen_config_server(node)
local tls = {
enabled = true,
- certificate_path = node.tls_certificateFile,
- key_path = node.tls_keyFile,
+ certificate_path = (node.tls_use_pem ~= "1") and node.tls_certificateFile or nil,
+ key_path = (node.tls_use_pem ~= "1") and node.tls_keyFile or nil,
+ certificate = (node.tls_use_pem == "1" and node.tls_certificate) and api.split(node.tls_certificate:gsub("\\n", "\n"), "\n") or nil,
+ key = (node.tls_use_pem == "1" and node.tls_key) and api.split(node.tls_key:gsub("\\n", "\n"), "\n") or nil,
alpn = (node.alpn and node.alpn ~= "default") and (function()
local alpn = {}
string.gsub(node.alpn, '[^,]+', function(w)
@@ -724,6 +726,8 @@ function gen_config_server(node)
if node.tls == "1" and node.reality == "1" then
tls.certificate_path = nil
tls.key_path = nil
+ tls.certificate = nil
+ tls.key = nil
tls.server_name = node.reality_handshake_server
tls.reality = {
enabled = true,
diff --git a/luci-app-passwall/luasrc/passwall/util_xray.lua b/luci-app-passwall/luasrc/passwall/util_xray.lua
index fb78f8523..0c1c94e44 100644
--- a/luci-app-passwall/luasrc/passwall/util_xray.lua
+++ b/luci-app-passwall/luasrc/passwall/util_xray.lua
@@ -181,7 +181,7 @@ function gen_outbound(flag, node, tag, proxy_table)
verifyPeerCertByName = node.tls_CertByName or "",
echConfigList = (node.ech == "1") and node.ech_config or nil,
certificates = (node.tls_certificate == "1" and node.tls_certificate_pem ~= "") and {
- certificate = api.split(node.tls_certificate_pem, "\n"),
+ certificate = api.split(node.tls_certificate_pem:gsub("\\n", "\n"), "\n"),
usage = "verify"
} or nil,
cipherSuites = node.cipherSuites or nil
@@ -697,8 +697,10 @@ function gen_config_server(node)
disableSystemRoot = false,
certificates = {
{
- certificateFile = node.tls_certificateFile,
- keyFile = node.tls_keyFile
+ certificateFile = (node.tls_use_pem ~= "1") and node.tls_certificateFile or nil,
+ keyFile = (node.tls_use_pem ~= "1") and node.tls_keyFile or nil,
+ certificate = (node.tls_use_pem == "1" and node.tls_certificate) and api.split(node.tls_certificate:gsub("\\n", "\n"), "\n") or nil,
+ key = (node.tls_use_pem == "1" and node.tls_key) and api.split(node.tls_key:gsub("\\n", "\n"), "\n") or nil
}
},
echServerKeys = (node.ech == "1") and node.ech_key or nil
diff --git a/luci-app-passwall/po/zh-cn/passwall.po b/luci-app-passwall/po/zh-cn/passwall.po
index 8228c4c5d..a4bd6593b 100644
--- a/luci-app-passwall/po/zh-cn/passwall.po
+++ b/luci-app-passwall/po/zh-cn/passwall.po
@@ -1655,8 +1655,17 @@ msgstr "ECH 密钥路径"
msgid "Can't find this file!"
msgstr "找不到这个文件!"
-msgid "Certificate or Private key path can not be empty!"
-msgstr "证书或私钥文件路径不能为空!"
+msgid "Certificate and Private key path can not be empty!"
+msgstr "证书和私钥文件路径不能为空!"
+
+msgid "Certificate and Private key PEM can not be empty!"
+msgstr "证书和私钥 PEM 不能为空!"
+
+msgid "Use PEM"
+msgstr "使用 PEM"
+
+msgid "Use certificate and private key PEM content."
+msgstr "使用证书和私钥的 PEM 内容。"
msgid "Server-Side"
msgstr "服务器端"
@@ -1850,6 +1859,12 @@ msgstr "TLS 证书(PEM)"
msgid "Full certificate (chain), PEM format."
msgstr "完整证书(链),PEM 格式。"
+msgid "TLS Private Key (PEM)"
+msgstr "TLS 私钥(PEM)"
+
+msgid "Private key in PEM format."
+msgstr "PEM 格式的私钥。"
+
msgid "ECH Config"
msgstr "ECH 配置"
diff --git a/netifd/Makefile b/netifd/Makefile
deleted file mode 100644
index d21bb7866..000000000
--- a/netifd/Makefile
+++ /dev/null
@@ -1,53 +0,0 @@
-include $(TOPDIR)/rules.mk
-
-PKG_NAME:=netifd
-PKG_RELEASE:=5
-
-PKG_SOURCE_PROTO:=git
-PKG_SOURCE_URL=$(PROJECT_GIT)/project/netifd.git
-PKG_SOURCE_DATE:=2026-02-26
-PKG_SOURCE_VERSION:=cbb83a1857407a28a63dc09412a1f209195914ef
-PKG_MIRROR_HASH:=skip
-PKG_MAINTAINER:=Felix Fietkau
-
-PKG_LICENSE:=GPL-2.0
-PKG_LICENSE_FILES:=
-
-PKG_BUILD_FLAGS:=lto
-
-include $(INCLUDE_DIR)/package.mk
-include $(INCLUDE_DIR)/cmake.mk
-
-define Package/netifd
- SECTION:=base
- CATEGORY:=Base system
- DEPENDS:=+libuci +libnl-tiny +libubus +ubus +ubusd +jshn +libubox +libudebug \
- +ucode +ucode-mod-fs +ucode-mod-ubus +ucode-mod-uloop +ucode-mod-uci
- TITLE:=OpenWrt Network Interface Configuration Daemon
-endef
-
-define Package/netifd/conffiles
-/etc/udhcpc.user
-/etc/udhcpc.user.d/
-endef
-
-TARGET_CFLAGS += \
- -I$(STAGING_DIR)/usr/include/libnl-tiny \
- -I$(STAGING_DIR)/usr/include
-
-CMAKE_OPTIONS += \
- -DLIBNL_LIBS=-lnl-tiny \
- -DDEBUG=1
-
-define Package/netifd/install
- $(INSTALL_DIR) $(1)/sbin
- $(INSTALL_BIN) $(PKG_BUILD_DIR)/netifd $(1)/sbin/
- $(CP) ./files/* $(1)/
- $(INSTALL_DIR) $(1)/etc/udhcpc.user.d/
- $(CP) \
- $(PKG_BUILD_DIR)/scripts/utils.sh \
- $(PKG_BUILD_DIR)/scripts/netifd-proto.sh \
- $(1)/lib/netifd/
-endef
-
-$(eval $(call BuildPackage,netifd))
diff --git a/netifd/files/etc/hotplug.d/iface/00-netstate b/netifd/files/etc/hotplug.d/iface/00-netstate
deleted file mode 100644
index 71ccb0191..000000000
--- a/netifd/files/etc/hotplug.d/iface/00-netstate
+++ /dev/null
@@ -1,6 +0,0 @@
-[ ifup = "$ACTION" ] && {
- uci_toggle_state network "$INTERFACE" up 1
- [ -n "$DEVICE" ] && {
- uci_toggle_state network "$INTERFACE" ifname "$DEVICE"
- }
-}
diff --git a/netifd/files/etc/init.d/network b/netifd/files/etc/init.d/network
deleted file mode 100755
index b8b2a219b..000000000
--- a/netifd/files/etc/init.d/network
+++ /dev/null
@@ -1,143 +0,0 @@
-#!/bin/sh /etc/rc.common
-
-START=20
-STOP=90
-
-USE_PROCD=1
-
-init_switch() {
- setup_switch() { return 0; }
-
- include /lib/network
- setup_switch
-}
-
-start_service() {
- init_switch
-
- procd_open_instance
- procd_set_param command /sbin/netifd
- procd_set_param respawn
- procd_set_param watch network.interface
- [ -e /proc/sys/kernel/core_pattern ] && {
- procd_set_param limits core="unlimited"
- }
- procd_close_instance
-}
-
-reload_service() {
- local rv=0
-
- init_switch
- ubus call network reload || rv=1
- return $rv
-}
-
-stop_service() {
- [ -x /sbin/wifi ] && /sbin/wifi down
- ifdown -a
- sleep 1
-}
-
-validate_atm_bridge_section()
-{
- uci_validate_section network "atm-bridge" "${1}" \
- 'unit:uinteger:0' \
- 'vci:range(32, 65535):35' \
- 'vpi:range(0, 255):8' \
- 'atmdev:uinteger:0' \
- 'encaps:or("llc", "vc"):llc' \
- 'payload:or("bridged", "routed"):bridged'
-}
-
-validate_route_section()
-{
- uci_validate_section network route "${1}" \
- 'interface:string' \
- 'target:cidr4' \
- 'netmask:netmask4' \
- 'gateway:ip4addr' \
- 'metric:uinteger' \
- 'mtu:uinteger' \
- 'table:or(range(0,65535),string)'
-}
-
-validate_route6_section()
-{
- uci_validate_section network route6 "${1}" \
- 'interface:string' \
- 'target:cidr6' \
- 'gateway:ip6addr' \
- 'metric:uinteger' \
- 'mtu:uinteger' \
- 'table:or(range(0,65535),string)'
-}
-
-validate_rule_section()
-{
- uci_validate_section network rule "${1}" \
- 'in:string' \
- 'out:string' \
- 'src:cidr4' \
- 'dest:cidr4' \
- 'tos:range(0,31)' \
- 'mark:string' \
- 'invert:bool' \
- 'lookup:or(range(0,65535),string)' \
- 'goto:range(0,65535)' \
- 'action:or("prohibit", "unreachable", "blackhole", "throw")'
-}
-
-validate_rule6_section()
-{
- uci_validate_section network rule6 "${1}" \
- 'in:string' \
- 'out:string' \
- 'src:cidr6' \
- 'dest:cidr6' \
- 'tos:range(0,31)' \
- 'mark:string' \
- 'invert:bool' \
- 'lookup:or(range(0,65535),string)' \
- 'goto:range(0,65535)' \
- 'action:or("prohibit", "unreachable", "blackhole", "throw")'
-}
-
-validate_switch_section()
-{
- uci_validate_section network switch "${1}" \
- 'name:string' \
- 'enable:bool' \
- 'enable_vlan:bool' \
- 'reset:bool' \
- 'ar8xxx_mib_poll_interval:uinteger' \
- 'ar8xxx_mib_type:range(0,1)'
-}
-
-validate_switch_vlan()
-{
- uci_validate_section network switch_vlan "${1}" \
- 'device:string' \
- 'vlan:uinteger' \
- 'ports:list(ports)'
-}
-
-service_triggers()
-{
- procd_add_reload_trigger network wireless
-
- procd_open_validate
- validate_atm_bridge_section
- validate_route_section
- [ -e /proc/sys/net/ipv6 ] && validate_route6_section
- validate_rule_section
- [ -e /proc/sys/net/ipv6 ] && validate_rule6_section
- validate_switch_section
- validate_switch_vlan
- procd_close_validate
-}
-
-shutdown() {
- ifdown -a
- sleep 1
-}
diff --git a/netifd/files/etc/init.d/packet_steering b/netifd/files/etc/init.d/packet_steering
deleted file mode 100755
index 5266a931a..000000000
--- a/netifd/files/etc/init.d/packet_steering
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/sh /etc/rc.common
-
-START=25
-USE_PROCD=1
-
-start_service() {
- reload_service
-}
-
-service_triggers() {
- procd_add_reload_trigger "network"
- procd_add_reload_trigger "firewall"
- procd_add_raw_trigger "interface.*" 1000 /etc/init.d/packet_steering reload
-}
-
-reload_service() {
- packet_steering="$(uci -q get "network.@globals[0].packet_steering")"
- steering_flows="$(uci -q get "network.@globals[0].steering_flows")"
- [ "${steering_flows:-0}" -gt 0 ] && opts="-l $steering_flows"
- if [ -e "/usr/libexec/platform/packet-steering.sh" ]; then
- /usr/libexec/platform/packet-steering.sh "$packet_steering"
- else
- /usr/libexec/network/packet-steering.uc $opts "$packet_steering"
- fi
-}
diff --git a/netifd/files/etc/uci-defaults/14_migrate-dhcp-release b/netifd/files/etc/uci-defaults/14_migrate-dhcp-release
deleted file mode 100644
index f1b384eec..000000000
--- a/netifd/files/etc/uci-defaults/14_migrate-dhcp-release
+++ /dev/null
@@ -1,23 +0,0 @@
-. /lib/functions.sh
-
-migrate_release() {
- local config="$1"
- local proto
- local release
-
- config_get proto "$config" proto
- config_get release "$config" release
-
- [ "$proto" = "dhcp" ] && [ -n "$release" ] && {
- norelease="$((!$release))"
- uci_set network "$config" norelease "$norelease"
- uci_remove network "$config" release
- }
-
-}
-
-config_load network
-config_foreach migrate_release interface
-uci commit network
-
-exit 0
diff --git a/netifd/files/etc/udhcpc.user b/netifd/files/etc/udhcpc.user
deleted file mode 100644
index 78e2ba5f1..000000000
--- a/netifd/files/etc/udhcpc.user
+++ /dev/null
@@ -1 +0,0 @@
-# This script is sourced by udhcpc's dhcp.script at every DHCP event.
diff --git a/netifd/files/lib/netifd/dhcp.script b/netifd/files/lib/netifd/dhcp.script
deleted file mode 100755
index db8deac9e..000000000
--- a/netifd/files/lib/netifd/dhcp.script
+++ /dev/null
@@ -1,120 +0,0 @@
-#!/bin/sh
-[ -z "$1" ] && echo "Error: should be run by udhcpc" && exit 1
-
-. /lib/functions.sh
-. /lib/netifd/netifd-proto.sh
-
-set_classless_routes() {
- local max=128
- while [ -n "$1" -a -n "$2" -a $max -gt 0 ]; do
- proto_add_ipv4_route "${1%%/*}" "${1##*/}" "$2" "$ip"
- max=$(($max-1))
- shift 2
- done
-}
-
-setup_interface () {
- proto_init_update "*" 1
- proto_add_ipv4_address "$ip" "${subnet:-255.255.255.0}"
- # TODO: apply $broadcast
-
- local ip_net IP PREFIX NETWORK NETMASK BROADCAST
- ipcalc "$ip/$mask" && ip_net="$NETWORK"
-
- local i
- for i in $router; do
- local gw_net
- ipcalc "$i/$mask" && gw_net="$NETWORK"
-
- [ "$ip_net" != "$gw_net" ] && proto_add_ipv4_route "$i" 32 "" "$ip"
- proto_add_ipv4_route 0.0.0.0 0 "$i" "$ip"
-
- local r
- for r in $CUSTOMROUTES; do
- proto_add_ipv4_route "${r%%/*}" "${r##*/}" "$i" "$ip"
- done
- done
-
- # CIDR STATIC ROUTES (rfc3442)
- [ -n "$staticroutes" ] && set_classless_routes $staticroutes
- [ -n "$msstaticroutes" ] && set_classless_routes $msstaticroutes
-
- for i in $dns; do
- proto_add_dns_server "$i"
- done
- for i in $domain; do
- proto_add_dns_search "$i"
- done
-
- # TODO: Deprecate timesvr in favor of timesrv
- if [ -n "$timesvr" -a -z "$timesrv" ]; then
- timesrv="$timesvr"
- echo "Environment variable 'timesvr' will be deprecated; use 'timesrv' instead."
- fi
-
- proto_add_data
- [ -n "$ZONE" ] && json_add_string zone "$ZONE"
- [ -n "$ntpsrv" ] && json_add_string ntpserver "$ntpsrv"
- [ -n "$timesrv" ] && json_add_string timeserver "$timesrv"
- [ -n "$hostname" ] && json_add_string hostname "$hostname"
- [ -n "$message" ] && json_add_string message "$message"
- [ -n "$timezone" ] && json_add_int timezone "$timezone"
- [ -n "$lease" ] && json_add_int leasetime "$lease"
- [ -n "$serverid" ] && json_add_string dhcpserver "$serverid"
- proto_close_data
-
- proto_send_update "$INTERFACE"
-
-
- if [ "$IFACE6RD" != 0 -a -n "$ip6rd" ]; then
- local v4mask="${ip6rd%% *}"
- ip6rd="${ip6rd#* }"
- local ip6rdprefixlen="${ip6rd%% *}"
- ip6rd="${ip6rd#* }"
- local ip6rdprefix="${ip6rd%% *}"
- ip6rd="${ip6rd#* }"
- local ip6rdbr="${ip6rd%% *}"
-
- [ -n "$ZONE" ] || ZONE=$(fw3 -q network $INTERFACE 2>/dev/null)
- [ -z "$IFACE6RD" -o "$IFACE6RD" = 1 ] && IFACE6RD=${INTERFACE}_6
-
- json_init
- json_add_string name "$IFACE6RD"
- json_add_string ifname "@$INTERFACE"
- json_add_string proto "6rd"
- json_add_string peeraddr "$ip6rdbr"
- json_add_int ip4prefixlen "$v4mask"
- json_add_string ip6prefix "$ip6rdprefix"
- json_add_int ip6prefixlen "$ip6rdprefixlen"
- json_add_string tunlink "$INTERFACE"
- [ -n "$IFACE6RD_DELEGATE" ] && json_add_boolean delegate "$IFACE6RD_DELEGATE"
- [ -n "$ZONE6RD" ] || ZONE6RD=$ZONE
- [ -n "$ZONE6RD" ] && json_add_string zone "$ZONE6RD"
- [ -n "$MTU6RD" ] && json_add_string mtu "$MTU6RD"
- json_close_object
-
- ubus call network add_dynamic "$(json_dump)"
- fi
-}
-
-deconfig_interface() {
- proto_init_update "*" 0
- proto_send_update "$INTERFACE"
-}
-
-case "$1" in
- deconfig)
- deconfig_interface
- ;;
- renew|bound)
- setup_interface
- ;;
-esac
-
-# user rules
-[ -f /etc/udhcpc.user ] && . /etc/udhcpc.user "$@"
-for f in /etc/udhcpc.user.d/*; do
- [ -f "$f" ] && (. "$f" "$@")
-done
-
-exit 0
diff --git a/netifd/files/lib/netifd/main.uc b/netifd/files/lib/netifd/main.uc
deleted file mode 100644
index 69677bb15..000000000
--- a/netifd/files/lib/netifd/main.uc
+++ /dev/null
@@ -1,79 +0,0 @@
-import * as uci from "uci";
-import * as uloop from "uloop";
-import * as ubus from "ubus";
-import { access, dirname } from "fs";
-
-function ex_handler(e)
-{
- netifd.log(netifd.L_WARNING, `Exception: ${e}\n${e.stacktrace[0].context}\n`);
-}
-
-uloop.guard(ex_handler);
-ubus.guard(ex_handler);
-
-let wireless;
-
-function uci_ctx()
-{
- let savedir = netifd.dummy_mode ? "./tmp" : null;
- let ctx = uci.cursor(netifd.config_path, savedir, null, {
- strict: false
- });
- return ctx;
-}
-
-function config_init()
-{
- let ctx = uci_ctx();
-
- if (wireless)
- wireless.config_init(ctx);
-}
-
-function config_start()
-{
- if (wireless)
- wireless.config_start();
-}
-
-function check_interfaces()
-{
- if (wireless)
- wireless.check_interfaces();
-}
-
-function hotplug(name, add)
-{
- if (wireless)
- wireless.hotplug(name, add);
-}
-
-function ex_wrap(cb)
-{
- let fn = cb;
- return (...args) => {
- try {
- return fn(...args);
- } catch (e) {
- netifd.log(netifd.L_WARNING, `${e}\n${e.stacktrace[0].context}`);
- }
- };
-}
-
-netifd.cb = {
- hotplug: ex_wrap(hotplug),
- config_init: ex_wrap(config_init),
- config_start: ex_wrap(config_start),
- check_interfaces: ex_wrap(check_interfaces),
-};
-
-const wireless_module = dirname(sourcepath()) + "/wireless.uc";
-if (access(wireless_module, "r")) {
- try {
- wireless = loadfile(wireless_module)();
- } catch (e) {
- netifd.log(netifd.L_WARNING, `Error loading wireless module: ${e}\n${e.stacktrace[0].context}\n`);
- }
-} else {
- netifd.log(netifd.L_WARNING, `Wireless module not found\n`);
-}
diff --git a/netifd/files/lib/netifd/proto/dhcp.sh b/netifd/files/lib/netifd/proto/dhcp.sh
deleted file mode 100755
index 5948183ef..000000000
--- a/netifd/files/lib/netifd/proto/dhcp.sh
+++ /dev/null
@@ -1,140 +0,0 @@
-#!/bin/sh
-
-[ -x /sbin/udhcpc ] || exit 0
-
-. /lib/functions.sh
-. /lib/functions/network.sh
-. ../netifd-proto.sh
-. /lib/config/uci.sh
-init_proto "$@"
-
-proto_dhcp_init_config() {
- renew_handler=1
-
- proto_config_add_string 'ipaddr:ipaddr'
- proto_config_add_string 'hostname:hostname'
- proto_config_add_string clientid
- proto_config_add_string sendclientid
- proto_config_add_string vendorid
- proto_config_add_boolean 'broadcast:bool'
- proto_config_add_boolean 'norelease:bool'
- proto_config_add_string 'reqopts:list(string)'
- proto_config_add_boolean 'defaultreqopts:bool'
- proto_config_add_string iface6rd
- proto_config_add_array 'sendopts:list(string)'
- proto_config_add_boolean delegate
- proto_config_add_string zone6rd
- proto_config_add_string zone
- proto_config_add_string mtu6rd
- proto_config_add_string customroutes
- proto_config_add_boolean classlessroute
-}
-
-proto_dhcp_add_sendopts() {
- [ -n "$1" ] && append "$3" "-x $1"
-}
-
-proto_dhcp_get_default_clientid() {
- [ -z "$1" ] && return
-
- local iface="$1"
- local duid
- local iaid
-
- duid="$(uci_get network @globals[0] dhcp_default_duid)"
- [ -n "$duid" ] && {
- duid="$(hexdump_2hex "$duid")"
- [ -z "$duid" ] && logger -p warn -t dhcp "$iface: ignoring invalid dhcp_default_duid value"
- }
- [ -z "$duid" ] && return
- network_generate_iface_iaid iaid "$iface"
- printf "ff%s%s" "$iaid" "$duid"
-}
-
-proto_dhcp_setup() {
- local config="$1"
- local iface="$2"
-
- local ipaddr hostname clientid sendclientid vendorid broadcast norelease reqopts defaultreqopts iface6rd sendopts delegate zone6rd zone mtu6rd customroutes classlessroute
- json_get_vars ipaddr hostname clientid sendclientid vendorid broadcast norelease reqopts defaultreqopts iface6rd delegate zone6rd zone mtu6rd customroutes classlessroute
-
- local opt dhcpopts
- for opt in $reqopts; do
- append dhcpopts "-O $opt"
- done
-
- json_for_each_item proto_dhcp_add_sendopts sendopts dhcpopts
-
- [ -z "$hostname" ] && hostname="$(cat /proc/sys/kernel/hostname)"
- [ "$hostname" = "*" ] && hostname=
-
- [ "$defaultreqopts" = 0 ] && defaultreqopts="-o" || defaultreqopts=
- [ "$broadcast" = 1 ] && broadcast="-B" || broadcast=
- [ "$norelease" = 1 ] && norelease="" || norelease="-R"
- case "$sendclientid" in
- global)
- clientid="$(proto_dhcp_get_default_clientid "$iface")"
- ;;
- hardware)
- clientid=''
- ;;
- none)
- clientid='-C'
- ;;
- auto|\
- *)
- [ -n "$clientid" ] && {
- clientid="$(hexdump_2hex "$clientid")"
- [ -z "$clientid" ] && {
- logger -p warn -t dhcp "$iface: ignoring invalid clientid value"
- }
- }
- [ -z "$clientid" ] && clientid="$(proto_dhcp_get_default_clientid "$iface")"
- ;;
- esac
- [ -n "${clientid##-C}" ] && clientid="-x 0x3d:$clientid"
- [ -n "$vendorid" ] && append dhcpopts "-x 0x3c:$(echo -n "$vendorid" | hexdump -ve '1/1 "%02x"')"
- [ -n "$iface6rd" ] && proto_export "IFACE6RD=$iface6rd"
- [ "$iface6rd" != 0 -a -f /lib/netifd/proto/6rd.sh ] && append dhcpopts "-O 212"
- [ -n "$zone6rd" ] && proto_export "ZONE6RD=$zone6rd"
- [ -n "$zone" ] && proto_export "ZONE=$zone"
- [ -n "$mtu6rd" ] && proto_export "MTU6RD=$mtu6rd"
- [ -n "$customroutes" ] && proto_export "CUSTOMROUTES=$customroutes"
- [ "$delegate" = "0" ] && proto_export "IFACE6RD_DELEGATE=0"
- # Request classless route option (see RFC 3442) by default
- [ "$classlessroute" = "0" ] || append dhcpopts "-O 121"
-
- # Avoid sending duplicate Option 60 values
- local emptyvendorid
- case "$dhcpopts" in
- *"-x 0"[xX]*"3"[cC]":"* |\
- *"-x 60:"* |\
- *"-x vendor:"*)
- emptyvendorid=1
- ;;
- esac
-
- proto_export "INTERFACE=$config"
- proto_run_command "$config" udhcpc \
- -p /var/run/udhcpc-$iface.pid \
- -s /lib/netifd/dhcp.script \
- -f -t 0 -i "$iface" \
- ${ipaddr:+-r ${ipaddr/\/*/}} \
- ${hostname:+-x "hostname:$hostname"} \
- ${emptyvendorid:+-V ""} \
- $clientid $defaultreqopts $broadcast $norelease $dhcpopts
-}
-
-proto_dhcp_renew() {
- local interface="$1"
- # SIGUSR1 forces udhcpc to renew its lease
- local sigusr1="$(kill -l SIGUSR1)"
- [ -n "$sigusr1" ] && proto_kill_command "$interface" $sigusr1
-}
-
-proto_dhcp_teardown() {
- local interface="$1"
- proto_kill_command "$interface"
-}
-
-add_protocol dhcp
diff --git a/netifd/files/lib/netifd/utils.uc b/netifd/files/lib/netifd/utils.uc
deleted file mode 100644
index 84db69d2f..000000000
--- a/netifd/files/lib/netifd/utils.uc
+++ /dev/null
@@ -1,131 +0,0 @@
-'use strict';
-
-import { glob, basename, realpath, chdir, mkstemp } from "fs";
-
-export const TYPE_ARRAY = 1;
-export const TYPE_STRING = 3;
-export const TYPE_INT = 5;
-export const TYPE_BOOL = 7;
-
-export function parse_bool(val)
-{
- switch (val) {
- case "1":
- case "true":
- return true;
- case "0":
- case "false":
- return false;
- }
-};
-
-export function parse_array(val)
-{
- if (type(val) != "array")
- val = split(val, /\s+/);
- return val;
-};
-
-function __type_parsers()
-{
- let ret = [];
-
- ret[TYPE_ARRAY] = parse_array;
- ret[TYPE_STRING] = function(val) {
- return val;
- };
- ret[TYPE_INT] = function(val) {
- return +val;
- };
- ret[TYPE_BOOL] = parse_bool;
-
- return ret;
-}
-export const type_parser = __type_parsers();
-
-export function handler_load(path, cb)
-{
- for (let script in glob(path + "/*.sh")) {
- script = basename(script);
-
- let f = mkstemp();
- let prev_dir = realpath(".");
- chdir(path);
- system(`./${script} "" "dump" >&${f.fileno()}`);
- chdir(prev_dir);
- f.seek();
- while (!f.error()) {
- let data = trim(f.read("line"));
- try {
- data = json(data);
- } catch (e) {
- continue;
- }
-
- if (type(data) != "object")
- continue;
-
- cb(script, data);
- }
- f.close();
- }
-};
-
-export function handler_attributes(data, extra, validate)
-{
- let ret = { ...extra };
- for (let cur in data) {
- let name_data = split(cur[0], ":", 2);
- let name = name_data[0];
- ret[name] = cur[1];
- if (validate && name_data[1])
- validate[name] = name_data[1];
- }
- return ret;
-};
-
-export function parse_attribute_list(data, spec)
-{
- let ret = {};
-
- for (let name, type_id in spec) {
- if (!(name in data))
- continue;
-
- let val = data[name];
- let parser = type_parser[type_id];
- if (parser)
- val = parser(val);
- ret[name] = val;
- }
-
- return ret;
-};
-
-export function is_equal(val1, val2) {
- let t1 = type(val1);
-
- if (t1 != type(val2))
- return false;
-
- if (t1 == "array") {
- if (length(val1) != length(val2))
- return false;
-
- for (let i = 0; i < length(val1); i++)
- if (!is_equal(val1[i], val2[i]))
- return false;
-
- return true;
- } else if (t1 == "object") {
- for (let key in val1)
- if (!is_equal(val1[key], val2[key]))
- return false;
- for (let key in val2)
- if (val1[key] == null)
- return false;
- return true;
- } else {
- return val1 == val2;
- }
-};
diff --git a/netifd/files/lib/network/config.sh b/netifd/files/lib/network/config.sh
deleted file mode 100755
index 4cd28e4ce..000000000
--- a/netifd/files/lib/network/config.sh
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/bin/sh
-# Copyright (C) 2011 OpenWrt.org
-
-. /usr/share/libubox/jshn.sh
-
-find_config() {
- local device="$1"
- local ifdev ifl3dev ifobj
- for ifobj in $(ubus list network.interface.\*); do
- interface="${ifobj##network.interface.}"
- (
- json_load "$(ifstatus $interface)"
- json_get_var ifdev device
- json_get_var ifl3dev l3_device
- if [ "$device" = "$ifdev" ] || [ "$device" = "$ifl3dev" ]; then
- echo "$interface"
- exit 0
- else
- exit 1
- fi
- ) && return
- done
-}
-
-unbridge() {
- return
-}
-
-ubus_call() {
- json_init
- local _data="$(ubus -S call "$1" "$2")"
- [ -z "$_data" ] && return 1
- json_load "$_data"
- return 0
-}
-
-
-fixup_interface() {
- local config="$1"
- local ifname type device l3dev
-
- config_get type "$config" type
- config_get ifname "$config" ifname
- [ "bridge" = "$type" ] && ifname="br-$config"
- ubus_call "network.interface.$config" status || return 0
- json_get_var l3dev l3_device
- [ -n "$l3dev" ] && ifname="$l3dev"
- json_init
- config_set "$config" ifname "$ifname"
-}
-
-scan_interfaces() {
- config_load network
- config_foreach fixup_interface interface
-}
-
-prepare_interface_bridge() {
- local config="$1"
-
- [ -n "$config" ] || return 0
- ubus call network.interface."$config" prepare
-}
-
-setup_interface() {
- local iface="$1"
- local config="$2"
-
- [ -n "$config" ] || return 0
- ubus call network.interface."$config" add_device "{ \"name\": \"$iface\" }"
-}
-
-do_sysctl() {
- [ -n "$2" ] && \
- sysctl -n -e -w "$1=$2" >/dev/null || \
- sysctl -n -e "$1"
-}
diff --git a/netifd/files/sbin/devstatus b/netifd/files/sbin/devstatus
deleted file mode 100755
index 3c35b26a4..000000000
--- a/netifd/files/sbin/devstatus
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/sh
-. /usr/share/libubox/jshn.sh
-DEVICE="$1"
-
-[ -n "$DEVICE" ] || {
- echo "Usage: $0 "
- exit 1
-}
-
-json_init
-json_add_string name "$DEVICE"
-ubus call network.device status "$(json_dump)"
diff --git a/netifd/files/sbin/ifdown b/netifd/files/sbin/ifdown
deleted file mode 120000
index a0e5c176a..000000000
--- a/netifd/files/sbin/ifdown
+++ /dev/null
@@ -1 +0,0 @@
-ifup
\ No newline at end of file
diff --git a/netifd/files/sbin/ifstatus b/netifd/files/sbin/ifstatus
deleted file mode 100755
index 8a951e6e1..000000000
--- a/netifd/files/sbin/ifstatus
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/sh
-INTERFACE="$1"
-
-[ -n "$INTERFACE" ] || {
- echo "Usage: $0 "
- exit 1
-}
-
-ubus -S list "network.interface.$INTERFACE" >/dev/null || {
- echo "Interface $INTERFACE not found"
- exit 1
-}
-ubus call network.interface status "{ \"interface\" : \"$INTERFACE\" }"
diff --git a/netifd/files/sbin/ifup b/netifd/files/sbin/ifup
deleted file mode 100755
index fbf2fd80c..000000000
--- a/netifd/files/sbin/ifup
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/bin/sh
-
-ifup_all=
-
-if_call() {
- local interface="$1"
- for mode in $modes; do
- ubus call network.interface $mode "{ \"interface\" : \"$interface\" }"
- done
-}
-
-case "$0" in
- *ifdown) modes=down;;
- *ifup)
- modes="down up"
- ;;
- *) echo "Invalid command: $0";;
-esac
-
-while :; do
- case "$1" in
- -a)
- ifup_all=1
- shift
- ;;
- *)
- break
- ;;
- esac
-done
-
-[ "$modes" = "down up" ] && ubus call network reload
-if [ -n "$ifup_all" ]; then
- for interface in $(ubus -S list 'network.interface.*'); do
- if_call "${interface##network.interface.}"
- done
- exit
-else
- ubus -S list "network.interface.$1" > /dev/null || {
- echo "Interface $1 not found"
- exit
- }
- if_call "$1"
-fi
diff --git a/netifd/files/usr/libexec/network/packet-steering.uc b/netifd/files/usr/libexec/network/packet-steering.uc
deleted file mode 100755
index 34df46d18..000000000
--- a/netifd/files/usr/libexec/network/packet-steering.uc
+++ /dev/null
@@ -1,279 +0,0 @@
-#!/usr/bin/env ucode
-'use strict';
-import { glob, basename, dirname, readlink, readfile, realpath, writefile, error, open } from "fs";
-
-let napi_weight = 1.0;
-let cpu_thread_weight = 0.75;
-let rx_weight = 0.75;
-let eth_bias = 2.0;
-let debug = 0, do_nothing = 0;
-let disable;
-let cpus;
-let all_cpus;
-let local_flows = 0;
-
-while (length(ARGV) > 0) {
- let arg = shift(ARGV);
- switch (arg) {
- case "-d":
- debug++;
- break;
- case "-n":
- do_nothing++;
- break;
- case '0':
- disable = true;
- break;
- case '2':
- all_cpus = true;
- break;
- case '-l':
- local_flows = +shift(ARGV);
- break;
- }
-}
-
-function task_name(pid)
-{
- let stat = open(`/proc/${pid}/status`, "r");
- if (!stat)
- return;
- let line = stat.read("line");
- stat.close();
- return trim(split(line, "\t", 2)[1]);
-}
-
-function set_task_cpu(pid, cpu) {
- if (disable)
- cpu = join(",", map(cpus, (cpu) => cpu.id));
- let name = task_name(pid);
- if (!name)
- return;
- if (debug || do_nothing)
- warn(`taskset -p -c ${cpu} ${name}\n`);
- if (!do_nothing)
- system(`taskset -p -c ${cpu} ${pid}`);
-}
-
-function cpu_mask(cpu)
-{
- let mask;
- if (cpu < 0)
- mask = (1 << length(cpus)) - 1;
- else
- mask = (1 << int(cpu));
- return sprintf("%x", mask);
-}
-
-function set_netdev_cpu(dev, cpu, rx_queue) {
- rx_queue ??= "rx-*";
- let queues = glob(`/sys/class/net/${dev}/queues/${rx_queue}/rps_cpus`);
- let val = cpu_mask(cpu);
- if (disable)
- val = 0;
- for (let queue in queues) {
- if (debug || do_nothing)
- warn(`echo ${val} > ${queue}\n`);
- if (!do_nothing)
- writefile(queue, `${val}`);
- }
- queues = glob(`/sys/class/net/${dev}/queues/${rx_queue}/rps_flow_cnt`);
- for (let queue in queues) {
- if (debug || do_nothing)
- warn(`echo ${local_flows} > ${queue}\n`);
- if (!do_nothing)
- writefile(queue, `${local_flows}`);
- }
-}
-
-function task_device_match(name, device)
-{
- let napi_match = match(name, /napi\/([^-]*)-\d+/);
- if (!napi_match)
- napi_match = match(name, /mt76-tx (phy\d+)/);
- if (napi_match &&
- (index(device.phy, napi_match[1]) >= 0 ||
- index(device.netdev, napi_match[1]) >= 0))
- return true;
-
- if (device.driver == "mtk_soc_eth" && match(name, /napi\/mtk_eth-/))
- return true;
-
- return false;
-}
-
-cpus = map(glob("/sys/bus/cpu/devices/*"), (path) => {
- return {
- id: int(match(path, /.*cpu(\d+)/)[1]),
- core: int(trim(readfile(`${path}/topology/core_id`))),
- load: 0.0,
- };
-});
-
-cpus = slice(cpus, 0, 64);
-if (length(cpus) < 2)
- exit(0);
-
-function cpu_add_weight(cpu_id, weight)
-{
- let cpu = cpus[cpu_id];
- cpu.load += weight;
- for (let sibling in cpus) {
- if (sibling == cpu || sibling.core != cpu.core)
- continue;
- sibling.load += weight * cpu_thread_weight;
- }
-}
-
-function get_next_cpu(weight, prev_cpu)
-{
- if (disable)
- return 0;
-
- let sort_cpus = sort(slice(cpus), (a, b) => a.load - b.load);
- let idx = 0;
-
- if (prev_cpu != null && sort_cpus[idx].id == prev_cpu)
- idx++;
-
- let cpu = sort_cpus[idx].id;
- cpu_add_weight(cpu, weight);
- return cpu;
-}
-
-let phys_devs = {};
-let netdev_phys = {};
-let netdevs = map(glob("/sys/class/net/*"), (dev) => basename(dev));
-
-for (let dev in netdevs) {
- let pdev_path = realpath(`/sys/class/net/${dev}/device`);
- if (!pdev_path)
- continue;
-
- if (length(glob(`/sys/class/net/${dev}/lower_*`)) > 0)
- continue;
-
- let pdev = phys_devs[pdev_path];
- if (!pdev) {
- pdev = phys_devs[pdev_path] = {
- path: pdev_path,
- driver: basename(readlink(`${pdev_path}/driver`)),
- netdev: [],
- phy: [],
- tasks: [],
- rx_tasks: [],
- rx_queues: map(glob(`/sys/class/net/${dev}/queues/rx-*/rps_cpus`),
- (v) => basename(dirname(v))),
- };
- }
-
- let phyidx = trim(readfile(`/sys/class/net/${dev}/phy80211/index`));
- if (phyidx != null) {
- let phy = `phy${phyidx}`;
- if (index(pdev.phy, phy) < 0)
- push(pdev.phy, phy);
- }
-
- push(pdev.netdev, dev);
- netdev_phys[dev] = pdev;
-}
-
-for (let path in glob("/proc/*/exe")) {
- readlink(path);
- if (error() != "No such file or directory")
- continue;
-
- let pid = basename(dirname(path));
- let name = task_name(pid);
- for (let devname in phys_devs) {
- let dev = phys_devs[devname];
- if (!task_device_match(name, dev))
- continue;
-
- push(dev.tasks, pid);
-
- let napi_match = match(name, /napi\/([^-]*)-(\d+)/);
- if (napi_match && napi_match[2] > 0)
- push(dev.rx_tasks, pid);
- break;
- }
-}
-
-function assign_dev_queues_cpu(dev) {
- let num = length(dev.rx_queues);
- if (num < length(dev.rx_tasks))
- num = length(dev.rx_tasks);
-
- for (let i = 0; i < num; i++) {
- let cpu;
-
- let task = dev.rx_tasks[i];
- if (num >= length(cpus))
- cpu = i % length(cpus);
- else if (task)
- cpu = get_next_cpu(napi_weight);
- else
- cpu = -1;
- set_task_cpu(task, cpu);
-
- let rxq = dev.rx_queues[i];
- if (!rxq)
- continue;
-
- if (num >= length(cpus))
- cpu = (i + 1) % length(cpus);
- else if (all_cpus)
- cpu = -1;
- else
- cpu = get_next_cpu(napi_weight, cpu);
- for (let netdev in dev.netdev)
- set_netdev_cpu(netdev, cpu, rxq);
- }
-}
-
-function assign_dev_cpu(dev) {
- if (length(dev.rx_queues) > 1 &&
- length(dev.rx_tasks) > 1)
- return assign_dev_queues_cpu(dev);
-
- if (length(dev.tasks) > 0) {
- let cpu = dev.napi_cpu = get_next_cpu(napi_weight);
- for (let task in dev.tasks)
- set_task_cpu(task, cpu);
- }
-
- if (length(dev.netdev) > 0) {
- let cpu;
- if (all_cpus)
- cpu = -1;
- else
- cpu = get_next_cpu(rx_weight, dev.napi_cpu);
- for (let netdev in dev.netdev)
- set_netdev_cpu(netdev, cpu);
- }
-}
-
-// Assign ethernet devices first
-for (let devname in phys_devs) {
- let dev = phys_devs[devname];
- if (!length(dev.phy))
- assign_dev_cpu(dev);
-}
-
-// Add bias to avoid assigning other tasks to CPUs with ethernet NAPI
-for (let devname in phys_devs) {
- let dev = phys_devs[devname];
- if (!length(dev.tasks) || dev.napi_cpu == null)
- continue;
- cpu_add_weight(dev.napi_cpu, eth_bias);
-}
-
-// Assign WLAN devices
-for (let devname in phys_devs) {
- let dev = phys_devs[devname];
- if (length(dev.phy) > 0)
- assign_dev_cpu(dev);
-}
-
-if (debug > 1)
- warn(sprintf("devices: %.J\ncpus: %.J\n", phys_devs, cpus));
diff --git a/netifd/files/usr/share/udhcpc/default.script b/netifd/files/usr/share/udhcpc/default.script
deleted file mode 100755
index 0a9eb0180..000000000
--- a/netifd/files/usr/share/udhcpc/default.script
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/bin/sh
-[ -z "$1" ] && echo "Error: should be run by udhcpc" && exit 1
-
-set_classless_routes() {
- local max=128
- local type
- while [ -n "$1" -a -n "$2" -a $max -gt 0 ]; do
- [ ${1##*/} -eq 32 ] && type=host || type=net
- echo "udhcpc: adding route for $type $1 via $2"
- route add -$type "$1" gw "$2" dev "$interface"
- max=$(($max-1))
- shift 2
- done
-}
-
-setup_interface() {
- echo "udhcpc: ip addr add $ip/${subnet:-255.255.255.0} broadcast ${broadcast:-+} dev $interface"
- ip addr add $ip/${subnet:-255.255.255.0} broadcast ${broadcast:-+} dev $interface
-
- [ -n "$router" ] && [ "$router" != "0.0.0.0" ] && [ "$router" != "255.255.255.255" ] && {
- echo "udhcpc: setting default routers: $router"
-
- local valid_gw=""
- for i in $router ; do
- route add default gw $i dev $interface
- valid_gw="${valid_gw:+$valid_gw|}$i"
- done
-
- eval $(route -n | awk '
- /^0.0.0.0\W{9}('$valid_gw')\W/ {next}
- /^0.0.0.0/ {print "route del -net "$1" gw "$2";"}
- ')
- }
-
- # CIDR STATIC ROUTES (rfc3442)
- [ -n "$staticroutes" ] && set_classless_routes $staticroutes
- [ -n "$msstaticroutes" ] && set_classless_routes $msstaticroutes
-}
-
-
-applied=
-case "$1" in
- deconfig)
- ip -4 addr flush dev "$interface"
- ;;
- renew)
- setup_interface update
- ;;
- bound)
- setup_interface ifup
- ;;
-esac
-
-# user rules
-[ -f /etc/udhcpc.user ] && . /etc/udhcpc.user
-
-exit 0
diff --git a/netifd/patches/default-ipv4-netmask-fallback-24.patch b/netifd/patches/default-ipv4-netmask-fallback-24.patch
deleted file mode 100644
index 677fcbdbc..000000000
--- a/netifd/patches/default-ipv4-netmask-fallback-24.patch
+++ /dev/null
@@ -1,11 +0,0 @@
---- a/proto.c
-+++ b/proto.c
-@@ -426,6 +426,8 @@ proto_apply_static_ip_settings(struct interface *iface, struct blob_attr *attr)
- error = "INVALID_NETMASK";
- goto error;
- }
-+ } else if (iface && !strncmp(iface->name, "lan", 3)) {
-+ netmask = 24;
- }
-
- if ((cur = tb[OPT_BROADCAST])) {
diff --git a/ttyd/Makefile b/ttyd/Makefile
new file mode 100644
index 000000000..62d2500f0
--- /dev/null
+++ b/ttyd/Makefile
@@ -0,0 +1,52 @@
+#
+# Copyright (C) 2016 Shuanglei Tao
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=ttyd
+PKG_VERSION:=1.7.7
+PKG_RELEASE:=1
+
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
+PKG_SOURCE_URL:=https://codeload.github.com/tsl0922/ttyd/tar.gz/$(PKG_VERSION)?
+PKG_HASH:=skip
+
+PKG_MAINTAINER:=Shuanglei Tao
+PKG_LICENSE:=MIT
+PKG_LICENSE_FILES:=LICENSE
+PKG_CPE_ID:=cpe:/a:ttyd_project:ttyd
+
+include $(INCLUDE_DIR)/package.mk
+include $(INCLUDE_DIR)/cmake.mk
+
+define Package/ttyd
+ SECTION:=utils
+ CATEGORY:=Utilities
+ TITLE:=Command-line tool for sharing terminal over the web
+ DEPENDS:=+libcap +libopenssl +libjson-c +libuv +zlib +libwebsockets-full
+ URL:=https://github.com/tsl0922/ttyd
+ SUBMENU:=Terminal
+endef
+
+define Package/ttyd/description
+ttyd is a command-line tool for sharing terminal over the web.
+endef
+
+define Package/ttyd/conffiles
+/etc/config/ttyd
+endef
+
+define Package/ttyd/install
+ $(INSTALL_DIR) $(1)/usr/bin
+ $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/ttyd $(1)/usr/bin/
+ $(INSTALL_DIR) $(1)/etc/config/
+ $(INSTALL_CONF) ./files/ttyd.config $(1)/etc/config/ttyd
+ $(INSTALL_DIR) $(1)/etc/init.d/
+ $(INSTALL_BIN) ./files/ttyd.init $(1)/etc/init.d/ttyd
+endef
+
+$(eval $(call BuildPackage,ttyd))
diff --git a/ttyd/files/ttyd.config b/ttyd/files/ttyd.config
new file mode 100644
index 000000000..cc6c71f49
--- /dev/null
+++ b/ttyd/files/ttyd.config
@@ -0,0 +1,4 @@
+
+config ttyd
+ option command '/bin/login'
+
diff --git a/ttyd/files/ttyd.init b/ttyd/files/ttyd.init
new file mode 100644
index 000000000..7e24aeab2
--- /dev/null
+++ b/ttyd/files/ttyd.init
@@ -0,0 +1,102 @@
+#!/bin/sh /etc/rc.common
+
+START=99
+STOP=50
+USE_PROCD=1
+
+NAME=ttyd
+PROG=/usr/bin/$NAME
+
+validate_section_ttyd()
+{
+ uci_load_validate ttyd ttyd "$1" "$2" \
+ 'enable:bool:1' \
+ 'port:port' \
+ 'interface:string' \
+ 'credential:string' \
+ 'uid:uinteger' \
+ 'gid:uinteger' \
+ 'signal:uinteger' \
+ 'url_arg:bool' \
+ 'readonly:bool:0' \
+ 'client_option:list(string)' \
+ 'terminal_type:string' \
+ 'check_origin:bool' \
+ 'max_clients:uinteger' \
+ 'once:bool' \
+ 'index:string' \
+ 'ipv6:bool' \
+ 'ssl:bool' \
+ 'ssl_cert:file' \
+ 'ssl_key:file' \
+ 'ssl_ca:file' \
+ 'debug:uinteger' \
+ 'command:string'
+}
+
+ttyd_instance()
+{
+ [ "$2" = 0 ] || {
+ echo "validation failed"
+ return 1
+ }
+
+ [ "$enable" = 0 ] && return 1
+ [ -z "$command" ] && return 1
+
+ [ "${interface::1}" = @ ] && {
+ interface=$(
+ . /lib/functions/network.sh
+ network_get_device device "${interface:1}"
+ echo -n "$device"
+ )
+ }
+
+ [ "$url_arg" = 0 ] && url_arg=""
+ [ "$readonly" = 0 ] && readonly="-W"
+ [ "$check_origin" = 0 ] && check_origin=""
+ [ "$once" = 0 ] && once=""
+ [ "$ipv6" = 0 ] && ipv6=""
+ [ "$ssl" = 0 ] && ssl=""
+
+ procd_open_instance
+ procd_set_param command "$PROG" \
+ ${port:+-p $port} \
+ ${interface:+-i $interface} \
+ ${credential:+-c $credential} \
+ ${uid:+-u $uid} \
+ ${gid:+-g $gid} \
+ ${signal:+-s $signal} \
+ ${url_arg:+-a} \
+ ${readonly:-} \
+ ${terminal_type:+-T $terminal_type} \
+ ${check_origin:+-O} \
+ ${max_clients:+-m $max_clients} \
+ ${once:+-o} \
+ ${index:+-I $index} \
+ ${ipv6:+-6} \
+ ${ssl:+-S} \
+ ${ssl_cert:+-C $ssl_cert} \
+ ${ssl_key:+-K $ssl_key} \
+ ${ssl_ca:+-A $ssl_ca} \
+ ${debug:+-d $debug}
+ config_list_foreach "$1" client_option "procd_append_param command -t"
+ procd_append_param command $command
+ procd_set_param stdout 1
+ procd_set_param stderr 1
+ procd_close_instance
+}
+
+start_service() {
+ config_load "$NAME"
+ config_foreach validate_section_ttyd ttyd ttyd_instance
+}
+
+shutdown() {
+ # close all open connections
+ killall "$NAME"
+}
+
+service_triggers() {
+ procd_add_reload_trigger "$NAME"
+}
diff --git a/ttyd/patches/100-log-to-syslog.patch b/ttyd/patches/100-log-to-syslog.patch
new file mode 100644
index 000000000..838f9d62d
--- /dev/null
+++ b/ttyd/patches/100-log-to-syslog.patch
@@ -0,0 +1,20 @@
+--- a/src/server.c
++++ b/src/server.c
+@@ -10,6 +10,7 @@
+ #include
+ #include
+ #include
++#include
+
+ #include "utils.h"
+
+@@ -520,7 +521,8 @@ int main(int argc, char **argv) {
+ return -1;
+ }
+
+- lws_set_log_level(debug_level, NULL);
++ openlog("ttyd", LOG_NDELAY | LOG_PID, LOG_DAEMON);
++ lws_set_log_level(debug_level, lwsl_emit_syslog);
+
+ char server_hdr[128] = "";
+ sprintf(server_hdr, "ttyd/%s (libwebsockets/%s)", TTYD_VERSION, LWS_LIBRARY_VERSION);