fix: Recovery-Rettungsweg gegen Endlosschleife, Werksreset und Fremdnetz absichern

C1: nl_recovery_try() verglich nichts mit der laufenden Firmware. Vor einer
laufenden Rettungsstation - laut Spec 9 der Normalfall, solange eine Charge
migriert - lud die Lampe bei jedem Boot dasselbe Image erneut und startete neu.
nl_ota_start_recovery() setzt jetzt ein Flag, das nl_fwup_verify_tail() nur im
Recovery-Pfad auswerten lässt: Trägt der Trailer dieselbe Version wie die
laufende Firmware (nl_trailer_version(), bisher ungenutzt), wird das Image
abgelehnt und der Boot läuft normal weiter. Ein absichtliches Neuflashen
derselben Version über die REST-API bleibt möglich. Kein neuer Wert im
last_ota_error-Vokabular (Spec 8.2); unterschieden wird über die Konsole.

C1/I2: boot_ms wurde erst nach nl_recovery_try() gesetzt und der
Power-Cycle-Zähler erst 5 s danach gelöscht - bei erfolgreicher Recovery also
nie. Nach NL_FACTORY_RESET_BOOTS Runden folgte ein Werksreset. boot_ms startet
jetzt am Anfang von app_task (damit gilt wieder das in Spec 3.4/5.5 zugesagte
5-s-Fenster statt faktisch ~8 s), und nl_app_clear_reset_counter() löscht den
Zähler zusätzlich, sobald die Rettungsstation gefunden ist.

I3: tls_sys_auto_mode_run() stößt direkt nach UserMain() einen SDK-eigenen
Verbindungsversuch an; das dabei entstehende NETIF_IP_NET_UP war in
recovery_join_event() nicht vom eigenen Join zu unterscheiden, die Lampe hätte
im falschen Netz geladen. nl_wifi_disable_sdk_auto_connect() schaltet das Flag
ab und kappt eine bestehende Verbindung jetzt am Anfang von nl_recovery_try();
zusätzlich prüft nl_wifi_join_blocking() über tls_wifi_get_current_bss(), dass
die Verbindung wirklich zur erwarteten SSID gehört.
This commit is contained in:
Michael Sauer
2026-09-12 03:14:16 +02:00
parent 1822014e48
commit cf1e6a351c
7 changed files with 119 additions and 19 deletions
+36 -7
View File
@@ -69,6 +69,25 @@ static void clear_softap_param(void)
}
}
/* I3: tls_sys_auto_mode_run() (sdk/Platform/Sys/tls_sys.c) kicks off an SDK-driven join right
* after UserMain() whenever the persisted auto-connect flag is on - and tls_sys.c:264-267 turns a
* temporary WIFI_AUTO_CNT_TMP_OFF back into WIFI_AUTO_CNT_ON and persists that, so the factory
* default "off" is not something we may rely on. Any such join is indistinguishable from our own
* in the netif status events, so switch it off (and drop a connection it already made) before the
* recovery scan, not only in nl_wifi_start(). */
void nl_wifi_disable_sdk_auto_connect(void)
{
u8 auto_mode = WIFI_AUTO_CNT_OFF;
tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_GET, &auto_mode);
if (auto_mode != WIFI_AUTO_CNT_OFF) {
printf("wifi: SDK auto-connect was %u, switching it off\r\n", auto_mode);
auto_mode = WIFI_AUTO_CNT_OFF;
tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_SET, &auto_mode);
}
tls_wifi_disconnect();
}
static void start_ap(void)
{
struct tls_softap_info_t ap;
@@ -220,7 +239,6 @@ static void net_task(void *arg)
void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass)
{
u8 auto_mode = WIFI_AUTO_CNT_OFF;
nl_wifi_actions_t a;
struct netif *nif;
@@ -233,12 +251,7 @@ void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass)
copy(s_ap_pass, ap_pass, sizeof s_ap_pass);
/* The SDK would otherwise reconnect on its own with credentials from its parameter area. */
tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_GET, &auto_mode);
if (auto_mode != WIFI_AUTO_CNT_OFF) {
auto_mode = WIFI_AUTO_CNT_OFF;
tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_SET, &auto_mode);
}
tls_wifi_disconnect();
nl_wifi_disable_sdk_auto_connect();
if (tls_wifi_softap_get_state() != WM_WIFI_DISCONNECTED) {
/* C2: the SDK already brought up its own softap (e.g. from NETIF_IP_NET_UP before
* we got a chance to run) - tear it down, our state machine owns AP lifecycle. */
@@ -369,5 +382,21 @@ bool nl_wifi_join_blocking(const char *ssid, const char *pass, uint32_t timeout_
tls_wifi_disconnect(); /* cancel a stuck/timed-out join attempt */
return false;
}
/* I3 backstop: NETIF_IP_NET_UP carries no SSID, so an SDK-initiated join to its own stored AP
* would look exactly like ours - and the lamp would then fetch http://192.168.8.1/... from a
* foreign network. tls_wifi_get_current_bss() does report the SSID actually associated with
* (sdk/Include/WiFi/wm_wifi.h:190), so verify it before the caller starts a download. */
{
struct tls_curr_bss_t bss;
size_t n = strlen(ssid);
memset(&bss, 0, sizeof bss);
tls_wifi_get_current_bss(&bss);
if (bss.ssid_len != (u8)n || memcmp(bss.ssid, ssid, n) != 0) {
printf("wifi: joined a different network than %s, dropping the connection\r\n", ssid);
tls_wifi_disconnect();
return false;
}
}
return true;
}