]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
AP: Keep channel survey over regulatory changes
authorBenjamin Berg <benjamin.berg@intel.com>
Fri, 21 Nov 2025 10:05:53 +0000 (11:05 +0100)
committerJouni Malinen <j@w1.fi>
Mon, 26 Jan 2026 17:27:16 +0000 (19:27 +0200)
When updatig the HW modes after a regulatory change, the internal lists
for the current survey would be lost, resulting in a crash when fetching
the survey results later on. This can happen if the regulatory changes
because of the ACS scan.

Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Reviewed-by: Andrei Otcheretianski <andrei.otcheretianski@intel.com>
src/ap/hw_features.c

index 7ca4e24e59d61ea6714f4fe2eaad74e37a07168b..a843425e6bfa5df369d3cc873fe086989557d491 100644 (file)
@@ -72,6 +72,49 @@ static char * dfs_info(struct hostapd_channel_data *chan)
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 
 
+static void move_survey_data(struct hostapd_channel_data *new_chan,
+                            struct hostapd_channel_data *old_chan)
+{
+       struct freq_survey *survey, *tmp;
+
+       /* Copy the survey list from the old to new channel */
+       if (old_chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED) {
+               dl_list_init(&new_chan->survey_list);
+               dl_list_for_each_safe(survey, tmp, &old_chan->survey_list,
+                                     struct freq_survey, list) {
+                       dl_list_del(&survey->list);
+                       dl_list_add(&new_chan->survey_list,
+                                   &survey->list);
+               }
+               new_chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED;
+       }
+}
+
+
+static void move_hw_mode_data(struct hostapd_hw_modes *new_feature,
+                             struct hostapd_hw_modes *old_feature)
+{
+       int i, j;
+
+       for (i = 0; i < new_feature->num_channels; i++) {
+               struct hostapd_channel_data *new_chan;
+               struct hostapd_channel_data *old_chan;
+
+               new_chan = &new_feature->channels[i];
+
+               for (j = 0; j < old_feature->num_channels; j++) {
+                       old_chan = &old_feature->channels[j];
+
+                       if (new_chan->freq != old_chan->freq)
+                               continue;
+
+                       move_survey_data(new_chan, old_chan);
+                       break;
+               }
+       }
+}
+
+
 int hostapd_get_hw_features(struct hostapd_iface *iface)
 {
        struct hostapd_data *hapd = iface->bss[0];
@@ -113,9 +156,6 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
                is_6ghz = iface->current_mode->is_6ghz;
                iface->current_mode = NULL;
        }
-       hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
-       iface->hw_features = modes;
-       iface->num_hw_features = num_modes;
 
        for (i = 0; i < num_modes; i++) {
                struct hostapd_hw_modes *feature = &modes[i];
@@ -163,8 +203,23 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
                                   feature->channels[j].max_tx_power,
                                   dfs ? dfs_info(&feature->channels[j]) : "");
                }
+
+               /* Move any old data that should be kept */
+               for (j = 0; j < iface->num_hw_features; j++) {
+                       struct hostapd_hw_modes *old_feature =
+                               &iface->hw_features[j];
+
+                       if (feature->mode != old_feature->mode)
+                               continue;
+
+                       move_hw_mode_data(feature, old_feature);
+               }
        }
 
+       hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
+       iface->hw_features = modes;
+       iface->num_hw_features = num_modes;
+
        if (orig_mode_valid && !iface->current_mode) {
                wpa_printf(MSG_ERROR,
                           "%s: Could not update iface->current_mode",