🗽 Sync 2026-09-12 10:51:28

This commit is contained in:
github-actions[bot]
2026-09-12 10:51:28 +08:00
parent f56e31d6d8
commit 5f6047bcba
11 changed files with 228 additions and 153 deletions
+2 -2
View File
@@ -12,7 +12,7 @@ PKG_ARCH_LINKEASE:=$(ARCH)
PKG_NAME:=linkease-common-bin
# use PKG_SOURCE_DATE instead of PKG_VERSION for compitable
PKG_SOURCE_DATE:=1.7.5
PKG_RELEASE:=31
PKG_RELEASE:=32
ARCH_HEXCODE:=
ifeq ($(ARCH),x86_64)
@@ -28,7 +28,7 @@ else ifeq ($(ARCH),mipsel)
ARCH_HEXCODE=1b0c
endif
PKG_SOURCE_VERSION:=d795d92450b318f7ae72ae04dd2694761b453919
PKG_SOURCE_VERSION:=4bb94bc5fe4da6d98dfcd730f40fe15f0d71ec4a
PKG_SOURCE:=linkease-common-bin-$(PKG_SOURCE_DATE)-linux-$(PKG_ARCH_LINKEASE).tar.gz
PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/linkease-runtime-v$(PKG_SOURCE_DATE)/
PKG_BUILD_DIR:=$(BUILD_DIR)/linkease-common-bin-$(PKG_SOURCE_DATE)-linux-$(PKG_ARCH_LINKEASE)
+2 -2
View File
@@ -12,7 +12,7 @@ PKG_ARCH_LINKEASE:=$(ARCH)
PKG_NAME:=linkease
# use PKG_SOURCE_DATE instead of PKG_VERSION for compitable
PKG_SOURCE_DATE:=1.7.5
PKG_RELEASE:=34
PKG_RELEASE:=35
ARCH_HEXCODE:=
ifeq ($(ARCH),x86_64)
@@ -28,7 +28,7 @@ else ifeq ($(ARCH),mipsel)
ARCH_HEXCODE=1b0c
endif
PKG_SOURCE_VERSION:=d795d92450b318f7ae72ae04dd2694761b453919
PKG_SOURCE_VERSION:=4bb94bc5fe4da6d98dfcd730f40fe15f0d71ec4a
PKG_SOURCE:=linkease-bin-$(PKG_SOURCE_DATE)-linux-$(PKG_ARCH_LINKEASE).tar.gz
PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/linkease-runtime-v$(PKG_SOURCE_DATE)/
PKG_BUILD_DIR:=$(BUILD_DIR)/linkease-bin-$(PKG_SOURCE_DATE)-linux-$(PKG_ARCH_LINKEASE)
+3 -3
View File
@@ -10,8 +10,8 @@ PKG_ARCH_LINKEASE:=$(ARCH)
PKG_NAME:=linkeasefull
# use PKG_SOURCE_DATE instead of PKG_VERSION for compitable
PKG_SOURCE_DATE:=3.0.19
PKG_RELEASE:=33
PKG_SOURCE_DATE:=3.0.20
PKG_RELEASE:=34
ARCH_HEXCODE:=
ifeq ($(ARCH),x86_64)
@@ -24,7 +24,7 @@ LINKEASE_RUNTIME_ARCH:=arm64
PKG_HASH:=skip
endif
PKG_SOURCE_VERSION:=d795d92450b318f7ae72ae04dd2694761b453919
PKG_SOURCE_VERSION:=4bb94bc5fe4da6d98dfcd730f40fe15f0d71ec4a
PKG_SOURCE:=linkease-runtime-$(PKG_SOURCE_DATE)-linux-$(LINKEASE_RUNTIME_ARCH).tar.gz
PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/linkeasefull-runtime-v$(PKG_SOURCE_DATE)/
PKG_BUILD_DIR:=$(BUILD_DIR)/linkease-runtime-$(PKG_SOURCE_DATE)-linux-$(LINKEASE_RUNTIME_ARCH)
+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_PKGARCH:=all
PKG_VERSION:=0.1.40
PKG_RELEASE:=11
PKG_VERSION:=0.1.41
PKG_RELEASE:=12
# Reproducible build: honor SOURCE_DATE_EPOCH from the build environment (set by docker-sdk.sh / CI).
ifdef SOURCE_DATE_EPOCH
@@ -9,7 +9,7 @@
*/
return baseclass.extend({
/* Keep in sync with openwrt-feed/luci-app-fwlive/Makefile PKG_VERSION. */
APP_VERSION: '0.1.40',
APP_VERSION: '0.1.41',
ROW_LIMIT_OPTIONS: [25, 50, 100, 250, 500, 1000, 2000],
DEFAULT_ROW_LIMIT: 100,
/* Row pass/deny tint (#40): classic green/red default; accessible teal/orange */
@@ -0,0 +1,124 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2025-2026 Lucas Albers <lucas.b.albers@gmail.com>
#
# GENERATED FILE — do not edit. Run: ./scripts/gen-all.sh
# source: core/fwlive-log.js CLASSIFY_SPEC
# Standalone awk program loaded by fwlive-is-firewall-event.sh.
function normalize(s, keys, n, i, k) {
n = split("IN OUT SRC DST PROTO SPT DPT LEN MAC TYPE CODE TTL TOS PREC DF", keys, " ")
for (i = 1; i <= n; i++) {
k = keys[i]
while (match(s, "[^[:space:]]" k "="))
s = substr(s, 1, RSTART) " " substr(s, RSTART + 1)
}
return s
}
function trim(s) {
sub(/^[[:space:]]+/, "", s)
sub(/[[:space:]]+$/, "", s)
return s
}
function has_kv(s, key) {
return s ~ "(^|[^A-Za-z0-9_])" key "="
}
function has_hint(s, lc) {
lc = tolower(s)
return lc ~ "(^|[^a-z0-9_])(fw4|nft|iptables|kernel|firewall)([^a-z0-9_]|$)"
}
function non_fw_prefix(s, lc) {
lc = tolower(s)
return lc ~ "^(dnsmasq|procd|ubusd|netifd|odhcpd|logd|dropbear|uhttpd|hostapd|wpad)([^a-z0-9_]|$)"
}
function detect_action(s, words, n, i, w, wl, lc, start, pos, before, afterc, best, bestpos) {
n = split("ACCEPT ALLOW PASS DROP REJECT DENY BLOCK", words, " ")
lc = tolower(s)
best = ""
bestpos = length(s) + 1
for (i = 1; i <= n; i++) {
w = words[i]
wl = tolower(w)
start = 1
while (start <= length(lc) && match(substr(lc, start), wl)) {
pos = start + RSTART - 1
before = (pos == 1) ? " " : substr(lc, pos - 1, 1)
afterc = substr(lc, pos + length(wl), 1)
if (before !~ /[a-z0-9_]/ && (afterc == "" || afterc !~ /[a-z0-9_]/)) {
if (pos < bestpos) { bestpos = pos; best = w }
break
}
start = pos + 1
}
}
return best == "" ? "UNKNOWN" : best
}
function json_unhex4(h, n, i, c, v) {
n = 0
h = tolower(h)
for (i = 1; i <= 4; i++) {
c = substr(h, i, 1)
v = index("0123456789abcdef", c)
if (v == 0) return -1
n = n * 16 + v - 1
}
return n
}
function json_get_msg(obj, s, i, c, esc, out, hex, n) {
if (!match(obj, /"msg"[[:space:]]*:[[:space:]]*"/)) return ""
s = substr(obj, RSTART + RLENGTH)
out = ""
esc = 0
for (i = 1; i <= length(s); i++) {
c = substr(s, i, 1)
if (esc) {
if (c == "n") out = out "\n"
else if (c == "t") out = out "\t"
else if (c == "r") out = out "\r"
else if (c == "b") out = out "\b"
else if (c == "f") out = out "\f"
else if (c == "u") {
hex = substr(s, i + 1, 4)
n = (length(hex) == 4) ? json_unhex4(hex) : -1
if (n >= 1 && n <= 255) out = out sprintf("%c", n)
else if (n < 0) out = out "u"
if (n >= 0) i += 4
} else out = out c
esc = 0
} else if (c == "\\") {
esc = 1
} else if (c == "\"") {
return out
} else {
out = out c
}
}
return out
}
function is_fw(s, action) {
s = trim(normalize(s))
if (s == "") return 0
if (non_fw_prefix(s)) return 0
action = detect_action(s)
if (has_kv(s, "SRC") && has_kv(s, "DST")) return 1
if ((has_kv(s, "IN") || has_kv(s, "OUT")) && (has_kv(s, "SRC") || has_kv(s, "DST") || has_kv(s, "PROTO") || has_kv(s, "SPT") || has_kv(s, "DPT"))) return 1
if (action != "UNKNOWN" && (has_kv(s, "IN") || has_kv(s, "OUT") || has_kv(s, "PROTO") || has_kv(s, "SRC") || has_kv(s, "DST"))) return 1
if (has_hint(s) && action != "UNKNOWN") return 1
if (has_hint(s) && (has_kv(s, "IN") || has_kv(s, "OUT") || has_kv(s, "SRC") || has_kv(s, "DST") || has_kv(s, "PROTO"))) return 1
return 0
}
BEGIN { if (MODE != "json") ORS = "" }
{
if (MODE == "json") {
msg = json_get_msg($0)
if (is_fw(msg)) {
if (out_n++) printf ","
printf "%s", $0
}
next
}
buf = (NR == 1) ? $0 : buf "\n" $0
}
END {
if (MODE != "json")
print is_fw(buf) ? 1 : 0
}
@@ -7,127 +7,12 @@
# Sourced library: do not add set -euo here (callers own strict mode, #291 C3).
# One awk process classifies a batch (MODE=json) or one message (default).
# The caller sets FILTER_DIR when this file is sourced. Resolve the asset
# once, not once per classification call.
CLASSIFY_AWK="${FILTER_DIR:-${0%/*}}/fwlive-is-firewall-event.awk"
_fwlive_run_classify() {
awk -v MODE="${1:-msg}" "$(cat <<'AWK'
function normalize(s, keys, n, i, k) {
n = split("IN OUT SRC DST PROTO SPT DPT LEN MAC TYPE CODE TTL TOS PREC DF", keys, " ")
for (i = 1; i <= n; i++) {
k = keys[i]
while (match(s, "[^[:space:]]" k "="))
s = substr(s, 1, RSTART) " " substr(s, RSTART + 1)
}
return s
}
function trim(s) {
sub(/^[[:space:]]+/, "", s)
sub(/[[:space:]]+$/, "", s)
return s
}
function has_kv(s, key) {
return s ~ "(^|[^A-Za-z0-9_])" key "="
}
function has_hint(s, lc) {
lc = tolower(s)
return lc ~ "(^|[^a-z0-9_])(fw4|nft|iptables|kernel|firewall)([^a-z0-9_]|$)"
}
function non_fw_prefix(s, lc) {
lc = tolower(s)
return lc ~ "^(dnsmasq|procd|ubusd|netifd|odhcpd|logd|dropbear|uhttpd|hostapd|wpad)([^a-z0-9_]|$)"
}
function detect_action(s, words, n, i, w, wl, lc, start, pos, before, afterc, best, bestpos) {
n = split("ACCEPT ALLOW PASS DROP REJECT DENY BLOCK", words, " ")
lc = tolower(s)
best = ""
bestpos = length(s) + 1
for (i = 1; i <= n; i++) {
w = words[i]
wl = tolower(w)
start = 1
while (start <= length(lc) && match(substr(lc, start), wl)) {
pos = start + RSTART - 1
before = (pos == 1) ? " " : substr(lc, pos - 1, 1)
afterc = substr(lc, pos + length(wl), 1)
if (before !~ /[a-z0-9_]/ && (afterc == "" || afterc !~ /[a-z0-9_]/)) {
if (pos < bestpos) { bestpos = pos; best = w }
break
}
start = pos + 1
}
}
return best == "" ? "UNKNOWN" : best
}
function json_unhex4(h, n, i, c, v) {
n = 0
h = tolower(h)
for (i = 1; i <= 4; i++) {
c = substr(h, i, 1)
v = index("0123456789abcdef", c)
if (v == 0) return -1
n = n * 16 + v - 1
}
return n
}
function json_get_msg(obj, s, i, c, esc, out, hex, n) {
if (!match(obj, /"msg"[[:space:]]*:[[:space:]]*"/)) return ""
s = substr(obj, RSTART + RLENGTH)
out = ""
esc = 0
for (i = 1; i <= length(s); i++) {
c = substr(s, i, 1)
if (esc) {
if (c == "n") out = out "\n"
else if (c == "t") out = out "\t"
else if (c == "r") out = out "\r"
else if (c == "b") out = out "\b"
else if (c == "f") out = out "\f"
else if (c == "u") {
hex = substr(s, i + 1, 4)
n = (length(hex) == 4) ? json_unhex4(hex) : -1
if (n >= 1 && n <= 255) out = out sprintf("%c", n)
else if (n < 0) out = out "u"
if (n >= 0) i += 4
} else out = out c
esc = 0
} else if (c == "\\") {
esc = 1
} else if (c == "\"") {
return out
} else {
out = out c
}
}
return out
}
function is_fw(s, action) {
s = trim(normalize(s))
if (s == "") return 0
if (non_fw_prefix(s)) return 0
action = detect_action(s)
if (has_kv(s, "SRC") && has_kv(s, "DST")) return 1
if ((has_kv(s, "IN") || has_kv(s, "OUT")) && (has_kv(s, "SRC") || has_kv(s, "DST") || has_kv(s, "PROTO") || has_kv(s, "SPT") || has_kv(s, "DPT"))) return 1
if (action != "UNKNOWN" && (has_kv(s, "IN") || has_kv(s, "OUT") || has_kv(s, "PROTO") || has_kv(s, "SRC") || has_kv(s, "DST"))) return 1
if (has_hint(s) && action != "UNKNOWN") return 1
if (has_hint(s) && (has_kv(s, "IN") || has_kv(s, "OUT") || has_kv(s, "SRC") || has_kv(s, "DST") || has_kv(s, "PROTO"))) return 1
return 0
}
BEGIN { if (MODE != "json") ORS = "" }
{
if (MODE == "json") {
msg = json_get_msg($0)
if (is_fw(msg)) {
if (out_n++) printf ","
printf "%s", $0
}
next
}
buf = (NR == 1) ? $0 : buf "\n" $0
}
END {
if (MODE != "json")
print is_fw(buf) ? 1 : 0
}
AWK
)"
awk -v MODE="${1:-msg}" -f "$CLASSIFY_AWK"
}
is_firewall_event_msg() {
@@ -25,8 +25,12 @@ FILTER_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck disable=SC1091 # classifier is a sibling file next to this script
. "$FILTER_DIR/fwlive-is-firewall-event.sh"
input="$(cat)"
[ -n "$input" ] || input='{"log":[]}'
# The classifier is a package asset, not generated at request time. Fail
# closed if an incomplete install or a damaged package removed it (#321).
if [ ! -r "$CLASSIFY_AWK" ]; then
printf '%s' '{"log":[],"error":"classifier_missing"}'
exit 1
fi
printf '%s' '{"log":['
# Prefer stdin over -s: Linux MAX_ARG_STRLEN is 128KiB; a raised logd ring
@@ -34,5 +38,5 @@ printf '%s' '{"log":['
# while this script still printed {"log":[]} and exited 0 (#234).
# jsonfilter miss / empty @.log is not fatal; must still close JSON (#220).
# set -e + pipefail cannot apply to this pipeline (#291 C3).
printf '%s' "$input" | jsonfilter -e '@.log[*]' 2>/dev/null | _fwlive_filter_json_entries || true
jsonfilter -e '@.log[*]' 2>/dev/null | _fwlive_filter_json_entries || true
printf '%s' ']}'
+64 -10
View File
@@ -3,8 +3,10 @@
# the Modems editor, live settings, and the shared resources (wwand.bands /
# wwand.modemopts / wwand.simlist) reused by the proto handler.
#
# Sources live in https://github.com/ddimension/luci-app-wwand — bump
# PKG_SOURCE_VERSION (and PKG_SOURCE_DATE) to pick up new source commits.
# Sources live in https://github.com/ddimension/luci-app-wwand, tagged vX.Y.Z
# together with wwand. Pin a new commit with
# `scripts/bump-source.sh luci-app-wwand <tag|commit>`: it derives PKG_VERSION
# (X.Y.Z at a tag, X.Y.Z_pN after it), PKG_RELEASE and the mirror hash.
#
# NOTE: built from a git PKG_SOURCE, so it does NOT use luci.mk — luci.mk's
# htdocs/root install is gated on those dirs living next to the Makefile
@@ -15,12 +17,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-wwand
PKG_RELEASE:=17
PKG_VERSION:=1.6.6_p3
PKG_RELEASE:=18
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/ddimension/luci-app-wwand.git
PKG_SOURCE_VERSION:=db03f95603aa70ef3682d462cfe476f935374392
PKG_SOURCE_DATE:=2026-09-11
PKG_SOURCE_VERSION:=4676454a1a8356de565fc02e44f66a6813c66b09
PKG_MIRROR_HASH:=skip
PKG_LICENSE:=GPL-2.0-only
@@ -44,16 +46,68 @@ endef
define Build/Compile
endef
define Package/luci-app-wwand-statistics
SECTION:=luci
CATEGORY:=LuCI
SUBMENU:=3. Applications
TITLE:=wwand graphs for luci-app-statistics
DEPENDS:=+luci-app-statistics +wwand
PKGARCH:=all
endef
define Package/luci-app-wwand-statistics/description
RRD graph definitions for the data `wwandctl collectd` feeds into collectd:
signal strength, band power, quality, SINR, modem temperature and state, one
line per radio technology.
Split out because it drops a file into luci-app-statistics' own resource tree,
which is only meaningful when that app is installed — luci-app-statistics
lists that directory at runtime, so nothing over there has to know about us.
Feeding it needs no extra package at all:
config statistics 'collectd_exec'
option enable '1'
config collectd_exec_input
option cmdline '/usr/bin/wwandctl collectd'
endef
# Named, not globbed. The same rule the wwand package follows for its ucode
# sources: a wildcard install silently ships whatever happens to be in the tree,
# so a file added for one package lands in another — which is exactly how the
# statistics graph definition would have been installed on boxes with no
# luci-app-statistics. A new resource has to be added here, and that is the
# point of listing them.
LUCI_WWAND_VIEW:=modems.js settings.js status.js
LUCI_WWAND_RES:=bands.js esim.js format.js graph.js mccmnc.js modemopts.js \
modemsid.js netsel.js rpc.js signal.svg simlist.js
define Package/luci-app-wwand/install
$(INSTALL_DIR) $(1)/www/luci-static/resources
$(CP) $(PKG_BUILD_DIR)/htdocs/luci-static/resources/* \
$(1)/www/luci-static/resources/
$(INSTALL_DIR) $(1)/www/luci-static/resources/view/wwand
$(INSTALL_DIR) $(1)/www/luci-static/resources/wwand
for f in $(LUCI_WWAND_VIEW); do \
$(INSTALL_DATA) $(PKG_BUILD_DIR)/htdocs/luci-static/resources/view/wwand/$$$$f \
$(1)/www/luci-static/resources/view/wwand/ ; \
done
for f in $(LUCI_WWAND_RES); do \
$(INSTALL_DATA) $(PKG_BUILD_DIR)/htdocs/luci-static/resources/wwand/$$$$f \
$(1)/www/luci-static/resources/wwand/ ; \
done
$(INSTALL_DIR) $(1)/usr/share/luci/menu.d
$(INSTALL_DATA) $(PKG_BUILD_DIR)/root/usr/share/luci/menu.d/*.json \
$(INSTALL_DATA) $(PKG_BUILD_DIR)/root/usr/share/luci/menu.d/luci-app-wwand.json \
$(1)/usr/share/luci/menu.d/
$(INSTALL_DIR) $(1)/usr/share/rpcd/acl.d
$(INSTALL_DATA) $(PKG_BUILD_DIR)/root/usr/share/rpcd/acl.d/*.json \
$(INSTALL_DATA) $(PKG_BUILD_DIR)/root/usr/share/rpcd/acl.d/luci-app-wwand.json \
$(1)/usr/share/rpcd/acl.d/
endef
define Package/luci-app-wwand-statistics/install
$(INSTALL_DIR) $(1)/www/luci-static/resources/statistics/rrdtool/definitions
$(INSTALL_DATA) \
$(PKG_BUILD_DIR)/htdocs/luci-static/resources/statistics/rrdtool/definitions/wwand.js \
$(1)/www/luci-static/resources/statistics/rrdtool/definitions/
endef
$(eval $(call BuildPackage,luci-app-wwand))
$(eval $(call BuildPackage,luci-app-wwand-statistics))
+7 -5
View File
@@ -5,8 +5,10 @@
# file collision, good-citizen coexistence), and interfaces are moved to
# `proto wwand` via the migration list in the LuCI modem page.
#
# Sources live in https://github.com/ddimension/luci-proto-wwand — bump
# PKG_SOURCE_VERSION (and PKG_SOURCE_DATE) to pick up new source commits.
# Sources live in https://github.com/ddimension/luci-proto-wwand, tagged vX.Y.Z
# together with wwand. Pin a new commit with
# `scripts/bump-source.sh luci-proto-wwand <tag|commit>`: it derives PKG_VERSION
# (X.Y.Z at a tag, X.Y.Z_pN after it), PKG_RELEASE and the mirror hash.
#
# NOTE: this is built from a git PKG_SOURCE, so it does NOT use luci.mk. luci.mk
# gates its htdocs/root install on those directories living NEXT TO the Makefile
@@ -18,12 +20,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-proto-wwand
PKG_RELEASE:=9
PKG_VERSION:=1.6.6_p1
PKG_RELEASE:=10
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/ddimension/luci-proto-wwand.git
PKG_SOURCE_VERSION:=a094000b65d69b1b8048f6c6523fc6457980f33e
PKG_SOURCE_DATE:=2026-09-10
PKG_SOURCE_VERSION:=6e214de502bae28cb4badb82f4a8c0674b760aa8
PKG_MIRROR_HASH:=skip
PKG_LICENSE:=GPL-2.0-only
+11 -5
View File
@@ -37,18 +37,24 @@
# CONFIG_WWAND_UCODE_SOURCE.
#
# NOTE: the split needs the ucode source that lazy-loads every backend
# (qmi_lazy.uc + the backend-neutral daemon). Bump PKG_SOURCE_VERSION (and
# PKG_SOURCE_DATE / PKG_MIRROR_HASH) to a wwand commit that includes them.
# (qmi_lazy.uc + the backend-neutral daemon), i.e. a wwand commit that
# includes them.
#
# Version: PKG_VERSION is the wwand release the pinned commit belongs to —
# X.Y.Z for the tag vX.Y.Z (what stable ships), X.Y.Z_pN for N commits after
# it (main). Do not bump by hand: `scripts/bump-source.sh wwand <tag|commit>`
# sets PKG_SOURCE_VERSION, PKG_VERSION, PKG_RELEASE and PKG_MIRROR_HASH
# together.
include $(TOPDIR)/rules.mk
PKG_NAME:=wwand
PKG_RELEASE:=31
PKG_VERSION:=1.6.6_p16
PKG_RELEASE:=32
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/ddimension/wwand.git
PKG_SOURCE_VERSION:=c27f72e6c35d055d7abbf13eacfbc3bf2e9780a4
PKG_SOURCE_DATE:=2026-09-11
PKG_SOURCE_VERSION:=78e295b05c360787631f914e389d7394c092ba4a
PKG_MIRROR_HASH:=skip
PKG_LICENSE:=GPL-2.0-only