mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-01 16:44:23 +08:00
🎨 Sync 2026-09-15 23:44:06
This commit is contained in:
1 parent
1a3656348a
commit
75e213b656
19 files changed
+130
-152
No files matched your search
+2
-2
@@ -6,11 +6,11 @@ include $(TOPDIR)/rules.mk
|
|||||||
|
|
||||||
PKG_NAME:=cloudreve
|
PKG_NAME:=cloudreve
|
||||||
PKG_VERSION:=4.18.0
|
PKG_VERSION:=4.18.0
|
||||||
PKG_RELEASE:=17
|
PKG_RELEASE:=18
|
||||||
|
|
||||||
PKG_SOURCE_PROTO:=git
|
PKG_SOURCE_PROTO:=git
|
||||||
PKG_SOURCE_URL:=https://github.com/cloudreve/Cloudreve.git
|
PKG_SOURCE_URL:=https://github.com/cloudreve/Cloudreve.git
|
||||||
PKG_SOURCE_VERSION:=ade1866be54a3614bc643ab5f513e93a1e0dc82f
|
PKG_SOURCE_VERSION:=e03a104a7d42a1fa46c697f57b66e550189350ea
|
||||||
PKG_MIRROR_HASH:=skip
|
PKG_MIRROR_HASH:=skip
|
||||||
|
|
||||||
PKG_LICENSE:=GPL-3.0-only
|
PKG_LICENSE:=GPL-3.0-only
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ LUCI_DEPENDS:=+luci-base +logd +jsonfilter
|
|||||||
LUCI_PKGARCH:=all
|
LUCI_PKGARCH:=all
|
||||||
|
|
||||||
PKG_VERSION:=0.1.41
|
PKG_VERSION:=0.1.41
|
||||||
PKG_RELEASE:=15
|
PKG_RELEASE:=16
|
||||||
|
|
||||||
# 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
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
* While paused, ingest grows up to fetchLinesMax. Live mode caps at rowLimit.
|
* While paused, ingest grows up to fetchLinesMax. Live mode caps at rowLimit.
|
||||||
* On resume, merge (do not replace) so pause-accumulated rows survive the first
|
* On resume, merge (do not replace) so pause-accumulated rows survive the first
|
||||||
* live poll — see issue #43 pause→resume data loss.
|
* live poll.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function ingestCap(paused, rowLimit, fetchLinesMax) {
|
function ingestCap(paused, rowLimit, fetchLinesMax) {
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ return baseclass.extend({
|
|||||||
APP_VERSION: '0.1.41',
|
APP_VERSION: '0.1.41',
|
||||||
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,
|
||||||
/* Row pass/deny tint (#40): classic green/red default; accessible teal/orange */
|
/* Row pass/deny tint: classic green/red default; accessible teal/orange */
|
||||||
ROW_TINT_OPTIONS: ['off', 'classic', 'accessible'],
|
ROW_TINT_OPTIONS: ['off', 'classic', 'accessible'],
|
||||||
DEFAULT_ROW_TINT: 'classic',
|
DEFAULT_ROW_TINT: 'classic',
|
||||||
FETCH_LINES_MAX: 2000 /* ubus poll / logd ring cap (~2000 lines ≈ typical ring) */,
|
FETCH_LINES_MAX: 2000 /* ubus poll / logd ring cap (~2000 lines ≈ typical ring) */,
|
||||||
/* DOM budget: ~250 new/updated rows painted per second on typical LuCI routers */
|
/* DOM budget: ~250 new/updated rows painted per second on typical LuCI routers */
|
||||||
RENDER_CAP_PER_SEC: 250,
|
RENDER_CAP_PER_SEC: 250,
|
||||||
/* #306 Layer 2 — measured client RTT → poll cadence (seconds). */
|
/* Layer 2 — measured client RTT → poll cadence (seconds). */
|
||||||
POLL_CADENCE_FAST_S: 1,
|
POLL_CADENCE_FAST_S: 1,
|
||||||
POLL_CADENCE_MID_S: 2,
|
POLL_CADENCE_MID_S: 2,
|
||||||
POLL_CADENCE_SLOW_S: 5,
|
POLL_CADENCE_SLOW_S: 5,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const callFwlivePoll = rpc.declare({
|
|||||||
object: 'fwlive',
|
object: 'fwlive',
|
||||||
method: 'poll',
|
method: 'poll',
|
||||||
params: ['addresses']
|
params: ['addresses']
|
||||||
/* Full reply object kept so reply.error reaches fetchEntries (#233). */
|
/* Full reply object kept so reply.error reaches fetchEntries. */
|
||||||
});
|
});
|
||||||
|
|
||||||
const callFwliveRules = rpc.declare({
|
const callFwliveRules = rpc.declare({
|
||||||
@@ -36,7 +36,7 @@ const callFwliveResolve = rpc.declare({
|
|||||||
object: 'fwlive',
|
object: 'fwlive',
|
||||||
method: 'resolve',
|
method: 'resolve',
|
||||||
params: ['addresses']
|
params: ['addresses']
|
||||||
/* Full reply kept so disabled:"load" reaches the view (#306 Layer 2). */
|
/* Full reply kept so disabled:"load" reaches the view. */
|
||||||
});
|
});
|
||||||
|
|
||||||
const callFwliveLoggingStatus = rpc.declare({
|
const callFwliveLoggingStatus = rpc.declare({
|
||||||
@@ -98,11 +98,11 @@ return view.extend({
|
|||||||
sessionSeen: null,
|
sessionSeen: null,
|
||||||
pauseBufferLoading: false,
|
pauseBufferLoading: false,
|
||||||
paused: false,
|
paused: false,
|
||||||
/* One-shot: first live poll after unpause merges instead of replacing (#43). */
|
/* One-shot: first live poll after unpause merges instead of replacing. */
|
||||||
resumeMerge: false,
|
resumeMerge: false,
|
||||||
pollFn: null,
|
pollFn: null,
|
||||||
pollDataInFlight: false,
|
pollDataInFlight: false,
|
||||||
/* #306 Layer 2 — visibility / RTT cadence / shed surfacing. */
|
/* Layer 2 — visibility / RTT cadence / shed surfacing. */
|
||||||
pollEpoch: 0,
|
pollEpoch: 0,
|
||||||
pollCadenceSec: 1,
|
pollCadenceSec: 1,
|
||||||
rttStreakKind: null,
|
rttStreakKind: null,
|
||||||
@@ -473,7 +473,7 @@ return view.extend({
|
|||||||
const res = await callFwliveRules();
|
const res = await callFwliveRules();
|
||||||
this.rulesMap = (res && res.rules) || {};
|
this.rulesMap = (res && res.rules) || {};
|
||||||
this.firewallBackend = (res && res.backend) || 'nft';
|
this.firewallBackend = (res && res.backend) || 'nft';
|
||||||
/* Bounds / mktemp failures are reply.error — same idea as poll (#245). */
|
/* Bounds / mktemp failures are reply.error — same idea as poll. */
|
||||||
this.lastRulesError = (res && res.error) || null;
|
this.lastRulesError = (res && res.error) || null;
|
||||||
if (this.lastRulesError) console.warn('fwlive rules map error:', this.lastRulesError);
|
if (this.lastRulesError) console.warn('fwlive rules map error:', this.lastRulesError);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -829,7 +829,7 @@ return view.extend({
|
|||||||
return typeof document !== 'undefined' && !!document.hidden;
|
return typeof document !== 'undefined' && !!document.hidden;
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Classify one RTT sample into hysteresis buckets (#306).
|
/* Classify one RTT sample into hysteresis buckets.
|
||||||
* error and >1.5s share the slow/degraded streak so mixed failures still trip N=3. */
|
* error and >1.5s share the slow/degraded streak so mixed failures still trip N=3. */
|
||||||
rttKindFromMs(ms, errored) {
|
rttKindFromMs(ms, errored) {
|
||||||
if (errored) return 'slow';
|
if (errored) return 'slow';
|
||||||
@@ -1099,7 +1099,7 @@ return view.extend({
|
|||||||
const limit = this.rowLimit;
|
const limit = this.rowLimit;
|
||||||
const suffix = this.statusSuffix();
|
const suffix = this.statusSuffix();
|
||||||
/* While paused the buffer can grow past the display limit — count matches
|
/* While paused the buffer can grow past the display limit — count matches
|
||||||
* over the full buffer so "matching" is not capped at visibleRows (#83). */
|
* over the full buffer so "matching" is not capped at visibleRows. */
|
||||||
let shown = matchCount;
|
let shown = matchCount;
|
||||||
if (this.paused) {
|
if (this.paused) {
|
||||||
const filters = this.readFilters();
|
const filters = this.readFilters();
|
||||||
@@ -1206,7 +1206,7 @@ return view.extend({
|
|||||||
|
|
||||||
if (wasPaused && !this.paused) {
|
if (wasPaused && !this.paused) {
|
||||||
this.followLive = true;
|
this.followLive = true;
|
||||||
/* Merge pause buffer with the first live poll — do not replace (#43). */
|
/* Merge pause buffer with the first live poll — do not replace. */
|
||||||
this.resumeMerge = true;
|
this.resumeMerge = true;
|
||||||
const epoch = this.pollEpoch;
|
const epoch = this.pollEpoch;
|
||||||
this.fetchEntries()
|
this.fetchEntries()
|
||||||
@@ -1671,7 +1671,7 @@ return view.extend({
|
|||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
/* Only the owning epoch clears — a stale hidden-era poll must not
|
/* Only the owning epoch clears — a stale hidden-era poll must not
|
||||||
* drop the catch-up guard after resume (Grok #331). Resume clears
|
* drop the catch-up guard after resume. Resume clears
|
||||||
* explicitly before starting the catch-up poll. */
|
* explicitly before starting the catch-up poll. */
|
||||||
if (epoch === this.pollEpoch) this.pollDataInFlight = false;
|
if (epoch === this.pollEpoch) this.pollDataInFlight = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,10 +18,10 @@
|
|||||||
# 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 0 in Layer 1 — do not ash-scan the filter JSON after
|
# messages_received is 0 in Layer 1 — do not ash-scan the filter JSON after
|
||||||
# end_cs (Grok #329 P1); accurate count belongs in the filter or a later layer.
|
# end_cs; accurate count belongs in the filter or a later layer.
|
||||||
|
|
||||||
FWLIVE_ADAPTIVE_STATE_FILE="${FWLIVE_ADAPTIVE_STATE_FILE:-/var/run/fwlive-state.json}"
|
FWLIVE_ADAPTIVE_STATE_FILE="${FWLIVE_ADAPTIVE_STATE_FILE:-/var/run/fwlive-state.json}"
|
||||||
# Optional overrides; when unset, lock/off paths are siblings of the current state.
|
# Optional overrides; when unset, lock/off paths are siblings of the current state.
|
||||||
@@ -238,7 +238,7 @@ fwlive_adaptive_with_lock() {
|
|||||||
"$@"
|
"$@"
|
||||||
return $?
|
return $?
|
||||||
fi
|
fi
|
||||||
# Create lock at 0600 (logging.lock #167 — world-readable fd can take LOCK_EX).
|
# Create lock at 0600 (world-readable fd can take LOCK_EX).
|
||||||
if [ ! -e "$_lock" ]; then
|
if [ ! -e "$_lock" ]; then
|
||||||
if ! ( umask 077; : >"$_lock" ) 2>/dev/null; then
|
if ! ( umask 077; : >"$_lock" ) 2>/dev/null; then
|
||||||
"$@"
|
"$@"
|
||||||
@@ -424,7 +424,7 @@ fwlive_adaptive_merge_reply() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Count log[*] via "msg" keys — helper for tests / future filter-side count.
|
# Count log[*] via "msg" keys — helper for tests / future filter-side count.
|
||||||
# NOT called on the poll hot path (Grok #329 P1): ash-scanning a 2000-entry
|
# NOT called on the poll hot path: ash-scanning a 2000-entry
|
||||||
# reply after end_cs is unmeasured overhead that cannot shed itself.
|
# reply after end_cs is unmeasured overhead that cannot shed itself.
|
||||||
fwlive_adaptive_count_log() {
|
fwlive_adaptive_count_log() {
|
||||||
_json=$1
|
_json=$1
|
||||||
|
|||||||
@@ -6,11 +6,11 @@
|
|||||||
# 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. Process
|
# Perf: one jsonfilter for @.log[*] plus one awk classify. Process
|
||||||
# count is constant per poll, not O(entries).
|
# count is 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
|
||||||
# shellcheck disable=SC3040
|
# shellcheck disable=SC3040
|
||||||
(set -o pipefail) 2>/dev/null && set -o pipefail
|
(set -o pipefail) 2>/dev/null && set -o pipefail
|
||||||
@@ -26,7 +26,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
|
||||||
@@ -35,8 +35,8 @@ fi
|
|||||||
printf '%s' '{"log":['
|
printf '%s' '{"log":['
|
||||||
# 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 printed {"log":[]} and exited 0 (#234).
|
# while this script still printed {"log":[]} and exited 0.
|
||||||
# jsonfilter miss / empty @.log is not fatal; must still close JSON (#220).
|
# jsonfilter miss / empty @.log is not fatal; must still close JSON.
|
||||||
# set -e + pipefail cannot apply to this pipeline (#291 C3).
|
# set -e + pipefail cannot apply to this pipeline.
|
||||||
jsonfilter -e '@.log[*]' 2>/dev/null | _fwlive_filter_json_entries || true
|
jsonfilter -e '@.log[*]' 2>/dev/null | _fwlive_filter_json_entries || true
|
||||||
printf '%s' ']}'
|
printf '%s' ']}'
|
||||||
@@ -7,13 +7,13 @@
|
|||||||
# Sourced library (rpcd plugin, package prerm). Do not `set -euo pipefail`
|
# Sourced library (rpcd plugin, package prerm). Do not `set -euo pipefail`
|
||||||
# here: prerm is best-effort (`restore ... || logger`; exit 0) and callers
|
# here: prerm is best-effort (`restore ... || logger`; exit 0) and callers
|
||||||
# expect soft failures. The rpcd entry point enables strict mode; critical
|
# expect soft failures. The rpcd entry point enables strict mode; critical
|
||||||
# paths use explicit `|| return 1` / `|| true` (#244, #291 C3).
|
# paths use explicit `|| return 1` / `|| true`.
|
||||||
|
|
||||||
NF_LOG_IPV4='/proc/sys/net/netfilter/nf_log/2'
|
NF_LOG_IPV4='/proc/sys/net/netfilter/nf_log/2'
|
||||||
NF_LOG_IPV6='/proc/sys/net/netfilter/nf_log/10'
|
NF_LOG_IPV6='/proc/sys/net/netfilter/nf_log/10'
|
||||||
|
|
||||||
# Serialize the WAN logging read->compute->set->commit window across
|
# Serialize the WAN logging read->compute->set->commit window across
|
||||||
# concurrent ubus write-ACL callers (#151): each toggle re-reads the current
|
# concurrent ubus write-ACL callers: each toggle re-reads the current
|
||||||
# firewall.<zone>.log bit, computes a target, then uci set + uci commit. Two
|
# firewall.<zone>.log bit, computes a target, then uci set + uci commit. Two
|
||||||
# concurrent callers could otherwise interleave and last-commit-wins.
|
# concurrent callers could otherwise interleave and last-commit-wins.
|
||||||
#
|
#
|
||||||
@@ -28,7 +28,7 @@ WAN_LOG_LOCK_FILE="${FWLIVE_WAN_LOG_LOCK_FILE:-/etc/fwlive/logging.lock}"
|
|||||||
WAN_LOG_BASELINE_FILE="${FWLIVE_WAN_LOG_BASELINE_FILE:-/etc/fwlive/wan-log-baseline}"
|
WAN_LOG_BASELINE_FILE="${FWLIVE_WAN_LOG_BASELINE_FILE:-/etc/fwlive/wan-log-baseline}"
|
||||||
|
|
||||||
# RFC 8259 string escape. Lives here so prerm can source this file
|
# RFC 8259 string escape. Lives here so prerm can source this file
|
||||||
# standalone (#222). rpcd sources us and must not redefine this.
|
# standalone. rpcd sources us and must not redefine this.
|
||||||
json_escape() {
|
json_escape() {
|
||||||
# Escape for JSON string content per RFC 8259 (including remaining C0 controls).
|
# Escape for JSON string content per RFC 8259 (including remaining C0 controls).
|
||||||
# Slurp stdin as one string. Default awk RS is newline — RS="" is
|
# Slurp stdin as one string. Default awk RS is newline — RS="" is
|
||||||
@@ -67,8 +67,8 @@ json_escape() {
|
|||||||
}'
|
}'
|
||||||
}
|
}
|
||||||
|
|
||||||
# Production lock dir must be owned by euid with no group/other write (#204).
|
# Production lock dir must be owned by euid with no group/other write.
|
||||||
# Avoid GNU/BusyBox `stat -c` — stock OpenWrt omits FEATURE_STAT_FORMAT (#232).
|
# Avoid GNU/BusyBox `stat -c` — stock OpenWrt omits FEATURE_STAT_FORMAT.
|
||||||
wan_log_lock_dir_safe() {
|
wan_log_lock_dir_safe() {
|
||||||
dir="$1"
|
dir="$1"
|
||||||
[ -n "$dir" ] || return 1
|
[ -n "$dir" ] || return 1
|
||||||
@@ -86,10 +86,10 @@ wan_log_lock_dir_safe() {
|
|||||||
# Acquire the exclusive logging lock on fd 9. Blocks until free; fails closed
|
# Acquire the exclusive logging lock on fd 9. Blocks until free; fails closed
|
||||||
# (return 1) only if the lock file cannot be opened or flock is unavailable.
|
# (return 1) only if the lock file cannot be opened or flock is unavailable.
|
||||||
# Create/tighten the lock to 0600 so unprivileged UIDs cannot take LOCK_EX on
|
# Create/tighten the lock to 0600 so unprivileged UIDs cannot take LOCK_EX on
|
||||||
# a world-readable fd (issue #167 / flock(2) allows exclusive locks on O_RDONLY).
|
# a world-readable fd (flock(2) allows exclusive locks on O_RDONLY).
|
||||||
acquire_wan_log_lock() {
|
acquire_wan_log_lock() {
|
||||||
# Fail closed on symlinks: chmod/chown/exec O_TRUNC follow the target as root
|
# Fail closed on symlinks: chmod/chown/exec O_TRUNC follow the target as root
|
||||||
# (#204). Default lock lives under /etc/fwlive (root-only), not world-writable
|
# Default lock lives under /etc/fwlive (root-only), not world-writable
|
||||||
# /var/lock. Re-check after create/tighten (TOCTOU).
|
# /var/lock. Re-check after create/tighten (TOCTOU).
|
||||||
lock_dir="$(dirname "$WAN_LOG_LOCK_FILE")"
|
lock_dir="$(dirname "$WAN_LOG_LOCK_FILE")"
|
||||||
[ -L "$lock_dir" ] && return 1
|
[ -L "$lock_dir" ] && return 1
|
||||||
@@ -107,7 +107,7 @@ acquire_wan_log_lock() {
|
|||||||
# Probe in a subshell first: a failed `exec` redirection aborts a POSIX
|
# Probe in a subshell first: a failed `exec` redirection aborts a POSIX
|
||||||
# non-interactive shell outright, so `|| return 1` on the real exec would
|
# non-interactive shell outright, so `|| return 1` on the real exec would
|
||||||
# never run — and `2>/dev/null` on the same exec would permanently
|
# never run — and `2>/dev/null` on the same exec would permanently
|
||||||
# silence this process's stderr on the success path (#244).
|
# silence this process's stderr on the success path.
|
||||||
( exec 9>>"$WAN_LOG_LOCK_FILE" ) 2>/dev/null || return 1
|
( exec 9>>"$WAN_LOG_LOCK_FILE" ) 2>/dev/null || return 1
|
||||||
exec 9>>"$WAN_LOG_LOCK_FILE"
|
exec 9>>"$WAN_LOG_LOCK_FILE"
|
||||||
flock 9 2>/dev/null || {
|
flock 9 2>/dev/null || {
|
||||||
@@ -124,16 +124,16 @@ release_wan_log_lock() {
|
|||||||
|
|
||||||
find_wan_zone_section() {
|
find_wan_zone_section() {
|
||||||
# Match anonymous (@zone[N]) and named (e.g. wan) sections whose name option
|
# Match anonymous (@zone[N]) and named (e.g. wan) sections whose name option
|
||||||
# is 'wan'. Prefer the first section whose type is zone (issue #168); skip
|
# is 'wan'. Prefer the first section whose type is zone; skip
|
||||||
# non-zone sections that happen to share name='wan'.
|
# non-zone sections that happen to share name='wan'.
|
||||||
# uci missing / no wan zone is empty, not fatal. pipefail + set -e
|
# uci missing / no wan zone is empty, not fatal. pipefail + set -e
|
||||||
# cannot apply to this pipeline (#291 C3).
|
# cannot apply to this pipeline.
|
||||||
_zones=$(uci -q show firewall 2>/dev/null \
|
_zones=$(uci -q show firewall 2>/dev/null \
|
||||||
| sed -n "s/^firewall\.\([^.]*\)\.name='wan'$/\1/p") || true
|
| sed -n "s/^firewall\.\([^.]*\)\.name='wan'$/\1/p") || true
|
||||||
for zone in $_zones; do
|
for zone in $_zones; do
|
||||||
[ -n "$zone" ] || continue
|
[ -n "$zone" ] || continue
|
||||||
# uci -q get exits 1 on a missing section. Capture with || true so
|
# uci -q get exits 1 on a missing section. Capture with || true so
|
||||||
# set -e cannot abort inside "$(…)" before || continue (#291 C3).
|
# set -e cannot abort inside "$(…)" before || continue.
|
||||||
_type=$(uci -q get "firewall.${zone}" 2>/dev/null || true)
|
_type=$(uci -q get "firewall.${zone}" 2>/dev/null || true)
|
||||||
[ "$_type" = "zone" ] || continue
|
[ "$_type" = "zone" ] || continue
|
||||||
printf '%s' "$zone"
|
printf '%s' "$zone"
|
||||||
@@ -143,7 +143,7 @@ find_wan_zone_section() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
firewall_changes_pending() {
|
firewall_changes_pending() {
|
||||||
# uci miss is "no pending changes". set -e cannot apply (#291 C3).
|
# uci miss is "no pending changes". set -e cannot apply.
|
||||||
pending="$(uci -q changes firewall 2>/dev/null || true)"
|
pending="$(uci -q changes firewall 2>/dev/null || true)"
|
||||||
[ -n "$pending" ]
|
[ -n "$pending" ]
|
||||||
}
|
}
|
||||||
@@ -151,7 +151,7 @@ firewall_changes_pending() {
|
|||||||
wan_zone_log_value() {
|
wan_zone_log_value() {
|
||||||
zone="$1"
|
zone="$1"
|
||||||
[ -n "$zone" ] || return 1
|
[ -n "$zone" ] || return 1
|
||||||
# Unset option is a valid empty value; uci -q get exits 1 (#291 C3).
|
# Unset option is a valid empty value; uci -q get exits 1.
|
||||||
uci -q get "firewall.${zone}.log" 2>/dev/null || true
|
uci -q get "firewall.${zone}.log" 2>/dev/null || true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +165,7 @@ uci_canonical_firewall_section() {
|
|||||||
| sed -n '1s/^firewall\.\([^=.]*\)=.*/\1/p'
|
| sed -n '1s/^firewall\.\([^=.]*\)=.*/\1/p'
|
||||||
}
|
}
|
||||||
|
|
||||||
# True when two firewall section ids refer to the same WAN zone (issue #239).
|
# True when two firewall section ids refer to the same WAN zone.
|
||||||
# Compare canonical cfg ids — do NOT treat every name=wan zone as identical
|
# Compare canonical cfg ids — do NOT treat every name=wan zone as identical
|
||||||
# (duplicate wan sections would under-match foreign .log deltas as ours).
|
# (duplicate wan sections would under-match foreign .log deltas as ours).
|
||||||
wan_firewall_zone_same() {
|
wan_firewall_zone_same() {
|
||||||
@@ -196,7 +196,7 @@ wan_log_staged_line_section() {
|
|||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
# Zone ids that may appear in `uci changes` for firewall.<id>.log (issue #239).
|
# Zone ids that may appear in `uci changes` for firewall.<id>.log.
|
||||||
wan_log_staged_zone_ids() {
|
wan_log_staged_zone_ids() {
|
||||||
_zone="$1"
|
_zone="$1"
|
||||||
_staged="$2"
|
_staged="$2"
|
||||||
@@ -296,7 +296,7 @@ maybe_snapshot_wan_log_baseline() {
|
|||||||
restore_wan_log_baseline() {
|
restore_wan_log_baseline() {
|
||||||
path="$(wan_log_baseline_path)"
|
path="$(wan_log_baseline_path)"
|
||||||
[ -f "$path" ] || return 0
|
[ -f "$path" ] || return 0
|
||||||
# Empty file is a valid "option was unset" baseline (#291 C3).
|
# Empty file is a valid "option was unset" baseline.
|
||||||
baseline=$(cat "$path" 2>/dev/null || true)
|
baseline=$(cat "$path" 2>/dev/null || true)
|
||||||
zone=$(find_wan_zone_section)
|
zone=$(find_wan_zone_section)
|
||||||
if [ -z "$zone" ]; then
|
if [ -z "$zone" ]; then
|
||||||
@@ -417,18 +417,18 @@ collect_logging_blockers() {
|
|||||||
check_nf_log_ipv6 || logging_blockers_append 'nf_log_ipv6_missing'
|
check_nf_log_ipv6 || logging_blockers_append 'nf_log_ipv6_missing'
|
||||||
|
|
||||||
# Report via LOGGING_BLOCKERS, not exit status: return 1 would abort
|
# Report via LOGGING_BLOCKERS, not exit status: return 1 would abort
|
||||||
# build_logging_status_json under set -e (#291 C3).
|
# build_logging_status_json under set -e.
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
collect_logging_warnings() {
|
collect_logging_warnings() {
|
||||||
LOGGING_WARNINGS=''
|
LOGGING_WARNINGS=''
|
||||||
|
|
||||||
# rpcd/fwlive run_with_timeout fail-closes to 127 without timeout (#229).
|
# rpcd/fwlive run_with_timeout fail-closes to 127 without timeout.
|
||||||
# Warnings are diagnostics only — do not gate the enable-logging CTA.
|
# Warnings are diagnostics only — do not gate the enable-logging CTA.
|
||||||
command -v timeout >/dev/null 2>&1 || logging_warnings_append 'timeout_missing'
|
command -v timeout >/dev/null 2>&1 || logging_warnings_append 'timeout_missing'
|
||||||
|
|
||||||
# Report via LOGGING_WARNINGS, not exit status (#291 C3).
|
# Report via LOGGING_WARNINGS, not exit status.
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,9 +444,9 @@ json_null_or_string() {
|
|||||||
|
|
||||||
build_logging_status_json() {
|
build_logging_status_json() {
|
||||||
zone=$(find_wan_zone_section)
|
zone=$(find_wan_zone_section)
|
||||||
# Empty zone / unset log bit are valid; set -e cannot apply (#291 C3).
|
# Empty zone / unset log bit are valid; set -e cannot apply.
|
||||||
log_val=$(wan_zone_log_value "$zone") || log_val=
|
log_val=$(wan_zone_log_value "$zone") || log_val=
|
||||||
# Unset log_limit is a valid empty value; uci -q get exits 1 (#291 C3).
|
# Unset log_limit is a valid empty value; uci -q get exits 1.
|
||||||
limit_val=
|
limit_val=
|
||||||
if [ -n "$zone" ]; then
|
if [ -n "$zone" ]; then
|
||||||
limit_val=$(uci -q get "firewall.${zone}.log_limit" 2>/dev/null || true)
|
limit_val=$(uci -q get "firewall.${zone}.log_limit" 2>/dev/null || true)
|
||||||
@@ -491,7 +491,7 @@ restore_wan_zone_log() {
|
|||||||
zone="$1"
|
zone="$1"
|
||||||
previous="$2"
|
previous="$2"
|
||||||
[ -n "$zone" ] || return 1
|
[ -n "$zone" ] || return 1
|
||||||
# Refuse to publish unrelated staged firewall deltas (issue #168).
|
# Refuse to publish unrelated staged firewall deltas.
|
||||||
if firewall_changes_pending; then
|
if firewall_changes_pending; then
|
||||||
logger -t fwlive "WAN log rollback skipped: firewall changes pending" 2>/dev/null || true
|
logger -t fwlive "WAN log rollback skipped: firewall changes pending" 2>/dev/null || true
|
||||||
return 1
|
return 1
|
||||||
@@ -508,7 +508,7 @@ restore_wan_zone_log() {
|
|||||||
# closes the read->compute->set->commit window so a concurrent toggle cannot
|
# closes the read->compute->set->commit window so a concurrent toggle cannot
|
||||||
# commit between our read and our write (no lost update / no stale overwrite).
|
# commit between our read and our write (no lost update / no stale overwrite).
|
||||||
#
|
#
|
||||||
# TOCTOU hardening (#191): UCI staging is global per config file, so a
|
# TOCTOU hardening: UCI staging is global per config file, so a
|
||||||
# non-cooperating writer (another admin's `uci set`, the LuCI firewall page)
|
# 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.
|
# can stage a delta AFTER the toggle's early firewall_changes_pending check.
|
||||||
# Staging and committing therefore live INSIDE this function — the only path
|
# Staging and committing therefore live INSIDE this function — the only path
|
||||||
@@ -544,7 +544,7 @@ commit_wan_log_change() {
|
|||||||
zone_json="$2"
|
zone_json="$2"
|
||||||
target="$3"
|
target="$3"
|
||||||
|
|
||||||
# Last-moment guard (#191): runs before OUR delta is staged, so a
|
# Last-moment guard: runs before OUR delta is staged, so a
|
||||||
# non-empty changes list here can only be a foreign writer's race.
|
# non-empty changes list here can only be a foreign writer's race.
|
||||||
if firewall_changes_pending; then
|
if firewall_changes_pending; then
|
||||||
logger -t fwlive "WAN log toggle aborted at commit gate: firewall changes staged by another writer" 2>/dev/null || true
|
logger -t fwlive "WAN log toggle aborted at commit gate: firewall changes staged by another writer" 2>/dev/null || true
|
||||||
@@ -568,7 +568,7 @@ commit_wan_log_change() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Post-stage guard (#191): anything besides our log option is foreign.
|
# Post-stage guard: anything besides our log option is foreign.
|
||||||
# Undo our staging only (set previous / delete to match committed); leave
|
# Undo our staging only (set previous / delete to match committed); leave
|
||||||
# foreign deltas untouched and abort without commit.
|
# foreign deltas untouched and abort without commit.
|
||||||
_staged=$(uci -q changes firewall 2>/dev/null || true)
|
_staged=$(uci -q changes firewall 2>/dev/null || true)
|
||||||
@@ -589,8 +589,8 @@ commit_wan_log_change() {
|
|||||||
# firewall_changes_pending from this package's own orphaned write —
|
# firewall_changes_pending from this package's own orphaned write —
|
||||||
# but ONLY when nothing foreign is staged: `uci revert firewall` is
|
# but ONLY when nothing foreign is staged: `uci revert firewall` is
|
||||||
# config-wide (uci has no option-level revert), and reverting would
|
# config-wide (uci has no option-level revert), and reverting would
|
||||||
# clobber a concurrent writer's uncommitted delta (CodeRabbit/luna
|
# clobber a concurrent writer's uncommitted delta. With foreign
|
||||||
# fold, #191). With foreign staging present, leave it and warn.
|
# staging present, leave it and warn.
|
||||||
_staged=$(uci -q changes firewall 2>/dev/null || true)
|
_staged=$(uci -q changes firewall 2>/dev/null || true)
|
||||||
_total=$(printf '%s\n' "$_staged" | grep -c . 2>/dev/null || true)
|
_total=$(printf '%s\n' "$_staged" | grep -c . 2>/dev/null || true)
|
||||||
_ours=$(wan_log_count_our_staged_lines "$zone" "$_staged")
|
_ours=$(wan_log_count_our_staged_lines "$zone" "$_staged")
|
||||||
@@ -603,7 +603,7 @@ commit_wan_log_change() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Post-commit verification (#191): confirm the committed config really
|
# Post-commit verification: confirm the committed config really
|
||||||
# carries what we wrote (empty target = option must now be gone/empty).
|
# carries what we wrote (empty target = option must now be gone/empty).
|
||||||
# A mismatch means another writer overtook the commit: warn loudly but do
|
# A mismatch means another writer overtook the commit: warn loudly but do
|
||||||
# NOT blind-revert (that would destroy unrelated committed data); the
|
# NOT blind-revert (that would destroy unrelated committed data); the
|
||||||
@@ -619,7 +619,7 @@ commit_wan_log_change() {
|
|||||||
# would block a concurrent toggle until the holder exits — BusyBox flock has
|
# would block a concurrent toggle until the holder exits — BusyBox flock has
|
||||||
# no -w timeout).
|
# no -w timeout).
|
||||||
#
|
#
|
||||||
# The ROLLBACK re-acquires the lock (luna fold 2026-08-10): read->compare->
|
# The ROLLBACK re-acquires the lock: read->compare->
|
||||||
# restore is only atomic when no other writer can commit between the read and
|
# restore is only atomic when no other writer can commit between the read and
|
||||||
# the restore. All writers hold the same flock, so re-acquiring it makes the
|
# the restore. All writers hold the same flock, so re-acquiring it makes the
|
||||||
# decision-and-restore a serialized unit. The lock is held only for the few
|
# decision-and-restore a serialized unit. The lock is held only for the few
|
||||||
@@ -686,7 +686,7 @@ enable_wan_logging() {
|
|||||||
# The log bit is re-read AFTER acquiring the lock so the target is computed
|
# The log bit is re-read AFTER acquiring the lock so the target is computed
|
||||||
# from the latest committed value; a concurrent toggle cannot interleave.
|
# from the latest committed value; a concurrent toggle cannot interleave.
|
||||||
# Staging + commit live inside commit_wan_log_change behind its last-moment
|
# Staging + commit live inside commit_wan_log_change behind its last-moment
|
||||||
# firewall_changes_pending re-check (#191): a foreign writer racing between
|
# firewall_changes_pending re-check: a foreign writer racing between
|
||||||
# the early check above and the commit aborts the toggle instead of having
|
# the early check above and the commit aborts the toggle instead of having
|
||||||
# its half-finished delta published with our log bit.
|
# its half-finished delta published with our log bit.
|
||||||
if ! acquire_wan_log_lock; then
|
if ! acquire_wan_log_lock; then
|
||||||
@@ -740,7 +740,7 @@ disable_wan_logging() {
|
|||||||
# The log bit is re-read AFTER acquiring the lock so the target is computed
|
# The log bit is re-read AFTER acquiring the lock so the target is computed
|
||||||
# from the latest committed value; a concurrent toggle cannot interleave.
|
# from the latest committed value; a concurrent toggle cannot interleave.
|
||||||
# Staging + commit live inside commit_wan_log_change behind its last-moment
|
# Staging + commit live inside commit_wan_log_change behind its last-moment
|
||||||
# firewall_changes_pending re-check (#191): a foreign writer racing between
|
# firewall_changes_pending re-check: a foreign writer racing between
|
||||||
# the early check above and the commit aborts the toggle instead of having
|
# the early check above and the commit aborts the toggle instead of having
|
||||||
# its half-finished delta published with our log bit.
|
# its half-finished delta published with our log bit.
|
||||||
if ! acquire_wan_log_lock; then
|
if ! acquire_wan_log_lock; then
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ ADAPTIVE_SH="$LIBEXEC_DIR/fwlive-adaptive-cap.sh"
|
|||||||
RESOLVE_MAX=32
|
RESOLVE_MAX=32
|
||||||
# Per-lookup nslookup timeout (integer seconds — BusyBox `timeout` truncates
|
# Per-lookup nslookup timeout (integer seconds — BusyBox `timeout` truncates
|
||||||
# fractional values to 0 on OpenWrt 23.05/1.36, so 0.5 would silently
|
# fractional values to 0 on OpenWrt 23.05/1.36, so 0.5 would silently
|
||||||
# degrade to no timeout; luna fold 2026-08-10). The wall-clock budget
|
# degrade to no timeout.. The wall-clock budget
|
||||||
# below is the real whole-call bound; this per-lookup cap only prevents a
|
# below is the real whole-call bound; this per-lookup cap only prevents a
|
||||||
# single hung resolver from dominating the budget. LAN/local-resolver
|
# single hung resolver from dominating the budget. LAN/local-resolver
|
||||||
# lookups are single-digit ms, so 1s is ample. Passed as argv to
|
# lookups are single-digit ms, so 1s is ample. Passed as argv to
|
||||||
@@ -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
|
||||||
@@ -255,7 +255,7 @@ _fwlive_tmp_dir_ok() {
|
|||||||
# Fail closed unless the dump dir is a real directory with the sticky
|
# Fail closed unless the dump dir is a real directory with the sticky
|
||||||
# bit. Without sticky, another uid can unlink a just-created mktemp
|
# bit. Without sticky, another uid can unlink a just-created mktemp
|
||||||
# file and replace it with a symlink before we reopen with `>` as
|
# file and replace it with a symlink before we reopen with `>` as
|
||||||
# root (same class as #204). Optional $1 is for CLI tests; production
|
# root (same class as the WAN logging lock). Optional $1 is for CLI tests; production
|
||||||
# always uses /tmp.
|
# always uses /tmp.
|
||||||
dir="${1:-/tmp}"
|
dir="${1:-/tmp}"
|
||||||
[ -n "$dir" ] || return 1
|
[ -n "$dir" ] || return 1
|
||||||
@@ -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,16 +435,16 @@ 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
|
fwlive_adaptive_merge_reply "$out" "$shed_flag" "$lines" 0 0
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 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.
|
||||||
@@ -455,12 +455,12 @@ fetch_firewall_logs() {
|
|||||||
fi
|
fi
|
||||||
[ -n "$out" ] || out='{"log":[],"error":"filter_empty"}'
|
[ -n "$out" ] || out='{"log":[],"error":"filter_empty"}'
|
||||||
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 [ "$end_cs" -ge "$start_cs" ]; then
|
if [ "$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"
|
||||||
fi
|
fi
|
||||||
# Layer 1: messages_received stays 0 — no post-filter ash count (Grok #329 P1).
|
# Layer 1: messages_received stays 0 — no post-filter ash count.
|
||||||
_trunc=0
|
_trunc=0
|
||||||
[ "$shed_flag" = 1 ] && _trunc=1
|
[ "$shed_flag" = 1 ] && _trunc=1
|
||||||
[ "$lines" -lt "$requested" ] && _trunc=1
|
[ "$lines" -lt "$requested" ] && _trunc=1
|
||||||
@@ -502,7 +502,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
|
||||||
@@ -510,7 +510,7 @@ 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 admitted hostname-shaped tokens of hex chars + dots/colons
|
# check 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
|
||||||
@@ -523,7 +523,7 @@ 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: nslookup only ever sees a single
|
||||||
# token. Note the check intentionally allows only hex/colon/dot.
|
# token. Note the check intentionally allows only hex/colon/dot.
|
||||||
case "$addr" in
|
case "$addr" in
|
||||||
*[!0-9a-fA-F:.]*) return 1 ;;
|
*[!0-9a-fA-F:.]*) return 1 ;;
|
||||||
@@ -596,7 +596,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"
|
||||||
@@ -604,14 +604,14 @@ 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
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Wall-clock budget clock starts at the TRUE function entry (luna
|
# Wall-clock budget clock starts at the TRUE function entry: the
|
||||||
# folds 2026-08-10): the budget must bound the whole call — including
|
# budget must bound the whole call — including
|
||||||
# read_rpc_input (stalled stdin must not escape the budget) and json
|
# read_rpc_input (stalled stdin must not escape the budget) and json
|
||||||
# parsing. `date +%s` can jump under NTP sync — accepted (worst case
|
# parsing. `date +%s` can jump under NTP sync — accepted (worst case
|
||||||
# the budget over- or under-runs by the jump; worker starvation is
|
# the budget over- or under-runs by the jump; worker starvation is
|
||||||
@@ -657,12 +657,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"
|
||||||
@@ -729,7 +729,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
|
||||||
@@ -757,7 +757,7 @@ run_selftest() {
|
|||||||
echo 'is_resolvable_address: expected accept IPv6-mapped ::ffff:192.0.2.1' >&2
|
echo 'is_resolvable_address: expected accept IPv6-mapped ::ffff:192.0.2.1' >&2
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
# Multi-line input must be rejected wholesale (CodeRabbit/luna fold): awk
|
# Multi-line input must be rejected wholesale: awk
|
||||||
# reads newline-separated records, so a valid first record must not mask
|
# reads newline-separated records, so a valid first record must not mask
|
||||||
# trailing garbage that would still reach nslookup.
|
# trailing garbage that would still reach nslookup.
|
||||||
if is_resolvable_address "$(printf '192.0.2.1\nnot-an-address')"; then
|
if is_resolvable_address "$(printf '192.0.2.1\nnot-an-address')"; then
|
||||||
@@ -849,7 +849,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
|
||||||
@@ -950,7 +950,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)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
|
|||||||
|
|
||||||
PKG_NAME:=luci-app-passwall
|
PKG_NAME:=luci-app-passwall
|
||||||
PKG_VERSION:=26.9.9
|
PKG_VERSION:=26.9.9
|
||||||
PKG_RELEASE:=274
|
PKG_RELEASE:=275
|
||||||
PKG_PO_VERSION:=$(PKG_VERSION)
|
PKG_PO_VERSION:=$(PKG_VERSION)
|
||||||
|
|
||||||
PKG_CONFIG_DEPENDS:= \
|
PKG_CONFIG_DEPENDS:= \
|
||||||
|
|||||||
@@ -20,7 +20,10 @@ for _, k in ipairs(com.order) do
|
|||||||
if k ~= "chinadns-ng" then
|
if k ~= "chinadns-ng" then
|
||||||
o = s:option(Value, k:gsub("%-","_") .. "_file", translatef("%s App Path", v.name))
|
o = s:option(Value, k:gsub("%-","_") .. "_file", translatef("%s App Path", v.name))
|
||||||
o.default = v.default_path or ("/usr/bin/" .. k)
|
o.default = v.default_path or ("/usr/bin/" .. k)
|
||||||
o.rmempty = false
|
o.placeholder = o.default
|
||||||
|
function o:remove(section)
|
||||||
|
self:write(section, self.default)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -408,16 +408,16 @@ load_acl() {
|
|||||||
else
|
else
|
||||||
[ -n "${DIRECT_DNSMASQ_PORT}" ] && dns_redirect=${DIRECT_DNSMASQ_PORT}
|
[ -n "${DIRECT_DNSMASQ_PORT}" ] && dns_redirect=${DIRECT_DNSMASQ_PORT}
|
||||||
fi
|
fi
|
||||||
if [ -n "${dns_redirect}" ]; then
|
if ([ -n "$tcp_port" ] || [ -n "$udp_port" ]) && [ -n "$dns_redirect" ]; then
|
||||||
$ipt_m -A PSW $(comment "$remarks") -p udp ${_ipt_source} --dport 53 -j RETURN
|
$ipt_m -A PSW $(comment "$remarks") -p udp ${_ipt_source} --dport 53 -j RETURN
|
||||||
[ "$_ipv4" != "1" ] && $ip6t_m -A PSW $(comment "$remarks") -p udp ${_ipt_source} --dport 53 -j RETURN 2>/dev/null
|
$ip6t_m -A PSW $(comment "$remarks") -p udp ${_ipt_source} --dport 53 -j RETURN 2>/dev/null
|
||||||
$ipt_m -A PSW $(comment "$remarks") -p tcp ${_ipt_source} --dport 53 -j RETURN
|
$ipt_m -A PSW $(comment "$remarks") -p tcp ${_ipt_source} --dport 53 -j RETURN
|
||||||
[ "$_ipv4" != "1" ] && $ip6t_m -A PSW $(comment "$remarks") -p tcp ${_ipt_source} --dport 53 -j RETURN 2>/dev/null
|
$ip6t_m -A PSW $(comment "$remarks") -p tcp ${_ipt_source} --dport 53 -j RETURN 2>/dev/null
|
||||||
$ipt_n -A PSW_DNS $(comment "$remarks") -p udp ${_ipt_source} --dport 53 -j REDIRECT --to-ports ${dns_redirect}
|
$ipt_n -A PSW_DNS $(comment "$remarks") -p udp ${_ipt_source} --dport 53 -j REDIRECT --to-ports ${dns_redirect}
|
||||||
[ "$_ipv4" != "1" ] && $ip6t_n -A PSW_DNS $(comment "$remarks") -p udp ${_ipt_source} --dport 53 -j REDIRECT --to-ports ${dns_redirect} 2>/dev/null
|
$ip6t_n -A PSW_DNS $(comment "$remarks") -p udp ${_ipt_source} --dport 53 -j REDIRECT --to-ports ${dns_redirect} 2>/dev/null
|
||||||
$ipt_n -A PSW_DNS $(comment "$remarks") -p tcp ${_ipt_source} --dport 53 -j REDIRECT --to-ports ${dns_redirect}
|
$ipt_n -A PSW_DNS $(comment "$remarks") -p tcp ${_ipt_source} --dport 53 -j REDIRECT --to-ports ${dns_redirect}
|
||||||
[ "$_ipv4" != "1" ] && $ip6t_n -A PSW_DNS $(comment "$remarks") -p tcp ${_ipt_source} --dport 53 -j REDIRECT --to-ports ${dns_redirect} 2>/dev/null
|
$ip6t_n -A PSW_DNS $(comment "$remarks") -p tcp ${_ipt_source} --dport 53 -j REDIRECT --to-ports ${dns_redirect} 2>/dev/null
|
||||||
[ -z "$(get_cache_var "ACL_${sid}_default")" ] && echolog " - ${msg}节点不同于全局配置,DNS 重定向到专用服务器[${dns_redirect}]。"
|
[ -z "$(get_cache_var "ACL_${sid}_default")" ] && echolog " - ${msg}节点不同于全局配置,DNS 重定向到专用服务器[${dns_redirect}]"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
[ -n "$tcp_port" ] || [ -n "$udp_port" ] && {
|
[ -n "$tcp_port" ] || [ -n "$udp_port" ] && {
|
||||||
@@ -597,7 +597,7 @@ load_acl() {
|
|||||||
[ -n "${DIRECT_DNSMASQ_PORT}" ] && DNS_REDIRECT=${DIRECT_DNSMASQ_PORT}
|
[ -n "${DIRECT_DNSMASQ_PORT}" ] && DNS_REDIRECT=${DIRECT_DNSMASQ_PORT}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "${DNS_REDIRECT}" ]; then
|
if ([ -n "${TCP_PROXY_MODE}" ] || [ -n "${UDP_PROXY_MODE}" ]) && [ -n "$DNS_REDIRECT" ]; then
|
||||||
$ipt_m -A PSW $(comment "默认") -p udp --dport 53 -j RETURN
|
$ipt_m -A PSW $(comment "默认") -p udp --dport 53 -j RETURN
|
||||||
$ip6t_m -A PSW $(comment "默认") -p udp --dport 53 -j RETURN 2>/dev/null
|
$ip6t_m -A PSW $(comment "默认") -p udp --dport 53 -j RETURN 2>/dev/null
|
||||||
$ipt_m -A PSW $(comment "默认") -p tcp --dport 53 -j RETURN
|
$ipt_m -A PSW $(comment "默认") -p tcp --dport 53 -j RETURN
|
||||||
@@ -606,6 +606,7 @@ load_acl() {
|
|||||||
$ip6t_n -A PSW_DNS $(comment "默认") -p udp --dport 53 -j REDIRECT --to-ports ${DNS_REDIRECT} 2>/dev/null
|
$ip6t_n -A PSW_DNS $(comment "默认") -p udp --dport 53 -j REDIRECT --to-ports ${DNS_REDIRECT} 2>/dev/null
|
||||||
$ipt_n -A PSW_DNS $(comment "默认") -p tcp --dport 53 -j REDIRECT --to-ports ${DNS_REDIRECT}
|
$ipt_n -A PSW_DNS $(comment "默认") -p tcp --dport 53 -j REDIRECT --to-ports ${DNS_REDIRECT}
|
||||||
$ip6t_n -A PSW_DNS $(comment "默认") -p tcp --dport 53 -j REDIRECT --to-ports ${DNS_REDIRECT} 2>/dev/null
|
$ip6t_n -A PSW_DNS $(comment "默认") -p tcp --dport 53 -j REDIRECT --to-ports ${DNS_REDIRECT} 2>/dev/null
|
||||||
|
echolog " - ${msg}DNS 重定向到专用服务器[${DNS_REDIRECT}]"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
[ -n "${TCP_PROXY_MODE}" ] || [ -n "${UDP_PROXY_MODE}" ] && {
|
[ -n "${TCP_PROXY_MODE}" ] || [ -n "${UDP_PROXY_MODE}" ] && {
|
||||||
|
|||||||
@@ -471,26 +471,13 @@ load_acl() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if ([ -n "$tcp_port" ] || [ -n "$udp_port" ]) && [ -n "$dns_redirect" ]; then
|
if ([ -n "$tcp_port" ] || [ -n "$udp_port" ]) && [ -n "$dns_redirect" ]; then
|
||||||
if [ "$PROXY_IPV6" = "1" ]; then
|
nft "add rule $NFTABLE_NAME PSW_MANGLE ip protocol udp ${_ipt_source} udp dport 53 counter return comment \"$remarks\""
|
||||||
nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 meta l4proto udp ${_ipt_source} udp dport 53 counter accept"
|
nft "add rule $NFTABLE_NAME PSW_MANGLE ip protocol tcp ${_ipt_source} tcp dport 53 counter return comment \"$remarks\""
|
||||||
nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 meta l4proto tcp ${_ipt_source} tcp dport 53 counter accept"
|
nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 meta l4proto udp ${_ipt_source} udp dport 53 counter return comment \"$remarks\""
|
||||||
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto udp ${_ipt_source} udp dport 53 counter redirect to :$dns_redirect comment \"$remarks\""
|
nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 meta l4proto tcp ${_ipt_source} tcp dport 53 counter return comment \"$remarks\""
|
||||||
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto tcp ${_ipt_source} tcp dport 53 counter redirect to :$dns_redirect comment \"$remarks\""
|
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto udp ${_ipt_source} udp dport 53 counter redirect to :$dns_redirect comment \"$remarks\""
|
||||||
else
|
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto tcp ${_ipt_source} tcp dport 53 counter redirect to :$dns_redirect comment \"$remarks\""
|
||||||
nft "add rule $NFTABLE_NAME PSW_MANGLE ip protocol udp ${_ipt_source} udp dport 53 counter accept"
|
[ -z "$(get_cache_var "ACL_${sid}_default")" ] && echolog " - ${msg}节点不同于全局配置,DNS 重定向到专用服务器[${dns_redirect}]"
|
||||||
nft "add rule $NFTABLE_NAME PSW_MANGLE ip protocol tcp ${_ipt_source} tcp dport 53 counter accept"
|
|
||||||
nft "add rule $NFTABLE_NAME PSW_DNS ip protocol udp ${_ipt_source} udp dport 53 counter redirect to :$dns_redirect comment \"$remarks\""
|
|
||||||
nft "add rule $NFTABLE_NAME PSW_DNS ip protocol tcp ${_ipt_source} tcp dport 53 counter redirect to :$dns_redirect comment \"$remarks\""
|
|
||||||
fi
|
|
||||||
[ -z "$(get_cache_var "ACL_${sid}_default")" ] && echolog " - ${msg}节点不同于全局配置,DNS 重定向到专用服务器[${dns_redirect}]。"
|
|
||||||
else
|
|
||||||
if [ "$PROXY_IPV6" = "1" ]; then
|
|
||||||
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto udp ${_ipt_source} udp dport 53 counter return comment \"$remarks\""
|
|
||||||
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto tcp ${_ipt_source} tcp dport 53 counter return comment \"$remarks\""
|
|
||||||
else
|
|
||||||
nft "add rule $NFTABLE_NAME PSW_DNS ip protocol udp ${_ipt_source} udp dport 53 counter return comment \"$remarks\""
|
|
||||||
nft "add rule $NFTABLE_NAME PSW_DNS ip protocol tcp ${_ipt_source} tcp dport 53 counter return comment \"$remarks\""
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
[ -n "$tcp_port" ] || [ -n "$udp_port" ] && {
|
[ -n "$tcp_port" ] || [ -n "$udp_port" ] && {
|
||||||
@@ -670,25 +657,13 @@ load_acl() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if ([ -n "${TCP_PROXY_MODE}" ] || [ -n "${UDP_PROXY_MODE}" ]) && [ -n "$DNS_REDIRECT" ]; then
|
if ([ -n "${TCP_PROXY_MODE}" ] || [ -n "${UDP_PROXY_MODE}" ]) && [ -n "$DNS_REDIRECT" ]; then
|
||||||
if [ "$PROXY_IPV6" = "1" ]; then
|
nft "add rule $NFTABLE_NAME PSW_MANGLE ip protocol udp udp dport 53 counter return comment \"默认\""
|
||||||
nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 meta l4proto udp udp dport 53 counter accept"
|
nft "add rule $NFTABLE_NAME PSW_MANGLE ip protocol tcp tcp dport 53 counter return comment \"默认\""
|
||||||
nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 meta l4proto tcp tcp dport 53 counter accept"
|
nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 meta l4proto udp udp dport 53 counter return comment \"默认\""
|
||||||
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto udp udp dport 53 counter redirect to :$DNS_REDIRECT comment \"默认\""
|
nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 meta l4proto tcp tcp dport 53 counter return comment \"默认\""
|
||||||
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto tcp tcp dport 53 counter redirect to :$DNS_REDIRECT comment \"默认\""
|
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto udp udp dport 53 counter redirect to :${DNS_REDIRECT} comment \"默认\""
|
||||||
else
|
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto tcp tcp dport 53 counter redirect to :${DNS_REDIRECT} comment \"默认\""
|
||||||
nft "add rule $NFTABLE_NAME PSW_MANGLE ip protocol udp udp dport 53 counter accept"
|
echolog " - ${msg}DNS 重定向到专用服务器[${DNS_REDIRECT}]"
|
||||||
nft "add rule $NFTABLE_NAME PSW_MANGLE ip protocol tcp tcp dport 53 counter accept"
|
|
||||||
nft "add rule $NFTABLE_NAME PSW_DNS ip protocol udp udp dport 53 counter redirect to :$DNS_REDIRECT comment \"默认\""
|
|
||||||
nft "add rule $NFTABLE_NAME PSW_DNS ip protocol tcp tcp dport 53 counter redirect to :$DNS_REDIRECT comment \"默认\""
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
if [ "$PROXY_IPV6" = "1" ]; then
|
|
||||||
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto udp udp dport 53 counter return comment \"默认\""
|
|
||||||
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto tcp tcp dport 53 counter return comment \"默认\""
|
|
||||||
else
|
|
||||||
nft "add rule $NFTABLE_NAME PSW_DNS ip protocol udp udp dport 53 counter return comment \"默认\""
|
|
||||||
nft "add rule $NFTABLE_NAME PSW_DNS ip protocol tcp tcp dport 53 counter return comment \"默认\""
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
[ -n "${TCP_PROXY_MODE}" ] || [ -n "${UDP_PROXY_MODE}" ] && {
|
[ -n "${TCP_PROXY_MODE}" ] || [ -n "${UDP_PROXY_MODE}" ] && {
|
||||||
@@ -1333,15 +1308,8 @@ add_firewall_rule() {
|
|||||||
|
|
||||||
if [ -n "$NODE" ] && ([ -n "${LOCALHOST_TCP_PROXY_MODE}" ] || [ -n "${LOCALHOST_UDP_PROXY_MODE}" ]); then
|
if [ -n "$NODE" ] && ([ -n "${LOCALHOST_TCP_PROXY_MODE}" ] || [ -n "${LOCALHOST_UDP_PROXY_MODE}" ]); then
|
||||||
[ -n "$DNS_REDIRECT_PORT" ] && {
|
[ -n "$DNS_REDIRECT_PORT" ] && {
|
||||||
if [ "$PROXY_IPV6" == "1" ]; then
|
nft "add rule $NFTABLE_NAME nat_output meta l4proto udp oif lo udp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"PSW_DNS\""
|
||||||
#nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 meta l4proto udp udp dport 53 counter accept"
|
nft "add rule $NFTABLE_NAME nat_output meta l4proto tcp oif lo tcp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"PSW_DNS\""
|
||||||
#nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 meta l4proto tcp tcp dport 53 counter accept"
|
|
||||||
nft "add rule $NFTABLE_NAME nat_output oif lo meta l4proto udp udp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"PSW_DNS\""
|
|
||||||
nft "add rule $NFTABLE_NAME nat_output oif lo meta l4proto tcp tcp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"PSW_DNS\""
|
|
||||||
else
|
|
||||||
nft "add rule $NFTABLE_NAME nat_output oif lo ip protocol udp udp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"PSW_DNS\""
|
|
||||||
nft "add rule $NFTABLE_NAME nat_output oif lo ip protocol tcp tcp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"PSW_DNS\""
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk
|
|||||||
|
|
||||||
PKG_NAME:=luci-app-passwall2
|
PKG_NAME:=luci-app-passwall2
|
||||||
PKG_VERSION:=26.9.12
|
PKG_VERSION:=26.9.12
|
||||||
PKG_RELEASE:=122
|
PKG_RELEASE:=123
|
||||||
PKG_PO_VERSION:=$(PKG_VERSION)
|
PKG_PO_VERSION:=$(PKG_VERSION)
|
||||||
|
|
||||||
PKG_CONFIG_DEPENDS:= \
|
PKG_CONFIG_DEPENDS:= \
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ local k, v
|
|||||||
for k, v in pairs(com) do
|
for k, v in pairs(com) do
|
||||||
o = s:option(Value, k:gsub("%-","_") .. "_file", translatef("%s App Path", v.name))
|
o = s:option(Value, k:gsub("%-","_") .. "_file", translatef("%s App Path", v.name))
|
||||||
o.default = v.default_path or ("/usr/bin/" .. k)
|
o.default = v.default_path or ("/usr/bin/" .. k)
|
||||||
o.rmempty = false
|
o.placeholder = o.default
|
||||||
|
function o:remove(section)
|
||||||
|
self:write(section, self.default)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
o = s:option(DummyValue, "tips", " ")
|
o = s:option(DummyValue, "tips", " ")
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ LUCI_TITLE:=luci-app-ssr-plus
|
|||||||
LUCI_PKGARCH:=all
|
LUCI_PKGARCH:=all
|
||||||
PKG_NAME:=luci-app-ssr-plus
|
PKG_NAME:=luci-app-ssr-plus
|
||||||
PKG_VERSION:=196
|
PKG_VERSION:=196
|
||||||
PKG_RELEASE:=72
|
PKG_RELEASE:=73
|
||||||
|
|
||||||
PKG_CONFIG_DEPENDS:= \
|
PKG_CONFIG_DEPENDS:= \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_Iptables_Transparent_Proxy \
|
CONFIG_PACKAGE_$(PKG_NAME)_Iptables_Transparent_Proxy \
|
||||||
|
|||||||
@@ -696,6 +696,12 @@ local function build_dns_section(dns_mode, user_dns)
|
|||||||
if not result["default-nameserver"] then
|
if not result["default-nameserver"] then
|
||||||
result["default-nameserver"] = get_fastest_dns()
|
result["default-nameserver"] = get_fastest_dns()
|
||||||
end
|
end
|
||||||
|
if not result["direct-nameserver"] then
|
||||||
|
result["direct-nameserver"] = get_fastest_dns()
|
||||||
|
end
|
||||||
|
if result["direct-nameserver-follow-policy"] == nil then
|
||||||
|
result["direct-nameserver-follow-policy"] = false
|
||||||
|
end
|
||||||
if result["respect-rules"] == nil then
|
if result["respect-rules"] == nil then
|
||||||
result["respect-rules"] = true
|
result["respect-rules"] = true
|
||||||
end
|
end
|
||||||
@@ -740,6 +746,8 @@ local function build_dns_section(dns_mode, user_dns)
|
|||||||
result["respect-rules"] = nil
|
result["respect-rules"] = nil
|
||||||
result["fallback-filter"] = nil
|
result["fallback-filter"] = nil
|
||||||
result["nameserver-policy"] = nil
|
result["nameserver-policy"] = nil
|
||||||
|
result["direct-nameserver"] = nil
|
||||||
|
result["direct-nameserver-follow-policy"] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local upstreams = build_dns_upstreams()
|
local upstreams = build_dns_upstreams()
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
|
|||||||
LUCI_TITLE:=LuCI Status Pages
|
LUCI_TITLE:=LuCI Status Pages
|
||||||
LUCI_DEPENDS:=+luci-base +luci-app-oui +libiwinfo +rpcd-mod-iwinfo +rpcd-mod-rrdns +ucode +ucode-mod-fs +ucode-mod-ubus +ucode-mod-socket
|
LUCI_DEPENDS:=+luci-base +luci-app-oui +libiwinfo +rpcd-mod-iwinfo +rpcd-mod-rrdns +ucode +ucode-mod-fs +ucode-mod-ubus +ucode-mod-socket
|
||||||
|
|
||||||
PKG_RELEASE:=10
|
PKG_RELEASE:=11
|
||||||
PKG_BUILD_DEPENDS:=iwinfo
|
PKG_BUILD_DEPENDS:=iwinfo
|
||||||
PKG_LICENSE:=Apache-2.0
|
PKG_LICENSE:=Apache-2.0
|
||||||
|
|
||||||
|
|||||||
@@ -88,11 +88,6 @@ return baseclass.extend({
|
|||||||
_('Architecture'), (cpuinfo.cpuinfo || boardinfo.system) + ' ' + cpubench.cpubench,
|
_('Architecture'), (cpuinfo.cpuinfo || boardinfo.system) + ' ' + cpubench.cpubench,
|
||||||
_('Target Platform'), (L.isObject(boardinfo.release) ? boardinfo.release.target : ''),
|
_('Target Platform'), (L.isObject(boardinfo.release) ? boardinfo.release.target : ''),
|
||||||
_('Firmware Version'), (L.isObject(boardinfo.release) ? boardinfo.release.description + ' / ' : '') + (luciversion || ''),
|
_('Firmware Version'), (L.isObject(boardinfo.release) ? boardinfo.release.description + ' / ' : '') + (luciversion || ''),
|
||||||
? '%s%s / '.format(
|
|
||||||
boardinfo.release.description || '',
|
|
||||||
boardinfo.release.revision ? boardinfo.release.revision : ''
|
|
||||||
)
|
|
||||||
: '') + (luciversion || ''),
|
|
||||||
_('Kernel Version'), boardinfo.kernel,
|
_('Kernel Version'), boardinfo.kernel,
|
||||||
_('Local Time'), datestr,
|
_('Local Time'), datestr,
|
||||||
_('Uptime'), systeminfo.uptime ? '%t'.format(systeminfo.uptime) : null,
|
_('Uptime'), systeminfo.uptime ? '%t'.format(systeminfo.uptime) : null,
|
||||||
|
|||||||
Reference in new issue
Block a user