Files
kernel-agent/patches/driver/bes2600/decrypt-storm-fast-recover-danctnix/0001-bes2600-pre-empt-AP-deauth-6-mac80211-reassoc-on-decrypt-fail-storm.patch
T
claude-noether 8b356aa11f patches/driver/bes2600/*-danctnix: reconstruct from cleanups (closes #29)
Replaces the 13 broken DKMS-path -danctnix mirrors from PR #17 + adds
9 new series-dirs for the c-stack patches that were never split
(Patches A/B/C-v3/F/D/E/C2/G/H) + retires the cumulative-c5x-danctnix
single-file interim from fleet/ohm.yaml.

Mechanism:
  cd marfrit/bes2600-dkms-mobian
  git format-patch fe73571..cleanups --no-merges -o /tmp/cleanups/
  git format-patch cleanups..bes2600/bh-c-fossil-cleanup --no-merges -o /tmp/h/
  for each commit: route to series-dir, sed-rewrite
                   a/bes2600/foo.c -> a/drivers/staging/bes2600/foo.c

The 29 cleanups commits + 1 Patch H commit map to 25 series-dirs (a
few series-dirs get multiple commits: lmac-recover gets c5.2 + c5.2.1
as 0001+0002; cw1200-fix-backports gets F3+F2+F1 as 0001-0003;
factory-series gets request_firmware + STANDARD_FACTORY_EFUSE_FLAG
as 0001+0002).

fleet/ohm.yaml apply order matches cleanups commit chronology, which
is what produced the working c5x interim. cumulative.patch from
ka-promote ohm now has 32 resolved patches (29 cleanups + 1 Patch H
+ scan-filter-5ghz + xor-neon SCS + besser#18-fix), 276 079 bytes,
b2sum 7418db5ddf8fe938b130bc9d0e9f7dc9060f3a13703cd50757835ac43140a13...

Apply order in cleanups + bh-c-fossil-cleanup:
  1   factory-series                       (c1 + factory-no-efuse-flag)
  3   factory-thread-dev
  4   pm-gate-on-handshake
  5   remove-chardev-user-interface
  6   enable-testmode
  7   tx-sdio-dma-oob-danctnix             (was 'staging-prep-series')
  8   factory-drop-kernel-write-danctnix
  9   drop-dpd-file-paths-danctnix
  10  drop-orphan-file-io-danctnix
  11  pm-timeout-silence-danctnix
  12  scan-defer-on-reject-danctnix        (c5.1)
  13  scan-defer-backoff-tune-danctnix     (c5.1.1)
  14  lmac-recover-via-mmc-hw-reset-danctnix  (c5.2 + c5.2.1)
  16  pm-state-resync-danctnix             (c6.1)
  17  pm-wake-consume-state-danctnix       (c6.2)
  18  pm-detect-firmware-unsupported-danctnix (c7)
  19  decrypt-storm-fast-recover-danctnix  (Patch A)
  20  connection-loss-fast-recover-danctnix (Patch B)
  21  cw1200-fix-backports-danctnix        (Patches F3 + F2 + F1)
  24  sdio-rx-no-relay-danctnix            (Patch C v3)
  25  license-spdx-restore-attribution-danctnix (Patch G)
  26  ba-lock-atomic-danctnix              (Patch D)
  27  ps-state-lock-skip-pm-disabled-danctnix (Patch E)
  28  rx-list-batch-delivery-danctnix      (Patch C2)
  29  bh-c-fossil-cleanup-danctnix         (Patch H)
  30  scan-filter-5ghz-danctnix            (besser#1)
  31  arch/arm64/xor-neon-...              (GCC 15 SCS)
  32  queue-pending-record-lock-bh-danctnix (besser#18)

Verification: pkgrel=6 build from this manifest in progress; if
srcversion == 26B0003FE9F2B05DCE838C4 (pkgrel=5's), source-tree is
byte-equivalent to the c5x interim + scan-filter + besser#18 stack
that's currently running on ohm.

Refs: #17 (the broken mirror), #28 (the interim PR that landed
cumulative-c5x), #31 (ka-promote trailer normalisation followup).
2026-05-19 06:41:37 +02:00

222 lines
9.0 KiB
Diff

From d0f14e3ba7408e81ed4c76941b8a4c7abda0c61b Mon Sep 17 00:00:00 2001
From: "Claude (noether)" <claude@reauktion.de>
Date: Wed, 6 May 2026 19:50:52 +0200
Subject: [PATCH 20/29] bes2600: pre-empt AP-deauth-6 with mac80211 reassoc on
decrypt-fail storm
When the BES2600 firmware reports WSM_STATUS_DECRYPTFAILURE for a burst
of received frames (typically because the host's PTK or GTK has fallen
out of sync with the AP), the AP eventually concludes that the STA is
not authenticated and emits an unprotected deauth-reason-6 ("Class 2
frame received from non-authenticated station"). On the deployed
pinetab2 + bes2600 stack this AP-initiated deauth has been observed to
leave the link blackholed for up to 109 s before userspace finds a
different SSID/channel to recover on. (Receipts at
https://git.reauktion.de/marfrit/besser, notes/phase5-2026-05-06.md.)
Add a sliding-window counter on each bes2600_vif: when 5 decrypt
failures fire within 5 s, schedule a worker that calls
ieee80211_connection_loss(vif). mac80211 then performs immediate
disassociation; userspace (NetworkManager / wpa_supplicant) reconnects
with fresh keys before the AP gets a chance to fire its unprotected
deauth.
Predicted Phase 7 delta vs the unpatched baseline:
- decrypt-burst rate: unchanged (this does not address root cause)
- AP-deauth-6 rate: <= 0.2 of baseline
- conditional probability of >5s blackhole given a burst:
100% -> <= 10%
- worst-case recovery time: 109s -> <5s
Contract pin: ieee80211_connection_loss() per
include/net/mac80211.h: "may also be called if the connection needs to
be terminated for some other reason... will cause immediate change to
disassociated state, without connection recovery attempts." Userspace
recovery is the existing NM/wpa_supplicant path. The worker context
satisfies the implicit process-context expectation.
Files touched:
- bes2600/bes2600.h: 4 new fields on struct bes2600_vif + 2 prototypes
- bes2600/txrx.c: new helpers + the call site at the existing
WSM_STATUS_DECRYPTFAILURE log point (the unconditional "goto drop"
branch in bes2600_rx_cb)
- bes2600/sta.c: bes2600_decrypt_storm_init() in bes2600_vif_setup;
cancel_work_sync() in bes2600_remove_interface, alongside the
existing per-vif cancel_*_work_sync block. Safe under the kernel
cancel_work_sync contract: the work_struct is INIT_WORK'd in setup,
so the call is valid; it blocks until any in-flight handler returns,
ensuring no use-after-free of priv when mac80211 frees the vif; and
it is idempotent (subsequent calls just return false).
- bes2600/debug.c: DecryptStormRecoveries seq_printf in the per-vif
status seq_file output
Threshold (5/5s) is set well above the steady-state per-vif decrypt-
fail rate observed in measurement (~1/min even under sustained 1 MB/s
load), so a true storm is required to trip it. The cw1200/cw1260
ancestor has no equivalent storm-recovery; this is a clean addition.
checkpatch.pl --no-tree --strict: clean (0/0/0).
Signed-off-by: Claude (noether) <claude@reauktion.de>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
bes2600/bes2600.h | 9 ++++++
bes2600/debug.c | 2 ++
bes2600/sta.c | 2 ++
bes2600/txrx.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 87 insertions(+)
diff --git a/drivers/staging/bes2600/bes2600.h b/drivers/staging/bes2600/bes2600.h
index 0e60960..66482f7 100644
--- a/drivers/staging/bes2600/bes2600.h
+++ b/drivers/staging/bes2600/bes2600.h
@@ -596,6 +596,11 @@ struct bes2600_vif {
unsigned long rx_timestamp;
u32 cipherType;
+ /* Decrypt-storm fast-recover (Trigger B). See txrx.c. */
+ unsigned long decrypt_storm_window_start;
+ unsigned int decrypt_storm_count;
+ unsigned int decrypt_storm_recoveries;
+ struct work_struct decrypt_storm_recover_work;
/* AP powersave */
u32 link_id_map;
@@ -856,4 +861,8 @@ int bes2600_btusb_setup_pipes(struct sbus_priv *sbus_priv);
void bes2600_btusb_uninit(struct usb_interface *interface);
#endif
+/* Decrypt-storm fast-recover helpers — see txrx.c. */
+void bes2600_decrypt_storm_init(struct bes2600_vif *priv);
+void bes2600_decrypt_storm_account(struct bes2600_vif *priv);
+
#endif /* BES2600_H */
diff --git a/drivers/staging/bes2600/debug.c b/drivers/staging/bes2600/debug.c
index 5228b22..ca223dd 100644
--- a/drivers/staging/bes2600/debug.c
+++ b/drivers/staging/bes2600/debug.c
@@ -542,6 +542,8 @@ static int bes2600_status_show_priv(struct seq_file *seq, void *v)
priv->listening ? " (listening)" : "");
seq_printf(seq, "Assoc: %s\n",
bes2600_debug_join_status[priv->join_status]);
+ seq_printf(seq, "DecryptStormRecoveries: %u\n",
+ priv->decrypt_storm_recoveries);
if (priv->rx_filter.promiscuous)
seq_puts(seq, "Filter: promisc\n");
else if (priv->rx_filter.fcs)
diff --git a/drivers/staging/bes2600/sta.c b/drivers/staging/bes2600/sta.c
index 5f1a456..1b78051 100644
--- a/drivers/staging/bes2600/sta.c
+++ b/drivers/staging/bes2600/sta.c
@@ -448,6 +448,7 @@ void bes2600_remove_interface(struct ieee80211_hw *dev,
cancel_delayed_work_sync(&priv->join_timeout);
cancel_delayed_work_sync(&priv->set_cts_work);
cancel_delayed_work_sync(&priv->pending_offchanneltx_work);
+ cancel_work_sync(&priv->decrypt_storm_recover_work);
del_timer_sync(&priv->mcast_timeout);
/* TODO:COMBO: May be reset of these variables "delayed_link_loss and
@@ -2619,6 +2620,7 @@ int bes2600_vif_setup(struct bes2600_vif *priv)
/* Setup per vif workitems and locks */
spin_lock_init(&priv->vif_lock);
+ bes2600_decrypt_storm_init(priv);
INIT_WORK(&priv->join_work, bes2600_join_work);
INIT_DELAYED_WORK(&priv->join_timeout, bes2600_join_timeout);
INIT_WORK(&priv->unjoin_work, bes2600_unjoin_work);
diff --git a/drivers/staging/bes2600/txrx.c b/drivers/staging/bes2600/txrx.c
index dbd1b23..346312c 100644
--- a/drivers/staging/bes2600/txrx.c
+++ b/drivers/staging/bes2600/txrx.c
@@ -25,6 +25,78 @@
#define BES2600_INVALID_RATE_ID (0xFF)
+/*
+ * Decrypt-storm fast-recover (Trigger B).
+ *
+ * When the BES2600 firmware reports WSM_STATUS_DECRYPTFAILURE for a
+ * burst of received frames (typically because the host's PTK or GTK
+ * has fallen out of sync with the AP), the AP eventually concludes that
+ * the STA is not authenticated and emits an unprotected deauth-reason-6
+ * ("Class 2 frame received from non-authenticated station"). On the
+ * deployed pinetab2 + bes2600 stack this AP-initiated deauth has been
+ * observed to leave the link blackholed for up to 109 s before
+ * userspace finds a different SSID/channel to recover on. (Receipts at
+ * https://git.reauktion.de/marfrit/besser, notes/phase5-2026-05-06.md.)
+ *
+ * Recovery here pre-empts the AP: when we see THRESHOLD decrypt
+ * failures within WINDOW, we ask mac80211 for a clean reassoc via
+ * ieee80211_connection_loss(), which causes immediate disassociation
+ * and lets userspace auto-reconnect with fresh keys.
+ *
+ * mac80211 contract: ieee80211_connection_loss() may be called
+ * regardless of IEEE80211_HW_CONNECTION_MONITOR; it causes immediate
+ * disassociation without driver-side recovery attempts. See
+ * include/net/mac80211.h for the canonical doc-comment.
+ *
+ * The threshold is set well above the steady-state per-vif
+ * decrypt-fail rate observed in measurement (~1/min even under
+ * sustained 1 MB/s load), so a true storm is required to trip it.
+ */
+#define BES2600_DECRYPT_STORM_THRESHOLD 5
+#define BES2600_DECRYPT_STORM_WINDOW_MS 5000
+
+static void bes2600_decrypt_storm_recover_work(struct work_struct *work)
+{
+ struct bes2600_vif *priv = container_of(work, struct bes2600_vif,
+ decrypt_storm_recover_work);
+
+ if (!priv->vif)
+ return;
+
+ bes_warn("[bes2600] decrypt-storm fast-recover: forcing reassoc\n");
+ ieee80211_connection_loss(priv->vif);
+ priv->decrypt_storm_recoveries++;
+}
+
+void bes2600_decrypt_storm_init(struct bes2600_vif *priv)
+{
+ INIT_WORK(&priv->decrypt_storm_recover_work,
+ bes2600_decrypt_storm_recover_work);
+ priv->decrypt_storm_window_start = 0;
+ priv->decrypt_storm_count = 0;
+ priv->decrypt_storm_recoveries = 0;
+}
+
+void bes2600_decrypt_storm_account(struct bes2600_vif *priv)
+{
+ unsigned long now = jiffies;
+ unsigned long window = msecs_to_jiffies(BES2600_DECRYPT_STORM_WINDOW_MS);
+
+ if (priv->decrypt_storm_window_start == 0 ||
+ time_after(now, priv->decrypt_storm_window_start + window)) {
+ priv->decrypt_storm_window_start = now;
+ priv->decrypt_storm_count = 1;
+ return;
+ }
+
+ if (++priv->decrypt_storm_count >= BES2600_DECRYPT_STORM_THRESHOLD) {
+ priv->decrypt_storm_count = 0;
+ /* Skew the window so we don't re-fire on the same storm. */
+ priv->decrypt_storm_window_start = now + window;
+ schedule_work(&priv->decrypt_storm_recover_work);
+ }
+}
+
#ifdef CONFIG_BES2600_TESTMODE
#include "bes_nl80211_testmode_msg.h"
#endif /* CONFIG_BES2600_TESTMODE */
@@ -1672,6 +1744,8 @@ void bes2600_rx_cb(struct bes2600_vif *priv,
goto drop;
} else {
bes_warn("[RX] Receive failure: %d.\n", arg->status);
+ if (arg->status == WSM_STATUS_DECRYPTFAILURE)
+ bes2600_decrypt_storm_account(priv);
goto drop;
}
}
--
2.54.0