Disable bss neighbor check/force 40 MHz channels in hostapd

Category: Gentoo Tags: Hostapd

Howto disable hostapd overlapping channel scanning or better howto force 40 Mhz channels even in dense wlan areas.

Current behavior

Hostapd does a check for overlapping channels with neighboring bss's before enabling 40 MHz channels as proposed by IEEE 802.11g. This however might result in switching to 20 MHz channels in dense wlan areas.

# hostapd -d /etc/hostapd/hostapd.conf
40 MHz affected channel range: [2407,2457] MHz
Neighboring BSS: 00:19:cb:a1:68:82 freq=2412 pri=0 sec=0
Neighboring BSS: 9c:c7:a6:ea:2f:7f freq=2412 pri=1 sec=0
Neighboring BSS: 88:25:2c:ac:0d:3c freq=2412 pri=1 sec=5
40 MHz pri/sec mismatch with BSS 88:25:2c:ac:0d:3c <2412,2432> (chan=1+) vs. <2442,2422>
20/40 MHz operation not permitted on channel pri=7 sec=3 based on overlapping BSSes

In fact I did scan my neighborhood and there are plenty of wlan-ap's that work with 40 Mhz channels.

Looks like hostapd works as intended by IEEE but other AP's don't give a f***.

If you want to keep it that way don't read any further!

But I thought, if they don't care why should I. At least the wlan should be as fast as my internet connection.


The patch

My setup is a linux box runing gentoo. It serves as internet gateway, router, nas, dlna server and ap.

There is no config option to disable the check in hostapd. So we need to change the source code. Gladly Gentoo is source based

At first I unpacked the sources and looked for the line where the check takes place. Found it in hostapd-2.1/src/ap/hw_features.c.

if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A)
    oper40 = ieee80211n_check_40mhz_5g(iface, scan_res);
else
    oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res);
wpa_scan_results_free(scan_res);

if (!oper40) {
    wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on "
        "channel pri=%d sec=%d based on overlapping BSSes",
        iface->conf->channel,
        iface->conf->channel +
        iface->conf->secondary_channel * 4);
    iface->conf->secondary_channel = 0;
}

I didn't want to change to much, so I just commented out the disabling of the secondary channel and created a patch.

--- hostapd-2.1.orig/src/ap/hw_features.c       2014-02-04 12:23:35.000000000 +0100
+++ hostapd-2.1/src/ap/hw_features.c    2014-02-14 07:52:08.727983248 +0100
@@ -477,7 +477,7 @@ static void ieee80211n_check_scan(struct
                           iface->conf->channel,
                           iface->conf->channel +
                           iface->conf->secondary_channel * 4);
-               iface->conf->secondary_channel = 0;
+               /* iface->conf->secondary_channel = 0; DILLIGAF */
        }

        res = ieee80211n_allowed_ht40_channel_pair(iface);


The injection

Now I needed emerge to apply the patch to hostapd.

Gentoo wiki provides this information.

Simply move your patch to /etc/portage/patches/net-wireless/hostapd.

Edit: As of hostapd version 2.6 the described way to inject the patch will not work anymore. See below.

Create /etc/portage/bashrc to hook post_src_unpack().

post_src_unpack() {
    if type epatch_user >& /dev/null; then
        epatch_user
    fi
}

This way patches in /etc/portage/patches will be applied even if the ebuild does not call epatch_user (like hostapd ebuild).

Finally emerge hostapd and the binary will be compiled with the patch applied and 40 Mhz channels should work.

40 MHz affected channel range: [2407,2457] MHz
Neighboring BSS: 00:19:cb:a1:68:82 freq=2412 pri=0 sec=0
Neighboring BSS: 9c:c7:a6:ea:2f:7f freq=2412 pri=1 sec=0
Neighboring BSS: 88:25:2c:ac:0d:3c freq=2412 pri=1 sec=5
40 MHz pri/sec mismatch with BSS 88:25:2c:ac:0d:3c <2412,2432> (chan=1+) vs. <2442,2422>
20/40 MHz operation not permitted on channel pri=7 sec=3 based on overlapping BSSes
HT40: control channel: 7  secondary channel: 3
Completing interface initialization


Last words

