]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
hostapd: Add supported channel bandwidth checking infrastructure
authorDmitry Lebed <dlebed@quantenna.com>
Thu, 1 Mar 2018 11:49:27 +0000 (14:49 +0300)
committerJouni Malinen <j@w1.fi>
Sun, 6 Jan 2019 09:54:34 +0000 (11:54 +0200)
This adds checks to common code to verify supported bandwidth options
for each channel using nl80211-provided info. No support of additional
modes is added, just additional checks. Such checks are needed because
driver/hardware can declare more strict limitations than declared in the
IEEE 802.11 standard. Without this patch hostapd might select
unsupported channel and that will fail because Linux kernel does check
channel bandwidth limitations.

Signed-off-by: Dmitry Lebed <dlebed@quantenna.com>
src/ap/hw_features.c
src/common/hw_features_common.c
src/common/hw_features_common.h

index 5279abca1f1faef03f4f84126f40a17f7466e32d..9d3d990a281fd749ee163453226031c9210db8c8 100644 (file)
@@ -229,9 +229,6 @@ static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
 {
        int pri_chan, sec_chan;
 
-       if (!iface->conf->secondary_channel)
-               return 1; /* HT40 not used */
-
        pri_chan = iface->conf->channel;
        sec_chan = pri_chan + iface->conf->secondary_channel * 4;
 
@@ -697,30 +694,25 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface)
 static int hostapd_is_usable_chan(struct hostapd_iface *iface,
                                  int channel, int primary)
 {
-       int i;
        struct hostapd_channel_data *chan;
 
        if (!iface->current_mode)
                return 0;
 
-       for (i = 0; i < iface->current_mode->num_channels; i++) {
-               chan = &iface->current_mode->channels[i];
-               if (chan->chan != channel)
-                       continue;
-
-               if (!(chan->flag & HOSTAPD_CHAN_DISABLED))
-                       return 1;
+       chan = hw_get_channel_chan(iface->current_mode, channel, NULL);
+       if (!chan)
+               return 0;
 
-               wpa_printf(MSG_DEBUG,
-                          "%schannel [%i] (%i) is disabled for use in AP mode, flags: 0x%x%s%s",
-                          primary ? "" : "Configured HT40 secondary ",
-                          i, chan->chan, chan->flag,
-                          chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
-                          chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
-       }
+       if ((primary && chan_pri_allowed(chan)) ||
+           (!primary && !(chan->flag & HOSTAPD_CHAN_DISABLED)))
+               return 1;
 
-       wpa_printf(MSG_INFO, "Channel %d (%s) not allowed for AP mode",
-                  channel, primary ? "primary" : "secondary");
+       wpa_printf(MSG_INFO,
+                  "Channel %d (%s) not allowed for AP mode, flags: 0x%x%s%s",
+                  channel, primary ? "primary" : "secondary",
+                  chan->flag,
+                  chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
+                  chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
        return 0;
 }
 
@@ -728,6 +720,12 @@ static int hostapd_is_usable_chan(struct hostapd_iface *iface,
 static int hostapd_is_usable_chans(struct hostapd_iface *iface)
 {
        int secondary_chan;
+       struct hostapd_channel_data *pri_chan;
+
+       pri_chan = hw_get_channel_chan(iface->current_mode,
+                                      iface->conf->channel, NULL);
+       if (!pri_chan)
+               return 0;
 
        if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1))
                return 0;
@@ -742,13 +740,15 @@ static int hostapd_is_usable_chans(struct hostapd_iface *iface)
 
        /* Both HT40+ and HT40- are set, pick a valid secondary channel */
        secondary_chan = iface->conf->channel + 4;
-       if (hostapd_is_usable_chan(iface, secondary_chan, 0)) {
+       if (hostapd_is_usable_chan(iface, secondary_chan, 0) &&
+           (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) {
                iface->conf->secondary_channel = 1;
                return 1;
        }
 
        secondary_chan = iface->conf->channel - 4;
-       if (hostapd_is_usable_chan(iface, secondary_chan, 0)) {
+       if (hostapd_is_usable_chan(iface, secondary_chan, 0) &&
+           (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) {
                iface->conf->secondary_channel = -1;
                return 1;
        }
index db403792718505e000a0dbd9db9eaf2c341289d0..49ed80657521abb921f72ef147f57c32cec4f959 100644 (file)
@@ -87,13 +87,29 @@ int hw_get_chan(struct hostapd_hw_modes *mode, int freq)
 int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
                              int sec_chan)
 {
-       int ok, j, first;
+       int ok, first;
        int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
                          149, 157, 165, 184, 192 };
        size_t k;
+       struct hostapd_channel_data *p_chan, *s_chan;
+       const int ht40_plus = pri_chan < sec_chan;
 
-       if (pri_chan == sec_chan || !sec_chan)
-               return 1; /* HT40 not used */
+       p_chan = hw_get_channel_chan(mode, pri_chan, NULL);
+       if (!p_chan)
+               return 0;
+
+       if (pri_chan == sec_chan || !sec_chan) {
+               if (chan_pri_allowed(p_chan))
+                       return 1; /* HT40 not used */
+
+               wpa_printf(MSG_ERROR, "Channel %d is not allowed as primary",
+                          pri_chan);
+               return 0;
+       }
+
+       s_chan = hw_get_channel_chan(mode, sec_chan, NULL);
+       if (!s_chan)
+               return 0;
 
        wpa_printf(MSG_DEBUG,
                   "HT40: control channel: %d  secondary channel: %d",
@@ -101,16 +117,9 @@ int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
 
        /* Verify that HT40 secondary channel is an allowed 20 MHz
         * channel */
-       ok = 0;
-       for (j = 0; j < mode->num_channels; j++) {
-               struct hostapd_channel_data *chan = &mode->channels[j];
-               if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
-                   chan->chan == sec_chan) {
-                       ok = 1;
-                       break;
-               }
-       }
-       if (!ok) {
+       if ((s_chan->flag & HOSTAPD_CHAN_DISABLED) ||
+           (ht40_plus && !(p_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) ||
+           (!ht40_plus && !(p_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M))) {
                wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
                           sec_chan);
                return 0;
@@ -553,3 +562,59 @@ int ieee80211ac_cap_check(u32 hw, u32 conf)
 }
 
 #endif /* CONFIG_IEEE80211AC */
+
+
+u32 num_chan_to_bw(int num_chans)
+{
+       switch (num_chans) {
+       case 2:
+       case 4:
+       case 8:
+               return num_chans * 20;
+       default:
+               return 20;
+       }
+}
+
+
+/* check if BW is applicable for channel */
+int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw,
+                   int ht40_plus, int pri)
+{
+       u32 bw_mask;
+
+       switch (bw) {
+       case 20:
+               bw_mask = HOSTAPD_CHAN_WIDTH_20;
+               break;
+       case 40:
+               /* HT 40 MHz support declared only for primary channel,
+                * just skip 40 MHz secondary checking */
+               if (pri && ht40_plus)
+                       bw_mask = HOSTAPD_CHAN_WIDTH_40P;
+               else if (pri && !ht40_plus)
+                       bw_mask = HOSTAPD_CHAN_WIDTH_40M;
+               else
+                       bw_mask = 0;
+               break;
+       case 80:
+               bw_mask = HOSTAPD_CHAN_WIDTH_80;
+               break;
+       case 160:
+               bw_mask = HOSTAPD_CHAN_WIDTH_160;
+               break;
+       default:
+               bw_mask = 0;
+               break;
+       }
+
+       return (chan->allowed_bw & bw_mask) == bw_mask;
+}
+
+
+/* check if channel is allowed to be used as primary */
+int chan_pri_allowed(const struct hostapd_channel_data *chan)
+{
+       return !(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+               (chan->allowed_bw & HOSTAPD_CHAN_WIDTH_20);
+}
index 9cddbd50e56a9a4b1ca7b49fb0b880e83ecc08b6..eb1f1c57f10f55c3f81f7227a50ed7f9b8ce1d84 100644 (file)
@@ -39,4 +39,9 @@ void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps,
                      int disabled);
 int ieee80211ac_cap_check(u32 hw, u32 conf);
 
+u32 num_chan_to_bw(int num_chans);
+int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw,
+                   int ht40_plus, int pri);
+int chan_pri_allowed(const struct hostapd_channel_data *chan);
+
 #endif /* HW_FEATURES_COMMON_H */