]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
ACS: Validate all channels in a segment before selection
authorSuraj P Kizhakkethil <suraj.kizhakkethil@oss.qualcomm.com>
Thu, 24 Jul 2025 05:45:11 +0000 (11:15 +0530)
committerJouni Malinen <j@w1.fi>
Tue, 12 Aug 2025 20:28:51 +0000 (23:28 +0300)
Currently, ACS validates only the first channel in a segment against
the current configuration. If this validation passes, the channel
with the lowest interference factor within that segment is selected
as the potential primary channel. However, this can result in selecting
a primary channel that does not comply with the current configuration.

For example, in 6 GHz band with 320 MHz bandwidth, if the chanlist is
set to 33-93, ACS may select a primary channel outside the given
chanlist, in the range 97-125.

Additionally, if the first channel fails validation, the other channels
in the segment are ignored, even if they have a lower interference
factor and meets the requirements of the current configuration.

For example, in 5 GHz band with 80 MHz bandwidth, if the chanlist is
set to 153-161, the AP fails to come up because the first channel of
the segment (149) is not present in the chanlist.

Fix this issue by validating all channels in a segment. If DFS,
max_tx_power or indoor channel validation fails for any channel in
a segment, skip the segment. If chanlist/freqlist validation fails for
any channel in a segment, choose another channel in the same segment.

Signed-off-by: Suraj P Kizhakkethil <suraj.kizhakkethil@oss.qualcomm.com>
src/ap/acs.c

index 2a43da48ff0ffb829119edb1e5cfbc17753e23a3..5a1e58207b9b0b4285ecbdeda9fa803e591d943e 100644 (file)
@@ -831,7 +831,8 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
                         struct hostapd_channel_data **ideal_chan,
                         long double *ideal_factor)
 {
-       struct hostapd_channel_data *chan, *adj_chan = NULL, *best;
+       struct hostapd_channel_data *chan, *adj_chan = NULL,
+               *chan2 = NULL, *best;
        long double factor;
        int i, j;
        int bw320_offset = 0, ideal_bw320_offset = 0;
@@ -850,43 +851,6 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
 
                chan = &mode->channels[i];
 
-               /* Since in the current ACS implementation the first channel is
-                * always a primary channel, skip channels not available as
-                * primary until more sophisticated channel selection is
-                * implemented.
-                *
-                * If this implementation is changed to allow any channel in
-                * the bandwidth to be the primary one, the last parameter to
-                * acs_update_puncturing_bitmap() should be changed to the index
-                * of the primary channel
-                */
-               if (!chan_pri_allowed(chan))
-                       continue;
-
-               if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
-                   iface->conf->acs_exclude_dfs)
-                       continue;
-
-               if (!is_in_chanlist(iface, chan))
-                       continue;
-
-               if (!is_in_freqlist(iface, chan))
-                       continue;
-
-               if (chan->max_tx_power < iface->conf->min_tx_power)
-                       continue;
-
-               if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
-                   iface->conf->country[2] == 0x4f)
-                       continue;
-
-               if (!chan_bw_allowed(chan, bw, secondary_channel != -1, 1)) {
-                       wpa_printf(MSG_DEBUG,
-                                  "ACS: Channel %d: BW %u is not supported",
-                                  chan->chan, bw);
-                       continue;
-               }
-
                /* HT40 on 5 GHz has a limited set of primary channels as per
                 * 11n Annex J */
                if (bw == 40 &&
@@ -931,35 +895,51 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
 
                factor = 0;
                best = NULL;
-               if (acs_usable_chan(chan)) {
-                       factor = chan->interference_factor;
-                       total_weight = 1;
-                       best = chan;
-               }
 
-               for (j = 1; j < n_chans; j++) {
-                       adj_chan = acs_find_chan(iface, chan->freq +
-                                                j * secondary_channel * 20);
-                       if (!adj_chan)
+               for (j = 0; j < n_chans; j++) {
+                       chan2 = acs_find_chan(iface, chan->freq +
+                                             j * secondary_channel * 20);
+                       if (!chan2)
                                break;
 
-                       if (!chan_bw_allowed(adj_chan, bw, 1, 0)) {
+                       if (!chan_bw_allowed(chan2, bw, secondary_channel != -1,
+                                            j == 0)) {
                                wpa_printf(MSG_DEBUG,
-                                          "ACS: PRI Channel %d: secondary channel %d BW %u is not supported",
-                                          chan->chan, adj_chan->chan, bw);
+                                          "ACS: Channel %d: BW %u is not supported",
+                                          chan2->chan, bw);
                                break;
                        }
 
-                       if (!acs_usable_chan(adj_chan))
+                       if ((chan2->flag & HOSTAPD_CHAN_RADAR) &&
+                           iface->conf->acs_exclude_dfs)
+                               break;
+
+                       if (chan2->max_tx_power < iface->conf->min_tx_power)
+                               break;
+
+                       if ((chan2->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
+                           iface->conf->country[2] == 0x4f)
+                               break;
+
+                       if (!acs_usable_chan(chan2))
                                continue;
 
-                       factor += adj_chan->interference_factor;
+                       factor += chan2->interference_factor;
                        total_weight += 1;
 
+                       if (!chan_pri_allowed(chan2))
+                               continue;
+
+                       if (!is_in_chanlist(iface, chan2))
+                               continue;
+
+                       if (!is_in_freqlist(iface, chan2))
+                               continue;
+
                        /* find the best channel in this segment */
-                       if (!best || adj_chan->interference_factor <
+                       if (!best || chan2->interference_factor <
                            best->interference_factor)
-                               best = adj_chan;
+                               best = chan2;
                }
 
                if (j != n_chans) {
@@ -968,11 +948,18 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
                        continue;
                }
 
+               if (!best) {
+                       wpa_printf(MSG_DEBUG,
+                                  "ACS: No valid channel found in the segment starting with channel %d",
+                                  chan->chan);
+                       continue;
+               }
+
                /* If the AP is in the 5 GHz or 6 GHz band, lets prefer a less
                 * crowded primary channel if one was found in the segment */
                if (iface->current_mode &&
                    iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
-                   best && chan != best) {
+                   chan != best) {
                        wpa_printf(MSG_DEBUG,
                                   "ACS: promoting channel %d over %d (less interference %Lg/%Lg)",
                                   best->chan, chan->chan,