🛸 Sync 2026-10-20 02:16:58

This commit is contained in:
github-actions[bot] committed 2026-10-20 02:16:58 +08:00
1 parent 64729a26c6
commit 590565b35a
25 files changed
+887 -408

No files matched your search

+1 -1
View File
@@ -9,7 +9,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=glorytun
PKG_RELEASE:=6
PKG_RELEASE:=7
PKG_SOURCE_PROTO:=git
PKG_SOURCE_VERSION:=95fa5ead2154adb546bcdb454c56c876e9f60bb5
PKG_SOURCE:=glorytun-$(PKG_VERSION).tar.gz
+2 -1
View File
@@ -56,7 +56,8 @@ start_instance() {
}
[ "${enable}" = "1" ] || return 1
[ "${proto}" = "tcp" ] || return 1
# unset means tcp: instances added from LuCI before it wrote proto have none
[ "${proto:-tcp}" = "tcp" ] || return 1
[ -n "${key}" ] || {
_err "Key empty"
return 1
+1 -1
View File
@@ -1 +1 @@
both
snapshot
@@ -1 +1 @@
26.291.65693~03c6023
26.291.67080~0a601b6
@@ -8,6 +8,10 @@
'require ui';
'require validation';
document.querySelector('head').appendChild(E('script', {
src: L.resource('fchomo/md5.js')
}));
function funcSed(originFunc, matrix = [], closure = []) {
if (isEmpty(matrix))
return originFunc;
@@ -1039,79 +1043,8 @@ const UIDropdown = ui.Dropdown.extend({ // @pr8758_merged
});
/* Method */
/* thanks to homeproxy */
function calcStringMD5(e) {
/* Thanks to https://stackoverflow.com/a/41602636 */
let h = (a, b) => {
let c, d, e, f, g;
c = a & 2147483648;
d = b & 2147483648;
e = a & 1073741824;
f = b & 1073741824;
g = (a & 1073741823) + (b & 1073741823);
return e & f ? g ^ 2147483648 ^ c ^ d : e | f ? g & 1073741824 ? g ^ 3221225472 ^ c ^ d : g ^ 1073741824 ^ c ^ d : g ^ c ^ d;
}, k = (a, b, c, d, e, f, g) => h((a = h(a, h(h(b & c | ~b & d, e), g))) << f | a >>> 32 - f, b),
l = (a, b, c, d, e, f, g) => h((a = h(a, h(h(b & d | c & ~d, e), g))) << f | a >>> 32 - f, b),
m = (a, b, c, d, e, f, g) => h((a = h(a, h(h(b ^ c ^ d, e), g))) << f | a >>> 32 - f, b),
n = (a, b, c, d, e, f, g) => h((a = h(a, h(h(c ^ (b | ~d), e), g))) << f | a >>> 32 - f, b),
p = a => { let b = '', d = ''; for (let c = 0; c <= 3; c++) d = a >>> 8 * c & 255, d = '0' + d.toString(16), b += d.substr(d.length - 2, 2); return b; };
let f = [], q, r, s, t, a, b, c, d;
e = (() => {
e = e.replace(/\r\n/g, '\n');
let b = '';
for (let d = 0; d < e.length; d++) {
let c = e.charCodeAt(d);
b += c < 128 ? String.fromCharCode(c) : c < 2048 ? String.fromCharCode(c >> 6 | 192) + String.fromCharCode(c & 63 | 128) :
String.fromCharCode(c >> 12 | 224) + String.fromCharCode(c >> 6 & 63 | 128) + String.fromCharCode(c & 63 | 128);
}
return b;
})();
f = (() => {
let c = e.length, a = c + 8, d = 16 * ((a - a % 64) / 64 + 1), b = Array(d - 1), f = 0, g = 0;
for (; g < c;) a = (g - g % 4) / 4, f = g % 4 * 8, b[a] |= e.charCodeAt(g) << f, g++;
a = (g - g % 4) / 4, b[a] |= 128 << g % 4 * 8, b[d - 2] = c << 3, b[d - 1] = c >>> 29;
return b;
})();
a = 1732584193, b = 4023233417, c = 2562383102, d = 271733878;
for (e = 0; e < f.length; e += 16) {
q = a, r = b, s = c, t = d;
a = k(a, b, c, d, f[e + 0], 7, 3614090360), d = k(d, a, b, c, f[e + 1], 12, 3905402710),
c = k(c, d, a, b, f[e + 2], 17, 606105819), b = k(b, c, d, a, f[e + 3], 22, 3250441966),
a = k(a, b, c, d, f[e + 4], 7, 4118548399), d = k(d, a, b, c, f[e + 5], 12, 1200080426),
c = k(c, d, a, b, f[e + 6], 17, 2821735955), b = k(b, c, d, a, f[e + 7], 22, 4249261313),
a = k(a, b, c, d, f[e + 8], 7, 1770035416), d = k(d, a, b, c, f[e + 9], 12, 2336552879),
c = k(c, d, a, b, f[e + 10], 17, 4294925233), b = k(b, c, d, a, f[e + 11], 22, 2304563134),
a = k(a, b, c, d, f[e + 12], 7, 1804603682), d = k(d, a, b, c, f[e + 13], 12, 4254626195),
c = k(c, d, a, b, f[e + 14], 17, 2792965006), b = k(b, c, d, a, f[e + 15], 22, 1236535329),
a = l(a, b, c, d, f[e + 1], 5, 4129170786), d = l(d, a, b, c, f[e + 6], 9, 3225465664),
c = l(c, d, a, b, f[e + 11], 14, 643717713), b = l(b, c, d, a, f[e + 0], 20, 3921069994),
a = l(a, b, c, d, f[e + 5], 5, 3593408605), d = l(d, a, b, c, f[e + 10], 9, 38016083),
c = l(c, d, a, b, f[e + 15], 14, 3634488961), b = l(b, c, d, a, f[e + 4], 20, 3889429448),
a = l(a, b, c, d, f[e + 9], 5, 568446438), d = l(d, a, b, c, f[e + 14], 9, 3275163606),
c = l(c, d, a, b, f[e + 3], 14, 4107603335), b = l(b, c, d, a, f[e + 8], 20, 1163531501),
a = l(a, b, c, d, f[e + 13], 5, 2850285829), d = l(d, a, b, c, f[e + 2], 9, 4243563512),
c = l(c, d, a, b, f[e + 7], 14, 1735328473), b = l(b, c, d, a, f[e + 12], 20, 2368359562),
a = m(a, b, c, d, f[e + 5], 4, 4294588738), d = m(d, a, b, c, f[e + 8], 11, 2272392833),
c = m(c, d, a, b, f[e + 11], 16, 1839030562), b = m(b, c, d, a, f[e + 14], 23, 4259657740),
a = m(a, b, c, d, f[e + 1], 4, 2763975236), d = m(d, a, b, c, f[e + 4], 11, 1272893353),
c = m(c, d, a, b, f[e + 7], 16, 4139469664), b = m(b, c, d, a, f[e + 10], 23, 3200236656),
a = m(a, b, c, d, f[e + 13], 4, 681279174), d = m(d, a, b, c, f[e + 0], 11, 3936430074),
c = m(c, d, a, b, f[e + 3], 16, 3572445317), b = m(b, c, d, a, f[e + 6], 23, 76029189),
a = m(a, b, c, d, f[e + 9], 4, 3654602809), d = m(d, a, b, c, f[e + 12], 11, 3873151461),
c = m(c, d, a, b, f[e + 15], 16, 530742520), b = m(b, c, d, a, f[e + 2], 23, 3299628645),
a = n(a, b, c, d, f[e + 0], 6, 4096336452), d = n(d, a, b, c, f[e + 7], 10, 1126891415),
c = n(c, d, a, b, f[e + 14], 15, 2878612391), b = n(b, c, d, a, f[e + 5], 21, 4237533241),
a = n(a, b, c, d, f[e + 12], 6, 1700485571), d = n(d, a, b, c, f[e + 3], 10, 2399980690),
c = n(c, d, a, b, f[e + 10], 15, 4293915773), b = n(b, c, d, a, f[e + 1], 21, 2240044497),
a = n(a, b, c, d, f[e + 8], 6, 1873313359), d = n(d, a, b, c, f[e + 15], 10, 4264355552),
c = n(c, d, a, b, f[e + 6], 15, 2734768916), b = n(b, c, d, a, f[e + 13], 21, 1309151649),
a = n(a, b, c, d, f[e + 4], 6, 4149444226), d = n(d, a, b, c, f[e + 11], 10, 3174756917),
c = n(c, d, a, b, f[e + 2], 15, 718787259), b = n(b, c, d, a, f[e + 9], 21, 3951481745),
a = h(a, q), b = h(b, r), c = h(c, s), d = h(d, t);
}
return (p(a) + p(b) + p(c) + p(d)).toLowerCase();
return md5(e);
}
/* Thanks to luci-app-ssr-plus */
@@ -1170,7 +1103,7 @@ function encodeBase64(input) {
* Unzip Gzip data
* @param {string|Array|ArrayBuffer|Uint8Array} input - Input data (Base64 string or binary object)
* @param {boolean} asText - Return a string? (true: text, false: arrayBuffer)
* @returns {Promise<string|Uint8Array>}
* @returns {Promise} string|Uint8Array
*/
async function decompressGzip(input, asText = false) {
if (isEmpty(input))
@@ -0,0 +1 @@
../../../../../js-md5/build/md5-lite.min.js
+141 -21
View File
@@ -629,6 +629,10 @@ msgstr ""
msgid "Common ports (bypass P2P traffic)"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1576
msgid "Complete"
msgstr ""
#: htdocs/luci-static/resources/view/fchomo/node.js:1040
msgid "Configuration Document"
msgstr ""
@@ -890,11 +894,11 @@ msgstr ""
msgid "Download bandwidth in Mbps."
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1507
#: htdocs/luci-static/resources/fchomo.js:1433
msgid "Download failed: %s"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1503
#: htdocs/luci-static/resources/fchomo.js:1429
msgid "Download successful."
msgstr ""
@@ -1132,6 +1136,28 @@ msgstr ""
#: htdocs/luci-static/resources/fchomo.js:525
#: htdocs/luci-static/resources/fchomo.js:528
#: htdocs/luci-static/resources/fchomo.js:531
#: htdocs/luci-static/resources/fchomo.js:1593
#: htdocs/luci-static/resources/fchomo.js:1601
#: htdocs/luci-static/resources/fchomo.js:1609
#: htdocs/luci-static/resources/fchomo.js:1632
#: htdocs/luci-static/resources/fchomo.js:1635
#: htdocs/luci-static/resources/fchomo.js:1642
#: htdocs/luci-static/resources/fchomo.js:1658
#: htdocs/luci-static/resources/fchomo.js:1667
#: htdocs/luci-static/resources/fchomo.js:1679
#: htdocs/luci-static/resources/fchomo.js:1682
#: htdocs/luci-static/resources/fchomo.js:1692
#: htdocs/luci-static/resources/fchomo.js:1704
#: htdocs/luci-static/resources/fchomo.js:1707
#: htdocs/luci-static/resources/fchomo.js:1720
#: htdocs/luci-static/resources/fchomo.js:1729
#: htdocs/luci-static/resources/fchomo.js:1739
#: htdocs/luci-static/resources/fchomo.js:1774
#: htdocs/luci-static/resources/fchomo.js:1776
#: htdocs/luci-static/resources/fchomo.js:1789
#: htdocs/luci-static/resources/fchomo.js:1795
#: htdocs/luci-static/resources/fchomo.js:1802
#: htdocs/luci-static/resources/fchomo.js:1811
#: htdocs/luci-static/resources/fchomo/listeners.js:284
#: htdocs/luci-static/resources/fchomo/listeners.js:339
#: htdocs/luci-static/resources/fchomo/listeners.js:854
@@ -1201,6 +1227,22 @@ msgstr ""
msgid "Factor"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1534
msgid "Failed to execute \"/etc/init.d/fchomo %s %s\" reason: %s"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1487
msgid "Failed to generate %s, error: %s."
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1911
msgid "Failed to upload %s, error: %s."
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1930
msgid "Failed to upload, error: %s."
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:280
#: htdocs/luci-static/resources/fchomo/listeners.js:378
msgid "Fallback"
@@ -1559,7 +1601,7 @@ msgstr ""
msgid "Host/SNI override"
msgstr ""
#: htdocs/luci-static/resources/view/fchomo/node.js:1062
#: htdocs/luci-static/resources/view/fchomo/node.js:1063
msgid "Hostname"
msgstr ""
@@ -1653,6 +1695,10 @@ msgstr ""
msgid "Idle timeout"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1635
msgid "If All ports is selected, uncheck others"
msgstr ""
#: htdocs/luci-static/resources/view/fchomo/client.js:71
msgid "If Block is selected, uncheck others"
msgstr ""
@@ -1866,6 +1912,13 @@ msgstr ""
msgid "L2"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:241
#: htdocs/luci-static/resources/fchomo.js:242
#: htdocs/luci-static/resources/fchomo.js:243
#: htdocs/luci-static/resources/fchomo.js:244
msgid "L3"
msgstr ""
#: htdocs/luci-static/resources/fchomo/listeners.js:129
#: htdocs/luci-static/resources/view/fchomo/client.js:1055
#: htdocs/luci-static/resources/view/fchomo/client.js:1347
@@ -2253,16 +2306,6 @@ msgstr ""
msgid "Network"
msgstr ""
#: htdocs/luci-static/resources/view/fchomo/node.js:1046
#: htdocs/luci-static/resources/view/fchomo/node.js:1057
msgid "Room name"
msgstr ""
#: htdocs/luci-static/resources/view/fchomo/node.js:1051
#: htdocs/luci-static/resources/view/fchomo/node.js:1057
msgid "Room secret"
msgstr ""
#: htdocs/luci-static/resources/fchomo/listeners.js:662
msgid "Network type"
msgstr ""
@@ -2336,7 +2379,7 @@ msgstr ""
msgid "Not Installed"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1463
#: htdocs/luci-static/resources/fchomo.js:1389
msgid "Not Running"
msgstr ""
@@ -2399,7 +2442,7 @@ msgstr ""
msgid "Only applies to the %s."
msgstr ""
#: htdocs/luci-static/resources/view/fchomo/node.js:1056
#: htdocs/luci-static/resources/view/fchomo/node.js:1057
msgid ""
"Only nodes using the same %s and %s as this network are permitted to perform "
"a handshake or relay traffic through this node."
@@ -2413,7 +2456,7 @@ msgstr ""
msgid "Only the address %s is valid."
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1457
#: htdocs/luci-static/resources/fchomo.js:1383
msgid "Open Dashboard"
msgstr ""
@@ -2612,6 +2655,10 @@ msgstr ""
msgid "Port"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1644
msgid "Port %s alrealy exists!"
msgstr ""
#: htdocs/luci-static/resources/view/fchomo/node.js:711
msgid "Port hop interval"
msgstr ""
@@ -2681,7 +2728,7 @@ msgstr ""
msgid "Private key"
msgstr ""
#: htdocs/luci-static/resources/view/fchomo/node.js:1055
#: htdocs/luci-static/resources/view/fchomo/node.js:1056
msgid "Private room"
msgstr ""
@@ -2906,7 +2953,7 @@ msgstr ""
msgid "Refresh every %s seconds."
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1450
#: htdocs/luci-static/resources/fchomo.js:1376
#: htdocs/luci-static/resources/view/fchomo/client.js:966
#: htdocs/luci-static/resources/view/fchomo/global.js:193
#: htdocs/luci-static/resources/view/fchomo/server.js:45
@@ -2942,6 +2989,11 @@ msgstr ""
msgid "Remote trace target"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1565
msgid "Remove"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1570
#: htdocs/luci-static/resources/view/fchomo/node.js:244
#: htdocs/luci-static/resources/view/fchomo/node.js:246
#: htdocs/luci-static/resources/view/fchomo/node.js:2341
@@ -3035,6 +3087,16 @@ msgstr ""
msgid "Reusing a single tunnel to carry multiple target connections within it."
msgstr ""
#: htdocs/luci-static/resources/view/fchomo/node.js:1046
#: htdocs/luci-static/resources/view/fchomo/node.js:1058
msgid "Room name"
msgstr ""
#: htdocs/luci-static/resources/view/fchomo/node.js:1051
#: htdocs/luci-static/resources/view/fchomo/node.js:1058
msgid "Room secret"
msgstr ""
#: htdocs/luci-static/resources/view/fchomo/global.js:846
msgid "Routing Control"
msgstr ""
@@ -3111,7 +3173,7 @@ msgstr ""
msgid "Ruleset-URI-Scheme"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1463
#: htdocs/luci-static/resources/fchomo.js:1389
msgid "Running"
msgstr ""
@@ -3383,6 +3445,10 @@ msgstr ""
msgid "Successfully updated."
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1927
msgid "Successfully uploaded."
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:192
#: htdocs/luci-static/resources/fchomo.js:229
msgid "Sudoku"
@@ -3778,7 +3844,7 @@ msgstr ""
msgid "UUID"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1510
#: htdocs/luci-static/resources/fchomo.js:1436
msgid "Unable to download unsupported type: %s"
msgstr ""
@@ -4121,6 +4187,10 @@ msgstr ""
msgid "YouTube"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1909
msgid "Your %s was successfully uploaded. Size: %sB."
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:240
msgid "ZeroTier"
msgstr ""
@@ -4321,6 +4391,13 @@ msgstr ""
msgid "no-resolve"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1632
#: htdocs/luci-static/resources/fchomo.js:1739
#: htdocs/luci-static/resources/fchomo.js:1774
#: htdocs/luci-static/resources/fchomo.js:1802
msgid "non-empty value"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:416
#: htdocs/luci-static/resources/fchomo.js:430
#: htdocs/luci-static/resources/fchomo.js:442
@@ -4470,6 +4547,10 @@ msgstr ""
msgid "unique identifier"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1811
msgid "unique value"
msgstr ""
#: htdocs/luci-static/resources/view/fchomo/node.js:2583
msgid "up"
msgstr ""
@@ -4508,10 +4589,45 @@ msgstr ""
msgid "v5"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1679
#: htdocs/luci-static/resources/fchomo.js:1682
msgid "valid JSON format"
msgstr ""
#: htdocs/luci-static/resources/view/fchomo/node.js:1542
msgid "valid SHA256 string with %d characters"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1704
#: htdocs/luci-static/resources/fchomo.js:1707
msgid "valid URL"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1729
msgid "valid base64 key with %d characters"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1789
#: htdocs/luci-static/resources/fchomo.js:1795
msgid "valid format: 2x, 2p, 4v"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1720
msgid "valid hex string with %d characters"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1776
msgid "valid key length with %d characters"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1642
msgid "valid port value"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1692
msgid "valid uuid"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:488
msgid "vless-mlkem768"
msgstr ""
@@ -4544,6 +4660,10 @@ msgstr ""
msgid "zero"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1512
#: htdocs/luci-static/resources/fchomo.js:1459
msgid "🔗"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1438
msgid "🡇"
msgstr ""
+141 -21
View File
@@ -643,6 +643,10 @@ msgstr "收集数据中..."
msgid "Common ports (bypass P2P traffic)"
msgstr "常用端口(绕过 P2P 流量)"
#: htdocs/luci-static/resources/fchomo.js:1576
msgid "Complete"
msgstr "完成"
#: htdocs/luci-static/resources/view/fchomo/node.js:1040
msgid "Configuration Document"
msgstr "配置文档"
@@ -906,11 +910,11 @@ msgstr "下载带宽"
msgid "Download bandwidth in Mbps."
msgstr "下载带宽(单位:Mbps)。"
#: htdocs/luci-static/resources/fchomo.js:1507
#: htdocs/luci-static/resources/fchomo.js:1433
msgid "Download failed: %s"
msgstr "下载失败: %s"
#: htdocs/luci-static/resources/fchomo.js:1503
#: htdocs/luci-static/resources/fchomo.js:1429
msgid "Download successful."
msgstr "下载成功。"
@@ -1154,6 +1158,28 @@ msgstr "预期状态"
#: htdocs/luci-static/resources/fchomo.js:525
#: htdocs/luci-static/resources/fchomo.js:528
#: htdocs/luci-static/resources/fchomo.js:531
#: htdocs/luci-static/resources/fchomo.js:1593
#: htdocs/luci-static/resources/fchomo.js:1601
#: htdocs/luci-static/resources/fchomo.js:1609
#: htdocs/luci-static/resources/fchomo.js:1632
#: htdocs/luci-static/resources/fchomo.js:1635
#: htdocs/luci-static/resources/fchomo.js:1642
#: htdocs/luci-static/resources/fchomo.js:1658
#: htdocs/luci-static/resources/fchomo.js:1667
#: htdocs/luci-static/resources/fchomo.js:1679
#: htdocs/luci-static/resources/fchomo.js:1682
#: htdocs/luci-static/resources/fchomo.js:1692
#: htdocs/luci-static/resources/fchomo.js:1704
#: htdocs/luci-static/resources/fchomo.js:1707
#: htdocs/luci-static/resources/fchomo.js:1720
#: htdocs/luci-static/resources/fchomo.js:1729
#: htdocs/luci-static/resources/fchomo.js:1739
#: htdocs/luci-static/resources/fchomo.js:1774
#: htdocs/luci-static/resources/fchomo.js:1776
#: htdocs/luci-static/resources/fchomo.js:1789
#: htdocs/luci-static/resources/fchomo.js:1795
#: htdocs/luci-static/resources/fchomo.js:1802
#: htdocs/luci-static/resources/fchomo.js:1811
#: htdocs/luci-static/resources/fchomo/listeners.js:284
#: htdocs/luci-static/resources/fchomo/listeners.js:339
#: htdocs/luci-static/resources/fchomo/listeners.js:854
@@ -1223,6 +1249,22 @@ msgstr "实验性"
msgid "Factor"
msgstr "条件"
#: htdocs/luci-static/resources/fchomo.js:1534
msgid "Failed to execute \"/etc/init.d/fchomo %s %s\" reason: %s"
msgstr "无法执行 \"/etc/init.d/fchomo %s %s\" 原因: %s"
#: htdocs/luci-static/resources/fchomo.js:1487
msgid "Failed to generate %s, error: %s."
msgstr "生成 %s 失败,错误:%s。"
#: htdocs/luci-static/resources/fchomo.js:1911
msgid "Failed to upload %s, error: %s."
msgstr "上传 %s 失败,错误:%s。"
#: htdocs/luci-static/resources/fchomo.js:1930
msgid "Failed to upload, error: %s."
msgstr "上传失败,错误:%s。"
#: htdocs/luci-static/resources/fchomo.js:280
#: htdocs/luci-static/resources/fchomo/listeners.js:378
msgid "Fallback"
@@ -1585,7 +1627,7 @@ msgstr "主机密钥算法"
msgid "Host/SNI override"
msgstr "主机/SNI 覆盖"
#: htdocs/luci-static/resources/view/fchomo/node.js:1062
#: htdocs/luci-static/resources/view/fchomo/node.js:1063
msgid "Hostname"
msgstr "主机名"
@@ -1679,6 +1721,10 @@ msgstr "闲置会话超时"
msgid "Idle timeout"
msgstr "闲置超时"
#: htdocs/luci-static/resources/fchomo.js:1635
msgid "If All ports is selected, uncheck others"
msgstr "如果选择了“所有端口”,则取消选中“其他”"
#: htdocs/luci-static/resources/view/fchomo/client.js:71
msgid "If Block is selected, uncheck others"
msgstr "如果选择了“阻止”,则取消选中“其他”"
@@ -1894,6 +1940,13 @@ msgstr "密钥对"
msgid "L2"
msgstr "L2"
#: htdocs/luci-static/resources/fchomo.js:241
#: htdocs/luci-static/resources/fchomo.js:242
#: htdocs/luci-static/resources/fchomo.js:243
#: htdocs/luci-static/resources/fchomo.js:244
msgid "L3"
msgstr ""
#: htdocs/luci-static/resources/fchomo/listeners.js:129
#: htdocs/luci-static/resources/view/fchomo/client.js:1055
#: htdocs/luci-static/resources/view/fchomo/client.js:1347
@@ -2285,16 +2338,6 @@ msgstr "原生外观"
msgid "Network"
msgstr ""
#: htdocs/luci-static/resources/view/fchomo/node.js:1046
#: htdocs/luci-static/resources/view/fchomo/node.js:1057
msgid "Room name"
msgstr "房间名称"
#: htdocs/luci-static/resources/view/fchomo/node.js:1051
#: htdocs/luci-static/resources/view/fchomo/node.js:1057
msgid "Room secret"
msgstr "房间密码"
#: htdocs/luci-static/resources/fchomo/listeners.js:662
msgid "Network type"
msgstr "网络类型"
@@ -2368,7 +2411,7 @@ msgstr "正常"
msgid "Not Installed"
msgstr "未安装"
#: htdocs/luci-static/resources/fchomo.js:1463
#: htdocs/luci-static/resources/fchomo.js:1389
msgid "Not Running"
msgstr "未在运行"
@@ -2431,7 +2474,7 @@ msgstr "仅当 %s 用作前端时适用。"
msgid "Only applies to the %s."
msgstr "只对%s生效。"
#: htdocs/luci-static/resources/view/fchomo/node.js:1056
#: htdocs/luci-static/resources/view/fchomo/node.js:1057
msgid ""
"Only nodes using the same %s and %s as this network are permitted to perform "
"a handshake or relay traffic through this node."
@@ -2445,7 +2488,7 @@ msgstr "只处理来自指定接口的流量。留空表示全部。"
msgid "Only the address %s is valid."
msgstr "只有地址 %s 是有效的。"
#: htdocs/luci-static/resources/fchomo.js:1457
#: htdocs/luci-static/resources/fchomo.js:1383
msgid "Open Dashboard"
msgstr "打开面板"
@@ -2647,6 +2690,10 @@ msgstr "插件类型"
msgid "Port"
msgstr "端口"
#: htdocs/luci-static/resources/fchomo.js:1644
msgid "Port %s alrealy exists!"
msgstr "端口 %s 已存在!"
#: htdocs/luci-static/resources/view/fchomo/node.js:711
msgid "Port hop interval"
msgstr "端口跳跃间隔"
@@ -2716,7 +2763,7 @@ msgstr "密钥密码"
msgid "Private key"
msgstr "私钥"
#: htdocs/luci-static/resources/view/fchomo/node.js:1055
#: htdocs/luci-static/resources/view/fchomo/node.js:1056
msgid "Private room"
msgstr "私密房间"
@@ -2941,7 +2988,7 @@ msgstr "降低后台流量和网络配置刷新频率。"
msgid "Refresh every %s seconds."
msgstr "每 %s 秒刷新。"
#: htdocs/luci-static/resources/fchomo.js:1450
#: htdocs/luci-static/resources/fchomo.js:1376
#: htdocs/luci-static/resources/view/fchomo/client.js:966
#: htdocs/luci-static/resources/view/fchomo/global.js:193
#: htdocs/luci-static/resources/view/fchomo/server.js:45
@@ -2977,6 +3024,11 @@ msgstr "远程跟踪级别"
msgid "Remote trace target"
msgstr "远程跟踪目标"
#: htdocs/luci-static/resources/fchomo.js:1565
msgid "Remove"
msgstr "移除"
#: htdocs/luci-static/resources/fchomo.js:1570
#: htdocs/luci-static/resources/view/fchomo/node.js:244
#: htdocs/luci-static/resources/view/fchomo/node.js:246
#: htdocs/luci-static/resources/view/fchomo/node.js:2341
@@ -3070,6 +3122,16 @@ msgstr "重用 HTTP 连接以减少每次建立连接的 RTT。"
msgid "Reusing a single tunnel to carry multiple target connections within it."
msgstr "复用单条隧道使其内部承载多条目标连线。"
#: htdocs/luci-static/resources/view/fchomo/node.js:1046
#: htdocs/luci-static/resources/view/fchomo/node.js:1058
msgid "Room name"
msgstr "房间名称"
#: htdocs/luci-static/resources/view/fchomo/node.js:1051
#: htdocs/luci-static/resources/view/fchomo/node.js:1058
msgid "Room secret"
msgstr "房间密码"
#: htdocs/luci-static/resources/view/fchomo/global.js:846
msgid "Routing Control"
msgstr "路由控制"
@@ -3146,7 +3208,7 @@ msgstr "规则集"
msgid "Ruleset-URI-Scheme"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1463
#: htdocs/luci-static/resources/fchomo.js:1389
msgid "Running"
msgstr "正在运行"
@@ -3418,6 +3480,10 @@ msgstr "已成功导入 %s 个%s (共 %s 个)。"
msgid "Successfully updated."
msgstr "更新成功。"
#: htdocs/luci-static/resources/fchomo.js:1927
msgid "Successfully uploaded."
msgstr "已成功上传。"
#: htdocs/luci-static/resources/fchomo.js:192
#: htdocs/luci-static/resources/fchomo.js:229
msgid "Sudoku"
@@ -3819,7 +3885,7 @@ msgstr "自动选择"
msgid "UUID"
msgstr "UUID"
#: htdocs/luci-static/resources/fchomo.js:1510
#: htdocs/luci-static/resources/fchomo.js:1436
msgid "Unable to download unsupported type: %s"
msgstr "无法下载不支持的类型: %s"
@@ -4162,6 +4228,10 @@ msgstr "您可以将 %s 添加到 %s 中,以解析虚拟网络内的 A/PTR 记
msgid "YouTube"
msgstr "油管"
#: htdocs/luci-static/resources/fchomo.js:1909
msgid "Your %s was successfully uploaded. Size: %sB."
msgstr "您的 %s 已成功上传。大小:%sB。"
#: htdocs/luci-static/resources/fchomo.js:240
msgid "ZeroTier"
msgstr ""
@@ -4362,6 +4432,13 @@ msgstr "new_reno"
msgid "no-resolve"
msgstr "no-resolve"
#: htdocs/luci-static/resources/fchomo.js:1632
#: htdocs/luci-static/resources/fchomo.js:1739
#: htdocs/luci-static/resources/fchomo.js:1774
#: htdocs/luci-static/resources/fchomo.js:1802
msgid "non-empty value"
msgstr "非空值"
#: htdocs/luci-static/resources/fchomo.js:416
#: htdocs/luci-static/resources/fchomo.js:430
#: htdocs/luci-static/resources/fchomo.js:442
@@ -4511,6 +4588,10 @@ msgstr "独立 UCI 标识"
msgid "unique identifier"
msgstr "独立标识"
#: htdocs/luci-static/resources/fchomo.js:1811
msgid "unique value"
msgstr "独立值"
#: htdocs/luci-static/resources/view/fchomo/node.js:2583
msgid "up"
msgstr "Hysteria 上传速率"
@@ -4549,10 +4630,45 @@ msgstr ""
msgid "v5"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1679
#: htdocs/luci-static/resources/fchomo.js:1682
msgid "valid JSON format"
msgstr "有效的 JSON 格式"
#: htdocs/luci-static/resources/view/fchomo/node.js:1542
msgid "valid SHA256 string with %d characters"
msgstr "包含 %d 个字符的有效 SHA256 字符串"
#: htdocs/luci-static/resources/fchomo.js:1704
#: htdocs/luci-static/resources/fchomo.js:1707
msgid "valid URL"
msgstr "有效网址"
#: htdocs/luci-static/resources/fchomo.js:1729
msgid "valid base64 key with %d characters"
msgstr "包含 %d 个字符的有效 base64 密钥"
#: htdocs/luci-static/resources/fchomo.js:1789
#: htdocs/luci-static/resources/fchomo.js:1795
msgid "valid format: 2x, 2p, 4v"
msgstr "有效格式: 2x, 2p, 4v"
#: htdocs/luci-static/resources/fchomo.js:1720
msgid "valid hex string with %d characters"
msgstr "包含 %d 个字符的有效 Hex 字符串"
#: htdocs/luci-static/resources/fchomo.js:1776
msgid "valid key length with %d characters"
msgstr "包含 %d 个字符的有效密钥"
#: htdocs/luci-static/resources/fchomo.js:1642
msgid "valid port value"
msgstr "有效端口值"
#: htdocs/luci-static/resources/fchomo.js:1692
msgid "valid uuid"
msgstr "有效 uuid"
#: htdocs/luci-static/resources/fchomo.js:488
msgid "vless-mlkem768"
msgstr ""
@@ -4585,6 +4701,10 @@ msgstr ""
msgid "zero"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1512
#: htdocs/luci-static/resources/fchomo.js:1459
msgid "🔗"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1438
msgid "🡇"
msgstr ""
+141 -21
View File
@@ -643,6 +643,10 @@ msgstr "收集資料中..."
msgid "Common ports (bypass P2P traffic)"
msgstr "常用連接埠(繞過 P2P 流量)"
#: htdocs/luci-static/resources/fchomo.js:1576
msgid "Complete"
msgstr "完成"
#: htdocs/luci-static/resources/view/fchomo/node.js:1040
msgid "Configuration Document"
msgstr "配置文檔"
@@ -906,11 +910,11 @@ msgstr "下載頻寬"
msgid "Download bandwidth in Mbps."
msgstr "下載頻寬(單位:Mbps)。"
#: htdocs/luci-static/resources/fchomo.js:1507
#: htdocs/luci-static/resources/fchomo.js:1433
msgid "Download failed: %s"
msgstr "下載失敗: %s"
#: htdocs/luci-static/resources/fchomo.js:1503
#: htdocs/luci-static/resources/fchomo.js:1429
msgid "Download successful."
msgstr "下載成功。"
@@ -1154,6 +1158,28 @@ msgstr "預期狀態"
#: htdocs/luci-static/resources/fchomo.js:525
#: htdocs/luci-static/resources/fchomo.js:528
#: htdocs/luci-static/resources/fchomo.js:531
#: htdocs/luci-static/resources/fchomo.js:1593
#: htdocs/luci-static/resources/fchomo.js:1601
#: htdocs/luci-static/resources/fchomo.js:1609
#: htdocs/luci-static/resources/fchomo.js:1632
#: htdocs/luci-static/resources/fchomo.js:1635
#: htdocs/luci-static/resources/fchomo.js:1642
#: htdocs/luci-static/resources/fchomo.js:1658
#: htdocs/luci-static/resources/fchomo.js:1667
#: htdocs/luci-static/resources/fchomo.js:1679
#: htdocs/luci-static/resources/fchomo.js:1682
#: htdocs/luci-static/resources/fchomo.js:1692
#: htdocs/luci-static/resources/fchomo.js:1704
#: htdocs/luci-static/resources/fchomo.js:1707
#: htdocs/luci-static/resources/fchomo.js:1720
#: htdocs/luci-static/resources/fchomo.js:1729
#: htdocs/luci-static/resources/fchomo.js:1739
#: htdocs/luci-static/resources/fchomo.js:1774
#: htdocs/luci-static/resources/fchomo.js:1776
#: htdocs/luci-static/resources/fchomo.js:1789
#: htdocs/luci-static/resources/fchomo.js:1795
#: htdocs/luci-static/resources/fchomo.js:1802
#: htdocs/luci-static/resources/fchomo.js:1811
#: htdocs/luci-static/resources/fchomo/listeners.js:284
#: htdocs/luci-static/resources/fchomo/listeners.js:339
#: htdocs/luci-static/resources/fchomo/listeners.js:854
@@ -1223,6 +1249,22 @@ msgstr "實驗性"
msgid "Factor"
msgstr "條件"
#: htdocs/luci-static/resources/fchomo.js:1534
msgid "Failed to execute \"/etc/init.d/fchomo %s %s\" reason: %s"
msgstr "無法執行 \"/etc/init.d/fchomo %s %s\" 原因: %s"
#: htdocs/luci-static/resources/fchomo.js:1487
msgid "Failed to generate %s, error: %s."
msgstr "生成 %s 失敗,錯誤:%s。"
#: htdocs/luci-static/resources/fchomo.js:1911
msgid "Failed to upload %s, error: %s."
msgstr "上傳 %s 失敗,錯誤:%s。"
#: htdocs/luci-static/resources/fchomo.js:1930
msgid "Failed to upload, error: %s."
msgstr "上傳失敗,錯誤:%s。"
#: htdocs/luci-static/resources/fchomo.js:280
#: htdocs/luci-static/resources/fchomo/listeners.js:378
msgid "Fallback"
@@ -1585,7 +1627,7 @@ msgstr "主機金鑰演算法"
msgid "Host/SNI override"
msgstr "主機/SNI 覆蓋"
#: htdocs/luci-static/resources/view/fchomo/node.js:1062
#: htdocs/luci-static/resources/view/fchomo/node.js:1063
msgid "Hostname"
msgstr "主機名稱"
@@ -1679,6 +1721,10 @@ msgstr "閒置會話逾時"
msgid "Idle timeout"
msgstr "閒置逾時"
#: htdocs/luci-static/resources/fchomo.js:1635
msgid "If All ports is selected, uncheck others"
msgstr "如果選擇了“所有連接埠”,則取消選取“其他”"
#: htdocs/luci-static/resources/view/fchomo/client.js:71
msgid "If Block is selected, uncheck others"
msgstr "如果選擇了“封鎖”,則取消選取“其他”"
@@ -1894,6 +1940,13 @@ msgstr "密鑰對"
msgid "L2"
msgstr "L2"
#: htdocs/luci-static/resources/fchomo.js:241
#: htdocs/luci-static/resources/fchomo.js:242
#: htdocs/luci-static/resources/fchomo.js:243
#: htdocs/luci-static/resources/fchomo.js:244
msgid "L3"
msgstr ""
#: htdocs/luci-static/resources/fchomo/listeners.js:129
#: htdocs/luci-static/resources/view/fchomo/client.js:1055
#: htdocs/luci-static/resources/view/fchomo/client.js:1347
@@ -2285,16 +2338,6 @@ msgstr "原生外觀"
msgid "Network"
msgstr ""
#: htdocs/luci-static/resources/view/fchomo/node.js:1046
#: htdocs/luci-static/resources/view/fchomo/node.js:1057
msgid "Room name"
msgstr "房間名稱"
#: htdocs/luci-static/resources/view/fchomo/node.js:1051
#: htdocs/luci-static/resources/view/fchomo/node.js:1057
msgid "Room secret"
msgstr "房間密碼"
#: htdocs/luci-static/resources/fchomo/listeners.js:662
msgid "Network type"
msgstr "網路類型"
@@ -2368,7 +2411,7 @@ msgstr "正常"
msgid "Not Installed"
msgstr "未安裝"
#: htdocs/luci-static/resources/fchomo.js:1463
#: htdocs/luci-static/resources/fchomo.js:1389
msgid "Not Running"
msgstr "未在運作"
@@ -2431,7 +2474,7 @@ msgstr "僅當 %s 用作前端時適用。"
msgid "Only applies to the %s."
msgstr "只對%s生效。"
#: htdocs/luci-static/resources/view/fchomo/node.js:1056
#: htdocs/luci-static/resources/view/fchomo/node.js:1057
msgid ""
"Only nodes using the same %s and %s as this network are permitted to perform "
"a handshake or relay traffic through this node."
@@ -2445,7 +2488,7 @@ msgstr "只處理來自指定介面的流量。留空表示全部。"
msgid "Only the address %s is valid."
msgstr "只有位址 %s 是有效的。"
#: htdocs/luci-static/resources/fchomo.js:1457
#: htdocs/luci-static/resources/fchomo.js:1383
msgid "Open Dashboard"
msgstr "打開面板"
@@ -2647,6 +2690,10 @@ msgstr "外掛類型"
msgid "Port"
msgstr "連接埠"
#: htdocs/luci-static/resources/fchomo.js:1644
msgid "Port %s alrealy exists!"
msgstr "連接埠 %s 已存在!"
#: htdocs/luci-static/resources/view/fchomo/node.js:711
msgid "Port hop interval"
msgstr "連接埠跳躍間隔"
@@ -2716,7 +2763,7 @@ msgstr "金鑰密碼"
msgid "Private key"
msgstr "私鑰"
#: htdocs/luci-static/resources/view/fchomo/node.js:1055
#: htdocs/luci-static/resources/view/fchomo/node.js:1056
msgid "Private room"
msgstr "私密房間"
@@ -2941,7 +2988,7 @@ msgstr "降低後台流量和網路配置刷新頻率。"
msgid "Refresh every %s seconds."
msgstr "每 %s 秒刷新。"
#: htdocs/luci-static/resources/fchomo.js:1450
#: htdocs/luci-static/resources/fchomo.js:1376
#: htdocs/luci-static/resources/view/fchomo/client.js:966
#: htdocs/luci-static/resources/view/fchomo/global.js:193
#: htdocs/luci-static/resources/view/fchomo/server.js:45
@@ -2977,6 +3024,11 @@ msgstr "遠端追蹤等級"
msgid "Remote trace target"
msgstr "遠端追蹤目標"
#: htdocs/luci-static/resources/fchomo.js:1565
msgid "Remove"
msgstr "移除"
#: htdocs/luci-static/resources/fchomo.js:1570
#: htdocs/luci-static/resources/view/fchomo/node.js:244
#: htdocs/luci-static/resources/view/fchomo/node.js:246
#: htdocs/luci-static/resources/view/fchomo/node.js:2341
@@ -3070,6 +3122,16 @@ msgstr "重用 HTTP 連接以減少每次建立連接的 RTT。"
msgid "Reusing a single tunnel to carry multiple target connections within it."
msgstr "複用單條隧道使其內部承載多條目標連線。"
#: htdocs/luci-static/resources/view/fchomo/node.js:1046
#: htdocs/luci-static/resources/view/fchomo/node.js:1058
msgid "Room name"
msgstr "房間名稱"
#: htdocs/luci-static/resources/view/fchomo/node.js:1051
#: htdocs/luci-static/resources/view/fchomo/node.js:1058
msgid "Room secret"
msgstr "房間密碼"
#: htdocs/luci-static/resources/view/fchomo/global.js:846
msgid "Routing Control"
msgstr "路由控制"
@@ -3146,7 +3208,7 @@ msgstr "規則集"
msgid "Ruleset-URI-Scheme"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1463
#: htdocs/luci-static/resources/fchomo.js:1389
msgid "Running"
msgstr "正在運作"
@@ -3418,6 +3480,10 @@ msgstr "已成功匯入 %s 個%s (共 %s 個)。"
msgid "Successfully updated."
msgstr "更新成功。"
#: htdocs/luci-static/resources/fchomo.js:1927
msgid "Successfully uploaded."
msgstr "已成功上傳。"
#: htdocs/luci-static/resources/fchomo.js:192
#: htdocs/luci-static/resources/fchomo.js:229
msgid "Sudoku"
@@ -3819,7 +3885,7 @@ msgstr "自動選擇"
msgid "UUID"
msgstr "UUID"
#: htdocs/luci-static/resources/fchomo.js:1510
#: htdocs/luci-static/resources/fchomo.js:1436
msgid "Unable to download unsupported type: %s"
msgstr "無法下載不支援的類型: %s"
@@ -4162,6 +4228,10 @@ msgstr "您可以將 %s 新增到 %s 中,以解析虛擬網路內的 A/PTR 記
msgid "YouTube"
msgstr "YouTube"
#: htdocs/luci-static/resources/fchomo.js:1909
msgid "Your %s was successfully uploaded. Size: %sB."
msgstr "您的 %s 已成功上傳。大小:%sB。"
#: htdocs/luci-static/resources/fchomo.js:240
msgid "ZeroTier"
msgstr ""
@@ -4362,6 +4432,13 @@ msgstr "new_reno"
msgid "no-resolve"
msgstr "no-resolve"
#: htdocs/luci-static/resources/fchomo.js:1632
#: htdocs/luci-static/resources/fchomo.js:1739
#: htdocs/luci-static/resources/fchomo.js:1774
#: htdocs/luci-static/resources/fchomo.js:1802
msgid "non-empty value"
msgstr "非空值"
#: htdocs/luci-static/resources/fchomo.js:416
#: htdocs/luci-static/resources/fchomo.js:430
#: htdocs/luci-static/resources/fchomo.js:442
@@ -4511,6 +4588,10 @@ msgstr "獨立 UCI 識別"
msgid "unique identifier"
msgstr "獨立標識"
#: htdocs/luci-static/resources/fchomo.js:1811
msgid "unique value"
msgstr "獨立值"
#: htdocs/luci-static/resources/view/fchomo/node.js:2583
msgid "up"
msgstr "Hysteria 上傳速率"
@@ -4549,10 +4630,45 @@ msgstr ""
msgid "v5"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1679
#: htdocs/luci-static/resources/fchomo.js:1682
msgid "valid JSON format"
msgstr "有效的 JSON 格式"
#: htdocs/luci-static/resources/view/fchomo/node.js:1542
msgid "valid SHA256 string with %d characters"
msgstr "包含 %d 個字元的有效 SHA256 字串"
#: htdocs/luci-static/resources/fchomo.js:1704
#: htdocs/luci-static/resources/fchomo.js:1707
msgid "valid URL"
msgstr "有效網址"
#: htdocs/luci-static/resources/fchomo.js:1729
msgid "valid base64 key with %d characters"
msgstr "包含 %d 個字元的有效 base64 金鑰"
#: htdocs/luci-static/resources/fchomo.js:1789
#: htdocs/luci-static/resources/fchomo.js:1795
msgid "valid format: 2x, 2p, 4v"
msgstr "有效格式: 2x, 2p, 4v"
#: htdocs/luci-static/resources/fchomo.js:1720
msgid "valid hex string with %d characters"
msgstr "包含 %d 个字符的有效 Hex 字串"
#: htdocs/luci-static/resources/fchomo.js:1776
msgid "valid key length with %d characters"
msgstr "包含 %d 個字元的有效金鑰"
#: htdocs/luci-static/resources/fchomo.js:1642
msgid "valid port value"
msgstr "有效連接埠值"
#: htdocs/luci-static/resources/fchomo.js:1692
msgid "valid uuid"
msgstr "有效 uuid"
#: htdocs/luci-static/resources/fchomo.js:488
msgid "vless-mlkem768"
msgstr ""
@@ -4585,6 +4701,10 @@ msgstr ""
msgid "zero"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1512
#: htdocs/luci-static/resources/fchomo.js:1459
msgid "🔗"
msgstr ""
#: htdocs/luci-static/resources/fchomo.js:1438
msgid "🡇"
msgstr ""
@@ -79,6 +79,13 @@ return L.view.extend({
o.rmempty = false;
o.modalonly = true;
// /etc/init.d/glorytun starts no instance without proto=tcp: an
// instance added here was never started
o = s.taboption('general', form.HiddenValue, 'proto');
o.default = 'tcp';
o.rmempty = false;
o.modalonly = true;
return m.render();
}
});
+5 -4
View File
@@ -5,8 +5,9 @@
%>
<script type="text/javascript">//<![CDATA[
var stxhr = new XHR();
// One XHR per request: the luci.js XHR shim drops the callback of a
// request posted from another request's callback on the same object,
// so the download result was never shown
function update_speed(field, proto, mode,omit,parallel,transmit,bitrate)
{
update_upload(field,proto,mode,omit,parallel,transmit,bitrate);
@@ -26,7 +27,7 @@
'<%:Waiting for command to complete...%>'
;
stxhr.post('<%=url('admin/services/iperf')%>/run_test' + '/' + addr + '/' + proto + '/' + mode + '/upload' + '/' + omit + '/' + parallel + '/' + transmit + '/' + bitrate, { token: '<%=token%>' },
new XHR().post('<%=url('admin/services/iperf')%>/run_test' + '/' + addr + '/' + proto + '/' + mode + '/upload' + '/' + omit + '/' + parallel + '/' + transmit + '/' + bitrate, { token: '<%=token%>' },
function(x)
{
if (x.responseText)
@@ -66,7 +67,7 @@
'<%:Waiting for command to complete...%>'
;
stxhr.post('<%=url('admin/services/iperf')%>/run_test' + '/' + addr + '/' + proto + '/' + mode + '/download' + '/' + omit + '/' + parallel + '/' + transmit + '/' + bitrate, { token: '<%=token%>' },
new XHR().post('<%=url('admin/services/iperf')%>/run_test' + '/' + addr + '/' + proto + '/' + mode + '/download' + '/' + omit + '/' + parallel + '/' + transmit + '/' + bitrate, { token: '<%=token%>' },
function(x)
{
if (x.responseText)
@@ -393,8 +393,11 @@ return L.view.extend({
o = s.option(form.Value, "multipath_weight", _("Weight"), _("Only used by *weight schedulers/path managers. Ignored if no weight scheduler is selected.") + '<br />' + _("A weight >100 make it more attractive, a weight <100 make it less attractive. Max 256"));
o.datatype = "uinteger";
o.rmempty = false;
o.default = 100;
// 100 shown as a placeholder, not a default: with a default every Save
// of this page wrote multipath_weight='100' on each interface that had
// none. Unset means 100 for every reader (omr-weight-sync, mqvpn).
o.placeholder = "100";
o.rmempty = true;
return m.render();
}
@@ -12,12 +12,10 @@ var callMptcpCheckTrace = rpc.declare({
return view.extend({
load: function() {
return Promise.all([
uci.load('network'),
// only for the old copy of the mode below: the ACL doesn't
// give this config (VPS passwords and tokens) to non-root users
L.resolveDefault(uci.load('openmptcprouter'), null)
]);
// network only: the old openmptcprouter.<iface>.multipath copy is
// left out, the ACL does not grant that config (VPS password and
// token)
return uci.load('network');
},
render: function() {
@@ -25,8 +23,7 @@ return view.extend({
uci.sections('network', 'interface', function(s) {
var name = s['.name'];
if (name === 'loopback') return;
var multipath = s.multipath ||
uci.get('openmptcprouter', name, 'multipath') || 'off';
var multipath = s.multipath || 'off';
if (multipath !== 'off')
ifaces.push(name);
});
@@ -1,6 +1,7 @@
'use strict';
'require form';
'require uci';
'require rpc';
/*
* Copyright (C) 2026 Ycarus (Yannick Chabanois) <contact@openmptcprouter.com> for OpenMPTCProuter
@@ -34,6 +35,13 @@ var DSCP_CLASSES = [
['ef', _('EF (46) - Voice')]
];
// mqvpn's state through rpcd: the ACL does not grant the mqvpn config (PSK)
var callMqvpnState = rpc.declare({
object: 'luci.mptcp',
method: 'mqvpn_state',
expect: { '': {} }
});
// Same WAN filter as this app's own mptcp.js "Interfaces Settings"
// section: excludes the pseudo-interfaces that are never a real WAN.
function isEligibleIface(name) {
@@ -76,21 +84,22 @@ return L.view.extend({
load: function() {
return Promise.all([
uci.load('network'),
// mqvpn may not be installed at all -- resolve to null instead
// of rejecting the whole page's load() in that case.
L.resolveDefault(uci.load('mqvpn'), null)
// mqvpn may not be installed at all -- resolve to "disabled"
// instead of rejecting the whole page's load() in that case.
L.resolveDefault(callMqvpnState(), {})
]);
},
render: function() {
render: function(data) {
var m, s, o;
var mqvpn = (data && data[1]) || {};
var mptcpScheduler = normalizeSchedulerValue(uci.get('network', 'globals', 'mptcp_scheduler'));
var mptcpHasDscpScheduler = (mptcpScheduler === 'bpf_dscp');
var mptcpHasWeightScheduler = (mptcpScheduler === 'bpf_weight' || mptcpScheduler === 'bpf_weight_rr' || mptcpScheduler === 'bpf_burstweight');
var mqvpnEnabled = (uci.get('mqvpn', 'settings', 'enable') === '1');
var mqvpnScheduler = uci.get('mqvpn', 'multipath', 'scheduler');
var mqvpnEnabled = (mqvpn.enabled === true);
var mqvpnScheduler = mqvpn.scheduler;
// mqvpn has no scheduler value dedicated to DSCP the way MPTCP has
// bpf_dscp -- 007-mqvpn-dscp pushes the DSCP mask unconditionally
// whenever mqvpn is enabled, regardless of which scheduler is
@@ -170,8 +179,10 @@ return L.view.extend({
o = s.option(form.Value, 'multipath_weight', _('Weight'),
_('A weight >100 makes a WAN more attractive, a weight <100 makes it less attractive. Max 256.'));
o.datatype = 'uinteger';
o.rmempty = false;
o.default = 100;
// a placeholder, not a default: a default made every Save write
// multipath_weight='100' on each WAN that had none (unset is 100)
o.placeholder = '100';
o.rmempty = true;
}
return m.render();
@@ -58,6 +58,26 @@ end
local IP_BIN = "/sbin/ip"
-- The VPS to trace to: the master server of the openmptcprouter config,
-- else the server of the proxy in use. shadowsocks-libev's sss0 alone was
-- read before, a stale or placeholder address with shadowsocks-rust or any
-- other proxy.
local function trace_server()
local server
uci:foreach("openmptcprouter", "server", function(s)
if server or s.disabled == "1" or s.master ~= "1" then return end
local ip = s.ip
if type(ip) == "table" then ip = ip[1] end
if ip and ip ~= "" then server = ip end
end)
if server then return server end
local proxy = uci:get("openmptcprouter", "settings", "proxy") or ""
if proxy:match("^shadowsocks%-rust") then
return uci:get("shadowsocks-rust", "sss0", "server") or ""
end
return uci:get("shadowsocks-libev", "sss0", "server") or ""
end
local function trim(s)
return (tostring(s or ""):gsub("^%s+", ""):gsub("%s+$", ""))
end
@@ -159,7 +179,7 @@ function parse_counters(raw)
end
-- Where to read the MPTcpExt* counters from for Diagnostics. Deliberately
-- NOT "multipath -m" here: that wraps "nstat -z", and nstat (without -a)
-- NOT "multipath -m" here: that wrapped "nstat -z", and nstat (without -a)
-- reports the delta since the *previous* nstat invocation by anyone on the
-- box (it keeps a shared per-uid history file) -- confirmed live: two
-- consecutive calls a couple seconds apart came back ~0, while "nstat -a -z"
@@ -896,6 +916,18 @@ local methods = {
end
},
-- Whether mqvpn is enabled and which scheduler it runs, for the DSCP /
-- Weight Routing page: these two values only, the page's ACL does not
-- grant the mqvpn config itself (it holds the PSK).
mqvpn_state = {
call = function()
return {
enabled = uci:get("mqvpn", "settings", "enable") == "1",
scheduler = uci:get("mqvpn", "multipath", "scheduler") or ""
}
end
},
-- Runs "multipath -f" and returns its output.
mptcp_fullmesh = {
call = function()
@@ -923,7 +955,7 @@ local methods = {
call = function(args)
local iface = args.iface or ""
local interface = get_device(iface)
local server = uci:get("shadowsocks-libev", "sss0", "server") or ""
local server = trace_server()
-- Placeholders the shadowsocks config ships until a VPS is set
-- (127.0.0.1 now, 192.168.1.3 in older releases): tracing to them
-- would report a path that has nothing to do with the proxy.
@@ -86,5 +86,16 @@
"depends": {
"acl": [ "luci-app-mptcp" ]
}
},
"admin/network/mptcp/mptcp_check": {
"title": "MPTCP Support Check",
"order": 70,
"action": {
"type": "view",
"path": "mptcp/mptcp_check"
},
"depends": {
"acl": [ "luci-app-mptcp" ]
}
}
}
@@ -23,6 +23,7 @@
"mptcp_check_trace",
"get_settings",
"get_interface_settings",
"mqvpn_state",
"diagnose"
]
}
+1 -1
View File
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall
PKG_VERSION:=26.10.16
PKG_RELEASE:=339
PKG_RELEASE:=340
PKG_PO_VERSION:=$(PKG_VERSION)
PKG_CONFIG_DEPENDS:= \
@@ -3,12 +3,86 @@
-- Copyright(c): lwb1978 2025–2027
local api = require("luci.passwall.api")
local urlencode = api.UrlEncode
local base64 = api.base64Encode
local json = api.jsonc
local domain_redir = {}
local function urlencode(value)
return api.UrlEncode(value == nil and "" or tostring(value))
end
local function add_param(params, key, value)
if value ~= nil and value ~= "" then
if type(value) == "boolean" then value = value and "1" or "0" end
params[#params + 1] = key .. "=" .. urlencode(value)
end
end
local function build_ech(node)
local opts = node["ech-opts"]
if type(opts) ~= "table" or opts.enable ~= true then return nil end
if opts.config and opts.config ~= "" then return opts.config end
local name = opts["query-server-name"]
if not name or name == "" then name = node.servername or node.sni or node.server end
if name == "" then name = node.server end
return name .. "+https://223.5.5.5/dns-query"
end
local function bandwidth_mbps(value)
if value == nil then return nil end
local number = tonumber(value)
if number then return number end
local amount, prefix, unit = tostring(value):match("^%s*(%d+)%s*([KMGT]?)([Bb])ps%s*$")
local scales = { [""] = 1, K = 1000, M = 1000000, G = 1000000000, T = 1000000000000 }
if not amount then return nil end
return tonumber(amount) * scales[prefix] * (unit == "B" and 8 or 1) / 1000000
end
local function inherit(base, overrides)
local result = {}
for key, value in pairs(base or {}) do result[key] = value end
for key, value in pairs(overrides or {}) do result[key] = value end
return result
end
local function build_xhttp_extra(opts)
local extra = {}
local fields = {
headers = "headers", ["x-padding-bytes"] = "xPaddingBytes",
["no-grpc-header"] = "noGRPCHeader", ["x-padding-obfs-mode"] = "xPaddingObfsMode",
["x-padding-key"] = "xPaddingKey", ["x-padding-header"] = "xPaddingHeader",
["x-padding-placement"] = "xPaddingPlacement", ["x-padding-method"] = "xPaddingMethod",
["uplink-http-method"] = "uplinkHTTPMethod", ["session-placement"] = "sessionIDPlacement",
["session-key"] = "sessionIDKey", ["session-table"] = "sessionIDTable",
["session-length"] = "sessionIDLength", ["seq-placement"] = "seqPlacement", ["seq-key"] = "seqKey",
["uplink-data-placement"] = "uplinkDataPlacement", ["uplink-data-key"] = "uplinkDataKey",
["uplink-chunk-size"] = "uplinkChunkSize", ["sc-max-each-post-bytes"] = "scMaxEachPostBytes",
["sc-min-posts-interval-ms"] = "scMinPostsIntervalMs"
}
for source, target in pairs(fields) do extra[target] = opts[source] end
-- jsonc treats empty tables as arrays and repeated table references as null.
if type(extra.headers) == "table" then
extra.headers = next(extra.headers) and inherit(extra.headers) or nil
end
local reuse = opts["reuse-settings"]
if reuse then
local xmux = {}
local ranges = {
["max-concurrency"] = "maxConcurrency", ["max-connections"] = "maxConnections",
["c-max-reuse-times"] = "cMaxReuseTimes", ["h-max-request-times"] = "hMaxRequestTimes",
["h-max-reusable-secs"] = "hMaxReusableSecs"
}
for source, target in pairs(ranges) do
local value = reuse[source]
if value ~= nil then xmux[target] = tonumber(value) or value end
end
xmux.hKeepAlivePeriod = tonumber(reuse["h-keep-alive-period"])
if next(xmux) then extra.xmux = xmux end
end
return extra
end
local function host_format(host)
if not host then return "" end
local str = host:match("%[(.-)%]") or host
@@ -62,18 +136,12 @@ local function build_common(node)
o.tls.sni = node.servername or node.sni
o.tls.fp = node["client-fingerprint"]
o.tls.pcs = node.fingerprint
o.tls.vcn = node["name-cert-verify"]
o.tls.insecure = node["skip-cert-verify"] == true
o.tls.alpn = build_alpn(node.alpn)
local ech_opts = node["ech-opts"]
if ech_opts and ech_opts.enable == true then
if ech_opts["query-server-name"] then
o.tls.ech = ech_opts["query-server-name"] .. "+https://223.5.5.5/dns-query"
elseif ech_opts.config then
o.tls.ech = ech_opts.config
end
end
o.tls.ech = build_ech(node)
-- ===== transport =====
o.transport = {}
@@ -89,16 +157,15 @@ local function build_common(node)
local opts = node["ws-opts"]
if opts then
local path = opts.path or "/"
local ed = opts["max-early-data"]
local eh = opts["early-data-header-name"]
if ed then
path = path .. "?ed=" .. ed
if opts["v2ray-http-upgrade"] ~= true and opts["max-early-data"] then
path = path .. (path:find("?", 1, true) and "&ed=" or "?ed=") .. opts["max-early-data"]
end
if eh then
path = path .. (path:find("?", 1, true) and "&eh=" or "?eh=") .. eh
if opts["v2ray-http-upgrade"] ~= true and opts["early-data-header-name"] then
path = path .. (path:find("?", 1, true) and "&eh=" or "?eh=") .. opts["early-data-header-name"]
end
o.transport.path = path
o.transport.host = opts.headers and opts.headers.Host
if opts["v2ray-http-upgrade"] == true then o.transport.type = "httpupgrade" end
end
elseif net == "grpc" then
@@ -108,12 +175,17 @@ local function build_common(node)
end
elseif net == "http" then
-- Use TCP + HTTP camouflage so each core selects its matching transport.
o.transport.type = "tcp"
o.transport.headerType = "http"
o.transport.host = node.server
o.transport.path = "/"
local opts = node["http-opts"]
if opts then
-- Clash http-opts carries the camouflage Host under headers.Host
-- (a list), like ws-opts; opts.host is not standard for http.
o.transport.host = get_first(opts.host) or (opts.headers and get_first(opts.headers.Host))
o.transport.path = get_first(opts.path)
o.transport.host = get_first(opts.host) or (opts.headers and get_first(opts.headers.Host)) or o.transport.host
o.transport.path = get_first(opts.path) or o.transport.path
end
elseif net == "h2" then
@@ -130,85 +202,45 @@ local function build_common(node)
o.transport.path = opts.path
o.transport.mode = opts.mode
local extra = {}
-- headers
if opts.headers then
extra.headers = opts.headers
end
if opts["x-padding-bytes"] then
extra.xPaddingBytes = opts["x-padding-bytes"]
end
if opts["no-grpc-header"] ~= nil then
extra.noGRPCHeader = opts["no-grpc-header"]
end
if opts["sc-max-each-post-bytes"] then
extra.scMaxEachPostBytes = opts["sc-max-each-post-bytes"]
end
if opts["sc-min-posts-interval-ms"] then
extra.scMinPostsIntervalMs = opts["sc-min-posts-interval-ms"]
end
-- xmux
if opts["reuse-settings"] then
local r = opts["reuse-settings"]
local xmux = {}
if r["max-concurrency"] then xmux.maxConcurrency = r["max-concurrency"] end
if r["max-connections"] then xmux.maxConnections = tonumber(r["max-connections"]) end
if r["c-max-reuse-times"] then xmux.cMaxReuseTimes = tonumber(r["c-max-reuse-times"]) end
if r["h-max-request-times"] then xmux.hMaxRequestTimes = r["h-max-request-times"] end
if r["h-max-reusable-secs"] then xmux.hMaxReusableSecs = r["h-max-reusable-secs"] end
if r["h-keep-alive-period"] then xmux.hKeepAlivePeriod = tonumber(r["h-keep-alive-period"]) end
if next(xmux) then extra.xmux = xmux end
end
local extra = build_xhttp_extra(opts)
-- download-settings
if opts["download-settings"] then
local d = opts["download-settings"]
local ds = {}
if d.server then ds.address = domain_redir[d.server] or d.server end
if d.port then ds.port = d.port end
ds.network = "xhttp"
-- TLS
if d.tls then
ds.security = "tls"
ds.tlsSettings = {}
if d.servername then
ds.tlsSettings.serverName = d.servername
end
if d["skip-cert-verify"] == true then
ds.tlsSettings.allowInsecure = true
end
if d["client-fingerprint"] then
ds.tlsSettings.fingerprint = d["client-fingerprint"]
end
if d.fingerprint then
ds.tlsSettings.pinnedPeerCertSha256 = d.fingerprint
end
if d.alpn then
ds.tlsSettings.alpn = d.alpn
local effective = inherit(node, d)
local ds = {
address = domain_redir[effective.server] or effective.server,
port = effective.port,
network = "xhttp",
security = effective.tls and "tls" or "none"
}
local sni = effective.servername or effective.sni or effective.server
local alpn = build_alpn(effective.alpn)
if effective.tls or effective["reality-opts"] then
ds.tlsSettings = {
serverName = sni,
allowInsecure = effective["skip-cert-verify"] == true,
fingerprint = effective["client-fingerprint"],
pinnedPeerCertSha256 = effective.fingerprint and api.sha256_sb_xray(effective.fingerprint) or nil,
verifyPeerCertByName = effective["name-cert-verify"],
alpn = alpn and api.split(alpn, ",") or nil,
echConfigList = build_ech(effective)
}
local reality = effective["reality-opts"]
if reality then
ds.security = "reality"
ds.realitySettings = {
serverName = sni, fingerprint = effective["client-fingerprint"] or "chrome",
publicKey = reality["public-key"], shortId = reality["short-id"] or ""
}
ds.tlsSettings = nil
end
end
-- xhttpSettings
local xs = {}
if d.path then xs.path = d.path end
if d.host then xs.host = d.host end
if next(xs) then
ds.xhttpSettings = xs
end
local xs = build_xhttp_extra(inherit(opts, d))
xs.path = d.path or opts.path or "/"
xs.host = d.host or opts.host or sni
xs.mode = opts.mode or "auto"
ds.xhttpSettings = xs
extra.downloadSettings = ds
end
@@ -229,27 +261,29 @@ local function encode_vless(node)
local link = "vless://" .. node.uuid .. "@" .. o.server .. ":" .. o.port
local p = {}
if type(node.flow) == "string" and node.flow:sub(1, 5) == "xtls-" then table.insert(p, "flow=" .. urlencode(node.flow)) end
if node.encryption then table.insert(p, "encryption=" .. urlencode(node.encryption)) end
if type(node.flow) == "string" and node.flow:sub(1, 5) == "xtls-" then add_param(p, "flow", node.flow) end
add_param(p, "encryption", node.encryption)
-- TLS
if o.tls.security then table.insert(p, "security=" .. o.tls.security) end
if o.tls.pbk then table.insert(p, "pbk=" .. urlencode(o.tls.pbk)) end
if o.tls.sid then table.insert(p, "sid=" .. urlencode(o.tls.sid)) end
if o.tls.sni then table.insert(p, "sni=" .. urlencode(o.tls.sni)) end
if o.tls.fp then table.insert(p, "fp=" .. urlencode(o.tls.fp)) end
if o.tls.alpn then table.insert(p, "alpn=" .. urlencode(o.tls.alpn)) end
if o.tls.ech then table.insert(p, "ech=" .. urlencode(o.tls.ech)) end
if o.tls.pcs then table.insert(p, "pcs=" .. urlencode(o.tls.pcs)) end
table.insert(p, "allowInsecure=" .. (o.tls.insecure and "1" or "0"))
add_param(p, "security", o.tls.security)
add_param(p, "pbk", o.tls.pbk)
add_param(p, "sid", o.tls.sid)
add_param(p, "sni", o.tls.sni)
add_param(p, "fp", o.tls.fp)
add_param(p, "alpn", o.tls.alpn)
add_param(p, "ech", o.tls.ech)
add_param(p, "pcs", o.tls.pcs)
add_param(p, "vcn", o.tls.vcn)
add_param(p, "allowInsecure", o.tls.insecure)
-- transport
table.insert(p, "type=" .. o.transport.type)
if o.transport.host then table.insert(p, "host=" .. urlencode(o.transport.host)) end
if o.transport.path then table.insert(p, "path=" .. urlencode(o.transport.path)) end
if o.transport.serviceName then table.insert(p, "serviceName=" .. urlencode(o.transport.serviceName)) end
if o.transport.mode then table.insert(p, "mode=" .. urlencode(o.transport.mode)) end
if o.transport.extra then table.insert(p, "extra=" .. urlencode(o.transport.extra)) end
add_param(p, "type", o.transport.type)
add_param(p, "headerType", o.transport.headerType)
add_param(p, "host", o.transport.host)
add_param(p, "path", o.transport.path)
add_param(p, "serviceName", o.transport.serviceName)
add_param(p, "mode", o.transport.mode)
add_param(p, "extra", o.transport.extra)
if #p > 0 then
link = link .. "?" .. table.concat(p, "&")
@@ -262,23 +296,24 @@ end
local function encode_trojan(node)
local o = build_common(node)
local link = "trojan://" .. node.password .. "@" .. o.server .. ":" .. o.port
local link = "trojan://" .. urlencode(node.password) .. "@" .. o.server .. ":" .. o.port
local p = {}
if o.tls.security then table.insert(p, "security=" .. o.tls.security) end
if o.tls.pbk then table.insert(p, "pbk=" .. urlencode(o.tls.pbk)) end
if o.tls.sid then table.insert(p, "sid=" .. urlencode(o.tls.sid)) end
if o.tls.sni then table.insert(p, "sni=" .. urlencode(o.tls.sni)) end
if o.tls.fp then table.insert(p, "fp=" .. urlencode(o.tls.fp)) end
if o.tls.alpn then table.insert(p, "alpn=" .. urlencode(o.tls.alpn)) end
if o.tls.ech then table.insert(p, "ech=" .. urlencode(o.tls.ech)) end
if o.tls.pcs then table.insert(p, "pcs=" .. urlencode(o.tls.pcs)) end
table.insert(p, "allowInsecure=" .. (o.tls.insecure and "1" or "0"))
table.insert(p, "type=" .. o.transport.type)
if o.transport.host then table.insert(p, "host=" .. urlencode(o.transport.host)) end
if o.transport.path then table.insert(p, "path=" .. urlencode(o.transport.path)) end
if o.transport.serviceName then table.insert(p, "serviceName=" .. urlencode(o.transport.serviceName)) end
add_param(p, "security", o.tls.security)
add_param(p, "pbk", o.tls.pbk)
add_param(p, "sid", o.tls.sid)
add_param(p, "sni", o.tls.sni)
add_param(p, "fp", o.tls.fp)
add_param(p, "alpn", o.tls.alpn)
add_param(p, "ech", o.tls.ech)
add_param(p, "pcs", o.tls.pcs)
add_param(p, "vcn", o.tls.vcn)
add_param(p, "allowInsecure", o.tls.insecure)
add_param(p, "type", o.transport.type)
add_param(p, "headerType", o.transport.headerType)
add_param(p, "host", o.transport.host)
add_param(p, "path", o.transport.path)
add_param(p, "serviceName", o.transport.serviceName)
if #p > 0 then
link = link .. "?" .. table.concat(p, "&")
@@ -301,21 +336,22 @@ local function encode_vmess(node)
net = o.transport.type,
security = node.cipher,
scy = node.cipher,
type = "none",
type = o.transport.headerType or "none",
host = o.transport.host or "",
path = o.transport.path or "",
path = urlencode(o.transport.path or ""),
tls = o.tls.security == "tls" and "tls" or "",
sni = o.tls.sni,
alpn = o.tls.alpn,
fp = o.tls.fp,
ech = o.tls.ech,
pcs = o.tls.pcs,
vcn = o.tls.vcn,
insecure = o.tls.insecure and "1" or "0",
tfo = node.tfo and "1" or "0"
}
if o.transport.type == "grpc" then
obj.path = o.transport.serviceName or ""
obj.path = urlencode(o.transport.serviceName or "")
end
return 0, "vmess://" .. base64(json.stringify(obj))
@@ -337,14 +373,14 @@ local function encode_ss(node)
local p = {}
if node.udp then table.insert(p, "udp=1") end
if node["udp-over-tcp"] then table.insert(p, "uot=1") end
if node.udp then add_param(p, "udp", true) end
if node["udp-over-tcp"] then add_param(p, "uot", true) end
if node.plugin then
local plugin = (node.plugin == "obfs") and "obfs-local" or node.plugin
if plugin == "shadow-tls" then
local shadow_tls = base64(json.stringify(node["plugin-opts"] or {}))
table.insert(p, "shadow-tls=" .. urlencode(shadow_tls))
add_param(p, "shadow-tls", shadow_tls)
elseif plugin == "obfs-local" or plugin == "v2ray-plugin" then
local opts = {}
for k, v in pairs(node["plugin-opts"] or {}) do
@@ -369,7 +405,7 @@ local function encode_ss(node)
end
end
if #opts > 0 then plugin = plugin .. ";" .. table.concat(opts, ";") end
table.insert(p, "plugin=" .. urlencode(plugin))
add_param(p, "plugin", plugin)
else
return 1, "订阅转换 → 丢弃 SS 节点:" .. (node.name or "") .. ",因 Core 不支持 " .. plugin .. " 插件"
end
@@ -383,22 +419,24 @@ local function encode_ss(node)
end
-- Hysteria
local function encode_hysteria2(node)
local function encode_hysteria(node)
if node.protocol and node.protocol ~= "udp" then
return 1, "订阅转换 → 丢弃 Hysteria 节点:" .. node.name .. ",当前 Core 不支持 " .. node.protocol .. " 传输"
end
local link = "hysteria://" .. host_format(node.server) .. ":" .. (node.port or "")
local p = {}
if node["auth-str"] then table.insert(p, "auth=" .. node["auth-str"]) end
if node["ports"] then table.insert(p, "mport=" .. node["ports"]) end
if node.obfs then table.insert(p, "obfsParam=" .. node.obfs) end
if node.sni then table.insert(p, "sni=" .. node.sni) end
if node.up then table.insert(p, "upmbps=" .. node.up) end
if node.down then table.insert(p, "downmbps=" .. node.down) end
if node["skip-cert-verify"] then table.insert(p, "insecure=1") end
if node["fingerprint"] then table.insert(p, "pinSHA256=" .. urlencode(node["fingerprint"])) end
add_param(p, "auth", node["auth-str"])
add_param(p, "mport", node.ports)
add_param(p, "obfsParam", node.obfs)
add_param(p, "sni", node.sni)
add_param(p, "upmbps", bandwidth_mbps(node.up))
add_param(p, "downmbps", bandwidth_mbps(node.down))
add_param(p, "insecure", node["skip-cert-verify"] == true)
add_param(p, "vcn", node["name-cert-verify"])
add_param(p, "pinSHA256", node.fingerprint)
if node.alpn then
table.insert(p, "alpn=" .. urlencode(build_alpn(node.alpn)))
end
add_param(p, "alpn", build_alpn(node.alpn))
if #p > 0 then
link = link .. "?" .. table.concat(p, "&")
@@ -409,20 +447,24 @@ end
-- Hysteria2
local function encode_hysteria2(node)
local link = "hysteria2://" .. (node.password or "") .. "@" .. host_format(node.server) .. ":" .. (node.port or "")
-- Hopping subscriptions may omit port; keep a valid fallback for all cores.
local port = node.port or tostring(node.ports or ""):match("^%s*(%d+)")
local link = "hysteria2://" .. urlencode(node.password or "") .. "@" .. host_format(node.server) .. ":" .. port
local p = {}
if node["ports"] then table.insert(p, "mport=" .. urlencode(node["ports"])) end
if node.obfs then table.insert(p, "obfs=" .. node.obfs) end
if node["obfs-password"] then table.insert(p, "obfs-password=" .. node["obfs-password"]) end
if node["obfs-min-packet-size"] then table.insert(p, "minPacketSize=" .. node["obfs-min-packet-size"]) end
if node["obfs-max-packet-size"] then table.insert(p, "maxPacketSize=" .. node["obfs-max-packet-size"]) end
if node.up then table.insert(p, "upmbps=" .. node.up) end
if node.down then table.insert(p, "downmbps=" .. node.down) end
if node.sni then table.insert(p, "sni=" .. urlencode(node.sni)) end
if node["skip-cert-verify"] then table.insert(p, "insecure=1") end
if node["fingerprint"] then table.insert(p, "pinSHA256=" .. urlencode(node["fingerprint"])) end
add_param(p, "mport", node.ports)
add_param(p, "hopInterval", node["hop-interval"])
add_param(p, "obfs", node.obfs)
add_param(p, "obfs-password", node["obfs-password"])
add_param(p, "minPacketSize", node["obfs-min-packet-size"])
add_param(p, "maxPacketSize", node["obfs-max-packet-size"])
add_param(p, "upmbps", bandwidth_mbps(node.up))
add_param(p, "downmbps", bandwidth_mbps(node.down))
add_param(p, "ech", build_ech(node))
add_param(p, "vcn", node["name-cert-verify"])
add_param(p, "sni", node.sni)
add_param(p, "insecure", node["skip-cert-verify"] == true)
add_param(p, "pinSHA256", node.fingerprint)
if #p > 0 then
link = link .. "?" .. table.concat(p, "&")
@@ -433,22 +475,23 @@ end
-- TUIC
local function encode_tuic(node)
local link = "tuic://" .. node.uuid .. ":" .. node.password .. "@" .. host_format(node.server) .. ":" .. node.port
if node.token then
return 1, "订阅转换 → 丢弃 TUIC 节点:" .. node.name .. ",当前 Core 不支持 TUIC v4"
end
if not node.uuid or node.password == nil then
return 1, "订阅转换 → 丢弃 TUIC 节点:" .. node.name .. ",认证信息不完整。"
end
local link = "tuic://" .. urlencode(node.uuid) .. ":" .. urlencode(node.password) .. "@" .. host_format(node.server) .. ":" .. node.port
local p = {}
if node["congestion-controller"] then
table.insert(p, "congestion_control=" .. node["congestion-controller"])
end
if node.alpn then
table.insert(p, "alpn=" .. urlencode(build_alpn(node.alpn)))
end
if node.sni then table.insert(p, "sni=" .. urlencode(node.sni)) end
if node["disable-sni"] then table.insert(p, "disable_sni=1") end
if node["skip-cert-verify"] then table.insert(p, "allowInsecure=1") end
if node["udp-relay-mode"] then table.insert(p, "udp_relay_mode=" .. node["udp-relay-mode"]) end
if node["fingerprint"] then table.insert(p, "pcs=" .. urlencode(node["fingerprint"])) end
add_param(p, "congestion_control", node["congestion-controller"])
add_param(p, "alpn", build_alpn(node.alpn))
add_param(p, "sni", node.sni)
if node["disable-sni"] then add_param(p, "disable_sni", true) end
add_param(p, "allowInsecure", node["skip-cert-verify"] == true)
add_param(p, "udp_relay_mode", node["udp-relay-mode"])
add_param(p, "pcs", node.fingerprint)
add_param(p, "vcn", node["name-cert-verify"])
if #p > 0 then
link = link .. "?" .. table.concat(p, "&")
@@ -474,18 +517,18 @@ local function encode_anytls(node)
local o = build_common(node)
local link = "anytls://" .. (node.password or "") .. "@" .. host_format(node.server) .. ":" .. node.port
local link = "anytls://" .. urlencode(node.password or "") .. "@" .. host_format(node.server) .. ":" .. node.port
local p = {}
if o.tls.security then table.insert(p, "security=" .. o.tls.security) end
if o.tls.pbk then table.insert(p, "pbk=" .. urlencode(o.tls.pbk)) end
if o.tls.sid then table.insert(p, "sid=" .. urlencode(o.tls.sid)) end
if o.tls.sni then table.insert(p, "sni=" .. urlencode(o.tls.sni)) end
if o.tls.alpn then table.insert(p, "alpn=" .. urlencode(o.tls.alpn)) end
if o.tls.fp then table.insert(p, "fp=" .. urlencode(o.tls.fp)) end
if o.tls.ech then table.insert(p, "ech=" .. urlencode(o.tls.ech)) end
if o.tls.pcs then table.insert(p, "pcs=" .. urlencode(o.tls.pcs)) end
table.insert(p, "insecure=" .. (o.tls.insecure and "1" or "0"))
add_param(p, "security", o.tls.security)
add_param(p, "pbk", o.tls.pbk)
add_param(p, "sid", o.tls.sid)
add_param(p, "sni", o.tls.sni)
add_param(p, "alpn", o.tls.alpn)
add_param(p, "fp", o.tls.fp)
add_param(p, "ech", o.tls.ech)
add_param(p, "pcs", o.tls.pcs)
add_param(p, "insecure", o.tls.insecure)
if #p > 0 then
link = link .. "?" .. table.concat(p, "&")
@@ -514,8 +557,8 @@ end
-- snell
local function encode_snell(node)
local err_msg
local obfs = node["obfs-opts"]
if obfs and obfs.mode ~= "http" and obfs.mode ~= "none" then
local obfs = node["obfs-opts"] or {}
if obfs.mode and obfs.mode ~= "http" and obfs.mode ~= "none" then
err_msg = obfs.mode
end
if err_msg then
@@ -532,13 +575,13 @@ local function encode_snell(node)
local link = "snell://" .. host_format(node.server) .. ":" .. node.port
local p = {}
if node.psk then table.insert(p, "psk=" .. urlencode(node.psk)) end
table.insert(p, "version=" .. version)
add_param(p, "psk", node.psk)
add_param(p, "version", version)
if obfs.mode == "http" then
table.insert(p, "obfs=http")
if obfs.host then table.insert(p, "obfs-host=" .. urlencode(obfs.host)) end
add_param(p, "obfs", "http")
add_param(p, "obfs-host", obfs.host)
end
table.insert(p, "reuse=" .. (node.reuse and "1" or "0"))
add_param(p, "reuse", node.reuse and "1" or "0")
if #p > 0 then
link = link .. "?" .. table.concat(p, "&")
@@ -548,9 +591,22 @@ local function encode_snell(node)
end
local function encode_node(node)
if (not node.type) or (not node.name) then return nil end
if type(node) ~= "table" or not node.type or not node.name then
return 1, "订阅转换 → 跳过格式不完整的节点。"
end
if not node.server or node.server == "" or not (node.port or (node.type == "hysteria2" and node.ports)) then
return 1, "订阅转换 → 丢弃节点:" .. tostring(node.name) .. ",节点信息不完整。"
end
node = inherit(node)
local t = node.type
if t == "hysteria" or t == "hysteria2" then
local up, down = bandwidth_mbps(node.up), bandwidth_mbps(node.down)
if (node.up ~= nil and up == nil) or (node.down ~= nil and down == nil) then
return 1, "订阅转换 → 丢弃节点:" .. tostring(node.name) .. ",带宽格式不正确。"
end
node.up, node.down = up, down
end
node.server = domain_redir[node.server] or node.server
@@ -570,17 +626,22 @@ end
function parseClashNode(raw, remark)
if not raw then return "" end
domain_redir = {}
local ok, lyaml = pcall(require, "lyaml")
if not ok then return raw end
local data = lyaml.load(raw)
local parsed, data = pcall(lyaml.load, raw)
if not parsed then
api.log('订阅转换 → YAML 解析失败:' .. tostring(remark or ""))
return ""
end
if not data or type(data) ~= "table" then return raw end
if not data.proxies then return "" end
if type(data.proxies) ~= "table" then return "" end
api.log('检测到 Clash 订阅,正在进行转换 ...')
-- Some airports use hosts domain redirect to disguise the real node domain name.
for k, v in pairs(data.hosts or {}) do
for k, v in pairs(type(data.hosts) == "table" and data.hosts or {}) do
if type(k) == "string" and type(v) == "string" then
if api.datatypes.hostname(k) and api.datatypes.hostname(v) then
domain_redir[k] = v
@@ -590,8 +651,11 @@ function parseClashNode(raw, remark)
local links = {}
for _, node in ipairs(data.proxies) do
local err, link = encode_node(node)
if err == 1 and link then
local converted, err, link = pcall(encode_node, node)
if not converted then
local name = type(node) == "table" and node.name or ""
api.log('订阅转换 → 丢弃节点:' .. tostring(name or "") .. ',转换失败,已跳过。')
elseif err == 1 and link then
api.log(link)
elseif link then
table.insert(links, link)
@@ -144,6 +144,14 @@ local log = function(...)
end
end
local function flush_log_buffer()
local buffered = log_buffer
log_buffer = nil
for _, msg in ipairs(buffered or {}) do
log(msg)
end
end
local nodes_table = {}
for k, e in ipairs(api.get_valid_nodes()) do
if e.node_type == "normal" then
@@ -889,6 +897,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
result.method = method
result.ss_method = method
result.password = password
result.uot = params.uot
result.tcp_fast_open = params.tfo
result.use_finalmask = (params.fm and params.fm ~= "") and "1" or nil
result.finalmask = (params.fm and params.fm ~= "") and api.base64Encode(params.fm) or nil
@@ -1043,7 +1052,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
end
local insecure = params.allowinsecure or params.allowInsecure or params.insecure
result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (sub_allowinsecure and "1" or "0")
result.uot = params.udp
result.uot = params.uot or params.udp
else
result.error_msg = "请更换 Xray 或 Sing-Box 来支持 SS 更多的传输方式。"
end
@@ -1424,7 +1433,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
for _, v in pairs(split(query[2], '&')) do
local s = v:find("=", 1, true)
if s and s > 1 then
params[v:sub(1, s - 1)] = v:sub(s + 1)
params[v:sub(1, s - 1)] = UrlDecode(v:sub(s + 1))
end
end
-- [2001:4860:4860::8888]:443
@@ -1451,6 +1460,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
result.hysteria_up_mbps = params.upmbps or sub_hy_up_mbps
result.hysteria_down_mbps = params.downmbps or sub_hy_down_mbps
result.hysteria_hop = params.mport
result.tls_CertByName = params.vcn
elseif szType == 'hysteria2' or szType == 'hy2' then
local alias = ""
@@ -1494,6 +1504,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
result.hysteria2_up_mbps = params.upmbps or (sub_cfg and sub_hy_up_mbps or nil)
result.hysteria2_down_mbps = params.downmbps or (sub_cfg and sub_hy_down_mbps or nil)
result.hysteria2_hop = params.mport
result.hysteria2_hop_interval = params.hopinterval or params["hop-interval"]
if params.ech and params.ech ~= "" then
result.ech = "1"
result.ech_config = params.ech
@@ -1573,6 +1584,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
end
result.tls_serverName = params.sni
result.tls_disable_sni = params.disable_sni
result.tls_CertByName = params.vcn
result.tls_pinSHA256 = params.pcs or params.pinsha256
result.tuic_alpn = params.alpn or "h3"
result.tuic_congestion_control = params.congestion_control or "cubic"
@@ -1998,10 +2010,29 @@ local function select_node(nodes, config, parentConfig)
end
end
-- 账户信息只在实际变化时写回,由调用方统一提交。
local function update_subscribe_info()
local changed = {}
for cfgid, info in pairs(subscribe_info) do
for key, value in pairs(info) do
value = value and value ~= "" and value or nil
if uci_get(cfgid, key) ~= value then
if value then
uci_set(cfgid, key, value)
else
uci_del(cfgid, key)
end
changed[cfgid] = true
end
end
end
return changed
end
local function update_node(manual)
if next(nodeResult) == nil then
log("没有可用的节点信息更新。")
return
return false
end
local group = {}
@@ -2091,17 +2122,6 @@ local function update_node(manual)
end
end
end
-- 更新机场信息
for cfgid, info in pairs(subscribe_info) do
for key, value in pairs(info) do
if value ~= "" then
uci_set(cfgid, key, value)
else
uci_del(cfgid, key)
end
end
end
if next(CONFIG) then
local nodes = {}
uci_foreach("nodes", function(node)
@@ -2123,9 +2143,7 @@ local function update_node(manual)
end
end
uci_save(true)
if manual ~= 1 then restart_required = true end
return true
end
local function parse_link(raw, add_mode, group, sub_cfg)
@@ -2289,26 +2307,17 @@ local execute = function()
if not manual_sub and new_md5 ~= "" and old_md5 == new_md5 then
log_buffer = nil -- 无物理变动,直接丢弃暂存的丢弃与计数明细
log('订阅:【' .. remark .. '】没有变化,无需更新。')
if subscribe_info[cfgid] then
log('订阅:【' .. remark .. '】已同步刷新账户用量与到期时间信息。')
end
else
-- 节点发生变动时,回放前置解析期间的节点统计与丢弃日志
if log_buffer then
local bufs = log_buffer
log_buffer = nil
for _, msg in ipairs(bufs) do
log(msg)
end
end
-- 仅在成功解析出有效节点时才写入配置并记录新 MD5,防止解析失败时误存空数据或伪造无变更状态
flush_log_buffer()
-- 暂存有效节点及其 MD5,完成节点更新后再一起写入配置。
if #parsed_nodes > 0 then
nodeResult[#nodeResult + 1] = {
remark = remark,
list = parsed_nodes,
sub_cfg = value
sub_cfg = value,
md5 = new_md5
}
uci_set(cfgid, "md5", new_md5)
end
end
else
@@ -2319,30 +2328,33 @@ local execute = function()
end
end
end
-- 无论节点物理配置是否变动,始终将最新提取到的账户用量与到期时间静默写回 UCI,确保 Web 面板实时刷新
for c_id, info in pairs(subscribe_info) do
for key, val in pairs(info) do
if val and val ~= "" then
uci_set(c_id, key, val)
else
uci_del(c_id, key)
end
end
end
uci_save(true)
if #fail_list > 0 then
for index, value in ipairs(fail_list) do
log(string.format('【%s】订阅失败,可能是订阅地址无效,或是网络问题,请诊断![%s]', (value.remark or ""), tostring(value.http_code or 0)))
end
end
update_node(0)
local nodes_changed = update_node(0)
local info_changed = update_subscribe_info()
-- 节点、引用关系和 MD5 必须在同一次提交中保存。
for _, result in ipairs(nodeResult) do
uci_set(result.sub_cfg[".name"], "md5", result.md5)
end
if nodes_changed or next(info_changed) then
uci_save(true)
restart_required = nodes_changed
for _, value in ipairs(subscribe_list) do
if info_changed[value[".name"]] then
log(string.format('刷新订阅【%s】账户用量与到期时间信息。', (value.remark or "")))
end
end
end
end
end
if arg[1] == "start" then
log('开始订阅...')
xpcall(execute, function(e)
flush_log_buffer()
log(e)
if type(debug) == "table" and type(debug.traceback) == "function" then
log(debug.traceback())
@@ -2355,7 +2367,7 @@ elseif arg[1] == "add" then
local raw = f:read('*all')
f:close()
parse_link(raw, "1", arg[2])
update_node(1)
if update_node(1) then uci_save(true) end
api.remove("/tmp/links.conf")
elseif arg[1] == "truncate" then
truncate_nodes(arg[2])
@@ -68,6 +68,19 @@ return view.extend({
" and prioritisation on one " +
"network interface.");
// For these options sqm-scripts and sqm-autorate read unset as the
// default: a stored value equal to it was deleted on save (rmempty),
// an unset one written (rmempty off). Keep either as it is.
var keepAsStored = function(o) {
var write = o.write;
o.rmempty = false;
o.write = function(section_id, value) {
if (value == this.default && uci.get('sqm', section_id, this.option) == null)
return;
return write.apply(this, arguments);
};
};
s = m.section(form.TypedSection, 'queue', _('Queues'));
s.tab("tab_basic", _("Basic Settings"));
s.tab("tab_qdisc", _("Queue Discipline"));
@@ -88,7 +101,7 @@ return view.extend({
}, this);
o = s.taboption("tab_basic", form.Flag, "autorate", _("Enable SQM autorate"));
o.rmempty = false;
keepAsStored(o);
o = s.taboption("tab_basic", widgets.DeviceSelect, "interface", _("Interface name"));
o.rmempty = false;
@@ -132,6 +145,7 @@ return view.extend({
o.value("8", "debug");
o.value("10", "trace");
o.default = "5";
keepAsStored(o);
o = s.taboption("tab_qdisc", form.ListValue, "qdisc", _("Queuing disciplines useable on this system. After installing a new qdisc, you need to restart the router to see updates!"),_("Must be set to cake if autorate is used."));
for (var i=0; i < qdiscs.length; i++) {
@@ -156,6 +170,7 @@ return view.extend({
o = s.taboption("tab_qdisc", form.Flag, "qdisc_advanced", _("Show and Use Advanced Configuration. Advanced options will only be used as long as this box is checked."));
o.default = false;
keepAsStored(o);
o = s.taboption("tab_qdisc", form.Flag, "use_mq", _("Enable multi-queue config"), _("Enable multi-queue qdisc on supported hardware. If .qos script does not support mq this option is ignored."));
o.default = false;
@@ -217,6 +232,7 @@ return view.extend({
o.value("ethernet", "Ethernet with overhead: select for e.g. VDSL2.");
o.value("atm", "ATM: select for e.g. ADSL1, ADSL2, ADSL2+.");
o.default = "none";
keepAsStored(o);
o = s.taboption("tab_linklayer", form.Value, "overhead", _("Per Packet Overhead (byte):"));
o.datatype = "and(integer,min(-1500))";
+1 -1
View File
@@ -10,7 +10,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=mptcp
PKG_VERSION:=6.1
PKG_RELEASE:=26
PKG_RELEASE:=27
PKG_MAINTAINER:=Ycarus (Yannick Chabanois) <ycarus@zugaina.org>
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+4 -1
View File
@@ -247,7 +247,10 @@ case $1 in
if [ -f /proc/net/mptcp_net/snmp ]; then
cat /proc/net/mptcp_net/snmp
else
nstat -z | grep -i mptcp
# Absolute counters (-a), without touching nstat's history (-s):
# plain "nstat -z" printed the delta since anyone's previous nstat
# call and reset it, so two viewers zeroed each other's numbers.
nstat -a -s -z | grep -i mptcp
fi
exit 0 ;;
"-v")
+1 -1
View File
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=sqm-autorate
PKG_VERSION:=0.1
PKG_RELEASE:=7
PKG_RELEASE:=8
include $(INCLUDE_DIR)/package.mk
@@ -14,6 +14,8 @@
# defaults launcher.sh uses, and for the same reason).
CONFIG_DIR="${SQM_AUTORATE_DIR:-/usr/share/sqm-autorate}"
RUN_DIR="${SQM_AUTORATE_RUN_DIR:-/var/run/sqm-autorate}"
SQM_RUN="${SQM_RUN_SH:-/usr/lib/sqm/run.sh}"
AUTORATE_INIT="${SQM_AUTORATE_INIT:-/etc/init.d/sqm-autorate}"
# UCI options readable via get_config / settable via set_config -- mirrors
# what luci-app-sqm-autorate's sqm.js form exposes, plus the autorate-only
@@ -112,6 +114,23 @@ _metrics_json() {
"$(_json_str "$base_ul")" "$(_json_str "$min_ul")" "$(_json_str "$max_ul")" "$(_iface_metrics_json "$ul_if")"
}
# Re-apply sqm-scripts on the section's device. "uci commit" from a shell
# fires no procd reload trigger, and run.sh start returns early while the
# device's state file exists, so without a stop+start the qdisc kept its
# old setup: CAKE stayed on after enabled=0, or never came up after
# enabled=1. The previous device is stopped too when the section moved.
_apply_sqm() {
local old_if="$1" new_if="$2"
[ -x "$SQM_RUN" ] || return 1
if [ -n "$old_if" ] && [ "$old_if" != "$new_if" ]; then
"$SQM_RUN" stop "$old_if" >/dev/null 2>&1
fi
[ -n "$new_if" ] || return 0
"$SQM_RUN" stop "$new_if" >/dev/null 2>&1
"$SQM_RUN" start "$new_if" >/dev/null 2>&1
return 0
}
# Ask launcher.sh to kill+respawn only this section's cake-autorate.sh
# instance, leaving every other WAN's warm state untouched (see
@@ -152,8 +171,9 @@ case "$1" in
# get_config/get_metrics: omit "section" to get every sqm queue section.
# set_config: pass any subset of the known sqm option names alongside
# "section" (e.g. download, max_download, delay_thr_ms, ...); only
# recognised options are applied. restart=true bounces the whole
# sqm-autorate service afterwards (regenerates and relaunches every
# recognised options are applied. restart=true re-applies the
# section's SQM qdisc on its device, then bounces the whole
# sqm-autorate service (regenerates and relaunches every
# instance). For a config change scoped to one WAN, prefer applying
# it with restart:false and then calling restart_instance for just
# that section instead -- it leaves every other WAN's cake-autorate
@@ -198,6 +218,7 @@ case "$1" in
exit 1
fi
old_if=$(uci -q get "sqm.${section}.interface" 2>/dev/null)
applied=""
for opt in $VALID_OPTIONS; do
val=$(echo "$input" | jsonfilter -q -e "@.${opt}" 2>/dev/null)
@@ -226,12 +247,17 @@ case "$1" in
uci -q commit sqm
sqm_applied=false
if [ "$restart" = "true" ]; then
/etc/init.d/sqm-autorate restart >/dev/null 2>&1 &
# Synchronous, so the qdisc matches the config when
# the call returns; the autorate instances restart
# after it, on the new qdisc.
_apply_sqm "$old_if" "$(uci -q get "sqm.${section}.interface" 2>/dev/null)" && sqm_applied=true
"$AUTORATE_INIT" restart >/dev/null 2>&1 &
fi
printf '{"result":"ok","section":"%s","applied":[%s],"restarted":%s}' \
"$(_json_str "$section")" "$applied" "$([ "$restart" = "true" ] && echo true || echo false)"
printf '{"result":"ok","section":"%s","applied":[%s],"restarted":%s,"sqm_applied":%s}' \
"$(_json_str "$section")" "$applied" "$([ "$restart" = "true" ] && echo true || echo false)" "$sqm_applied"
;;
get_metrics)