🎉 Sync 2026-10-13 02:34:41

This commit is contained in:
github-actions[bot] committed 2026-10-13 02:34:41 +08:00
1 parent b537a62e59
commit 72c44a6325
38 files changed
+3510 -1734

No files matched your search

@@ -302,10 +302,11 @@ call)
hw=$(uci -q get firewall.@defaults[0].flow_offloading_hw)
poll=$(uci -q get trafficctl.main.poll_interval)
window=$(uci -q get trafficctl.main.avg_window)
printf '{"enabled":%s,"default_mode":"%s","offload_mode":"%s","sw":%s,"hw":%s,"poll_interval":%d,"avg_window":%d}' \
printf '{"enabled":%s,"default_mode":"%s","offload_mode":"%s","nss":"%s","sw":%s,"hw":%s,"poll_interval":%d,"avg_window":%d}' \
"$([ "$enabled" = "1" ] && echo true || echo false)" \
"${mode:-limiter}" \
"$offload" \
"$(tctl_nss_status)" \
"$([ "$sw" = "1" ] && echo true || echo false)" \
"$([ "$hw" = "1" ] && echo true || echo false)" \
"${poll:-2}" \
@@ -43,6 +43,9 @@ if tctl_block_add "$IP" "$COMMENT"; then
# flow offload those are never re-evaluated against the new rule — the
# device would stay online over v6 indefinitely while the UI reads blocked.
MAC=$(tctl_target_mac "$IP" 2>/dev/null) && tctl_conntrack_flush_v6 "$MAC"
# Same problem one layer further out: a flow already accelerated by the
# NSS firmware is not re-evaluated against this rule either.
tctl_nss_defunct
tctl_persist_enabled && tctl_persist_save "block" "$IP" "$LABEL"
tctl_log "block" "$IP" "$LABEL" "${TCTL_VIA:-cli}" "${TCTL_SRC:-local}"
@@ -58,10 +61,12 @@ if tctl_block_add "$IP" "$COMMENT"; then
else
NOTE=" — IPv4 only: no MAC known for $IP (no DHCP lease, no neighbour entry), so IPv6 is not covered"
fi
NOTE="$NOTE$(tctl_nss_note)"
NSS="$(tctl_nss_status)"
if [ "$SELF_BLOCK" = "true" ]; then
echo "{\"ok\":true,\"ipv6\":$([ "$TCTL_BLOCK_IPV6" = "1" ] && echo true || echo false),\"msg\":\"internet blocked for $IP (your device — LuCI access preserved)$NOTE\"}"
echo "{\"ok\":true,\"ipv6\":$([ "$TCTL_BLOCK_IPV6" = "1" ] && echo true || echo false),\"nss\":\"$NSS\",\"msg\":\"internet blocked for $IP (your device — LuCI access preserved)$NOTE\"}"
else
echo "{\"ok\":true,\"ipv6\":$([ "$TCTL_BLOCK_IPV6" = "1" ] && echo true || echo false),\"msg\":\"internet blocked for $IP$NOTE\"}"
echo "{\"ok\":true,\"ipv6\":$([ "$TCTL_BLOCK_IPV6" = "1" ] && echo true || echo false),\"nss\":\"$NSS\",\"msg\":\"internet blocked for $IP$NOTE\"}"
fi
else
echo "{\"ok\":false,\"msg\":\"failed to block $IP\"}"
@@ -1149,3 +1149,81 @@ tctl_get_offload_mode() {
echo "none"
fi
}
# ── Qualcomm NSS / ECM Detection ───────────────────────────────────────────
#
# On Qualcomm NSS builds (ipq807x/ipq60xx with qca-nss-ecm, qca-nss-drv and
# qca-nss-clients) the Enhanced Connection Manager installs flow rules into
# the NSS firmware cores, and an accelerated flow NEVER TRAVERSES THE LINUX
# NETWORK STACK. Both of this package's enforcement paths are therefore inert
# on such a router: the nftables netdev policer never sees the packets, and
# Linux HTB/IFB never gets to queue them. Reported in issue #81, where a user
# applied WiFi client limits and nothing was limited.
#
# Nothing here attempts to fix that — an NSS backend would mean nsshtb/nsstbl
# and act_nssmirred in place of htb and ifb, which needs hardware to develop
# against. What these helpers do is let the callers STOP REPORTING SUCCESS for
# a rule that cannot be enforced, which is the part that can be made correct
# without hardware and is the actual complaint.
#
# Named by absolute path, as it must be on the router; the tests sed this line
# into their scratch directory (same approach as TCTL_LEASES_FILE above).
TCTL_ECM_DEBUGFS="/sys/kernel/debug/ecm"
# absent — no ECM on this system; every rule this package installs applies
# normally and nothing needs qualifying.
# stopped — ECM is present but its front end has been stopped, so flows are
# back on the Linux path and our rules DO apply.
# active — ECM is accelerating. Our rules are installed but not enforced for
# any accelerated flow.
#
# The distinction between "stopped" and "active" is deliberate and is read
# from the kernel rather than inferred from the packages installed: somebody
# who has turned acceleration off to get working QoS must not be told their
# limits do not work.
tctl_nss_status() {
[ -d "$TCTL_ECM_DEBUGFS" ] || { printf 'absent\n'; return 0; }
local v4 v6
v4=$(cat "$TCTL_ECM_DEBUGFS/front_end_ipv4_stop" 2>/dev/null)
v6=$(cat "$TCTL_ECM_DEBUGFS/front_end_ipv6_stop" 2>/dev/null)
# Both stopped is the only state in which the Linux datapath is whole. A
# missing knob is NOT read as stopped: an ECM build whose debugfs layout we
# do not recognise is still accelerating, and guessing "stopped" there
# would restore exactly the false success this exists to remove.
if [ "$v4" = "1" ] && [ "$v6" = "1" ]; then
printf 'stopped\n'
else
printf 'active\n'
fi
}
tctl_nss_active() { [ "$(tctl_nss_status)" = "active" ]; }
# The suffix every caller appends to its own message. One wording in one place,
# so the limiter, the shaper and the block path cannot drift into describing
# the same condition three different ways.
tctl_nss_note() {
tctl_nss_active || return 0
printf ' [NOT ENFORCED: Qualcomm NSS acceleration is active — accelerated flows bypass the Linux network stack entirely, so this rule is installed but has no effect. Stop acceleration (echo 1 > %s/front_end_ipv4_stop, same for ipv6) or set acceleration_engine in /etc/config/ecm, then re-apply]' \
"$TCTL_ECM_DEBUGFS"
}
# Defunct the flows ECM has already accelerated.
#
# This is the NSS counterpart of the `conntrack -D` every rule change already
# does, and it is needed for the same reason: a flow that is ALREADY offloaded
# is not re-evaluated against a rule that appeared afterwards, so without this
# a new block or limit would only affect connections started later — which
# presents as "it works sometimes".
#
# Best effort by design: absent on every non-NSS router, and a write failure
# must never fail the operation the caller is performing. UNVERIFIED ON
# HARDWARE — there is no IPQ device here to test it against, so it is wired in
# as an addition to the existing flush rather than as something any result
# depends on.
tctl_nss_defunct() {
local f="$TCTL_ECM_DEBUGFS/ecm_db/defunct_all"
[ -w "$f" ] || return 0
echo 1 > "$f" 2>/dev/null || true
return 0
}
@@ -96,11 +96,21 @@ else
tctl_persist_enabled && tctl_persist_save "ratelimit" "$IP" "$RATE" "$MODE" \
"$([ "$RATE_UP" != "$RATE" ] && echo "$RATE_UP")"
tctl_log "ratelimit_set" "$IP" "$(rate_text)" "${TCTL_VIA:-cli}" "${TCTL_SRC:-local}"
# An already-accelerated flow is not re-evaluated against a rule that
# appeared afterwards, exactly as with flow offload — so the NSS flow
# table gets the same treatment the conntrack table does.
tctl_nss_defunct
# On a Qualcomm NSS router the rules above are installed and inert: the
# packets never reach the Linux stack. Reporting an unqualified success
# there is the defect in #81, so every message below carries the verdict
# and the result carries the status as a field.
NSSNOTE="$(tctl_nss_note)"
NSS="$(tctl_nss_status)"
if [ "$TCTL_RL_DOWNLOAD_FAILED" = "1" ]; then
echo "{\"ok\":true,\"ipv6_upload\":$([ "$TCTL_RL_UPLOAD6_OK" = "1" ] && echo true || echo false),\"msg\":\"rate limit ${RATE_UP} kbit/s applied to $IP UPLOAD ONLY — WAN device not resolvable$V6NOTE\"}"
echo "{\"ok\":true,\"ipv6_upload\":$([ "$TCTL_RL_UPLOAD6_OK" = "1" ] && echo true || echo false),\"nss\":\"$NSS\",\"msg\":\"rate limit ${RATE_UP} kbit/s applied to $IP UPLOAD ONLY — WAN device not resolvable$V6NOTE$NSSNOTE\"}"
elif [ "$TCTL_RL_UPLOAD_FAILED" = "1" ]; then
echo "{\"ok\":true,\"ipv6_upload\":false,\"msg\":\"rate limit ${RATE} kbit/s applied to $IP DOWNLOAD ONLY — no LAN ingress device [IPv4 only]\"}"
echo "{\"ok\":true,\"ipv6_upload\":false,\"nss\":\"$NSS\",\"msg\":\"rate limit ${RATE} kbit/s applied to $IP DOWNLOAD ONLY — no LAN ingress device [IPv4 only]$NSSNOTE\"}"
else
echo "{\"ok\":true,\"ipv6_upload\":$([ "$TCTL_RL_UPLOAD6_OK" = "1" ] && echo true || echo false),\"msg\":\"rate limit $(rate_text) for $IP (both directions, $MODE)$V6NOTE\"}"
echo "{\"ok\":true,\"ipv6_upload\":$([ "$TCTL_RL_UPLOAD6_OK" = "1" ] && echo true || echo false),\"nss\":\"$NSS\",\"msg\":\"rate limit $(rate_text) for $IP (both directions, $MODE)$V6NOTE$NSSNOTE\"}"
fi
fi
@@ -303,14 +303,21 @@ do_add() {
save_shape "$IP" "$RATE" "$classid" "$RATE_UP"
local rates="${RATE} kbit/s"
[ "$RATE_UP" != "$RATE" ] && rates="${RATE} kbit/s down, ${RATE_UP} kbit/s up"
# Linux HTB never gets to queue a flow the NSS firmware is forwarding, so
# a shape on such a router is installed and inert (#81). NSS builds ship
# their own qdiscs (nsshtb/nsstbl) which this does not yet use; until it
# does, say so instead of reporting success.
tctl_nss_defunct
NSSNOTE="$(tctl_nss_note)"
NSS="$(tctl_nss_status)"
if [ "$ul_ok" = "1" ]; then
echo "{\"ok\":true,\"msg\":\"shape $rates applied to $IP both directions (class $classid)\"}"
echo "{\"ok\":true,\"nss\":\"$NSS\",\"msg\":\"shape $rates applied to $IP both directions (class $classid)$NSSNOTE\"}"
elif [ "$RATE_UP" != "$RATE" ]; then
# Saying "applied" here would be a lie about the half the operator
# explicitly asked to differ.
echo "{\"ok\":true,\"msg\":\"shape ${RATE} kbit/s applied to $IP for download only — the ${RATE_UP} kbit/s upload ceiling was NOT applied, it needs kmod-ifb: opkg/apk install kmod-ifb kmod-sched\"}"
echo "{\"ok\":true,\"nss\":\"$NSS\",\"msg\":\"shape ${RATE} kbit/s applied to $IP for download only — the ${RATE_UP} kbit/s upload ceiling was NOT applied, it needs kmod-ifb: opkg/apk install kmod-ifb kmod-sched$NSSNOTE\"}"
else
echo "{\"ok\":true,\"msg\":\"shape ${RATE} kbit/s applied to $IP (download only — upload needs kmod-ifb: opkg/apk install kmod-ifb kmod-sched)\"}"
echo "{\"ok\":true,\"nss\":\"$NSS\",\"msg\":\"shape ${RATE} kbit/s applied to $IP (download only — upload needs kmod-ifb: opkg/apk install kmod-ifb kmod-sched)$NSSNOTE\"}"
fi
}