🦄 Sync 2026-09-17 23:34:47

This commit is contained in:
github-actions[bot] committed 2026-09-17 23:34:47 +08:00
1 parent 6ebed6f578
commit 5fc8a48415
29 files changed
+1816 -1317

No files matched your search

+1 -1
View File
@@ -14,7 +14,7 @@ PKG_SOURCE_URL:=https://github.com/Ysurac/dsvpn.git
PKG_SOURCE_VERSION:=aae011339bba733d3fa4ffe8b5dd95dab2efb4ab PKG_SOURCE_VERSION:=aae011339bba733d3fa4ffe8b5dd95dab2efb4ab
PKG_NAME:=dsvpn PKG_NAME:=dsvpn
PKG_VERSION:=0.1.5 PKG_VERSION:=0.1.5
PKG_RELEASE:=2 PKG_RELEASE:=3
PKG_LICENSE:=MIT PKG_LICENSE:=MIT
PKG_LICENSE_FILES:=LICENSE PKG_LICENSE_FILES:=LICENSE
+15 -1
View File
@@ -1,6 +1,16 @@
#!/bin/sh /etc/rc.common #!/bin/sh /etc/rc.common
# Copyright (C) 2019 Ycarus (Yannick Chabanois) <ycarus@zugaina.org> for OpenMPTCProuter project # Copyright (C) 2019 Ycarus (Yannick Chabanois) <ycarus@zugaina.org> for OpenMPTCProuter project
# network.globals.multipath='disable' means the kernel is running with
# net.mptcp.enabled=0. Asking for an MPTCP socket then fails outright with
# ENOPROTOOPT ("Protocol not available") -- the kernel has no implicit
# fallback to plain TCP, every caller has to do it itself, and most don't.
# Only an explicit "disable" counts, so this package keeps its stock
# behaviour wherever network.globals.multipath doesn't exist at all.
_omr_mptcp_globally_disabled() {
[ "$(uci -q get network.globals.multipath)" = "disable" ]
}
START=90 START=90
STOP=10 STOP=10
@@ -75,7 +85,11 @@ start_instance() {
_log "starting ${PROG_NAME} ${1} instance $*" _log "starting ${PROG_NAME} ${1} instance $*"
if version_over_5_4; then # mptcpize LD_PRELOADs a socket() that always asks for IPPROTO_MPTCP and
# never falls back, so with MPTCP globally off dsvpn cannot create its
# socket at all ("Couldn't create connection: Protocol not available")
# and procd just respawns it forever. Run it unwrapped instead.
if version_over_5_4 && ! _omr_mptcp_globally_disabled; then
PROG="mptcpize run ${PROG}" PROG="mptcpize run ${PROG}"
fi fi
+1 -1
View File
@@ -9,7 +9,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=glorytun PKG_NAME:=glorytun
PKG_RELEASE:=4 PKG_RELEASE:=5
PKG_SOURCE_PROTO:=git PKG_SOURCE_PROTO:=git
PKG_SOURCE_VERSION:=95fa5ead2154adb546bcdb454c56c876e9f60bb5 PKG_SOURCE_VERSION:=95fa5ead2154adb546bcdb454c56c876e9f60bb5
PKG_SOURCE:=glorytun-$(PKG_VERSION).tar.gz PKG_SOURCE:=glorytun-$(PKG_VERSION).tar.gz
+14
View File
@@ -3,6 +3,16 @@
# Copyright (C) 2015 ovh.com # Copyright (C) 2015 ovh.com
# Copyright (C) 2017 Ycarus (Yannick Chabanois) <ycarus@zugaina.org> # Copyright (C) 2017 Ycarus (Yannick Chabanois) <ycarus@zugaina.org>
# network.globals.multipath='disable' means the kernel is running with
# net.mptcp.enabled=0. Asking for an MPTCP socket then fails outright with
# ENOPROTOOPT ("Protocol not available") -- the kernel has no implicit
# fallback to plain TCP, every caller has to do it itself, and most don't.
# Only an explicit "disable" counts, so this package keeps its stock
# behaviour wherever network.globals.multipath doesn't exist at all.
_omr_mptcp_globally_disabled() {
[ "$(uci -q get network.globals.multipath)" = "disable" ]
}
START=90 START=90
STOP=10 STOP=10
@@ -81,6 +91,10 @@ start_instance() {
${dev:+dev "$dev"} ${dev:+dev "$dev"}
[ "${mode}" = "listener" ] && procd_append_param command listener [ "${mode}" = "listener" ] && procd_append_param command listener
# Glorytun asks the kernel for an MPTCP socket when told "mptcp"; with
# MPTCP globally off that socket cannot be created, so drop the argument
# and let it run over plain TCP instead of failing to connect at all.
[ "${mptcp}" = "1" ] && _omr_mptcp_globally_disabled && mptcp=0
[ "${mptcp}" = "1" ] && procd_append_param command mptcp [ "${mptcp}" = "1" ] && procd_append_param command mptcp
[ "${chacha20}" = "1" ] && procd_append_param command chacha20 [ "${chacha20}" = "1" ] && procd_append_param command chacha20
[ "${multiqueue}" = "1" ] && procd_append_param command multiqueue [ "${multiqueue}" = "1" ] && procd_append_param command multiqueue
+2 -2
View File
@@ -11,8 +11,8 @@ LUCI_DESCRIPTION:=Live firewall log table (nftables/fw4 and iptables LOG) with f
LUCI_DEPENDS:=+luci-base +logd +jsonfilter LUCI_DEPENDS:=+luci-base +logd +jsonfilter
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
PKG_VERSION:=0.1.42 PKG_VERSION:=0.1.43
PKG_RELEASE:=22 PKG_RELEASE:=23
# Reproducible build: honor SOURCE_DATE_EPOCH from the build environment (set by docker-sdk.sh / CI). # Reproducible build: honor SOURCE_DATE_EPOCH from the build environment (set by docker-sdk.sh / CI).
ifdef SOURCE_DATE_EPOCH ifdef SOURCE_DATE_EPOCH
+1
View File
@@ -19,6 +19,7 @@ LuCI **Firewall Live View** — client-side JS view polling `ubus fwlive poll` (
| `htdocs/luci-static/resources/fwlive/buffer.js` | Ring-buffer apply/merge helpers (pause ingest + resume merge) | | `htdocs/luci-static/resources/fwlive/buffer.js` | Ring-buffer apply/merge helpers (pause ingest + resume merge) |
| `htdocs/luci-static/resources/fwlive/poll-coordinator.js` | Request coalescing, visibility, cadence, and disposal | | `htdocs/luci-static/resources/fwlive/poll-coordinator.js` | Request coalescing, visibility, cadence, and disposal |
| `htdocs/luci-static/resources/fwlive/render-policy.js` | Pure weak-device display-cap and render-cost decisions | | `htdocs/luci-static/resources/fwlive/render-policy.js` | Pure weak-device display-cap and render-cost decisions |
| `htdocs/luci-static/resources/fwlive/render-scheduler.js` | Epoch-safe frame coalescing and render token-bucket state |
| `htdocs/luci-static/resources/fwlive/hostname.js` | Hostname cache LRU + failure TTL helpers | | `htdocs/luci-static/resources/fwlive/hostname.js` | Hostname cache LRU + failure TTL helpers |
| `root/usr/share/luci/menu.d/*.json` | Menu entry (`admin/status/fwlive`) | | `root/usr/share/luci/menu.d/*.json` | Menu entry (`admin/status/fwlive`) |
| `root/usr/share/rpcd/acl.d/*.json` | ubus ACL (read + write for logging enable/disable) | | `root/usr/share/rpcd/acl.d/*.json` | ubus ACL (read + write for logging enable/disable) |
@@ -9,7 +9,7 @@
*/ */
return baseclass.extend({ return baseclass.extend({
/* Keep in sync with openwrt-feed/luci-app-fwlive/Makefile PKG_VERSION. */ /* Keep in sync with openwrt-feed/luci-app-fwlive/Makefile PKG_VERSION. */
APP_VERSION: '0.1.42', APP_VERSION: '0.1.43',
ROW_LIMIT_OPTIONS: [25, 50, 100, 250, 500, 1000, 2000], ROW_LIMIT_OPTIONS: [25, 50, 100, 250, 500, 1000, 2000],
DEFAULT_ROW_LIMIT: 100, DEFAULT_ROW_LIMIT: 100,
FETCH_MODE_OPTIONS: ['auto', 'manual'], FETCH_MODE_OPTIONS: ['auto', 'manual'],
@@ -0,0 +1,131 @@
'use strict';
/* SPDX-License-Identifier: Apache-2.0 */
/* Copyright 2025-2026 Lucas Albers <lucas.b.albers@gmail.com> */
'require baseclass';
/* Own queued render intent and the token bucket; the view owns DOM work.
* Adapters supply the epoch, paint callback, cost policy, clock and frame APIs.
* Without a frame API, schedule() paints synchronously. */
function createScheduler(options) {
const { getEpoch, render, renderCost, capacity, requestFrame, cancelFrame } = options;
if (!Number.isFinite(capacity) || capacity <= 0)
throw new TypeError('capacity must be a positive number');
const now = options.now || Date.now;
let pending = null;
let nextForce = null;
let frameId = null;
let bucket = capacity;
let bucketMs = null;
let floodSuppressed = false;
let lastRenderedRowCount = 0;
let lastRenderedHeadId = '';
let disposed = false;
function schedule(force) {
if (disposed) return;
force = !!force || nextForce !== null;
nextForce = null;
const epoch = getEpoch();
if (typeof requestFrame !== 'function') {
render(!!force);
return;
}
if (pending) {
/* Retain display changes (e.g. resolved names) even when a new epoch
* replaces the frame. The callback always reads current view state. */
pending.force = pending.force || !!force;
pending.epoch = epoch;
return;
}
pending = { epoch: epoch, force: !!force };
frameId = requestFrame(function () {
const request = pending;
pending = null;
frameId = null;
if (disposed || request.epoch !== getEpoch()) return;
/* A current request queued behind stale work gets its own frame. */
if (epoch !== request.epoch) schedule(request.force);
else render(request.force);
});
}
/* A successful decision reserves tokens, but only markRendered commits
* row identity after the view has actually painted. Forced paints bypass
* the bucket without clearing an existing high-rate warning. */
function shouldRender(rows, force, lastBatchNewIdCount) {
if (disposed) return false;
if (force) return true;
const cost = renderCost({
visibleRowCount: rows.length,
visibleHeadId: rows.length ? rows[0].id : '',
lastRenderedRowCount: lastRenderedRowCount,
lastRenderedHeadId: lastRenderedHeadId,
lastBatchNewIdCount: lastBatchNewIdCount
});
if (cost <= 0) {
floodSuppressed = false;
return false;
}
const timestamp = now();
const elapsed = bucketMs === null ? 0 : Math.max(0, timestamp - bucketMs);
bucketMs = timestamp;
bucket = Math.min(capacity, bucket + (elapsed * capacity) / 1000);
floodSuppressed = cost > bucket;
if (floodSuppressed) return false;
bucket -= cost;
return true;
}
function markRendered(rows) {
lastRenderedRowCount = rows.length;
lastRenderedHeadId = rows.length ? rows[0].id : '';
}
function resetBudget() {
bucket = capacity;
bucketMs = now();
floodSuppressed = false;
}
/* Reserve force for the next scheduled render. The caller can cancel its
* own unused reservation, never a queued frame or a newer reservation. */
function forceNextRender() {
const reservation = {};
if (!disposed) nextForce = reservation;
return function () {
if (nextForce === reservation) nextForce = null;
};
}
function dispose() {
if (disposed) return;
disposed = true;
pending = null;
nextForce = null;
if (frameId !== null && typeof cancelFrame === 'function') cancelFrame(frameId);
frameId = null;
}
return {
schedule: schedule,
shouldRender: shouldRender,
markRendered: markRendered,
resetBudget: resetBudget,
forceNextRender: forceNextRender,
isFloodSuppressed: function () {
return floodSuppressed;
},
dispose: dispose
};
}
return baseclass.extend({
create: createScheduler
});
@@ -21,6 +21,7 @@
'require fwlive.proto as proto'; 'require fwlive.proto as proto';
'require fwlive.poll-coordinator as pollCoordinator'; 'require fwlive.poll-coordinator as pollCoordinator';
'require fwlive.render-policy as renderPolicy'; 'require fwlive.render-policy as renderPolicy';
'require fwlive.render-scheduler as renderScheduler';
const callFwlivePoll = rpc.declare({ const callFwlivePoll = rpc.declare({
object: 'fwlive', object: 'fwlive',
@@ -108,6 +109,7 @@ return view.extend({
/* One-shot: first live poll after unpause merges instead of replacing. */ /* One-shot: first live poll after unpause merges instead of replacing. */
resumeMerge: false, resumeMerge: false,
pollCoordinator: null, pollCoordinator: null,
renderScheduler: null,
pagehideHandler: null, pagehideHandler: null,
/* Layer 2 — visibility / RTT cadence / shed surfacing. */ /* Layer 2 — visibility / RTT cadence / shed surfacing. */
rttStreakKind: null, rttStreakKind: null,
@@ -126,14 +128,8 @@ return view.extend({
summaryRowsShown: false, summaryRowsShown: false,
summaryData: null, summaryData: null,
resolveLoadShed: false, resolveLoadShed: false,
renderRaf: 0,
filterInputTimer: null, filterInputTimer: null,
messageLayout: 'wrap', messageLayout: 'wrap',
renderBucket: constants.RENDER_CAP_PER_SEC,
renderBucketMs: 0,
floodSuppressed: false,
pendingForceRender: false,
pendingRenderEpoch: null,
/* Session-new IDs from the last applied batch; this is not buffer growth. */ /* Session-new IDs from the last applied batch; this is not buffer growth. */
lastBatchNewIdCount: 0, lastBatchNewIdCount: 0,
showHostnames: false, showHostnames: false,
@@ -146,8 +142,6 @@ return view.extend({
resolveGeneration: 0, resolveGeneration: 0,
lastPollError: false, lastPollError: false,
lastRulesError: null, lastRulesError: null,
lastRenderedRowCount: 0,
lastRenderedHeadId: '',
followLive: true, followLive: true,
rulesMap: {}, rulesMap: {},
firewallBackend: 'nft', firewallBackend: 'nft',
@@ -1002,7 +996,8 @@ return view.extend({
const cap = this.ingestCap(); const cap = this.ingestCap();
if (this.entries.length >= cap && cap > 0) bits.push(_('buffer full')); if (this.entries.length >= cap && cap > 0) bits.push(_('buffer full'));
if (this.floodSuppressed) bits.push(_('render paused (high rate)')); if (this.ensureRenderScheduler().isFloodSuppressed())
bits.push(_('render paused (high rate)'));
if (this.weakDevice && this.rowLimit > constants.WEAK_DEVICE_DISPLAY_ROW_CAP) if (this.weakDevice && this.rowLimit > constants.WEAK_DEVICE_DISPLAY_ROW_CAP)
bits.push( bits.push(
_('Display limited to %d rows on this device').format( _('Display limited to %d rows on this device').format(
@@ -1072,6 +1067,26 @@ return view.extend({
return this.pollCoordinator; return this.pollCoordinator;
}, },
ensureRenderScheduler() {
if (this.renderScheduler) return this.renderScheduler;
this.renderScheduler = renderScheduler.create({
getEpoch: () => this.currentPollEpoch(),
render: (force) => this.renderRows(force),
renderCost: renderPolicy.renderCost,
now: () => this.nowMs(),
capacity: constants.RENDER_CAP_PER_SEC,
requestFrame:
typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function'
? (cb) => window.requestAnimationFrame(cb)
: null,
cancelFrame:
typeof window !== 'undefined' && typeof window.cancelAnimationFrame === 'function'
? (id) => window.cancelAnimationFrame(id)
: null
});
return this.renderScheduler;
},
currentPollEpoch() { currentPollEpoch() {
return this.ensurePollCoordinator().getState().epoch; return this.ensurePollCoordinator().getState().epoch;
}, },
@@ -1112,6 +1127,7 @@ return view.extend({
disposeView() { disposeView() {
if (this.pollCoordinator) this.pollCoordinator.dispose(); if (this.pollCoordinator) this.pollCoordinator.dispose();
if (this.renderScheduler) this.renderScheduler.dispose();
this.resolveGeneration = (this.resolveGeneration || 0) + 1; this.resolveGeneration = (this.resolveGeneration || 0) + 1;
this.resolveInFlight = false; this.resolveInFlight = false;
if (this.pagehideHandler) { if (this.pagehideHandler) {
@@ -1283,76 +1299,7 @@ return view.extend({
}, },
scheduleRenderRows(force) { scheduleRenderRows(force) {
const doForce = !!force; this.ensureRenderScheduler().schedule(!!force);
const epoch = this.currentPollEpoch();
if (typeof requestAnimationFrame !== 'function') {
this.renderRows(doForce);
return;
}
if (this.renderRaf) {
this.pendingForceRender = this.pendingForceRender || doForce;
this.pendingRenderEpoch = epoch;
return;
}
this.renderRaf = requestAnimationFrame(
function () {
this.renderRaf = 0;
const f = doForce || !!this.pendingForceRender;
const pendingEpoch = this.pendingRenderEpoch;
this.pendingForceRender = false;
this.pendingRenderEpoch = null;
if (epoch !== this.currentPollEpoch()) {
/* A newer epoch may have requested a render while this frame was
* queued. Drop stale paint and requeue only that current request. */
if (pendingEpoch === this.currentPollEpoch()) this.scheduleRenderRows(f);
return;
}
this.renderRows(f);
}.bind(this)
);
},
refillRenderBucket() {
const now = Date.now();
if (!this.renderBucketMs) this.renderBucketMs = now;
const elapsed = now - this.renderBucketMs;
this.renderBucketMs = now;
this.renderBucket = Math.min(
constants.RENDER_CAP_PER_SEC,
this.renderBucket + (elapsed * constants.RENDER_CAP_PER_SEC) / 1000
);
},
consumeRenderBudget(cost) {
if (cost <= 0) {
this.floodSuppressed = false;
return true;
}
this.refillRenderBucket();
if (cost <= this.renderBucket) {
this.renderBucket -= cost;
this.floodSuppressed = false;
return true;
}
this.floodSuppressed = true;
return false;
},
/** Charge by new log events per poll, not full table size (avoids false throttle at high limits). */
renderBudgetCost(rows) {
const count = rows ? rows.length : 0;
const headId = count ? rows[0].id : '';
return renderPolicy.renderCost({
visibleRowCount: count,
visibleHeadId: headId,
lastRenderedRowCount: this.lastRenderedRowCount,
lastRenderedHeadId: this.lastRenderedHeadId,
lastBatchNewIdCount: this.lastBatchNewIdCount
});
}, },
updateFloodBanner() { updateFloodBanner() {
@@ -1360,7 +1307,7 @@ return view.extend({
if (!el) return; if (!el) return;
if (!el.style) el.style = { display: '' }; if (!el.style) el.style = { display: '' };
if (this.floodSuppressed) { if (this.ensureRenderScheduler().isFloodSuppressed()) {
el.style.display = 'block'; el.style.display = 'block';
el.textContent = _( el.textContent = _(
'High event rate — table refresh is throttled to protect the browser. The buffer still updates; refresh will resume automatically.' 'High event rate — table refresh is throttled to protect the browser. The buffer still updates; refresh will resume automatically.'
@@ -1547,9 +1494,8 @@ return view.extend({
this.saveRowLimit(); this.saveRowLimit();
this.updateHash(this.readFilters()); this.updateHash(this.readFilters());
/* Reset flood throttle so Limit changes paint even during ping -A. */ /* Reset flood throttle so Limit changes paint even during ping -A. */
this.renderBucket = constants.RENDER_CAP_PER_SEC; this.ensureRenderScheduler().resetBudget();
this.floodSuppressed = false; const cancelForce = this.ensureRenderScheduler().forceNextRender();
this.pendingForceRender = true;
if (!this.tablePaused) this.renderRows(true); if (!this.tablePaused) this.renderRows(true);
else this.updateStatus(); else this.updateStatus();
const epoch = this.currentPollEpoch(); const epoch = this.currentPollEpoch();
@@ -1560,9 +1506,7 @@ return view.extend({
if (this.tablePaused) this.updateStatus(); if (this.tablePaused) this.updateStatus();
else this.renderRows(true); else this.renderRows(true);
}) })
.finally(() => { .finally(cancelForce);
this.pendingForceRender = false;
});
}, },
limitSelectOptions() { limitSelectOptions() {
@@ -1825,17 +1769,14 @@ return view.extend({
this.updateHash(this.readFilters()); this.updateHash(this.readFilters());
const rows = this.filteredRows(); const rows = this.filteredRows();
const cost = force ? Math.max(1, rows.length) : this.renderBudgetCost(rows); const paint = this.ensureRenderScheduler().shouldRender(
rows,
!!force,
this.lastBatchNewIdCount
);
this.updateLoggingToolbarUi(); this.updateLoggingToolbarUi();
if (!force && cost === 0) { if (!paint) {
this.floodSuppressed = false;
this.updateFloodBanner();
this.updateStatus(rows);
return;
}
if (!force && !this.consumeRenderBudget(cost)) {
this.updateFloodBanner(); this.updateFloodBanner();
this.updateStatus(rows); this.updateStatus(rows);
return; return;
@@ -1875,15 +1816,14 @@ return view.extend({
else scroll.scrollTop = prevScroll; else scroll.scrollTop = prevScroll;
} }
this.lastRenderedRowCount = rows.length; this.ensureRenderScheduler().markRendered(rows);
this.lastRenderedHeadId = rows.length ? rows[0].id : '';
if (rows.length && this.rowTintEnabled() && !this.tintProbeDone) { if (rows.length && this.rowTintEnabled() && !this.tintProbeDone) {
const runProbe = () => { const runProbe = () => {
if (!this.tintProbeDone) this.probeRowTintPaint(); if (!this.tintProbeDone) this.probeRowTintPaint();
}; };
if (typeof requestAnimationFrame === 'function') if (typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function')
requestAnimationFrame(() => requestAnimationFrame(runProbe)); window.requestAnimationFrame(() => window.requestAnimationFrame(runProbe));
else setTimeout(runProbe, 0); else setTimeout(runProbe, 0);
} else if (!this.rowTintEnabled() && this.tintFallbackActive) { } else if (!this.rowTintEnabled() && this.tintFallbackActive) {
this.clearTintFallback(document.querySelector('.fwlive-map')); this.clearTintFallback(document.querySelector('.fwlive-map'));
@@ -1997,7 +1937,7 @@ return view.extend({
* and stays behind the explicit Show rows control. */ * and stays behind the explicit Show rows control. */
if (this.tablePaused) this.updateStatus(); if (this.tablePaused) this.updateStatus();
else if (this.summaryMode) this.renderSummary(); else if (this.summaryMode) this.renderSummary();
else this.scheduleRenderRows(!!this.pendingForceRender); else this.scheduleRenderRows();
try { try {
await this.resolveHostnamesForEntries(this.filteredRows()); await this.resolveHostnamesForEntries(this.filteredRows());
@@ -2,7 +2,7 @@
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
# Copyright 2025-2026 Lucas Albers <lucas.b.albers@gmail.com> # Copyright 2025-2026 Lucas Albers <lucas.b.albers@gmail.com>
# #
# Layer 1 adaptive poll cap (#306). Sourced by rpcd/fwlive. # Layer 1 adaptive poll cap. Sourced by rpcd/fwlive.
# Always on unless test/triage override (no UCI / no product config): # Always on unless test/triage override (no UCI / no product config):
# FWLIVE_ADAPTIVE=0|false|off|no # FWLIVE_ADAPTIVE=0|false|off|no
# or sentinel ${FWLIVE_ADAPTIVE_OFF_FILE:-<state-dir>/fwlive-adaptive-off} # or sentinel ${FWLIVE_ADAPTIVE_OFF_FILE:-<state-dir>/fwlive-adaptive-off}
@@ -18,7 +18,7 @@
# flock, lock busy, or corrupt state. Lock-busy ⇒ unlocked last-writer-wins is # flock, lock busy, or corrupt state. Lock-busy ⇒ unlocked last-writer-wins is
# acceptable (state stays one valid JSON line; ordering is not guaranteed). # acceptable (state stays one valid JSON line; ordering is not guaranteed).
# Failed ubus log.read must NOT call record() — a ~0 ms failure is not "cold" # Failed ubus log.read must NOT call record() — a ~0 ms failure is not "cold"
# health and must not clear an existing hot/shed cap (#329 Hermes Q1). # health and must not clear an existing hot/shed cap.
# Outside the measured duration interval: plan (pre), record/merge (post). # Outside the measured duration interval: plan (pre), record/merge (post).
# messages_received is supplied by the filter-side jsonfilter enumeration; # messages_received is supplied by the filter-side jsonfilter enumeration;
# failed reads still use 0 without scanning the response in ash. # failed reads still use 0 without scanning the response in ash.
@@ -4,7 +4,7 @@
# GENERATED FILE — do not edit. Run: ./scripts/gen-all.sh # GENERATED FILE — do not edit. Run: ./scripts/gen-all.sh
# source: core/fwlive-log.js CLASSIFY_SPEC # source: core/fwlive-log.js CLASSIFY_SPEC
# Shared isFirewallEvent parity logic (shell). Sourced by fwlive-log-filter.sh and tests. # Shared isFirewallEvent parity logic (shell). Sourced by fwlive-log-filter.sh and tests.
# Sourced library: do not add set -euo here (callers own strict mode, #291 C3). # Sourced library: do not add set -euo here; callers own strict mode.
# One awk process classifies a batch (MODE=json/json_reply) or one message (default). # One awk process classifies a batch (MODE=json/json_reply) or one message (default).
# The caller sets FILTER_DIR when this file is sourced. Resolve the asset # The caller sets FILTER_DIR when this file is sourced. Resolve the asset
@@ -6,12 +6,12 @@
# Log messages are treated as data (jsonfilter + awk stdin); never interpolated # Log messages are treated as data (jsonfilter + awk stdin); never interpolated
# into shell command strings. Usage: ubus call log read '...' | fwlive-log-filter.sh # into shell command strings. Usage: ubus call log read '...' | fwlive-log-filter.sh
# #
# Perf (#219): one jsonfilter for @.log[*] plus one awk classify. The same # Performance: one jsonfilter for @.log[*] plus one awk classify. The same
# awk pass counts enumerated entries for messages_received; process count is # awk pass counts enumerated entries for messages_received; process count is
# constant per poll, not O(entries). # constant per poll, not O(entries).
# #
# Entry point (pipeline). The classifier sibling is sourced and must not set # Entry point (pipeline). The classifier sibling is sourced and must not set
# strict mode itself (#291 C3). # strict mode itself.
set -eu set -eu
if ! command -v jsonfilter >/dev/null 2>&1; then if ! command -v jsonfilter >/dev/null 2>&1; then
@@ -25,7 +25,7 @@ FILTER_DIR="$(cd "$(dirname "$0")" && pwd)"
. "$FILTER_DIR/fwlive-is-firewall-event.sh" . "$FILTER_DIR/fwlive-is-firewall-event.sh"
# The classifier is a package asset, not generated at request time. Fail # The classifier is a package asset, not generated at request time. Fail
# closed if an incomplete install or a damaged package removed it (#321). # closed if an incomplete install or a damaged package removed it.
if [ ! -r "$CLASSIFY_AWK" ]; then if [ ! -r "$CLASSIFY_AWK" ]; then
printf '%s' '{"log":[],"error":"classifier_missing"}' printf '%s' '{"log":[],"error":"classifier_missing"}'
exit 1 exit 1
@@ -33,9 +33,9 @@ fi
# Prefer stdin over -s: Linux MAX_ARG_STRLEN is 128KiB; a raised logd ring # Prefer stdin over -s: Linux MAX_ARG_STRLEN is 128KiB; a raised logd ring
# (or paused FETCH_LINES_MAX poll) can exceed that and make jsonfilter fail # (or paused FETCH_LINES_MAX poll) can exceed that and make jsonfilter fail
# while this script still returns a valid empty result (#234). # while this script still returns a valid empty result.
# A valid input with no log entries is not fatal; the classifier closes JSON # A valid input with no log entries is not fatal; the classifier closes JSON
# and reports messages_received:0 (#220). A jsonfilter failure is different: # and reports messages_received:0. A jsonfilter failure is different:
# it must remain an error so rpcd does not record a fast healthy sample. Keep # it must remain an error so rpcd does not record a fast healthy sample. Keep
# jsonfilter output in a secure temporary file before classification so a # jsonfilter output in a secure temporary file before classification so a
# partial pipeline cannot produce malformed JSON on failure. # partial pipeline cannot produce malformed JSON on failure.
+26 -26
View File
@@ -42,7 +42,7 @@ NFT_TIMEOUT=5
# CLI-only iptables fixture path for __rulesmap_iptables (no arbitrary paths). # CLI-only iptables fixture path for __rulesmap_iptables (no arbitrary paths).
RULESMAP_IPTABLES_FILE='/tmp/rulesmap' RULESMAP_IPTABLES_FILE='/tmp/rulesmap'
# json_escape lives in fwlive-logging.sh so prerm can source that file alone (#222). # json_escape lives in fwlive-logging.sh so prerm can source that file alone.
# shellcheck disable=SC1090,SC1091 # LOGGING_SH resolves to sibling ../fwlive-logging.sh # shellcheck disable=SC1090,SC1091 # LOGGING_SH resolves to sibling ../fwlive-logging.sh
. "$LOGGING_SH" . "$LOGGING_SH"
# shellcheck disable=SC1090,SC1091 # shellcheck disable=SC1090,SC1091
@@ -52,10 +52,10 @@ slug_key() {
echo "$1" | tr '[:upper:]' '[:lower:]' | tr ' _' '--' echo "$1" | tr '[:upper:]' '[:lower:]' | tr ' _' '--'
} }
# Cap rules JSON size so a huge ruleset cannot unbound an rpcd reply (#229). # Cap rules JSON size so a huge ruleset cannot unbound an rpcd reply.
RULES_MAP_MAX_KEYS=512 RULES_MAP_MAX_KEYS=512
RULES_MAP_MAX_BYTES=65536 RULES_MAP_MAX_BYTES=65536
# Same wall-clock bound as nft for iptables-save / ip6tables-save (#229). # Same wall-clock bound as nft for iptables-save / ip6tables-save.
# Overridable for host tests (e.g. FWLIVE_IPTABLES_TIMEOUT=1). # Overridable for host tests (e.g. FWLIVE_IPTABLES_TIMEOUT=1).
IPTABLES_TIMEOUT="${FWLIVE_IPTABLES_TIMEOUT:-5}" IPTABLES_TIMEOUT="${FWLIVE_IPTABLES_TIMEOUT:-5}"
@@ -70,7 +70,7 @@ map_add() {
# ("foo" vs "foobar") and value false positives ("see foo"). # ("foo" vs "foobar") and value false positives ("see foo").
# Quoting "$esc_key" makes the match literal (no glob escaping needed). # Quoting "$esc_key" makes the match literal (no glob escaping needed).
# Callers apply UCI, then labeled (!fw4:) prefixes, then unlabeled # Callers apply UCI, then labeled (!fw4:) prefixes, then unlabeled
# cosmetics so first-wins prefers authoritative names (#230). # cosmetics so first-wins prefers authoritative names.
case "$OUT" in case "$OUT" in
*"\"$esc_key\":"*) return 0 ;; *"\"$esc_key\":"*) return 0 ;;
esac esac
@@ -92,7 +92,7 @@ map_add() {
uci_rule_names() { uci_rule_names() {
# uci missing / empty firewall config is "no names", not fatal. # uci missing / empty firewall config is "no names", not fatal.
# pipefail + set -e cannot apply to this pipeline (#291 C3). # pipefail + set -e cannot apply to this pipeline.
uci -q show firewall 2>/dev/null | sed -n "s/^firewall\.@rule\[[0-9]*\]\.name='\(.*\)'$/\1/p" || true uci -q show firewall 2>/dev/null | sed -n "s/^firewall\.@rule\[[0-9]*\]\.name='\(.*\)'$/\1/p" || true
} }
@@ -142,7 +142,7 @@ run_with_timeout() {
if command -v timeout >/dev/null 2>&1; then if command -v timeout >/dev/null 2>&1; then
timeout "$secs" "$@" 2>/dev/null timeout "$secs" "$@" 2>/dev/null
else else
# Fail closed (#229): never run unbounded when timeout is absent. # Fail closed: never run unbounded when timeout is absent.
return 127 return 127
fi fi
} }
@@ -170,7 +170,7 @@ map_uci_rule_names() {
# Line-wise (here-doc, not an unquoted for-loop) so a name with # Line-wise (here-doc, not an unquoted for-loop) so a name with
# spaces stays one token. Names that fail is_uci_style_name are # spaces stays one token. Names that fail is_uci_style_name are
# skipped: parseRuleHint() cannot match them, and fragments of # skipped: parseRuleHint() cannot match them, and fragments of
# "My Rule" must not shadow a real rule named Rule (#226). # "My Rule" must not shadow a real rule named Rule.
# The while-read stays in the current shell (here-doc, not a # The while-read stays in the current shell (here-doc, not a
# pipeline) so map_add mutations reach OUT. # pipeline) so map_add mutations reach OUT.
while IFS= read -r name || [ -n "$name" ]; do while IFS= read -r name || [ -n "$name" ]; do
@@ -187,7 +187,7 @@ EOF
} }
# pass=labeled|unlabeled|all — labeled before unlabeled so !fw4: beats # pass=labeled|unlabeled|all — labeled before unlabeled so !fw4: beats
# an earlier cosmetic for the same prefix under first-wins (#230). # an earlier cosmetic for the same prefix under first-wins.
map_from_nft_stream() { map_from_nft_stream() {
pass="${1:-all}" pass="${1:-all}"
while IFS= read -r line; do while IFS= read -r line; do
@@ -288,7 +288,7 @@ build_rules_map() {
backend=$(detect_firewall_backend) backend=$(detect_firewall_backend)
# UCI first, then labeled stream prefixes, then unlabeled cosmetics # UCI first, then labeled stream prefixes, then unlabeled cosmetics
# (#230). First-wins keeps UCI over nft and !fw4: over cosmetic. # First-wins keeps UCI over nft and !fw4: over cosmetic.
map_uci_rule_names map_uci_rule_names
case "$backend" in case "$backend" in
@@ -435,10 +435,10 @@ fetch_firewall_logs() {
shed_flag=${2:-0} shed_flag=${2:-0}
# Adaptive plan prints: lines shed bucket # Adaptive plan prints: lines shed bucket
start_cs=$(fwlive_adaptive_clock_cs) start_cs=$(fwlive_adaptive_clock_cs)
# Drop dead oneshot flag — only consumed on logd's stream branch (#306). # Drop dead oneshot flag — only consumed on logd's stream branch.
if ! raw=$(ubus call log read "{\"lines\":$lines,\"stream\":false}" 2>/dev/null); then if ! raw=$(ubus call log read "{\"lines\":$lines,\"stream\":false}" 2>/dev/null); then
# Do not record ~0 ms as cold: failure is not healthy fast processing # Do not record ~0 ms as cold: failure is not healthy fast processing
# and must not clear an existing hot/shed cap (#329 Hermes Q1). # and must not clear an existing hot/shed cap.
out='{"log":[],"error":"log_read_failed"}' out='{"log":[],"error":"log_read_failed"}'
fwlive_adaptive_merge_reply "$out" "$shed_flag" "$lines" 0 0 0 fwlive_adaptive_merge_reply "$out" "$shed_flag" "$lines" 0 0 0
return 0 return 0
@@ -446,7 +446,7 @@ fetch_firewall_logs() {
# Summary aggregation is part of the adaptive contract. Keep it out of # Summary aggregation is part of the adaptive contract. Keep it out of
# adaptive=0 replies, while the classifier still remains the single pass # adaptive=0 replies, while the classifier still remains the single pass
# over decoded log entries (#306). # over decoded log entries.
if fwlive_adaptive_enabled; then if fwlive_adaptive_enabled; then
FWLIVE_SUMMARY=1 FWLIVE_SUMMARY=1
else else
@@ -454,7 +454,7 @@ fetch_firewall_logs() {
fi fi
export FWLIVE_SUMMARY export FWLIVE_SUMMARY
# Do not swallow a filter failure into a silent empty table (#220). # Do not swallow a filter failure into a silent empty table.
# If the filter exits non-zero but still printed JSON, keep it. # If the filter exits non-zero but still printed JSON, keep it.
# Empty success is not valid poll JSON — the shipped filter always # Empty success is not valid poll JSON — the shipped filter always
# prints {"log":[...]}, but a stub/regression must not emit a blank body. # prints {"log":[...]}, but a stub/regression must not emit a blank body.
@@ -476,7 +476,7 @@ fetch_firewall_logs() {
*'"error":'*) _filter_ok=0 ;; *'"error":'*) _filter_ok=0 ;;
esac esac
end_cs=$(fwlive_adaptive_clock_cs) end_cs=$(fwlive_adaptive_clock_cs)
# Uptime wrap / skew: end < start — do not record as cold (Grok #329 P2). # Uptime wrap / skew: end < start — do not record as cold.
if [ "$_filter_ok" = 1 ] && [ "$end_cs" -ge "$start_cs" ]; then if [ "$_filter_ok" = 1 ] && [ "$end_cs" -ge "$start_cs" ]; then
_dur=$(((end_cs - start_cs) * 10)) _dur=$(((end_cs - start_cs) * 10))
fwlive_adaptive_record "$_dur" "$lines" fwlive_adaptive_record "$_dur" "$lines"
@@ -524,7 +524,7 @@ resolve_hostname() {
[ -n "$ip" ] || return 1 [ -n "$ip" ] || return 1
is_resolvable_address "$ip" || return 1 is_resolvable_address "$ip" || return 1
command -v nslookup >/dev/null 2>&1 || return 1 command -v nslookup >/dev/null 2>&1 || return 1
# timeout/nslookup miss is "no PTR", not a plugin abort (#291 C3). # timeout/nslookup miss is "no PTR", not a plugin abort.
out=$(run_with_timeout "$RESOLVE_TIMEOUT" nslookup "$ip") || return 1 out=$(run_with_timeout "$RESOLVE_TIMEOUT" nslookup "$ip") || return 1
[ -n "$out" ] || return 1 [ -n "$out" ] || return 1
name=$(parse_nslookup_name "$out") || return 1 name=$(parse_nslookup_name "$out") || return 1
@@ -532,8 +532,8 @@ resolve_hostname() {
printf '%s' "$name" printf '%s' "$name"
} }
# Strict IPv4/IPv6 validation before nslookup (issue #190): the old char-class # Strict IPv4/IPv6 validation before nslookup: the old char-class check
# check admitted hostname-shaped tokens of hex chars + dots/colons # admitted hostname-shaped tokens of hex chars + dots/colons
# (e.g. "dead.beef.cafe.baad"), letting the resolver fall back to upstream DNS # (e.g. "dead.beef.cafe.baad"), letting the resolver fall back to upstream DNS
# queries chosen by an authenticated session. Validate the real address # queries chosen by an authenticated session. Validate the real address
# families here so nslookup only ever sees genuine numeric addresses. # families here so nslookup only ever sees genuine numeric addresses.
@@ -545,8 +545,8 @@ is_resolvable_address() {
# Reject anything outside the address alphabet BEFORE awk, so embedded # Reject anything outside the address alphabet BEFORE awk, so embedded
# newlines/whitespace (which awk would treat as record separators) can # newlines/whitespace (which awk would treat as record separators) can
# never let a valid first record mask trailing garbage that still reaches # never let a valid first record mask trailing garbage that still reaches
# nslookup (#190, CodeRabbit/luna fold): nslookup only ever sees a single # nslookup. It must see a single token. Note the check intentionally allows
# token. Note the check intentionally allows only hex/colon/dot. # only hex/colon/dot.
case "$addr" in case "$addr" in
*[!0-9a-fA-F:.]*) return 1 ;; *[!0-9a-fA-F:.]*) return 1 ;;
esac esac
@@ -618,7 +618,7 @@ is_resolvable_address() {
} }
resolve_addresses() { resolve_addresses() {
# Missing resolver must not look like "no PTR" (#218). Check before # Missing resolver must not look like "no PTR". Check before
# starting the budget clock or reading stdin. # starting the budget clock or reading stdin.
if ! command -v nslookup >/dev/null 2>&1; then if ! command -v nslookup >/dev/null 2>&1; then
command -v logger >/dev/null 2>&1 && logger -t fwlive "nslookup not found; resolve disabled" command -v logger >/dev/null 2>&1 && logger -t fwlive "nslookup not found; resolve disabled"
@@ -626,7 +626,7 @@ resolve_addresses() {
return 0 return 0
fi fi
# Load shed: previous poll was hot (#306 Layer 1). # Load shed: previous poll was hot.
if fwlive_adaptive_is_hot; then if fwlive_adaptive_is_hot; then
printf '{"names":{},"disabled":"load"}' printf '{"names":{},"disabled":"load"}'
return 0 return 0
@@ -679,12 +679,12 @@ resolve_addresses() {
# never START a lookup past the budget. # never START a lookup past the budget.
now=$(date +%s) now=$(date +%s)
[ $((now - start)) -ge "$RESOLVE_BUDGET" ] && break [ $((now - start)) -ge "$RESOLVE_BUDGET" ] && break
# Missing/empty element: set -u cannot read bare $ip (#291 C3). # Missing/empty element: set -u cannot read bare $ip.
if ! is_resolvable_address "${ip:-}"; then if ! is_resolvable_address "${ip:-}"; then
continue continue
fi fi
# NXDOMAIN / timeout: resolve_hostname returns 1. set -e # NXDOMAIN / timeout: resolve_hostname returns 1. set -e
# cannot apply to this assignment (#291 C3). # cannot apply to this assignment.
name=$(resolve_hostname "$ip") || name= name=$(resolve_hostname "$ip") || name=
if [ -n "$name" ]; then if [ -n "$name" ]; then
map_add "$ip" "$name" map_add "$ip" "$name"
@@ -751,7 +751,7 @@ run_selftest() {
return 1 return 1
fi fi
# Issue #190: hostname-shaped tokens of hex chars + dots/colons passed the # Hostname-shaped tokens of hex chars + dots/colons passed the
# old char-class check and made the resolver fall back to upstream DNS. Each case # old char-class check and made the resolver fall back to upstream DNS. Each case
# below must REJECT (and would be ACCEPTED by the reverted char-class code). # below must REJECT (and would be ACCEPTED by the reverted char-class code).
if is_resolvable_address 'ab.cd.ef.01'; then if is_resolvable_address 'ab.cd.ef.01'; then
@@ -871,7 +871,7 @@ Address: 127.0.0.1:53")
run_logging_selftest || return 1 run_logging_selftest || return 1
# Strict-mode smoke (#291 C3): status JSON uses soft-fail helpers # Strict-mode smoke: status JSON uses soft-fail helpers
# (uci miss, empty WAN zone). Must not abort under dash/ash set -e. # (uci miss, empty WAN zone). Must not abort under dash/ash set -e.
_status=$(build_logging_status_json) || { _status=$(build_logging_status_json) || {
echo "build_logging_status_json: aborted under set -e" >&2 echo "build_logging_status_json: aborted under set -e" >&2
@@ -972,7 +972,7 @@ case "${1:-}" in
build_rules_map build_rules_map
;; ;;
poll) poll)
# rpcd may omit argv $3 and pass JSON on stdin (#291 C3). # rpcd may omit argv $3 and pass JSON on stdin.
poll_logs "${3:-}" poll_logs "${3:-}"
;; ;;
resolve) resolve)
+5 -3
View File
@@ -1,9 +1,9 @@
#
msgid "" msgid ""
msgstr "" msgstr ""
"PO-Revision-Date: 2020-08-14 08:04+0000\n" "PO-Revision-Date: 2020-08-14 08:04+0000\n"
"Last-Translator: Andreas Dorfer <adorferen@gmail.com>\n" "Last-Translator: Andreas Dorfer <adorferen@gmail.com>\n"
"Language-Team: German <http://weblate.openmptcprouter.com/projects/omr/" "Language-Team: German <http://weblate.openmptcprouter.com/projects/omr/luciapplicationsiperf/de/>\n"
"luciapplicationsiperf/de/>\n"
"Language: de\n" "Language: de\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
@@ -30,7 +30,7 @@ msgstr "Downstream"
#: luci-app-iperf/root/usr/share/rpcd/acl.d/luci-app-iperf.json:3 #: luci-app-iperf/root/usr/share/rpcd/acl.d/luci-app-iperf.json:3
msgid "Grant UCI access for luci-app-iperf" msgid "Grant UCI access for luci-app-iperf"
msgstr "" msgstr "UCI-Zugriff für luci-app-iperf gewähren"
#: luci-app-iperf/luasrc/view/iperf/test.htm:114 #: luci-app-iperf/luasrc/view/iperf/test.htm:114
msgid "Internet protocol" msgid "Internet protocol"
@@ -60,6 +60,8 @@ msgstr "Server"
#: luci-app-iperf/luasrc/view/iperf/test.htm:165 #: luci-app-iperf/luasrc/view/iperf/test.htm:165
msgid "Server VPS IP is bypassed, so this will test only default route speed." msgid "Server VPS IP is bypassed, so this will test only default route speed."
msgstr "" msgstr ""
"Die IP des VPS-Servers wird umgangen, daher wird nur die Geschwindigkeit der"
" Standardroute getestet."
#: luci-app-iperf/luasrc/view/iperf/test.htm:102 #: luci-app-iperf/luasrc/view/iperf/test.htm:102
msgid "Settings" msgid "Settings"
+5 -3
View File
@@ -1,9 +1,9 @@
#
msgid "" msgid ""
msgstr "" msgstr ""
"PO-Revision-Date: 2020-09-21 12:51+0000\n" "PO-Revision-Date: 2020-09-21 12:51+0000\n"
"Last-Translator: Weblate Admin <contact@openmptcprouter.com>\n" "Last-Translator: Weblate Admin <contact@openmptcprouter.com>\n"
"Language-Team: Italian <http://weblate.openmptcprouter.com/projects/omr/" "Language-Team: Italian <http://weblate.openmptcprouter.com/projects/omr/luciapplicationsiperf/it/>\n"
"luciapplicationsiperf/it/>\n"
"Language: it\n" "Language: it\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
@@ -28,7 +28,7 @@ msgstr "Scarica"
#: luci-app-iperf/root/usr/share/rpcd/acl.d/luci-app-iperf.json:3 #: luci-app-iperf/root/usr/share/rpcd/acl.d/luci-app-iperf.json:3
msgid "Grant UCI access for luci-app-iperf" msgid "Grant UCI access for luci-app-iperf"
msgstr "" msgstr "Concedi l'accesso UCI a luci-app-iperf"
#: luci-app-iperf/luasrc/view/iperf/test.htm:114 #: luci-app-iperf/luasrc/view/iperf/test.htm:114
msgid "Internet protocol" msgid "Internet protocol"
@@ -58,6 +58,8 @@ msgstr "Server"
#: luci-app-iperf/luasrc/view/iperf/test.htm:165 #: luci-app-iperf/luasrc/view/iperf/test.htm:165
msgid "Server VPS IP is bypassed, so this will test only default route speed." msgid "Server VPS IP is bypassed, so this will test only default route speed."
msgstr "" msgstr ""
"L'IP del server VPS è escluso, quindi verrà testata solo la velocità della "
"rotta predefinita."
#: luci-app-iperf/luasrc/view/iperf/test.htm:102 #: luci-app-iperf/luasrc/view/iperf/test.htm:102
msgid "Settings" msgid "Settings"
+5 -3
View File
@@ -1,9 +1,9 @@
#
msgid "" msgid ""
msgstr "" msgstr ""
"PO-Revision-Date: 2020-08-21 20:21+0000\n" "PO-Revision-Date: 2020-08-21 20:21+0000\n"
"Last-Translator: Quentin PAGÈS <githubou@quentino.fr>\n" "Last-Translator: Quentin PAGÈS <githubou@quentino.fr>\n"
"Language-Team: Occitan <http://weblate.openmptcprouter.com/projects/omr/" "Language-Team: Occitan <http://weblate.openmptcprouter.com/projects/omr/luciapplicationsiperf/oc/>\n"
"luciapplicationsiperf/oc/>\n"
"Language: oc\n" "Language: oc\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
@@ -28,7 +28,7 @@ msgstr "Telecargament"
#: luci-app-iperf/root/usr/share/rpcd/acl.d/luci-app-iperf.json:3 #: luci-app-iperf/root/usr/share/rpcd/acl.d/luci-app-iperf.json:3
msgid "Grant UCI access for luci-app-iperf" msgid "Grant UCI access for luci-app-iperf"
msgstr "" msgstr "Acordar l'accès UCI a luci-app-iperf"
#: luci-app-iperf/luasrc/view/iperf/test.htm:114 #: luci-app-iperf/luasrc/view/iperf/test.htm:114
msgid "Internet protocol" msgid "Internet protocol"
@@ -58,6 +58,8 @@ msgstr "Servidor"
#: luci-app-iperf/luasrc/view/iperf/test.htm:165 #: luci-app-iperf/luasrc/view/iperf/test.htm:165
msgid "Server VPS IP is bypassed, so this will test only default route speed." msgid "Server VPS IP is bypassed, so this will test only default route speed."
msgstr "" msgstr ""
"L'IP del servidor VPS es contornejada, doncas aquò testarà solament la "
"velocitat de la rota per defaut."
#: luci-app-iperf/luasrc/view/iperf/test.htm:102 #: luci-app-iperf/luasrc/view/iperf/test.htm:102
msgid "Settings" msgid "Settings"
+4 -4
View File
@@ -1,9 +1,9 @@
#
msgid "" msgid ""
msgstr "" msgstr ""
"PO-Revision-Date: 2020-08-14 08:04+0000\n" "PO-Revision-Date: 2020-08-14 08:04+0000\n"
"Last-Translator: Andreas Dorfer <adorferen@gmail.com>\n" "Last-Translator: Andreas Dorfer <adorferen@gmail.com>\n"
"Language-Team: German <http://weblate.openmptcprouter.com/projects/omr/" "Language-Team: German <http://weblate.openmptcprouter.com/projects/omr/luciapplicationsmail/de/>\n"
"luciapplicationsmail/de/>\n"
"Language: de\n" "Language: de\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
@@ -12,7 +12,7 @@ msgstr ""
#: luci-app-mail/root/usr/share/luci/menu.d/luci-app-mail.json:3 #: luci-app-mail/root/usr/share/luci/menu.d/luci-app-mail.json:3
msgid "E-Mail" msgid "E-Mail"
msgstr "" msgstr "E-Mail"
#: luci-app-mail/luasrc/model/cbi/mail.lua:34 #: luci-app-mail/luasrc/model/cbi/mail.lua:34
msgid "From" msgid "From"
@@ -20,7 +20,7 @@ msgstr "Von"
#: luci-app-mail/root/usr/share/rpcd/acl.d/luci-app-mail.json:3 #: luci-app-mail/root/usr/share/rpcd/acl.d/luci-app-mail.json:3
msgid "Grant UCI access for luci-app-mail" msgid "Grant UCI access for luci-app-mail"
msgstr "" msgstr "UCI-Zugriff für luci-app-mail gewähren"
#: luci-app-mail/luasrc/controller/mail.lua:4 #: luci-app-mail/luasrc/controller/mail.lua:4
#: luci-app-mail/luasrc/model/cbi/mail.lua:4 #: luci-app-mail/luasrc/model/cbi/mail.lua:4
+4 -4
View File
@@ -1,9 +1,9 @@
#
msgid "" msgid ""
msgstr "" msgstr ""
"PO-Revision-Date: 2020-12-01 10:49+0000\n" "PO-Revision-Date: 2020-12-01 10:49+0000\n"
"Last-Translator: Giuseppe Dipierro <giuseppe.dipierro@uania.com>\n" "Last-Translator: Giuseppe Dipierro <giuseppe.dipierro@uania.com>\n"
"Language-Team: Italian <http://weblate.openmptcprouter.com/projects/omr/" "Language-Team: Italian <http://weblate.openmptcprouter.com/projects/omr/luciapplicationsmail/it/>\n"
"luciapplicationsmail/it/>\n"
"Language: it\n" "Language: it\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
@@ -12,7 +12,7 @@ msgstr ""
#: luci-app-mail/root/usr/share/luci/menu.d/luci-app-mail.json:3 #: luci-app-mail/root/usr/share/luci/menu.d/luci-app-mail.json:3
msgid "E-Mail" msgid "E-Mail"
msgstr "" msgstr "E-mail"
#: luci-app-mail/luasrc/model/cbi/mail.lua:34 #: luci-app-mail/luasrc/model/cbi/mail.lua:34
msgid "From" msgid "From"
@@ -20,7 +20,7 @@ msgstr "Da"
#: luci-app-mail/root/usr/share/rpcd/acl.d/luci-app-mail.json:3 #: luci-app-mail/root/usr/share/rpcd/acl.d/luci-app-mail.json:3
msgid "Grant UCI access for luci-app-mail" msgid "Grant UCI access for luci-app-mail"
msgstr "" msgstr "Concedi l'accesso UCI a luci-app-mail"
#: luci-app-mail/luasrc/controller/mail.lua:4 #: luci-app-mail/luasrc/controller/mail.lua:4
#: luci-app-mail/luasrc/model/cbi/mail.lua:4 #: luci-app-mail/luasrc/model/cbi/mail.lua:4
+6 -6
View File
@@ -1,9 +1,9 @@
#
msgid "" msgid ""
msgstr "" msgstr ""
"PO-Revision-Date: 2020-08-21 20:21+0000\n" "PO-Revision-Date: 2020-08-21 20:21+0000\n"
"Last-Translator: Quentin PAGÈS <githubou@quentino.fr>\n" "Last-Translator: Quentin PAGÈS <githubou@quentino.fr>\n"
"Language-Team: Occitan <http://weblate.openmptcprouter.com/projects/omr/" "Language-Team: Occitan <http://weblate.openmptcprouter.com/projects/omr/luciapplicationsmail/oc/>\n"
"luciapplicationsmail/oc/>\n"
"Language: oc\n" "Language: oc\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
@@ -12,7 +12,7 @@ msgstr ""
#: luci-app-mail/root/usr/share/luci/menu.d/luci-app-mail.json:3 #: luci-app-mail/root/usr/share/luci/menu.d/luci-app-mail.json:3
msgid "E-Mail" msgid "E-Mail"
msgstr "" msgstr "Corrièr electronic"
#: luci-app-mail/luasrc/model/cbi/mail.lua:34 #: luci-app-mail/luasrc/model/cbi/mail.lua:34
msgid "From" msgid "From"
@@ -20,7 +20,7 @@ msgstr "De"
#: luci-app-mail/root/usr/share/rpcd/acl.d/luci-app-mail.json:3 #: luci-app-mail/root/usr/share/rpcd/acl.d/luci-app-mail.json:3
msgid "Grant UCI access for luci-app-mail" msgid "Grant UCI access for luci-app-mail"
msgstr "" msgstr "Acordar l'accès UCI a luci-app-mail"
#: luci-app-mail/luasrc/controller/mail.lua:4 #: luci-app-mail/luasrc/controller/mail.lua:4
#: luci-app-mail/luasrc/model/cbi/mail.lua:4 #: luci-app-mail/luasrc/model/cbi/mail.lua:4
@@ -50,8 +50,8 @@ msgstr "Servidor"
#: luci-app-mail/luasrc/model/cbi/mail.lua:4 #: luci-app-mail/luasrc/model/cbi/mail.lua:4
msgid "Set mail settings for services that need to send mails." msgid "Set mail settings for services that need to send mails."
msgstr "" msgstr ""
"Definissètz los paramètres de messatjariá pels servicis que devon enviar d’e-" "Definissètz los paramètres de messatjariá pels servicis que devon enviar "
"mails." "d’e-mails."
#: luci-app-mail/luasrc/model/cbi/mail.lua:21 #: luci-app-mail/luasrc/model/cbi/mail.lua:21
msgid "TLS" msgid "TLS"
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall PKG_NAME:=luci-app-passwall
PKG_VERSION:=26.9.16 PKG_VERSION:=26.9.16
PKG_RELEASE:=277 PKG_RELEASE:=278
PKG_PO_VERSION:=$(PKG_VERSION) PKG_PO_VERSION:=$(PKG_VERSION)
PKG_CONFIG_DEPENDS:= \ PKG_CONFIG_DEPENDS:= \
@@ -1486,6 +1486,7 @@ function gen_config(var)
w = api.trim(w) w = api.trim(w)
if w == "" or w:find("#") == 1 then return end if w == "" or w:find("#") == 1 then return end
if w:find("rule-set:", 1, true) == 1 or w:find("rs:") == 1 then return end if w:find("rule-set:", 1, true) == 1 or w:find("rs:") == 1 then return end
if w:find("ext:", 1, true) == 1 then return end -- Rule currently not support ext:
table.insert(domains, w) table.insert(domains, w)
table.insert(domain_table.domain, w) table.insert(domain_table.domain, w)
end) end)
@@ -1504,6 +1505,7 @@ function gen_config(var)
w = api.trim(w) w = api.trim(w)
if w == "" or w:find("#") == 1 then return end if w == "" or w:find("#") == 1 then return end
if w:find("rule-set:", 1, true) == 1 or w:find("rs:") == 1 then return end if w:find("rule-set:", 1, true) == 1 or w:find("rs:") == 1 then return end
if w:find("ext:", 1, true) == 1 then return end -- Rule currently not support ext:
table.insert(ip, w) table.insert(ip, w)
end) end)
if #ip == 0 then ip = nil end if #ip == 0 then ip = nil end
+1 -1
View File
@@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall2 PKG_NAME:=luci-app-passwall2
PKG_VERSION:=26.9.16 PKG_VERSION:=26.9.16
PKG_RELEASE:=126 PKG_RELEASE:=127
PKG_PO_VERSION:=$(PKG_VERSION) PKG_PO_VERSION:=$(PKG_VERSION)
PKG_CONFIG_DEPENDS:= \ PKG_CONFIG_DEPENDS:= \
@@ -1461,6 +1461,7 @@ function gen_config(var)
string.gsub(e.domain_list, '[^' .. "\r\n" .. ']+', function(w) string.gsub(e.domain_list, '[^' .. "\r\n" .. ']+', function(w)
if w:find("#") == 1 then return end if w:find("#") == 1 then return end
if w:find("rule-set:", 1, true) == 1 or w:find("rs:") == 1 then return end if w:find("rule-set:", 1, true) == 1 or w:find("rs:") == 1 then return end
if w:find("ext:", 1, true) == 1 then return end -- Rule currently not support ext:
table.insert(domains, w) table.insert(domains, w)
table.insert(domain_table.domain, w) table.insert(domain_table.domain, w)
end) end)
@@ -1483,6 +1484,7 @@ function gen_config(var)
string.gsub(e.ip_list, '[^' .. "\r\n" .. ']+', function(w) string.gsub(e.ip_list, '[^' .. "\r\n" .. ']+', function(w)
if w:find("#") == 1 then return end if w:find("#") == 1 then return end
if w:find("rule-set:", 1, true) == 1 or w:find("rs:") == 1 then return end if w:find("rule-set:", 1, true) == 1 or w:find("rs:") == 1 then return end
if w:find("ext:", 1, true) == 1 then return end -- Rule currently not support ext:
table.insert(ip, w) table.insert(ip, w)
end) end)
if #ip == 0 then ip = nil end if #ip == 0 then ip = nil end