Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save trinitronx/bb7201b032c314d78e3cf04fe27226b0 to your computer and use it in GitHub Desktop.

Select an option

Save trinitronx/bb7201b032c314d78e3cf04fe27226b0 to your computer and use it in GitHub Desktop.

Revisions

  1. trinitronx revised this gist Oct 28, 2025. 1 changed file with 42 additions and 1 deletion.
    43 changes: 42 additions & 1 deletion LIFX Lights, Connectivity Issues and hostapd Configuration.md
    Original file line number Diff line number Diff line change
    @@ -175,6 +175,44 @@ basic_rates=60 120 240
    > #... other radio config settings here...
    > option legacy_rates '0'
    #### Best Practices: IoT 2.4GHz Network

    A lot of older IoT devices do not support newer wireless standards such as
    `802.11` `n`/`a`/`ax`, nor roaming related standards (e.g. `802.11` `r`/`k`/`v`).
    Therefore, it's recommended to use a dedicated 2.4GHz 802.11 **_g_** network for
    these IoT devices, with no modern frills. This can be achieved with the
    following config:


    ```
    config wifi-device 'radio0'
    option type 'mac80211'
    option htmode 'NOHT' # <- Turn off 802.11n bandwidths, leaving 20MHz
    option legacy_rates '0'
    # option hwmode '11g' # <- Legacy OpenWrt 802.11g mode option
    option band '2g' # <- 802.11g mode (NEW OpenWrt 21.02.2, replaces hwmode)
    ```

    If for some reason your requirements _prevent_ you from disabling
    `802.11n`, then it's a good idea to try disabling LDPC
    (Low-Density Parity Check code) capability because some Marvell based routers
    [do not transmit LDPC correctly for 802.11n][5.1] (e.g. [wrt1900ac][5.2]).

    This is known to affect some ESP32 based clients (e.g. LIFX bulbs), so disabling
    LDPC on the 2.4GHz radio may improve connectivity as per
    [this `mwlwifi` issue #278 comment][5.3]. Of course, WMM can also be disabled
    on the interface, which would also disable `802.11n/ac` capability tremendously,
    thus slowing throughput, just as using the newer `band '2g'` option would.

    ```
    config wifi-device 'radio0'
    option ldpc '0' # <- Disable LDPC
    option wmm '0' # <- Disable WMM (802.11/n/ac)
    ```


    #### Multicast

    One more thing that can impact multicast: **DTIM** Delivery Traffic Indication <sub>bit</sub>Map interval.

    Multicast and broadcast packets are sent at the [DTIM interval][6] (`hostapd` default: `dtim_period=1` which means a DTIM is sent every `1` [beacon frames / intervals][7]). Many newer routers are configured with `dtim_period=3`, which allows iOS, Android, and battery-powered IoT or mobile devices to sleep longer between waking up at the DTIM interval (every `3` beacon frames) to receive any multicast and broadcast packets.
    @@ -295,8 +333,11 @@ With some combination of these `hostapd` and OpenWrt settings, hopefully your LI
    [3]: https://en.wikipedia.org/wiki/Multicast_DNS
    [4]: https://en.wikipedia.org/wiki/Zero-configuration_networking
    [5]: https://en.wikipedia.org/wiki/Bonjour_(software)
    [5.1]: https://github.com/kaloz/mwlwifi/issues/278#issuecomment-694766487
    [5.2]: https://openwrt.org/toh/linksys/wrt1900ac#esp_chip_devices_won_t_connect
    [5.3]: https://github.com/kaloz/mwlwifi/issues/278#issuecomment-697433463
    [6]: https://routerguide.net/dtim-interval-period-best-setting/
    [7]: https://www.cwnp.com/cwnp-wifi-blog/80211-beacon-intervals/
    [8]: https://openwrt.org/docs/guide-user/network/wifi/basic#inactivity_timeout_options
    [9]: https://github.com/latelee/hostapd/blob/b97f32062c9a29fe6b12b5082832e34ed565b7de/src/ap/sta_info.c#L399-L400
    [10]: https://github.com/latelee/hostapd/blob/b97f32062c9a29fe6b12b5082832e34ed565b7de/src/ap/ap_config.h#L399
    [10]: https://github.com/latelee/hostapd/blob/b97f32062c9a29fe6b12b5082832e34ed565b7de/src/ap/ap_config.h#L399
  2. trinitronx revised this gist May 23, 2025. 1 changed file with 2 additions and 1 deletion.
    Original file line number Diff line number Diff line change
    @@ -13,7 +13,7 @@ The comments in the default example `hostapd.conf` explain what this setting doe
    #disassoc_low_ack=1
    ```

    As it turns out, the LIFX bulbs that I've observed have low powered WiFi radios [run by an Espressif ESP32][0] that often cause `hostapd` to disassociate them over and over again due to low powered responses from those radios / "_stations_". **_NOTE_**: The lights act as WiFi clients, and all clients are called "`STA`" in the `hostapd` logs, which is short for "station".
    As it turns out, the LIFX bulbs that I've observed have low powered WiFi radios [run by an Espressif ESP32][0] that often cause `hostapd` to disassociate them over and over again due to low powered responses from those radios / "_stations_". [The bulbs use a PCB antenna][0.1] similar to most IoT devices. **_NOTE_**: The lights act as WiFi clients, and all clients are called "`STA`" in the `hostapd` logs, which is short for "station".

    This can be seen in the `hostapd` logs similar to the following example:

    @@ -289,6 +289,7 @@ Usually a good rule of thumb is to prefer to choose channels `1`, `6`, and `11`
    With some combination of these `hostapd` and OpenWrt settings, hopefully your LIFX lights and IoT devices can stay associated and be more reliable!

    [0]: https://www.hackster.io/news/the-problem-with-throwing-away-a-smart-device-75c8b35ee3c7
    [0.1]: https://www.reddit.com/r/lifx/comments/kuk934/comment/gitk4cx/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button
    [1]: https://www.rfc-editor.org/rfc/rfc9542.html#section-2.1.4
    [2]: https://github.com/sensepost/hostapd-mana/blob/8853d5ac7f97b140b7899d2cf074eca921e55c3b/hostapd/hostapd.conf#L508
    [3]: https://en.wikipedia.org/wiki/Multicast_DNS
  3. trinitronx revised this gist May 23, 2025. 1 changed file with 1 addition and 1 deletion.
    Original file line number Diff line number Diff line change
    @@ -252,7 +252,7 @@ config wifi-iface
    option ieee80211r '0'
    ```

    Alternatively, `uci` may be used to chang the setting:
    Alternatively, `uci` may be used to change the setting:

    ```
    uci set wireless.@wifi-iface[0].ieee80211r=0
  4. trinitronx revised this gist May 23, 2025. 1 changed file with 1 addition and 1 deletion.
    Original file line number Diff line number Diff line change
    @@ -181,7 +181,7 @@ Multicast and broadcast packets are sent at the [DTIM interval][6] (`hostapd` de

    These settings can affect Apple Homekit with IoT devices such as LIFX bulbs, or any other IoT devices that need mDNS to advertise services and be discovered. LIFX has not published anything regarding what their bulbs recommended DTIM interval is. Apple recommends `dtim_interval=3` for iOS devices.

    However, we can infer that LIFX bulbs do sleep rather long at times based on observing that they often are unresponsive to both the `max_listen_interval=100` time limit (remaining silent for over `100` beacon periods `~10.24 seconds`), and even sometimes even the `ap_max_inactivity=300` (**5 minutes** [_+/- 20 seconds random jitter_][9]). Note that this apparent intermittent 5 minute inactivity could also be due to bad WiFi signal, and noise in the neighborhood from other WiFi APs using the same channels. In this case, we might also need to set `disassoc_low_ack=0`. However, based on log data alone it does appear to happen and eventually the LIFX bulbs fail to rejoin the network until `hostapd` is restarted. I didn't have a great WiFi capture card at the time to conclusively determine if this was due to noisy neighbors, low reception or signal from LIFX bulbs, or simply bulbs sleeping too long.
    However, we can infer that LIFX bulbs do sleep rather long at times based on observing that they often are unresponsive to both the `max_listen_interval=100` time limit (remaining silent for over `100` beacon periods `~10.24 seconds`), and even sometimes the `ap_max_inactivity=300` (**5 minutes** [_+/- 20 seconds random jitter_][9]). Note that this apparent intermittent 5 minute inactivity could also be due to bad WiFi signal, and noise in the neighborhood from other WiFi APs using the same channels. In this case, we might also need to set `disassoc_low_ack=0`. However, based on log data alone it does appear to happen and eventually the LIFX bulbs fail to rejoin the network until `hostapd` is restarted. I didn't have a great WiFi capture card at the time to conclusively determine if this was due to noisy neighbors, low reception or signal from LIFX bulbs, or simply bulbs sleeping too long.

    Nonetheless, we can try to mitigate all these issues by increasing the `dtim_period` to match Apple's recommendations to accomodate IoT devices that sleep longer to save power, and increasing the timeout limits for inactivity. We can also increase the [`max_listen_interval`][10] to the maximum allowed value for a 16-bit unsigned integer in C: `65535`. This should prevent the `hostapd` AP from deassociating the LIFX bulbs for inactivity as often, and allow clients the longest possible interval to remain quiet. Also, since the LIFX bulbs are older hardware and do not support fast roaming `802.11r`, we can try to adjust settings to avoid deassociating them as frequently. It makes sense to also disable `802.11r` on an IoT-specific WiFi AP. When have you seen a LIFX bulb roaming around your house?

  5. trinitronx revised this gist May 23, 2025. 1 changed file with 1 addition and 1 deletion.
    Original file line number Diff line number Diff line change
    @@ -139,7 +139,7 @@ sudo tcpdump -nnXs 0 -i eth0 -w /tmp/ipv4-mdns.pcap ip4 and udp port 5353

    That can help to debug multicast DNS packet delivery problems over the WiFi. Try capturing mDNS traffic from another WiFi client, and also on the router if possible (if using OpenWrt or dd-wrt, etc...). You might find that multicast packets are not being delivered at timings when the LIFX lights are asleep.

    You also might find that multicast packets are being sent by the AP at the slowest supported 802.11 rate on the network, which is the least common denominator supported by all WiFi clients. If the AP is still supporting `802.11` **_b_**, this could mean `2`, `5`, and `11` Mbps rates. If the AP is configured to use 802.11**_b_** and legacy rates, it can cause a massive slowdown over the entire WiFi network for multicast packets.
    You also might find that multicast packets are being sent by the AP at the slowest supported 802.11 rate on the network, which is the least common denominator supported by all WiFi clients. If the AP is still supporting `802.11` **_b_**, this could mean `2`, `5`, and `11` Mbps rates. If the AP is configured to use 802.11 **_b_** and legacy rates, it can cause a massive slowdown over the entire WiFi network for multicast packets.

    Some examples of basic rates in `hostapd.conf`:

  6. trinitronx revised this gist May 23, 2025. 1 changed file with 46 additions and 1 deletion.
    47 changes: 46 additions & 1 deletion LIFX Lights, Connectivity Issues and hostapd Configuration.md
    Original file line number Diff line number Diff line change
    @@ -215,7 +215,36 @@ skip_inactivity_poll=0
    ```

    If running OpenWrt, disable `802.11r` fast roaming with the following in `/etc/config/wireless`:
    > [!NOTE]
    >
    > For OpenWrt, these settings can be changed in `/etc/config/wireless`:
    >
    > config wifi-device 'radio0'
    > # ... Existing Config for IoT WiFi radio PHY device
    > option beacon_int '100'
    >
    > config wifi-iface
    > option device 'radio0'
    > option mode 'ap'
    > # ... Existing Config for IoT WiFi radio AP
    > option max_listen_interval '65535'
    > option disassoc_low_ack '0'
    > option max_inactivity '3600'
    > option dtim_period '3'
    > option skip_inactivity_poll '0'
    >
    > Or, using `uci`:
    >
    > uci set wireless.radio0.beacon_int='100'
    > uci set wireless.@wifi-iface[0].max_listen_interval='65535'
    > uci set wireless.@wifi-iface[0].disassoc_low_ack='0'
    > uci set wireless.@wifi-iface[0].max_inactivity='3600'
    > uci set wireless.@wifi-iface[0].dtim_period='3'
    > uci set wireless.@wifi-iface[0].skip_inactivity_poll='0'
    > uci commit
    >
    If running OpenWrt, also disable `802.11r` fast roaming with the following in `/etc/config/wireless`:

    ```
    config wifi-iface
    @@ -232,11 +261,27 @@ uci commit

    One other setting is `ap_isolate=1`, which isolates WiFi clients. Setting `ap_isolate=1` stops the AP from doing L2 forwarding between clients using the pairwise keys. This can impact Layer 2 packet delivery between WiFi clients. For a home network with a lot of IoT devices, it's probably recommended to turn this off and allow clients to speak to one another. Ideally you'd want to have a separate IoT WiFi AP anyway that guests do not connect to, avoiding security and isolation concerns.

    `/etc/config/hostapd.conf` (OpenWrt generates these per-device in `/var/run/hostapd-phyNN.conf`):

    ```
    # Do NOT isolate WiFI clients / IoT devices on Layer 2
    ap_isolate=0
    ```

    > [!NOTE]
    >
    > For OpenWrt, this setting should be changed in `/etc/config/wireless`:
    >
    > config wifi-iface
    > # ... Existing Config for IoT WiFi radio AP
    > option isolate '0'
    >
    > Or with `uci`:
    >
    > uci set wireless.@wifi-iface[0].isolate=0
    > uci commit
    >
    Finally, it should go without saying that ideally you should be using a WiFi channel / frequency that is not being used by neighboring WiFi APs. This avoids the "noisy neighbors" problem of the WiFi APs struggling to talk over one another on the same radio frequencies. While this is not always possible to avoid, it should be a first line of action when dealing with frequent client disassociations. Do a wireless site survey and see which channels are already being used by other WiFi APs in the neighborhood. Pick a channel that is the least noisy.

    Usually a good rule of thumb is to prefer to choose channels `1`, `6`, and `11` to avoid overlapping. However, if this is not possible and all of those are being used, pick `3`, `4`, `8`, or `9` and look for the one with the least noisy two adjacent channels. If all of those are being used, then fallback to `2`, `5`, `7`, or `10` while again picking the one with the least noisy neighbors (the lowest `dBm` AP signals).
  7. trinitronx revised this gist May 23, 2025. 1 changed file with 1 addition and 1 deletion.
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    ### LIFX Lights & Connectivity Issues: `hostapd` (_mis_)configuration

    Aside from such unfixable hardware issues as in the example case study above, there are some issues that can be fixed on home routers with Open Source firmware such as OpenWrt.
    There are some issues with LIFX lights reliability that can be fixed on home routers by using Open Source firmware such as OpenWrt.

    As I've recently observed on my network, the LIFX bulbs often don't respond to the AP frequently enough to avoid being disassociated for inactivity (see notes later about `ap_max_inactivity` and `max_listen_interval`). Another issue common to OpenWrt or any Linux-based router that uses `hostapd` is that `disassoc_low_ack=1` is the default setting unless you turn it off explicitly with `disassoc_low_ack=0`.

  8. trinitronx created this gist May 23, 2025.
    256 changes: 256 additions & 0 deletions LIFX Lights, Connectivity Issues and hostapd Configuration.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,256 @@
    ### LIFX Lights & Connectivity Issues: `hostapd` (_mis_)configuration

    Aside from such unfixable hardware issues as in the example case study above, there are some issues that can be fixed on home routers with Open Source firmware such as OpenWrt.

    As I've recently observed on my network, the LIFX bulbs often don't respond to the AP frequently enough to avoid being disassociated for inactivity (see notes later about `ap_max_inactivity` and `max_listen_interval`). Another issue common to OpenWrt or any Linux-based router that uses `hostapd` is that `disassoc_low_ack=1` is the default setting unless you turn it off explicitly with `disassoc_low_ack=0`.

    The comments in the default example `hostapd.conf` explain what this setting does:

    ```
    # Disassociate stations based on excessive transmission failures or other
    # indications of connection loss. This depends on the driver capabilities and
    # may not be available with all drivers.
    #disassoc_low_ack=1
    ```

    As it turns out, the LIFX bulbs that I've observed have low powered WiFi radios [run by an Espressif ESP32][0] that often cause `hostapd` to disassociate them over and over again due to low powered responses from those radios / "_stations_". **_NOTE_**: The lights act as WiFi clients, and all clients are called "`STA`" in the `hostapd` logs, which is short for "station".

    This can be seen in the `hostapd` logs similar to the following example:

    Note: `00:00:5E:00:53:00 -> 00:00:5E:00:53:FF` used for documentation/examples (as per [RFC 9542][1])

    ```
    hostapd: wlan0: STA 00:00:5E:00:53:00 WPA: group key handshake completed (RSN)
    hostapd: wlan0: STA 00:00:5E:00:53:00 IEEE 802.11: disconnected due to excessive missing ACKs
    hostapd: wlan0: STA 00:00:5E:00:53:00 IEEE 802.11: deauthenticated due to inactivity (timer DEAUTH/REMOVE)
    ```

    I'm sure that a lot of embedded proprietary routers actually run Linux and `hostapd` under the hood, but unfortunately don't often let the user configure detailed `hostapd` settings like `disassoc_low_ack`, `max_listen_interval`, or `ap_max_inactivity`. If possible, consider switching to OpenWrt or a router that is supported by OpenWrt so you can configure these settings.

    Speaking of which, one other problem is that the LIFX bulbs sometimes don't respond frequently enough to the AP and get disassociated due to inactivity. There are a few configuration settings for `hostapd` related to this, as we can read about in the [example `hostapd.conf`][2].

    Also see: [OpenWrt wireless inactivity settings][8]

    ```
    # Station inactivity limit
    #
    # If a station does not send anything in ap_max_inactivity seconds, an
    # empty data frame is sent to it in order to verify whether it is
    # still in range. If this frame is not ACKed, the station will be
    # disassociated and then deauthenticated. This feature is used to
    # clear station table of old entries when the STAs move out of the
    # range.
    #
    # The station can associate again with the AP if it is still in range;
    # this inactivity poll is just used as a nicer way of verifying
    # inactivity; i.e., client will not report broken connection because
    # disassociation frame is not sent immediately without first polling
    # the STA with a data frame.
    # default: 300 (i.e., 5 minutes)
    #ap_max_inactivity=300
    #
    # The inactivity polling can be disabled to disconnect stations based on
    # inactivity timeout so that idle stations are more likely to be disconnected
    # even if they are still in range of the AP. This can be done by setting
    # skip_inactivity_poll to 1 (default 0).
    #skip_inactivity_poll=0
    # Disassociate stations based on excessive transmission failures or other
    # indications of connection loss. This depends on the driver capabilities and
    # may not be available with all drivers.
    #disassoc_low_ack=1
    # Maximum allowed Listen Interval (how many Beacon periods STAs are allowed to
    # remain asleep). Default: 65535 (no limit apart from field size)
    #max_listen_interval=100
    ```

    Finally, some newer LIFX light models use multicast DNS for Apple HomeKit support. Multicast DNS (a.k.a. [mDNS][3], [Zeroconf][4], [Bonjour][5]) which requires multicast packets to be sent and received over the WiFi radio. This in and of itself is fine, but depending on the `hostapd` WiFi AP configuration and how frequently and how long client stations sleep, those multicast packets can suffer speed or delivery timing problems.

    For example, we can use `avahi-daemon` (Linux) or `dns-sd` (macOS) to look for LIFX lights advertising the HomeKit Accessory Protocol `HAP` (`_hap._tcp`):

    Note: This example uses `avahi-browse`. For macOS, use `dns-sd` instead.

    ```
    $ avahi-browse -d local _hap._tcp --resolve -t --verbose
    Server version: avahi 0.8; Host name: saturn.local
    E Ifce Prot Name Type Domain
    + eth0 IPv6 LIFX Color 005300 _hap._tcp local
    + eth0 IPv6 LIFX Pls A19 005301 _hap._tcp local
    + eth0 IPv4 LIFX Pls A19 005301 _hap._tcp local
    + eth0 IPv4 LIFX Color 005300 _hap._tcp local
    = eth0 IPv6 LIFX Color 005300 _hap._tcp local
    hostname = [LIFX-Color-005300.local]
    address = [192.168.1.123]
    port = [50029]
    txt = ["c#=3" "ff=2" "id=00:00:5E:00:53:00" "md=LIFX Color" "pv=1.1" "s#=177" "sf=0" "ci=5" "sh=LEQg9g=="]
    = eth0 IPv4 LIFX Color 005300 _hap._tcp local
    hostname = [LIFX-Color-005300.local]
    address = [192.168.1.123]
    port = [50029]
    txt = ["c#=3" "ff=2" "id=00:00:5E:00:53:00" "md=LIFX Color" "pv=1.1" "s#=177" "sf=0" "ci=5" "sh=LEQg9g=="]
    = eth0 IPv6 LIFX Pls A19 005301 _hap._tcp local
    hostname = [LIFX-Pls-A19-005301.local]
    address = [192.168.1.239]
    port = [49152]
    txt = ["sh=DpXTZw==" "ci=5" "sf=0" "s#=1" "pv=1.1" "md=LIFX Pls A19" "id=00:00:5E:00:53:01" "ff=1" "c#=3"]
    = eth0 IPv4 LIFX Pls A19 005301 _hap._tcp local
    hostname = [LIFX-Pls-A19-005301.local]
    address = [192.168.1.239]
    port = [49152]
    txt = ["sh=DpXTZw==" "ci=5" "sf=0" "s#=1" "pv=1.1" "md=LIFX Pls A19" "id=00:00:5E:00:53:01" "ff=1" "c#=3"]
    : Cache exhausted
    : All for now
    ```

    macOS example:

    ```
    # Browse for advertised _hap._tcp devices
    $ dns-sd -B _hap._tcp
    Browsing for _hap._tcp
    DATE: ---Thu 22 May 2025---
    15:56:03.519 ...STARTING...
    Timestamp A/R Flags if Domain Service Type Instance Name
    15:56:03.520 Add 3 4 local. _hap._tcp. LIFX Color 005300
    15:56:03.520 Add 2 4 local. _hap._tcp. LIFX Pls A19 005301
    # Hint: see the full zone-file-like view with: dns-sd -Z _hap._tcp
    # Resolve the record for an advertised LIFX bulb
    # Hint: Names are oddly separated with octal '\032' internally in the dns-sd
    # utility, which is ASCII 0x1A = SUB (substitute) control character.
    # On the CLI, we replace or substitute these with
    # spaces ' ' to resolve or lookup an mDNS record.
    $ dns-sd -L 'LIFX Color 005300' _hap._tcp
    Lookup LIFX Color 005300._hap._tcp.local
    DATE: ---Thu 22 May 2025---
    16:05:27.225 ...STARTING...
    16:05:27.226 LIFX\032Color\032005300._hap._tcp.local. can be reached at LIFX-Color-005300.local.:50029 (interface 4)
    sh=RpSH1w== ci=5 sf=0 s#=177 pv=1.1 md=LIFX\ Color id=00:00:5E:00:53:00 ff=2 c#=3
    ```

    So we can see that two LIFX bulbs are advertising mDNS records over multicast on the network. We can capture mDNS traffic by filtering on port `5353` with `tcpdump`:

    ```
    sudo tcpdump -nnXs 0 -i eth0 -w /tmp/ipv4-mdns.pcap ip4 and udp port 5353
    ```

    That can help to debug multicast DNS packet delivery problems over the WiFi. Try capturing mDNS traffic from another WiFi client, and also on the router if possible (if using OpenWrt or dd-wrt, etc...). You might find that multicast packets are not being delivered at timings when the LIFX lights are asleep.

    You also might find that multicast packets are being sent by the AP at the slowest supported 802.11 rate on the network, which is the least common denominator supported by all WiFi clients. If the AP is still supporting `802.11` **_b_**, this could mean `2`, `5`, and `11` Mbps rates. If the AP is configured to use 802.11**_b_** and legacy rates, it can cause a massive slowdown over the entire WiFi network for multicast packets.

    Some examples of basic rates in `hostapd.conf`:

    Note that the slowest rate is `1 Mbps` in this example!
    Multicast will be sent at this rate and thus use more radio airtime.

    ```
    # The entries in this list are in 100 kbps, i.e., 11 Mbps = 110.
    # 802.11b / legacy rates (1 Mbps - 54 Mbps)
    supported_rates=10 20 55 110 60 90 120 180 240 360 480 540
    basic_rates=10 20 55 110
    ```

    For most LIFX bulbs, they support `802.11g`, but not anything newer (e.g. Not . `802.11n/a/ax`, and no fast roaming `802.11r`). So, we can bump up the basic rate speed to the `802.11g` rates:

    ```
    # 802.11g rates (6Mbps - 54 Mbps)
    supported_rates=60 90 120 180 240 360 480 540
    basic_rates=60 120 240
    ```

    > [!NOTE]
    > In OpenWrt, this is what the `option legacy_rates '0'` setting does: turn off `802.11b` rates and only enable `802.11g` and above.
    >
    > When using OpenWrt, just set the `legacy_rates` option to `0` / off.
    >
    > uci set wireless.radio0.legacy_rates=0
    > uci commit
    >
    > Or simply add the following to `/etc/config/wireless`
    >
    > config wifi-device 'radio0'
    > #... other radio config settings here...
    > option legacy_rates '0'
    One more thing that can impact multicast: **DTIM** Delivery Traffic Indication <sub>bit</sub>Map interval.

    Multicast and broadcast packets are sent at the [DTIM interval][6] (`hostapd` default: `dtim_period=1` which means a DTIM is sent every `1` [beacon frames / intervals][7]). Many newer routers are configured with `dtim_period=3`, which allows iOS, Android, and battery-powered IoT or mobile devices to sleep longer between waking up at the DTIM interval (every `3` beacon frames) to receive any multicast and broadcast packets.

    These settings can affect Apple Homekit with IoT devices such as LIFX bulbs, or any other IoT devices that need mDNS to advertise services and be discovered. LIFX has not published anything regarding what their bulbs recommended DTIM interval is. Apple recommends `dtim_interval=3` for iOS devices.

    However, we can infer that LIFX bulbs do sleep rather long at times based on observing that they often are unresponsive to both the `max_listen_interval=100` time limit (remaining silent for over `100` beacon periods `~10.24 seconds`), and even sometimes even the `ap_max_inactivity=300` (**5 minutes** [_+/- 20 seconds random jitter_][9]). Note that this apparent intermittent 5 minute inactivity could also be due to bad WiFi signal, and noise in the neighborhood from other WiFi APs using the same channels. In this case, we might also need to set `disassoc_low_ack=0`. However, based on log data alone it does appear to happen and eventually the LIFX bulbs fail to rejoin the network until `hostapd` is restarted. I didn't have a great WiFi capture card at the time to conclusively determine if this was due to noisy neighbors, low reception or signal from LIFX bulbs, or simply bulbs sleeping too long.

    Nonetheless, we can try to mitigate all these issues by increasing the `dtim_period` to match Apple's recommendations to accomodate IoT devices that sleep longer to save power, and increasing the timeout limits for inactivity. We can also increase the [`max_listen_interval`][10] to the maximum allowed value for a 16-bit unsigned integer in C: `65535`. This should prevent the `hostapd` AP from deassociating the LIFX bulbs for inactivity as often, and allow clients the longest possible interval to remain quiet. Also, since the LIFX bulbs are older hardware and do not support fast roaming `802.11r`, we can try to adjust settings to avoid deassociating them as frequently. It makes sense to also disable `802.11r` on an IoT-specific WiFi AP. When have you seen a LIFX bulb roaming around your house?

    ```
    # Default beacon period (100 TUs / "Time Units")
    # 1 Time Unit = 1024 µs (microseconds)
    # Therefore 100 TUs = 102,400 µs = 102.4 ms
    beacon_int=100
    # Send multicast DTIM every 3 beacon periods
    # 3 * 100 TUs = 300 TUs = 30.72 seconds
    dtim_period=3
    # Allow clients to remain quiet for the maximum possible
    # 65535 * 100 TUs = 6710784000 µs = 1.86410667 hours
    max_listen_interval=65535
    # Do not disassociate clients for low signal quality issues,
    # or excessive transmission failures.
    # Should help LIFX bulbs with low-powered radios from being deassociated as
    # often.
    disassoc_low_ack=0
    # Increase STA inactivity timeout to 1 hour
    # 60 min = 3600 seconds
    ap_max_inactivity=3600
    # Do NOT skip inactivity polling
    # Still send the inactivity probes and wait for ACKs from STA clients
    skip_inactivity_poll=0
    ```

    If running OpenWrt, disable `802.11r` fast roaming with the following in `/etc/config/wireless`:

    ```
    config wifi-iface
    # ... Existing Config for IoT WiFi radio AP (e.g. radio0) ...
    option ieee80211r '0'
    ```

    Alternatively, `uci` may be used to chang the setting:

    ```
    uci set wireless.@wifi-iface[0].ieee80211r=0
    uci commit
    ```

    One other setting is `ap_isolate=1`, which isolates WiFi clients. Setting `ap_isolate=1` stops the AP from doing L2 forwarding between clients using the pairwise keys. This can impact Layer 2 packet delivery between WiFi clients. For a home network with a lot of IoT devices, it's probably recommended to turn this off and allow clients to speak to one another. Ideally you'd want to have a separate IoT WiFi AP anyway that guests do not connect to, avoiding security and isolation concerns.

    ```
    # Do NOT isolate WiFI clients / IoT devices on Layer 2
    ap_isolate=0
    ```

    Finally, it should go without saying that ideally you should be using a WiFi channel / frequency that is not being used by neighboring WiFi APs. This avoids the "noisy neighbors" problem of the WiFi APs struggling to talk over one another on the same radio frequencies. While this is not always possible to avoid, it should be a first line of action when dealing with frequent client disassociations. Do a wireless site survey and see which channels are already being used by other WiFi APs in the neighborhood. Pick a channel that is the least noisy.

    Usually a good rule of thumb is to prefer to choose channels `1`, `6`, and `11` to avoid overlapping. However, if this is not possible and all of those are being used, pick `3`, `4`, `8`, or `9` and look for the one with the least noisy two adjacent channels. If all of those are being used, then fallback to `2`, `5`, `7`, or `10` while again picking the one with the least noisy neighbors (the lowest `dBm` AP signals).

    With some combination of these `hostapd` and OpenWrt settings, hopefully your LIFX lights and IoT devices can stay associated and be more reliable!

    [0]: https://www.hackster.io/news/the-problem-with-throwing-away-a-smart-device-75c8b35ee3c7
    [1]: https://www.rfc-editor.org/rfc/rfc9542.html#section-2.1.4
    [2]: https://github.com/sensepost/hostapd-mana/blob/8853d5ac7f97b140b7899d2cf074eca921e55c3b/hostapd/hostapd.conf#L508
    [3]: https://en.wikipedia.org/wiki/Multicast_DNS
    [4]: https://en.wikipedia.org/wiki/Zero-configuration_networking
    [5]: https://en.wikipedia.org/wiki/Bonjour_(software)
    [6]: https://routerguide.net/dtim-interval-period-best-setting/
    [7]: https://www.cwnp.com/cwnp-wifi-blog/80211-beacon-intervals/
    [8]: https://openwrt.org/docs/guide-user/network/wifi/basic#inactivity_timeout_options
    [9]: https://github.com/latelee/hostapd/blob/b97f32062c9a29fe6b12b5082832e34ed565b7de/src/ap/sta_info.c#L399-L400
    [10]: https://github.com/latelee/hostapd/blob/b97f32062c9a29fe6b12b5082832e34ed565b7de/src/ap/ap_config.h#L399