This way is pretty much bruteforce but so far there seems no other way to achieve this. Maybe there will be a conf option in future.

The patch I created is for hostapd-2.1 (as of 02/14/2014 masked by ~amd64). It might be work with other versions too but you have to check it.

Also note it is only that easy because gentoo builds binaries from source and offers possibilities to hook into the build process. If you use other distributions you might need to do it yourself.


01/15/2016


Important update!

Short intro... I switched to a new kernel (4.1 from 3.10) and did a bit more observing than normal.

In the logs there suddenly appeared at lof of messages like these:

kernel: ath: phy0: Failed to stop TX DMA, queues=0x002!
kernel: ath: phy0: Failed to stop TX DMA, queues=0x00a!

(I use a wifi card with Atheros AR9380 chip together with ath9k driver.)

Well I checked kernel configuration but did not find any mistakes.

And then I was seeing this ... right before the messages appeared:

hostapd: wlp1s0: STA 38:59:f9:26:af:0b IEEE 802.11: Switching to 20 MHz operation

Ouch. Not only my hard enabled 40Mhz was switched off also the driver seems to have an issue when switching channel widths.

So I searched the source code and found another place where channel widths could be switched. It is a bss coexistence check.

I am not sure if I enabled/disabled something or it is the new kernel that caused this.

Anyway I changed the patch to circumvent the switch.
As I am using hostapd 2.4, there is only the patch for 2.4 right now.

As of 3/15/2016 there is a new patch for 2.5 too.


10/12/2016


Changes in Gentoo - EAPI 6

When you try to use the patch for hostapd-2.6 as described above, you will get a fatal error in the emerge process.

With version 2.6 of Hostapd, the EAPI version used in the ebuild changed from 4 to 6.

Prior version 6 of EAPI patches have been applied with call of epatch_user function from within the ebuild itself or, like described above, by hooking in the emerge process. With version 6 of EAPI there is a new function eapply_user which is called by default in the src_prepare phase. See the description.

The problem is that the working directory at this point is a subdirectory of the extracted archive. While the patch addresses files which are not in this subdirectory, it fails to apply.

A patch itself cannot by applied to files outside the current directory. So there is no way to change the patch itself to work again. The only way I found is to hook into the emerge process and apply the patch.

At first one could simply change the bashrc from above to call eapply_user rather then epatch_user. But this will not work as eapply_user can only be called from a src_prepare hook. Also one needs to keep track of the current working directory, so the patch can applied. It is better to address only hostapd by the hook.
This can be achieved by creating the file /etc/portage/env/net-wireless/hostapd.

The file will only be sourced when hostapd is emerged. One could create a file that will only be sourced by hostapd-2.6 but I assume the all future hostapd ebuilds will also use EAPI version 6.
There is a global variable EAPI which can be used for checking the current used version. See description.

To apply the patch before it is applied by default, we can use the pre_src_prepare hook. Now we just have to make sure, it applied from within the right directory and everything should be just fine. See the supplied version of /etc/portage/env/net-wireless/hostapd.

Much text for a little change but it is necessary to know what happened behind the scenes.

#/etc/portage/env/net-wireless/hostapd
pre_src_prepare() {
    if [ "$EAPI" -eq 6 ]; then
        pwd="$(pwd)"
        cd "${WORKDIR}/${PF}"
        eapply_user
        cd "${pwd}"
    fi
}


10/17/2017


Bug in patch injection

Gentoo has released a revision release to address vulnerabilities in wpa/wpa2 (see #634438).
This results in a revision version (hostapd-2.6-r1). Somehow our patch fails to apply cause the injection uses the full version to change the work dir.
I fixed it and the file /etc/portage/env/net-wireless/hostapd should now look like this:

#!/usr/bin/env bash

pre_src_prepare() {
    if [ "$EAPI" -eq 6 ]; then
        pwd="$(pwd)"
        cd "${WORKDIR}/${P}"
        eapply_user
        cd "${pwd}"
    fi
}


01/13/2021


Overlay with Patch available

I got contacted by another gentoo user who is providing an portage overlay which includes hostapd and a patch.
See for yourself.
patch
overlay


06/04/2014


Patch for 2.2

diff -Naur hostapd-2.2.orig/src/ap/hw_features.c hostapd-2.2/src/ap/hw_features.c
--- hostapd-2.2.orig/src/ap/hw_features.c       2014-06-04 15:26:14.000000000 +0200
+++ hostapd-2.2/src/ap/hw_features.c    2014-06-16 14:58:26.740289954 +0200
@@ -539,7 +539,7 @@
                           iface->conf->channel,
                           iface->conf->channel +
                           iface->conf->secondary_channel * 4);
