mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-30 12:35:13 +08:00
🌴 Sync 2026-09-22 14:48:47
This commit is contained in:
1 parent
bf4289d9a4
commit
fdc72bdc39
17 files changed
+260
-91
No files matched your search
@@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-daede
|
||||
PKG_VERSION:=1.15
|
||||
PKG_RELEASE:=43
|
||||
PKG_RELEASE:=44
|
||||
PKG_MAINTAINER:=kenzok8
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
|
||||
@@ -336,6 +336,15 @@ function renderDaeForms(ctx) {
|
||||
|
||||
s = m.section(form.NamedSection, 'config', 'dae', _('Logging'));
|
||||
s.addremove = false;
|
||||
o = s.option(form.ListValue, 'log_level', _('Log level'),
|
||||
_('Debug and trace show per-connection routing details, but increase CPU usage and log volume. Enable them only while troubleshooting.'));
|
||||
o.value('error', 'ERROR');
|
||||
o.value('warn', 'WARN');
|
||||
o.value('info', 'INFO');
|
||||
o.value('debug', 'DEBUG');
|
||||
o.value('trace', 'TRACE');
|
||||
o.default = 'info';
|
||||
o.rmempty = false;
|
||||
o = s.option(form.Value, 'log_maxsize', _('Max Log Size (MB)'),
|
||||
_('Rotate the log file once it grows past this many megabytes.'));
|
||||
o.datatype = 'uinteger';
|
||||
|
||||
@@ -37,6 +37,7 @@ const CSS = [
|
||||
'.dd-log-pane .dd-line.dd-warn{color:#e8b95a}',
|
||||
'.dd-log-pane .dd-line.dd-error{color:#ea7878;background:rgba(234,120,120,.06)}',
|
||||
'.dd-log-pane .dd-line.dd-debug{color:#7a8290;opacity:.7}',
|
||||
'.dd-log-pane .dd-line.dd-trace{color:#68717d;opacity:.62}',
|
||||
'.dd-log-pane .dd-line.dd-hidden{display:none}',
|
||||
'.dd-log-pane .dd-empty{opacity:.5;font-style:italic}',
|
||||
/* 简化后的字段视觉 */
|
||||
@@ -46,6 +47,7 @@ const CSS = [
|
||||
'.dd-log-pane .dd-lvl-warn{color:#e8b95a;background:rgba(232,185,90,.10)}',
|
||||
'.dd-log-pane .dd-lvl-error{color:#ea7878;background:rgba(234,120,120,.10)}',
|
||||
'.dd-log-pane .dd-lvl-debug{color:#7a8290;background:rgba(122,130,144,.10)}',
|
||||
'.dd-log-pane .dd-lvl-trace{color:#68717d;background:rgba(104,113,125,.10)}',
|
||||
'.dd-log-pane .dd-msg{color:inherit}',
|
||||
'.dd-log-pane .dd-kv{margin-left:6px;color:#6b7480;font-size:11px;opacity:.85}',
|
||||
'body.dark .dd-log-card,html[data-theme="dark"] .dd-log-card,html[data-bs-theme="dark"] .dd-log-card{border-color:rgba(255,255,255,.08);background:rgba(255,255,255,.02)}',
|
||||
@@ -54,20 +56,39 @@ const CSS = [
|
||||
|
||||
/* 拆字段:time="May 25 07:04:59" level=info msg="..." key=val key="val with space" ... */
|
||||
const RE_LINE = /^time="([^"]*)"\s+level=(\w+)\s+msg=(?:"((?:[^"\\]|\\.)*)"|(\S+))\s*(.*)$/;
|
||||
const RE_PREFIXED_LINE = /^\[([^\]]+)\]\s+(DEBUG|INFO|WARN(?:ING)?|ERROR|FATAL|PANIC)\s*(.*)$/i;
|
||||
const RE_PLAIN_LEVEL_LINE = /^\s*(DEBUG|INFO|WARN(?:ING)?|ERROR|FATAL|PANIC)\s+(.*)$/i;
|
||||
const RE_PREFIXED_LINE = /^\[([^\]]+)\]\s+(TRACE|DEBUG|INFO|WARN(?:ING)?|ERROR|FATAL|PANIC)\s*(.*)$/i;
|
||||
const RE_PLAIN_LEVEL_LINE = /^\s*(TRACE|DEBUG|INFO|WARN(?:ING)?|ERROR|FATAL|PANIC)\s+(.*)$/i;
|
||||
|
||||
function detectLevel(line) {
|
||||
// daed/dae logs use lvl=info / [INFO] / level=warning style
|
||||
const m = line.match(/\b(DEBUG|INFO|WARN(?:ING)?|ERROR|FATAL|PANIC)\b/i);
|
||||
const m = line.match(/\b(TRACE|DEBUG|INFO|WARN(?:ING)?|ERROR|FATAL|PANIC)\b/i);
|
||||
if (!m) return '';
|
||||
const lvl = m[1].toUpperCase();
|
||||
if (lvl === 'TRACE') return 'dd-trace';
|
||||
if (lvl === 'DEBUG') return 'dd-debug';
|
||||
if (lvl === 'INFO') return 'dd-info';
|
||||
if (lvl.startsWith('WARN')) return 'dd-warn';
|
||||
return 'dd-error';
|
||||
}
|
||||
|
||||
function matchesFilter(line, filter) {
|
||||
if (!filter)
|
||||
return true;
|
||||
|
||||
if (filter === 'trace' || filter === 'debug' || filter === 'info' || filter === 'warn' || filter === 'error')
|
||||
return detectLevel(line) === 'dd-' + filter;
|
||||
|
||||
if (filter === 'node_status')
|
||||
return /\balive\b/i.test(line)
|
||||
|| /\bgroup\b.*\bselects dialer\b/i.test(line)
|
||||
|| /\bgroup\s+'[^']+'\s+\[[^\]]+\]:/i.test(line);
|
||||
|
||||
if (filter === 'proxy_traffic')
|
||||
return line.indexOf('<->') !== -1;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 格式化时间戳并按北京时间显示:
|
||||
* - daed 默认输出 UTC ISO 8601 (e.g. "2026-05-28T19:07:54Z"),转成北京时间
|
||||
* - 新格式和旧 logrus 短格式无时区信息,保留原始完整日期时间
|
||||
@@ -101,6 +122,7 @@ function lvlShort(level) {
|
||||
|
||||
function lvlClass(level) {
|
||||
const u = (level || '').toUpperCase();
|
||||
if (u === 'TRACE') return 'dd-lvl-trace';
|
||||
if (u === 'DEBUG') return 'dd-lvl-debug';
|
||||
if (u === 'INFO') return 'dd-lvl-info';
|
||||
if (u === 'WARN' || u === 'WARNING') return 'dd-lvl-warn';
|
||||
@@ -209,11 +231,13 @@ return view.extend({
|
||||
|
||||
const selFilter = E('select', { 'class': 'dd-log-btn' }, [
|
||||
E('option', { 'value': '' }, _('All')),
|
||||
E('option', { 'value': 'trace' }, 'TRACE'),
|
||||
E('option', { 'value': 'debug' }, 'DEBUG'),
|
||||
E('option', { 'value': 'info' }, 'INFO'),
|
||||
E('option', { 'value': 'warn' }, 'WARN'),
|
||||
E('option', { 'value': 'error' }, 'ERROR'),
|
||||
E('option', { 'value': 'alive' }, _('Node Status')),
|
||||
E('option', { 'value': 'my_group' }, _('Proxy Traffic'))
|
||||
E('option', { 'value': 'node_status' }, _('Node Status')),
|
||||
E('option', { 'value': 'proxy_traffic' }, _('Proxy Traffic'))
|
||||
]);
|
||||
selFilter.addEventListener('change', function() {
|
||||
state.filter = selFilter.value;
|
||||
@@ -271,7 +295,7 @@ return view.extend({
|
||||
function applyFilter() {
|
||||
const f = state.filter;
|
||||
pane.querySelectorAll('.dd-line').forEach(function(el) {
|
||||
if (!f || el.textContent.toLowerCase().indexOf(f) !== -1)
|
||||
if (matchesFilter(el.textContent, f))
|
||||
el.classList.remove('dd-hidden');
|
||||
else
|
||||
el.classList.add('dd-hidden');
|
||||
@@ -285,7 +309,7 @@ return view.extend({
|
||||
const ln = lines[i];
|
||||
if (!ln) continue;
|
||||
const el = buildLine(ln);
|
||||
if (state.filter && ln.toLowerCase().indexOf(state.filter) === -1)
|
||||
if (!matchesFilter(ln, state.filter))
|
||||
el.classList.add('dd-hidden');
|
||||
frag.appendChild(el);
|
||||
}
|
||||
|
||||
@@ -156,6 +156,13 @@ function renderStatusCard(ctx, listenAddr) {
|
||||
let busy = false;
|
||||
let lastError = '';
|
||||
let refreshGeneration = 0;
|
||||
const probeState = {
|
||||
running: false,
|
||||
text: '',
|
||||
kind: '',
|
||||
visibleUntil: 0,
|
||||
hideTimer: null
|
||||
};
|
||||
const body = E('div', { 'id': 'dd-status-body' }, E('em', {}, _('Collecting data…')));
|
||||
const card = E('div', { 'class': 'dd-card dd-status-card' }, [
|
||||
E('h4', { 'class': 'dd-card-title' }, _('Service Status')),
|
||||
@@ -265,13 +272,28 @@ function renderStatusCard(ctx, listenAddr) {
|
||||
}
|
||||
if (state.running && be.name === 'dae') {
|
||||
const ckBtn = E('button', { 'class': 'cbi-button cbi-button-action' }, _('Test YouTube'));
|
||||
const ckRes = E('span', { 'class': 'dd-meta', style: 'margin-left:8px;display:none' }, '');
|
||||
const probeVisible = probeState.running || probeState.visibleUntil > Date.now();
|
||||
const ckRes = E('span', {
|
||||
'class': 'dd-meta' + (probeState.kind ? ' ' + probeState.kind : ''),
|
||||
style: 'margin-left:8px;' + (probeVisible ? '' : 'display:none')
|
||||
}, probeState.text);
|
||||
ckBtn.disabled = probeState.running;
|
||||
ckBtn.addEventListener('click', function(ev) {
|
||||
ev.preventDefault();
|
||||
if (probeState.running)
|
||||
return;
|
||||
if (probeState.hideTimer) {
|
||||
clearTimeout(probeState.hideTimer);
|
||||
probeState.hideTimer = null;
|
||||
}
|
||||
probeState.running = true;
|
||||
probeState.text = _('Testing…');
|
||||
probeState.kind = '';
|
||||
probeState.visibleUntil = 0;
|
||||
ckBtn.disabled = true;
|
||||
ckRes.style.display = 'inline';
|
||||
ckRes.classList.remove('dd-ok', 'dd-err');
|
||||
ckRes.textContent = _('Testing…');
|
||||
ckRes.textContent = probeState.text;
|
||||
/* actually probe YouTube through dae's transparent proxy */
|
||||
fs.exec('/usr/share/luci-app-daede/proxy-check.sh', []).then(function(r) {
|
||||
const out = (r && r.stdout) || '';
|
||||
@@ -279,20 +301,28 @@ function renderStatusCard(ctx, listenAddr) {
|
||||
const code = (out.match(/code=(\S+)/) || [])[1] || '000';
|
||||
const ms = parseInt((out.match(/ms=(\d+)/) || [])[1] || '0');
|
||||
if (ok) {
|
||||
ckRes.classList.add('dd-ok');
|
||||
ckRes.textContent = _('YouTube reachable · %d ms').format(ms);
|
||||
probeState.kind = 'dd-ok';
|
||||
probeState.text = _('YouTube reachable · %d ms').format(ms);
|
||||
} else {
|
||||
ckRes.classList.add('dd-err');
|
||||
ckRes.textContent = (code === '000')
|
||||
probeState.kind = 'dd-err';
|
||||
probeState.text = (code === '000')
|
||||
? _('YouTube unreachable (timeout)')
|
||||
: _('YouTube unreachable (HTTP %s)').format(code);
|
||||
}
|
||||
}).catch(function() {
|
||||
ckRes.classList.add('dd-err');
|
||||
ckRes.textContent = _('YouTube unreachable (timeout)');
|
||||
probeState.kind = 'dd-err';
|
||||
probeState.text = _('YouTube unreachable (timeout)');
|
||||
}).finally(function() {
|
||||
ckBtn.disabled = false;
|
||||
setTimeout(function() { ckRes.style.display = 'none'; }, 8000);
|
||||
probeState.running = false;
|
||||
probeState.visibleUntil = Date.now() + 8000;
|
||||
refresh(true);
|
||||
probeState.hideTimer = setTimeout(function() {
|
||||
probeState.text = '';
|
||||
probeState.kind = '';
|
||||
probeState.visibleUntil = 0;
|
||||
probeState.hideTimer = null;
|
||||
refresh(true);
|
||||
}, 8000);
|
||||
});
|
||||
});
|
||||
actions.push(ckBtn);
|
||||
@@ -314,7 +344,11 @@ function renderStatusCard(ctx, listenAddr) {
|
||||
|
||||
poll.add(refresh);
|
||||
refresh();
|
||||
card._ddCleanup = function() { poll.remove(refresh); };
|
||||
card._ddCleanup = function() {
|
||||
poll.remove(refresh);
|
||||
if (probeState.hideTimer)
|
||||
clearTimeout(probeState.hideTimer);
|
||||
};
|
||||
return card;
|
||||
}
|
||||
|
||||
|
||||
@@ -891,3 +891,9 @@ msgstr ""
|
||||
|
||||
msgid "TTL returned to LAN DNS clients. 0 keeps dae default behavior."
|
||||
msgstr ""
|
||||
|
||||
msgid "Log level"
|
||||
msgstr ""
|
||||
|
||||
msgid "Debug and trace show per-connection routing details, but increase CPU usage and log volume. Enable them only while troubleshooting."
|
||||
msgstr ""
|
||||
@@ -663,6 +663,12 @@ msgstr "代理流量"
|
||||
msgid "Logging"
|
||||
msgstr "日志"
|
||||
|
||||
msgid "Log level"
|
||||
msgstr "日志级别"
|
||||
|
||||
msgid "Debug and trace show per-connection routing details, but increase CPU usage and log volume. Enable them only while troubleshooting."
|
||||
msgstr "Debug 和 Trace 会显示逐连接的分流详情,但会增加 CPU 占用和日志量,仅建议排障时启用。"
|
||||
|
||||
msgid "Save manual config"
|
||||
msgstr "保存手动配置"
|
||||
|
||||
|
||||
@@ -663,6 +663,12 @@ msgstr "代理流量"
|
||||
msgid "Logging"
|
||||
msgstr "日志"
|
||||
|
||||
msgid "Log level"
|
||||
msgstr "日志级别"
|
||||
|
||||
msgid "Debug and trace show per-connection routing details, but increase CPU usage and log volume. Enable them only while troubleshooting."
|
||||
msgstr "Debug 和 Trace 会显示逐连接的分流详情,但会增加 CPU 占用和日志量,仅建议排障时启用。"
|
||||
|
||||
msgid "Save manual config"
|
||||
msgstr "保存手动配置"
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ LUCI_DEPENDS:=+luci-base +logd +jsonfilter
|
||||
LUCI_PKGARCH:=all
|
||||
|
||||
PKG_VERSION:=0.1.45
|
||||
PKG_RELEASE:=30
|
||||
PKG_RELEASE:=31
|
||||
|
||||
# Reproducible build: honor SOURCE_DATE_EPOCH from the build environment (set by docker-sdk.sh / CI).
|
||||
ifdef SOURCE_DATE_EPOCH
|
||||
|
||||
@@ -39,6 +39,7 @@ return baseclass.extend({
|
||||
'hostapd',
|
||||
'wpad'
|
||||
],
|
||||
nonFirewallPrefixHyphenContinuation: true,
|
||||
firewallHints: ['fw4', 'nft', 'iptables', 'kernel', 'firewall'],
|
||||
actionWords: ['ACCEPT', 'ALLOW', 'PASS', 'DROP', 'REJECT', 'DENY', 'BLOCK'],
|
||||
rules: [
|
||||
@@ -59,7 +60,8 @@ return baseclass.extend({
|
||||
]
|
||||
},
|
||||
|
||||
TCP_FLAG_TAIL: /\b(SYN|ACK|FIN|RST|PSH|URG)(?:\s+(?:SYN|ACK|FIN|RST|PSH|URG))*\s*$/i,
|
||||
TCP_FLAG_TAIL:
|
||||
/\b((?:SYN|ACK|FIN|RST|PSH|URG)(?:\s+(?:SYN|ACK|FIN|RST|PSH|URG))*)(?:\s+[A-Z][A-Z0-9_]*=[^\s]+)*\s*$/i,
|
||||
NETFILTER_KV_GLUE:
|
||||
/([^\s])(?=(IN|OUT|SRC|DST|PROTO|SPT|DPT|LEN|MAC|TYPE|CODE|TTL|TOS|PREC|DF)=)/g,
|
||||
|
||||
@@ -73,10 +75,11 @@ return baseclass.extend({
|
||||
},
|
||||
|
||||
NON_FIREWALL_PREFIX:
|
||||
/^(dnsmasq|procd|ubusd|netifd|odhcpd|logd|dropbear|uhttpd|hostapd|wpad)([^A-Za-z0-9_]|$)/i,
|
||||
/^(dnsmasq|procd|ubusd|netifd|odhcpd|logd|dropbear|uhttpd|hostapd|wpad)([^A-Za-z0-9_-]|$)/i,
|
||||
FIREWALL_HINT: /(^|[^A-Za-z0-9_])(fw4|nft|iptables|kernel|firewall)([^A-Za-z0-9_]|$)/i,
|
||||
ACTION_RE: /(^|[^A-Za-z0-9_])(ACCEPT|ALLOW|PASS|DROP|REJECT|DENY|BLOCK)([^A-Za-z0-9_]|$)/i,
|
||||
DENY_ACTION: /(^|[^A-Za-z0-9_])(DROP|REJECT|DENY|BLOCK)([^A-Za-z0-9_]|$)/i,
|
||||
DENY_ACTION_UNDERSCORE: /(?:^|[^A-Za-z0-9])(?:DROP|REJECT|DENY|BLOCK)(?:[^A-Za-z0-9]|$)/i,
|
||||
MAX_DATE_SECONDS: 8640000000000,
|
||||
|
||||
normalizeNetfilterMessage: function (message) {
|
||||
@@ -201,7 +204,8 @@ return baseclass.extend({
|
||||
|
||||
const msg = this.normalizeNetfilterMessage(message || '');
|
||||
const withoutKv = msg.replace(/\b[A-Z]+=[^\s]*/g, ' ');
|
||||
if (this.DENY_ACTION.test(withoutKv)) return 'UNKNOWN';
|
||||
if (this.DENY_ACTION.test(withoutKv) || this.DENY_ACTION_UNDERSCORE.test(withoutKv))
|
||||
return 'UNKNOWN';
|
||||
|
||||
if (/^kernel:/i.test(msg.trim())) return 'UNKNOWN';
|
||||
|
||||
@@ -218,7 +222,7 @@ return baseclass.extend({
|
||||
const m = message.match(this.TCP_FLAG_TAIL);
|
||||
if (!m) return '';
|
||||
|
||||
return m[0].trim().toUpperCase().replace(/\s+/g, ',');
|
||||
return m[1].trim().toUpperCase().replace(/\s+/g, ',');
|
||||
},
|
||||
|
||||
parseLength: function (kv) {
|
||||
@@ -386,21 +390,18 @@ return baseclass.extend({
|
||||
},
|
||||
|
||||
normalizeEntry: function (entry) {
|
||||
const kv = this.parseKeyValueLog(entry.msg || '');
|
||||
const message = this.normalizeNetfilterMessage(entry.msg || '');
|
||||
const kv = this.parseKeyValueLog(message);
|
||||
const tsUnix = this.timestampUnix(entry);
|
||||
const tsDisplay = this.formatTimestampDisplay(entry);
|
||||
const proto = (kv.PROTO || '').toUpperCase();
|
||||
const actionRaw = this.inferActionRaw(
|
||||
entry.msg || '',
|
||||
kv,
|
||||
this.detectAction(entry.msg || '')
|
||||
);
|
||||
const actionRaw = this.inferActionRaw(message, kv, this.detectAction(message));
|
||||
const action = this.normalizeAction(actionRaw);
|
||||
const addrs = this.extractAddrs(kv);
|
||||
const ifs = this.extractIfaces(kv);
|
||||
const flags = this.parseFlags(entry.msg || '', kv);
|
||||
const flags = this.parseFlags(message, kv);
|
||||
const length = this.parseLength(kv);
|
||||
const ruleHint = this.parseRuleHint(entry.msg || '');
|
||||
const ruleHint = this.parseRuleHint(message);
|
||||
const ruleLabel = this.formatRuleLabel(ruleHint);
|
||||
|
||||
return {
|
||||
|
||||
@@ -1620,11 +1620,15 @@ return view.extend({
|
||||
},
|
||||
|
||||
resolveNamesFromReply(res) {
|
||||
return res && typeof res === 'object' && res.names && typeof res.names === 'object'
|
||||
? res.names
|
||||
: res && typeof res === 'object' && !Array.isArray(res)
|
||||
? res
|
||||
: {};
|
||||
if (!res || typeof res !== 'object' || Array.isArray(res)) return null;
|
||||
if (Object.prototype.hasOwnProperty.call(res, 'error')) return null;
|
||||
if (Object.prototype.hasOwnProperty.call(res, 'names')) {
|
||||
return res.names && typeof res.names === 'object' && !Array.isArray(res.names)
|
||||
? res.names
|
||||
: null;
|
||||
}
|
||||
/* Legacy expect-unwrap replies are the names map itself. */
|
||||
return res;
|
||||
},
|
||||
|
||||
async resolveHostnamesForEntries(entries) {
|
||||
@@ -1666,14 +1670,12 @@ return view.extend({
|
||||
|
||||
this.resolveLoadShed = false;
|
||||
this.resolveShedUntil = 0;
|
||||
/* RPC-level failure (no_resolver, jshn_missing, invalid_input): the
|
||||
* reply carries no per-address signal, so return without failMark —
|
||||
* otherwise every address looks like "no PTR" and retries stall
|
||||
* for the failure TTL. */
|
||||
if (res && typeof res === 'object' && typeof res.error === 'string' && res.error)
|
||||
return;
|
||||
/* Full reply: names map under .names; legacy expect-unwrap was the map. */
|
||||
const names = this.resolveNamesFromReply(res);
|
||||
/* RPC-level failures carry no per-address signal. Do not turn numeric,
|
||||
* null, or malformed replies into negative hostname cache entries. */
|
||||
if (names === null) return;
|
||||
const truncated = res && typeof res === 'object' && res.truncated === true;
|
||||
let updated = false;
|
||||
|
||||
for (let i = 0; i < need.length; i++) {
|
||||
@@ -1682,7 +1684,9 @@ return view.extend({
|
||||
hostname.lruSet(this.hostnameCache, ip, names[ip]);
|
||||
this.hostnameFailed.delete(ip);
|
||||
updated = true;
|
||||
} else {
|
||||
} else if (Object.prototype.hasOwnProperty.call(names, ip) || !truncated) {
|
||||
/* Empty-string names are completed NXDOMAIN/timeout lookups.
|
||||
* Truncated replies omit unprocessed addresses entirely. */
|
||||
hostname.failMark(this.hostnameFailed, ip, now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,7 +454,8 @@ fwlive_adaptive_merge_reply() {
|
||||
_adapt=1
|
||||
fwlive_adaptive_enabled || _adapt=0
|
||||
case "$_body" in
|
||||
*\}) ;;
|
||||
'{}') _sep= ;;
|
||||
*\}) _sep=, ;;
|
||||
*) printf '%s' "$_body"; return 0 ;;
|
||||
esac
|
||||
_base=${_body%\}}
|
||||
@@ -466,26 +467,26 @@ fwlive_adaptive_merge_reply() {
|
||||
esac
|
||||
if [ "$_adapt" = 0 ]; then
|
||||
if [ "$_has_msgs" = 1 ]; then
|
||||
printf '%s,"adaptive":0}' "$_base"
|
||||
printf '%s%s"adaptive":0}' "$_base" "$_sep"
|
||||
else
|
||||
printf '%s,"adaptive":0,"messages_received":%s}' "$_base" "$_msgs"
|
||||
printf '%s%s"adaptive":0,"messages_received":%s}' "$_base" "$_sep" "$_msgs"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
if [ "$_shed" = 1 ]; then
|
||||
if [ "$_has_msgs" = 1 ]; then
|
||||
printf '%s,"adaptive":1%s,"truncated":%s,"shed":{"level":"hot","limit":%s}}' \
|
||||
"$_base" "$_effective" "$_trunc" "$_limit"
|
||||
printf '%s%s"adaptive":1%s,"truncated":%s,"shed":{"level":"hot","limit":%s}}' \
|
||||
"$_base" "$_sep" "$_effective" "$_trunc" "$_limit"
|
||||
else
|
||||
printf '%s,"adaptive":1,"messages_received":%s%s,"truncated":%s,"shed":{"level":"hot","limit":%s}}' \
|
||||
"$_base" "$_msgs" "$_effective" "$_trunc" "$_limit"
|
||||
printf '%s%s"adaptive":1,"messages_received":%s%s,"truncated":%s,"shed":{"level":"hot","limit":%s}}' \
|
||||
"$_base" "$_sep" "$_msgs" "$_effective" "$_trunc" "$_limit"
|
||||
fi
|
||||
else
|
||||
if [ "$_has_msgs" = 1 ]; then
|
||||
printf '%s,"adaptive":1%s,"truncated":%s}' "$_base" "$_effective" "$_trunc"
|
||||
printf '%s%s"adaptive":1%s,"truncated":%s}' "$_base" "$_sep" "$_effective" "$_trunc"
|
||||
else
|
||||
printf '%s,"adaptive":1,"messages_received":%s%s,"truncated":%s}' \
|
||||
"$_base" "$_msgs" "$_effective" "$_trunc"
|
||||
printf '%s%s"adaptive":1,"messages_received":%s%s,"truncated":%s}' \
|
||||
"$_base" "$_sep" "$_msgs" "$_effective" "$_trunc"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
@@ -28,7 +28,7 @@ function has_hint(s, lc) {
|
||||
}
|
||||
function non_fw_prefix(s, lc) {
|
||||
lc = tolower(s)
|
||||
return lc ~ "^(dnsmasq|procd|ubusd|netifd|odhcpd|logd|dropbear|uhttpd|hostapd|wpad)([^a-z0-9_]|$)"
|
||||
return lc ~ "^(dnsmasq|procd|ubusd|netifd|odhcpd|logd|dropbear|uhttpd|hostapd|wpad)([^a-z0-9_-]|$)"
|
||||
}
|
||||
function detect_action(s, words, n, i, w, wl, lc, start, pos, before, afterc, best, bestpos) {
|
||||
n = split("ACCEPT ALLOW PASS DROP REJECT DENY BLOCK", words, " ")
|
||||
|
||||
@@ -445,40 +445,41 @@ restore_wan_log_baseline() {
|
||||
}
|
||||
|
||||
wan_filter_log_enabled() {
|
||||
log_val="$1"
|
||||
[ -n "$log_val" ] || return 1
|
||||
case "$log_val" in
|
||||
*[!0-9]*) return 1 ;;
|
||||
esac
|
||||
log_val=$(wan_filter_log_decimal "$1") || return 1
|
||||
[ $((log_val & 1)) -ne 0 ]
|
||||
}
|
||||
|
||||
wan_filter_log_decimal() {
|
||||
_log_val="$1"
|
||||
case "$_log_val" in
|
||||
''|*[!0-9]*) return 1 ;;
|
||||
esac
|
||||
while [ "${_log_val#0}" != "$_log_val" ]; do
|
||||
_log_val=${_log_val#0}
|
||||
done
|
||||
[ -n "$_log_val" ] || _log_val=0
|
||||
printf '%s' "$_log_val"
|
||||
}
|
||||
|
||||
wan_filter_log_target_value() {
|
||||
current="$1"
|
||||
current=$(wan_filter_log_decimal "$1") || {
|
||||
printf '1'
|
||||
return 0
|
||||
}
|
||||
if wan_filter_log_enabled "$current"; then
|
||||
printf '%s' "$current"
|
||||
return 0
|
||||
fi
|
||||
case "$current" in
|
||||
''|*[!0-9]*)
|
||||
printf '1'
|
||||
;;
|
||||
*)
|
||||
printf '%d' $((current | 1))
|
||||
;;
|
||||
esac
|
||||
printf '%d' $((current | 1))
|
||||
}
|
||||
|
||||
# Clear filter-log bit 0 only. Prints remaining value, or empty when the option
|
||||
# should be deleted (no bits left / non-numeric / already empty).
|
||||
wan_filter_log_clear_value() {
|
||||
current="$1"
|
||||
case "$current" in
|
||||
''|*[!0-9]*)
|
||||
printf ''
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
current=$(wan_filter_log_decimal "$1") || {
|
||||
printf ''
|
||||
return 0
|
||||
}
|
||||
cleared=$((current & ~1))
|
||||
if [ "$cleared" -eq 0 ]; then
|
||||
printf ''
|
||||
@@ -677,11 +678,27 @@ restore_wan_zone_log() {
|
||||
logger -t fwlive "WAN log rollback skipped: firewall changes pending" 2>/dev/null || true
|
||||
return 1
|
||||
fi
|
||||
# Capture the committed value before staging our rollback. If another
|
||||
# writer stages a firewall delta after this point, the post-stage guard
|
||||
# below must be able to undo only our rollback staging without reverting
|
||||
# the other writer's change.
|
||||
committed=$(wan_zone_log_value "$zone")
|
||||
if [ -z "$previous" ]; then
|
||||
uci -q delete "firewall.${zone}.log" 2>/dev/null || true
|
||||
else
|
||||
uci -q set "firewall.${zone}.log=${previous}" 2>/dev/null || true
|
||||
fi
|
||||
_staged=$(uci -q changes firewall 2>/dev/null || true)
|
||||
_foreign=$(wan_log_foreign_staged_lines "$zone" "$_staged")
|
||||
if [ -n "$_foreign" ]; then
|
||||
if [ -z "$committed" ]; then
|
||||
uci delete "firewall.${zone}.log" 2>/dev/null || true
|
||||
else
|
||||
uci set "firewall.${zone}.log=${committed}" 2>/dev/null || true
|
||||
fi
|
||||
logger -t fwlive "WAN log rollback skipped after stage: firewall changes staged by another writer" 2>/dev/null || true
|
||||
return 1
|
||||
fi
|
||||
uci commit firewall 2>/dev/null || true
|
||||
}
|
||||
|
||||
@@ -692,13 +709,14 @@ restore_wan_zone_log() {
|
||||
# TOCTOU hardening: UCI staging is global per config file, so a
|
||||
# non-cooperating writer (another admin's `uci set`, the LuCI firewall page)
|
||||
# can stage a delta AFTER the toggle's early firewall_changes_pending check.
|
||||
# Staging and committing therefore live INSIDE this function — the only path
|
||||
# to `uci commit firewall` in the toggle flow, so callers cannot bypass it —
|
||||
# gated by:
|
||||
# Staging and committing therefore live INSIDE this function, gated by:
|
||||
# 1. a pending re-check BEFORE our own delta exists in staging (foreign-only);
|
||||
# 2. a post-stage re-check AFTER our set/delete: if anything besides our
|
||||
# log option is staged, undo our staging (restore the pre-stage committed
|
||||
# value) and abort without commit — foreign staging stays intact.
|
||||
# The reload-failure rollback has its own pre/post-stage guard for the same
|
||||
# reason; it must not bypass this invariant merely because it is compensating
|
||||
# for a failed reload.
|
||||
# Residual window: a writer that stages between the post-stage check and
|
||||
# `uci commit` can still ride along; post-commit verification detects a
|
||||
# mismatched log bit. Same-option races (another writer also staging
|
||||
@@ -977,6 +995,20 @@ run_logging_selftest() {
|
||||
echo 'wan_filter_log_enabled 2: expected false' >&2
|
||||
return 1
|
||||
fi
|
||||
for leading_value in 011 010 08 0; do
|
||||
if ! wan_filter_log_decimal "$leading_value" >/dev/null; then
|
||||
echo "wan_filter_log_decimal $leading_value: expected decimal digits" >&2
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
if wan_filter_log_enabled '08' || wan_filter_log_enabled '010'; then
|
||||
echo 'leading-zero even values: expected false' >&2
|
||||
return 1
|
||||
fi
|
||||
if ! wan_filter_log_enabled '011'; then
|
||||
echo 'leading-zero odd value: expected true' >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
got=$(wan_filter_log_target_value '')
|
||||
if [ "$got" != '1' ]; then
|
||||
@@ -995,6 +1027,16 @@ run_logging_selftest() {
|
||||
echo "wan_filter_log_target_value 1: expected 1 got $got" >&2
|
||||
return 1
|
||||
fi
|
||||
got=$(wan_filter_log_target_value '08')
|
||||
if [ "$got" != '9' ]; then
|
||||
echo "wan_filter_log_target_value 08: expected 9 got $got" >&2
|
||||
return 1
|
||||
fi
|
||||
got=$(wan_filter_log_target_value '011')
|
||||
if [ "$got" != '11' ]; then
|
||||
echo "wan_filter_log_target_value 011: expected 11 got $got" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Disable clears bit 0 only: log=3 -> 2; log=1 -> delete (empty).
|
||||
got=$(wan_filter_log_clear_value '3')
|
||||
@@ -1015,6 +1057,17 @@ run_logging_selftest() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
got=$(wan_filter_log_clear_value '010')
|
||||
if [ "$got" != '10' ]; then
|
||||
echo "wan_filter_log_clear_value 010: expected 10 got $got" >&2
|
||||
return 1
|
||||
fi
|
||||
got=$(wan_filter_log_clear_value '0')
|
||||
if [ -n "$got" ]; then
|
||||
echo "wan_filter_log_clear_value 0: expected empty got $got" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Enable/disable parity around multi-bit values.
|
||||
got=$(wan_filter_log_target_value '2')
|
||||
if [ "$got" != '3' ]; then
|
||||
|
||||
@@ -564,7 +564,8 @@ resolve_addresses() {
|
||||
start=$(date +%s)
|
||||
# Optional JSON on $1 (rpcd may also pass argv $3 at the call site); else stdin.
|
||||
input=$(read_rpc_input "$1")
|
||||
count=0
|
||||
lookups=0
|
||||
truncated=0
|
||||
OUT=''
|
||||
|
||||
if ! command -v jshn >/dev/null 2>&1; then
|
||||
@@ -597,27 +598,51 @@ resolve_addresses() {
|
||||
return 0
|
||||
}
|
||||
while IFS= read -r ip; do
|
||||
[ "$count" -ge "$RESOLVE_MAX" ] && break
|
||||
if [ "$lookups" -ge "$RESOLVE_MAX" ]; then
|
||||
truncated=1
|
||||
break
|
||||
fi
|
||||
# Abort cleanly once the whole-call wall-clock budget is spent;
|
||||
# never START a lookup past the budget.
|
||||
now=$(date +%s)
|
||||
[ $((now - start)) -ge "$RESOLVE_BUDGET" ] && break
|
||||
if [ $((now - start)) -ge "$RESOLVE_BUDGET" ]; then
|
||||
truncated=1
|
||||
break
|
||||
fi
|
||||
# Missing/empty element: set -u cannot read bare $ip.
|
||||
if ! is_resolvable_address "${ip:-}"; then
|
||||
continue
|
||||
fi
|
||||
lookups=$((lookups + 1))
|
||||
# NXDOMAIN / timeout: resolve_hostname returns 1. set -e
|
||||
# cannot apply to this assignment.
|
||||
name=$(resolve_hostname "$ip") || name=
|
||||
if [ -n "$name" ]; then
|
||||
map_add "$ip" "$name"
|
||||
count=$((count + 1))
|
||||
else
|
||||
# Record a completed miss so the view can fail-mark NXDOMAIN
|
||||
# without treating the unprocessed truncated tail as tried.
|
||||
# map_add skips empty values (rules-map labels); resolve needs them.
|
||||
esc_key=$(printf '%s' "$ip" | json_escape)
|
||||
case "$OUT" in
|
||||
*"\"$esc_key\":"*) ;;
|
||||
*)
|
||||
if [ -n "$OUT" ]; then
|
||||
OUT="${OUT},"
|
||||
fi
|
||||
OUT="${OUT}\"${esc_key}\":\"\""
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
done <<EOF
|
||||
$_addresses
|
||||
EOF
|
||||
|
||||
printf '{"names":{%s}}' "$OUT"
|
||||
if [ "$truncated" -eq 1 ]; then
|
||||
printf '{"names":{%s},"truncated":true}' "$OUT"
|
||||
else
|
||||
printf '{"names":{%s}}' "$OUT"
|
||||
fi
|
||||
}
|
||||
|
||||
check_eq() {
|
||||
|
||||
@@ -186,7 +186,7 @@ return view.extend({
|
||||
{ tab: 'netsetup', type: form.Value, id: 'lan_ipaddr', title: _('LAN IPv4 Address'), datatype: 'ip4addr', placeholder: '10.0.0.1' },
|
||||
{ tab: 'netsetup', type: form.DynamicList, id: 'lan_dns', title: _('Custom DNS Server(s)'), datatype: 'ipaddr', placeholder: '223.5.5.5', desc: _('Leave empty for ISP DNS.') },
|
||||
{ tab: 'netsetup', type: form.Value, id: 'lan_gateway', title: _('Gateway Address'), datatype: 'ip4addr', placeholder: '', depends: { wan_proto: 'siderouter' }, desc: _('Primary router IP address used when operating in side-router mode.') },
|
||||
{ tab: 'netsetup', type: form.Flag, id: 'dhcp', title: _('Enable DHCP Server'), desc: _('If this DHCP is enabled, disable the main router's DHCP. If disabled, manually set client devices' gateway and DNS to this bypass router's IP.') },
|
||||
{ tab: 'netsetup', type: form.Flag, id: 'dhcp', title: _('Enable DHCP Server'), desc: _("If this DHCP is enabled, disable the main router's DHCP. If disabled, manually set client devices' gateway and DNS to this bypass router's IP.") },
|
||||
{ tab: 'netsetup', type: form.Flag, id: 'ipv6', title: _('IPv6 Support'), desc: _('Enable or disable IPv6 router advertisements and DHCPv6.') },
|
||||
|
||||
// 固件与系统设置
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-wwand
|
||||
PKG_VERSION:=1.6.7_p14
|
||||
PKG_RELEASE:=26
|
||||
PKG_VERSION:=1.6.7_p15
|
||||
PKG_RELEASE:=27
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://github.com/ddimension/luci-app-wwand.git
|
||||
PKG_SOURCE_VERSION:=f46ba800d6f195c5c78a8bf566b4f340b9f1ee25
|
||||
PKG_SOURCE_VERSION:=33741b35256ff21c4322e9f9351c9749cd369a97
|
||||
PKG_MIRROR_HASH:=skip
|
||||
|
||||
PKG_LICENSE:=GPL-2.0-only
|
||||
|
||||
+3
-3
@@ -50,12 +50,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=wwand
|
||||
PKG_VERSION:=1.6.7_p63
|
||||
PKG_RELEASE:=52
|
||||
PKG_VERSION:=1.6.7_p64
|
||||
PKG_RELEASE:=53
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://github.com/ddimension/wwand.git
|
||||
PKG_SOURCE_VERSION:=2c428ced46e584daae9719f722d2ed7c9aa28fff
|
||||
PKG_SOURCE_VERSION:=b1b5c5f68c932828a120dd7f94b73e470f11da23
|
||||
PKG_MIRROR_HASH:=skip
|
||||
|
||||
PKG_LICENSE:=GPL-2.0-only
|
||||
|
||||
Reference in new issue
Block a user