Complete Phase 12 dual-stack networking

Add IPv6-aware Wi-Fi state, HTTPS/SSH listeners, mDNS service
reconciliation, and browser Wi-Fi administration.

Include a guarded build-local fix for mDNS 1.12.0 membership handling,
focused regression suites, and Phase 12 acceptance documentation.
This commit is contained in:
2026-09-20 22:35:34 +02:00
parent ece4ba77e3
commit 8902b25d78
52 changed files with 3042 additions and 163 deletions
+57
View File
@@ -0,0 +1,57 @@
# Phase 12 SSH listener regression
Run `python3 tests/ssh_phase12/run.py` from the repository root. Requires Python 3
and a host C compiler (`cc`). It extracts the production functions without rewriting
them and compiles with `-std=c11 -Wall -Wextra -Werror`.
Coverage:
- One AF_INET6/TCP wildcard listener, explicitly checked IPV6_V6ONLY=0 before bind.
- Failures at socket, dual-stack option, bind, listen and both nonblocking steps:
no published descriptor, exactly one close after socket allocation.
- Permanent accept errors withdraw availability, invalidate the service generation
without wrap, and request owner-side session cleanup; temporary errors retain
the listener. Capacity rejection respects the existing four-accept budget.
- IPv4, global IPv6, scoped link-local IPv6, mapped IPv4, maximum numeric scope,
unknown families, and bounded/truncated peer formatting.
`tests/ssh_management/runtime.py` additionally tests availability across real
production start/stop functions with context/listener/cleanup doubles, including
failed session cleanup retaining the context, successful restart, and failed start.
Existing management/security regressions remain responsible for authentication,
identity ownership and generation fencing.
## SDK contract checked during implementation
Read-only inspection of installed ESP-IDF **5.5.0**
(`framework-espidf@3.50500.0`, not the separately installed 5.5.3):
- `components/lwip/lwip/src/include/lwip/sockets.h`: `sockaddr_in6.sin6_scope_id`
is `u32_t`; `IPV6_V6ONLY` is supported.
- `components/lwip/lwip/src/include/lwip/inet.h`: `IN6ADDR_ANY_INIT`.
- `components/lwip/lwip/src/api/sockets.c`: `lwip_setsockopt_impl` applies
`netconn_set_ipv6only`; `IP6ADDR_PORT_TO_SOCKADDR` copies the interface zone;
`lwip_accept` publishes the peer address and maps a closed listener to EINVAL,
non-TCP sockets to EOPNOTSUPP, and descriptor exhaustion to ENFILE.
- `components/lwip/lwip/src/api/api_msg.c`: wildcard IPv6 bind/listen with V6ONLY
disabled selects `IPADDR_TYPE_ANY`, accepting both families on one listener.
These host doubles do **not** execute lwIP, actual networking, wolfSSH handshakes,
RTOS concurrency, discovery traffic or hardware. No PlatformIO build is required
or claimed. Device follow-up must verify IPv4-only, IPv6-only and dual-stack
connections, scoped link-local access, address changes/reconnects, simultaneous
clients and binary UART traffic, advertisement convergence after start/stop/error,
and independent UART0/native USB recovery.
## Resource and handoff notes
No extra socket, task, session, queue, I/O buffer or heap allocation is introduced.
Peer arrays grow from 48 to 65 bytes: 68 additional raw bytes across the two owner
slots and two published snapshots, plus ABI padding. Public snapshot copies also
grow. Exact linked RAM/flash and stack headroom were not measured without a target
build; host tests do not establish ESP32 memory headroom.
Project agent memory is intentionally not edited under the exclusive-write scope.
The parent should record the one-socket dual-stack contract, scoped peer capacity,
and owner-driven mDNS availability (eventual Wi-Fi-manager reconciliation) in the
relevant durable memory when integrating Phase 12.