-               iface->conf->secondary_channel = 0;
+               /* iface->conf->secondary_channel = 0; DILLIGAF */
                if (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) {
                        /*
                         * TODO: Could consider scheduling another scan to check

Patch 2.2 works for 2.3 too.


01/15/2016


Patch for 2.4 (Changed see update above)

diff -upr hostapd-2.4.orig/src/ap/hw_features.c hostapd-2.4/src/ap/hw_features.c
--- hostapd-2.4.orig/src/ap/hw_features.c       2015-03-15 18:30:39.000000000 +0100
+++ hostapd-2.4/src/ap/hw_features.c    2016-01-15 16:15:37.271124328 +0100
@@ -310,7 +310,7 @@ static void ieee80211n_check_scan(struct
                           iface->conf->channel,
                           iface->conf->channel +
                           iface->conf->secondary_channel * 4);
-               iface->conf->secondary_channel = 0;
+               /* iface->conf->secondary_channel = 0; DILLIGAF */
                if (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) {
                        /*
                         * TODO: Could consider scheduling another scan to check
diff -upr hostapd-2.4.orig/src/ap/ieee802_11_ht.c hostapd-2.4/src/ap/ieee802_11_ht.c
--- hostapd-2.4.orig/src/ap/ieee802_11_ht.c     2015-03-15 18:30:39.000000000 +0100
+++ hostapd-2.4/src/ap/ieee802_11_ht.c  2016-01-15 16:21:46.114132193 +0100
@@ -282,6 +282,7 @@ void hostapd_2040_coex_action(struct hos
        wpa_printf(MSG_DEBUG, "is_ht_allowed=%d num_sta_ht40_intolerant=%d",
                   is_ht_allowed, iface->num_sta_ht40_intolerant);

+    /* DILLIGAF
        if (!is_ht_allowed &&
            (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
                if (iface->conf->secondary_channel) {
@@ -306,6 +307,7 @@ void hostapd_2040_coex_action(struct hos
                                   delay_time);
                }
        }
+    */
 }


03/15/2016


Patch for 2.5 (Changed see update above)

diff -upr hostapd-2.5.orig/src/ap/hw_features.c hostapd-2.5/src/ap/hw_features.c
--- hostapd-2.5.orig/src/ap/hw_features.c       2015-09-27 21:02:05.000000000 +0200
+++ hostapd-2.5/src/ap/hw_features.c    2016-03-15 17:00:00.163082949 +0100
@@ -316,7 +316,7 @@ static void ieee80211n_check_scan(struct
                           iface->conf->channel,
                           iface->conf->channel +
                           iface->conf->secondary_channel * 4);
-               iface->conf->secondary_channel = 0;
+               /* iface->conf->secondary_channel = 0; DILLIGAF */
                if (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) {
                        /*
                         * TODO: Could consider scheduling another scan to check
diff -upr hostapd-2.5.orig/src/ap/ieee802_11_ht.c hostapd-2.5/src/ap/ieee802_11_ht.c
--- hostapd-2.5.orig/src/ap/ieee802_11_ht.c     2015-09-27 21:02:05.000000000 +0200
+++ hostapd-2.5/src/ap/ieee802_11_ht.c  2016-03-15 17:04:06.571590399 +0100
@@ -282,6 +282,7 @@ void hostapd_2040_coex_action(struct hos
        wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d",
                   is_ht40_allowed, iface->num_sta_ht40_intolerant);

+    /* DILLIGAF
        if (!is_ht40_allowed &&
            (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
                if (iface->conf->secondary_channel) {
@@ -306,6 +307,7 @@ void hostapd_2040_coex_action(struct hos
                                   delay_time);
                }
        }
+    */
 }


10/12/2016


Patch for version 2.6

--- hostapd-2.6.orig/src/ap/hw_features.c       2016-10-02 20:51:11.000000000 +0200
+++ hostapd-2.6/src/ap/hw_features.c    2016-10-11 12:11:28.430817177 +0200
@@ -316,7 +316,7 @@
                           iface->conf->channel,
                           iface->conf->channel +
                           iface->conf->secondary_channel * 4);
