Fix hello generation endianness for discovery/quick hello frames#18
Merged
d3vi1 merged 13 commits intoclaude/lltd-darwin-automata-OloDEfrom Jan 7, 2026
Merged
Conversation
commit c310def Author: Claude <noreply@anthropic.com> Date: Wed Jan 7 20:32:46 2026 +0000 Add Darwin Wi-Fi metrics module using CoreWLAN Implements a pure C API for querying Wi-Fi metrics on macOS using CoreWLAN framework. This provides RSSI, noise, link speed, and computed max link speed without relying on external commands like wdutil or airport. New files: - lltd_wifi_corewlan.h: Pure C89 header with lltd_wifi_metrics_t struct - lltd_wifi_corewlan.m: Objective-C shim using CoreWLAN - lltd_wifi_rates.c/h: Rate calculation tables for HT/VHT/HE PHY modes - tools/lltd-wifi-metrics.c: CLI tool for testing/verification Features: - Queries RSSI, noise, PHY mode, channel width via CoreWLAN - Computes theoretical max link speed based on PHY/width/NSS - Infers NSS/MCS/GI from observed link rate using rate tables - Supports 802.11a/b/g/n/ac/ax with proper rate calculations - Handles missing selectors gracefully for SDK compatibility The module is Darwin-only and guarded with __APPLE__. No Objective-C headers leak into the cross-platform C codebase. commit e35ad88 Author: Claude <noreply@anthropic.com> Date: Wed Jan 7 20:21:10 2026 +0000 Add single-instance check to prevent duplicate daemon processes AF_NDRV sockets allow multiple binds but only the first process receives packets. This caused silent failures when a previous lltdDaemon was still running. Now uses flock() on /tmp/lltdDaemon.lock to ensure only one instance runs at a time.
d3vi1
added a commit
that referenced
this pull request
Jan 7, 2026
#10) * Implement LLTD automata v1.0 for Darwin with session table and RepeatBand Core automata improvements: - Fix switch_state_session() to return session automaton instead of calling switch_state_mapping() which caused infinite recursion on timeout - Session automaton now starts in Nascent state (index 1) instead of Temporary (index 0) as per LLTD specification - Add mapping_state struct for charge timeout counter (ctc) and inactive timeout tracking (30s inactive timeout per spec) Session table implementation: - New session_table struct keyed by mapper MAC + generation/seq number - Supports up to 16 concurrent mapper sessions (SESSION_TABLE_MAX_ENTRIES) - Automatic eviction of oldest entries when table is full - Session entries track state, completion status, and timestamps - derive_session_event() analyzes frames to produce sess_* events: sess_discover_acking, sess_discover_noack, sess_discover_conflicting, _chgd_xid variants, sess_topo_reset, sess_reset, sess_hello RepeatBand algorithm: - band_state extended with hello_timeout_ts and block_timeout_ts (ms) - band_init_stats(): Initialize Ni=ALPHA, r=0, begun=false - band_update_stats(): Update Ni = min(NMAX, ALPHA * r^BETA) on block timeout - band_choose_hello_time(): Calculate next hello based on RepeatBand formula - band_do_hello(): Schedule next hello, mark begun=true - band_on_hello_received(): Increment r counter Periodic tick mechanism: - automata_tick() called every 100ms via select() timeout in lltdLoop - Handles mapping inactive/charge timeouts even without RX traffic - Processes session table timeouts (1s inactivity removal) - Triggers hello/block timeouts for enumeration RepeatBand Darwin integration: - network_interface_t gains sessionTable and MapperGeneration fields - sendHelloMessage() broadcasts Hello with RepeatBand timing - lltdLoop uses select() with 100ms timeout for tick-driven timeouts - Session table cleaned up in deviceDisappeared() TLV correctness fixes: - Fix setUuidTLV() to copy from machineUUID, not from TLV header pointer - setHardwareIdTLV() properly calls getHwId() on Darwin - setDetailedIconTLV() and setComponentTableTLV() call platform functions - All TLV stubs return 0 or proper data, maintaining frame validity Testing: - Extended test_lltd_tlv_ops.c with UUID size, endianness, EOP marker tests - Updated lltdTestShim.h with new network_interface_t fields - Added stub implementations for getHwId, getDetailedIconImage, getComponentTable Documentation: - Added Documentation/automata_runtime.md describing how the three automata (Mapping, Session, Enumeration) are wired into the Darwin runtime * Move platform-specific TLV code to platform ops pattern - Remove #ifdef __APPLE__ guards from lltdTlvOps.c, now calls platform ops functions unconditionally (getHwId, getDetailedIconImage, etc.) - Add sendHelloMessage stub to win32 stubs to fix Windows linker error - Follows project pattern: all OS-specific code in ${os}_ops.c * Fix getBSSID null pointer crash when Wi-Fi not associated - Check cfDATA is not NULL before using in getBSSID (darwin-ops.c) - Fix setBSSIDTLV to properly use allocated pointer from getBSSID, matching pattern used by setSSIDTLV and other TLV functions - Return 0 (skip TLV) when BSSID unavailable instead of crashing * Fix WiFi property lookup for Skywalk stack on Tahoe+ - Update copyWiFiProperty() to search parent nodes when property not found in children. On Skywalk, WiFi properties (IO80211BSSID, IO80211SSID, etc.) are on IOUserNetworkWLAN which is a parent of the BSD interface node. - Refactor getBSSID() to use copyWiFiProperty() for consistent behavior across legacy IOKit and Skywalk stacks - Add type and size validation for BSSID CFData * Fix automata issues: session timeout, RepeatBand transitions - Increase session timeout from 1s to 60s - sessions were expiring before any useful work could be done - Add transition Quiescent -> Pausing with enum_new_session so that receiving a Discover frame immediately starts enumeration - Change Wait -> Quiescent transition for enum_sess_complete so that empty session table returns to idle state - Add Wait -> Pausing transition for enum_new_session - Prevent tick spam by not sending inputs when already in Quiescent - Only transition Wait -> Quiescent when table is empty, not from Pausing * Fix RepeatBand infinite loop after Reset received - When session table becomes empty (Reset received), immediately return RepeatBand to Quiescent state from any active state (Pausing or Wait) - Reset hello and block timeouts when returning to Quiescent - Prevents infinite Hello sending loop after mapping session ends * Update .gitignore for XCode build artifacts * Clear session table when Mapping transitions to Quiescent on timeout The state-based timeout in switch_state_mapping (1s for Command state) was transitioning Mapping to Quiescent without clearing the session table. This caused RepeatBand to continue running indefinitely because it only stopped when the session table was empty. Now we track the previous state before calling switch_state_mapping and clear the session table if we just transitioned to Quiescent from any active state (Command or Emit). This handles both state-based timeouts and any other transition to Quiescent. * Fix inactive timeout spam, MTU retrieval, and Hello TLV building 1. Fix inactive timeout spam: Reset inactive_timeout_ts to 0 after triggering so it doesn't fire on every tick. 2. Fix MTU retrieval: Use kIOMaxPacketSize from IONetworkController instead of kIOMaxTransferUnit from IONetworkInterface. This is more reliable on modern macOS with Skywalk. Also fixed bug where IONetworkController was used after being released. 3. Enhance Hello TLV building: Add PhysicalMediumTLV for interface type identification, wireless-specific TLVs (WirelessTLV, BSSIDTLV, SSIDTLV, 80211MediumTLV) for 802.11 interfaces, and IconImageTLV and FriendlyNameTLV for station identification. * Increase Mapping Command state timeout from 1s to 5s The 1 second timeout was too short - the mapper can have gaps of more than 1 second between frames during the mapping process. This was causing the responder to timeout and clear the session table while the mapper was still active, resulting in the station not appearing on the network map. Increased to 5 seconds to allow adequate time for the complete mapping sequence (Discover -> Query -> Train -> Probe -> etc). * Set IANA ifType for PhysicalMediumTLV The ifType field was never being initialized, causing PhysicalMediumTLV to send 0 instead of the correct IANA interface type. This likely caused Windows to not properly identify the device on the network map. Now sets ifType alongside interfaceType: - 71 for ieee80211 (WiFi) - 6 for ethernetCsmacd (Ethernet) * Fix multiple TLV encoding issues found via Wireshark - setPerfCounterTLV: Fix pointer arithmetic bug (was writing 4 bytes past TLV header instead of 2 due to pointer math on struct pointer) - setWifiMaxRateTLV: Fix length from uint32_t to uint16_t per LLTD spec, convert rate to 0.5 Mbps units - setWifiRssiTLV: Fix length from int8_t to int32_t per LLTD spec - setQosCharacteristicsTLV: Fix length from uint16_t to uint32_t per spec - Remove duplicate set80211MediumTLV call (was using same TLV type 0x03 as setPhysicalMediumTLV, causing duplicate Physical Medium TLVs) * Fix more TLV encoding issues per Wireshark analysis - Characteristics TLV: Change to uint32_t, flags in upper 16 bits - Performance Counter TLV: Change to uint64_t (8 bytes) per LLTD spec - QoS Characteristics TLV: Shift flags to upper 16 bits - Remove duplicate set80211MediumTLV call from answerHello - Remove Hardware ID, Detailed Icon, Component Table from Hello frame (these are QueryLargeTLV-only, not meant to be in Hello) Note: Link Speed, WiFi Max Rate, and RSSI showing 0 are macOS IORegistry property retrieval issues, not TLV encoding bugs. * Unify Hello TLVs and remove broken UUID - Remove UUID TLV (0x12) from Hello frames - parsing issue to investigate - Update sendHelloMessage to include all TLVs matching answerHello: IPv4, IPv6, PerfCounter, LinkSpeed, Hostname, QoS Characteristics, and WiFi-specific TLVs (MaxRate, RSSI) - Both Hello implementations now send consistent TLV sets * Fix QueryLargeTLV handling and make helloSent per-interface QueryLargeTLV changes: - Refactor sendImage to generic sendLargeTlvResponse function - Add handling for Friendly Name (0x11) and Hardware ID (0x13) TLV types - Icon image retrieval uses existing caching mechanism - Add proper error handling when data is NULL or empty - Add debug logging for all QueryLargeTLV requests helloSent fix: - Move helloSent from global variable to per-interface field - Prevents Hello suppression issues with multiple interfaces - Add helloSent field to network_interface_t in all platform headers * Fix Hello Spam, correct sequence handling, Fix incorrect Discover offset Fixed Darwin responder: stopped enumeration “Hello spam” loop by transitioning the RepeatBand automaton from Pausing to Wait when the session table is complete, preventing endless periodic Hello retransmissions that kept the mapper from converging. Correct LLTD sequence number handling on outbound frames: Hello/Probe/Train now use the current MapperSeqNumber (derived from the active mapper session) instead of a constant 0x00, improving interoperability with Windows’ LLTD mapper. Repaired answerHello() frame parsing and validation: Fixed incorrect Discover-header offset calculation (pointer-size vs struct-size). Validated fields against the received frame header (not a freshly-built outbound buffer). Ensured replies preserve the active session sequence number (seqNumber) so responses remain in-session and are not ignored by the mapper. Notes This patch primarily addresses cases where mapping never populates because the responder continuously re-enters Hello timeouts and/or emits frames that the mapper treats as out-of-session. * More Darwin Hello Fixes - darwin: Reply to LLTD Quick Discovery Hello (TOS=0x01, opcode=0x01) instead of ignoring it, mirroring the mapper’s sequence/generation to prevent mapper Reset and allow the host to appear in Network Map. - darwin: Factor Hello transmission into sendHelloMessageEx() to support explicit mapper real/apparent MACs and per-exchange sequencing. * More Darwin Hello Fixes - darwin: Reply to LLTD Quick Discovery Hello (TOS=0x01, opcode=0x01) instead of ignoring it, mirroring the mapper’s sequence/generation to prevent mapper Reset and allow the host to appear in Network Map. - darwin: Factor Hello transmission into sendHelloMessageEx() to support explicit mapper real/apparent MACs and per-exchange sequencing. # Conflicts: # lltdDaemon/lltdBlock.c * More Darwin Hello Fixes - darwin: Reply to LLTD Quick Discovery Hello (TOS=0x01, opcode=0x01) instead of ignoring it, mirroring the mapper’s sequence/generation to prevent mapper Reset and allow the host to appear in Network Map. - darwin: Factor Hello transmission into sendHelloMessageEx() to support explicit mapper real/apparent MACs and per-exchange sequencing. # Conflicts: # lltdDaemon/lltdBlock.c * Attempt at fixing macOS build * Works for me New attempt * Fix building if there are SPACES in the path to the tree * Fix LLTD Discovery/Hello compliance behind bridges/APs Remove incorrect validation that dropped Discover frames when LLTD Real_Source_Address != Ethernet header source MAC. This mismatch is expected behavior behind bridges/APs where the Ethernet source is rewritten by the network infrastructure. Changes: - answerHello() no longer returns early on address mismatch - Keep diagnostic log (debug level) showing the mismatch for debugging - Add clarifying comments on Hello header construction: - apparentMapper = frameHeader.source (Ethernet src, may be bridge) - currentMapper = realSource (true mapper MAC from LLTD header) - Use memcpy for MapperHwAddress (cleaner than byte-by-byte) Expected behavior: Discover frames with rewritten Ethernet source will now trigger Hello response with both mapper addresses correctly preserved in the Hello upper header. * Add diagnostic dump when TARGET_BUILD_DIR is missing When xcodebuild -showBuildSettings doesn't provide TARGET_BUILD_DIR, print the first 60 lines of the captured settings to stderr before failing. This helps debug build failures on different macOS/Xcode configurations. - Uses printf + head for bash 3.2 compatibility - Separate checks for TARGET_BUILD_DIR and FULL_PRODUCT_NAME * Fix MS-LLTD compliance: broadcast Hello and conditional QueryResp Per MS-LLTD specification: - Hello frames MUST be sent to Ethernet broadcast (FF:FF:FF:FF:FF:FF) - QueryResp/QueryLargeTlvResp frames MUST be broadcast when the mapper is behind a bridge/AP (Real Source != Ethernet source), otherwise unicast to Real Source Also improves send failure diagnostics with errno details. * Fix sendto() in sendHelloMessageEx to use socketAddr AF_NDRV sockets on Darwin require the socket address structure to identify the interface, not NULL. * Fix LLTD mapping for bridged networks: real vs apparent addressing Major fixes for LLTD mapping phase in bridged/AP topologies: A) Real vs Apparent addressing: - Add setLltdHeaderEx() to set Ethernet and LLTD addresses independently - Add MapperApparentAddress field to track bridge-level mapper address - parseProbe: Accept frames based on realDestination (not Ethernet dest) - sendProbeMsg: Use proper dual addressing (Ethernet=apparent, LLTD=real) - ACKs now correctly target apparent mapper through bridge B) Sequence number handling: - Remove global MapperSeqNumber update on every frame - Update only on session-defining frames (Discover, Emit, Query) C) Probe/Train fixes: - sendProbeMsg: Add missing return value, fix memory leak - Add proper error handling and buffer cleanup D) Debug logging: - Add compact debug logs for Emit, Query, Probe/Train frames - Log opcode, tos, seq, all addresses, forUs decision - Enhanced send failure logs with errno details * Correct Endianness conversion for Generation * Fix hello generation endianness for discovery/quick hello frames (#18) * Fix hello generation byte order for discovery * Enforce LLTD sequence and generation byte order * Add offline LLTD pcap regression checker * Add mapper-id hello diagnostics and warnings * Add LLTD generation harness for Discover vs Hello * Throttle hello timeouts and log mapper discovery timing * Always reply to Discover and add frame logs * Fix TLV sizes for perf counter and QoS * Restore QoS characteristics TLV width * Restore perf counter TLV width * Avoid unaligned TLV payload writes * Use SO_RCVTIMEO for AF_NDRV recv loop * Squashed commit of the following: commit c310def Author: Claude <noreply@anthropic.com> Date: Wed Jan 7 20:32:46 2026 +0000 Add Darwin Wi-Fi metrics module using CoreWLAN Implements a pure C API for querying Wi-Fi metrics on macOS using CoreWLAN framework. This provides RSSI, noise, link speed, and computed max link speed without relying on external commands like wdutil or airport. New files: - lltd_wifi_corewlan.h: Pure C89 header with lltd_wifi_metrics_t struct - lltd_wifi_corewlan.m: Objective-C shim using CoreWLAN - lltd_wifi_rates.c/h: Rate calculation tables for HT/VHT/HE PHY modes - tools/lltd-wifi-metrics.c: CLI tool for testing/verification Features: - Queries RSSI, noise, PHY mode, channel width via CoreWLAN - Computes theoretical max link speed based on PHY/width/NSS - Infers NSS/MCS/GI from observed link rate using rate tables - Supports 802.11a/b/g/n/ac/ax with proper rate calculations - Handles missing selectors gracefully for SDK compatibility The module is Darwin-only and guarded with __APPLE__. No Objective-C headers leak into the cross-platform C codebase. commit e35ad88 Author: Claude <noreply@anthropic.com> Date: Wed Jan 7 20:21:10 2026 +0000 Add single-instance check to prevent duplicate daemon processes AF_NDRV sockets allow multiple binds but only the first process receives packets. This caused silent failures when a previous lltdDaemon was still running. Now uses flock() on /tmp/lltdDaemon.lock to ensure only one instance runs at a time. --------- Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
generationfield which caused Topology/Quick Discovery Hellos to be sent with the wrong endianness on some hosts.Quick DiscoveryHello was already converted to network order but other code paths were still byte-swapped, leading to mismatched generation values.generationvalue is represented and passed so wire (network) order is used when serializing the Hello upper header.Description
setHelloHeaderinlltdDaemon/lltdTlvOps.cto treat thegenerationparameter as already in network order and removed thehtons()conversion.answerHelloinlltdDaemon/lltdBlock.cnow forwardsdiscoverHeader->generationand the Quick Discovery reply forwardsinHello->generationdirectly.lltdDaemon/darwin/darwin-main.cby convertingMapperGenerationto network order withhtons()before callingsendHelloMessageEx, and adjusted the debug log to display the host-order value withntohs().lltdDaemon/lltdTlvOps.h(no semantic change).Testing
generationserialization and call sites.Codex Task