-               iface->conf->secondary_channel = 0;
+               /* iface->conf->secondary_channel = 0; DILLIGAF */
                if (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) {
                        /*
                         * TODO: Could consider scheduling another scan to check
--- hostapd-2.6.orig/src/ap/ieee802_11_ht.c     2016-10-02 20:51:11.000000000 +0200
+++ hostapd-2.6/src/ap/ieee802_11_ht.c  2016-10-11 12:14:18.473952410 +0200
@@ -305,6 +305,7 @@
        wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d",
                   is_ht40_allowed, iface->num_sta_ht40_intolerant);

+/* DILLIGAF
        if (!is_ht40_allowed &&
            (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
                if (iface->conf->secondary_channel) {
@@ -329,6 +330,7 @@
                                   delay_time);
                }
        }
+*/
 }


02/13/2019


Patch for version 2.7

diff -Naur a/src/ap/hw_features.c b/src/ap/hw_features.c
--- a/src/ap/hw_features.c      2018-12-02 20:34:59.000000000 +0100
+++ b/src/ap/hw_features.c      2019-02-13 12:08:39.638437370 +0100
@@ -319,7 +319,7 @@
                           iface->conf->channel,
                           iface->conf->channel +
                           iface->conf->secondary_channel * 4);
-               iface->conf->secondary_channel = 0;
+/*             iface->conf->secondary_channel = 0; */
                if (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) {
                        /*
                         * TODO: Could consider scheduling another scan to check
diff -Naur a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
--- a/src/ap/ieee802_11_ht.c    2018-12-02 20:34:59.000000000 +0100
+++ b/src/ap/ieee802_11_ht.c    2019-02-13 12:09:51.891777936 +0100
@@ -349,7 +349,7 @@
        wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d",
                   is_ht40_allowed, iface->num_sta_ht40_intolerant);

-       if (!is_ht40_allowed &&
+/*     if (!is_ht40_allowed &&
            (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
                if (iface->conf->secondary_channel) {
                        hostapd_logger(hapd, mgmt->sa,
@@ -372,7 +372,7 @@
                                   "Reschedule HT 20/40 timeout to occur in %u seconds",
                                   delay_time);
                }
-       }
+       } */
 }


06/27/2019


Patch for version 2.8

diff -Naur hostapd-2.8.orig/src/ap/hw_features.c hostapd-2.8/src/ap/hw_features.c
--- hostapd-2.8.orig/src/ap/hw_features.c       2019-04-21 09:10:22.000000000 +0200
+++ hostapd-2.8/src/ap/hw_features.c    2019-06-25 08:32:00.555631536 +0200
@@ -316,7 +316,7 @@
                           iface->conf->channel,
                           iface->conf->channel +
                           iface->conf->secondary_channel * 4);
-               iface->conf->secondary_channel = 0;
+//             iface->conf->secondary_channel = 0;
                if (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) {
                        /*
                         * TODO: Could consider scheduling another scan to check
diff -Naur hostapd-2.8.orig/src/ap/ieee802_11_ht.c hostapd-2.8/src/ap/ieee802_11_ht.c
--- hostapd-2.8.orig/src/ap/ieee802_11_ht.c     2019-04-21 09:10:22.000000000 +0200
+++ hostapd-2.8/src/ap/ieee802_11_ht.c  2019-06-25 08:38:51.760302846 +0200
@@ -349,7 +349,7 @@
        wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d",
                   is_ht40_allowed, iface->num_sta_ht40_intolerant);

-       if (!is_ht40_allowed &&
+/*     if (!is_ht40_allowed &&
            (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
                if (iface->conf->secondary_channel) {
                        hostapd_logger(hapd, mgmt->sa,
@@ -372,7 +372,7 @@
                                   "Reschedule HT 20/40 timeout to occur in %u seconds",
                                   delay_time);
                }
-       }
+       } */
 }