Initialize iface->sta_seen on allocation
[mech_eap.git] / src / ap / hostapd.c
index 9aaa9a6..5e83fbc 100644 (file)
@@ -43,6 +43,8 @@
 #include "x_snoop.h"
 #include "dhcp_snoop.h"
 #include "ndisc_snoop.h"
+#include "neighbor_db.h"
+#include "rrm.h"
 
 
 static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
@@ -204,10 +206,12 @@ int hostapd_reload_config(struct hostapd_iface *iface)
 
 
 static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd,
-                                             char *ifname)
+                                             const char *ifname)
 {
        int i;
 
+       if (!ifname)
+               return;
        for (i = 0; i < NUM_WEP_KEYS; i++) {
                if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i,
                                        0, NULL, 0, NULL, 0)) {
@@ -335,6 +339,8 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd)
        wpabuf_free(hapd->mesh_pending_auth);
        hapd->mesh_pending_auth = NULL;
 #endif /* CONFIG_MESH */
+
+       hostapd_clean_rrm(hapd);
 }
 
 
@@ -512,6 +518,9 @@ static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface)
        if (hostapd_drv_none(hapd))
                return 0;
 
+       if (iface->conf->use_driver_iface_addr)
+               return 0;
+
        /* Generate BSSID mask that is large enough to cover the BSSIDs. */
 
        /* Determine the bits necessary to cover the number of BSSIDs. */
@@ -905,12 +914,9 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
        hapd->started = 1;
 
        if (!first || first == -1) {
-               if (is_zero_ether_addr(conf->bssid)) {
-                       /* Allocate the next available BSSID. */
-                       do {
-                               inc_byte_array(hapd->own_addr, ETH_ALEN);
-                       } while (mac_in_conf(hapd->iconf, hapd->own_addr));
-               } else {
+               u8 *addr = hapd->own_addr;
+
+               if (!is_zero_ether_addr(conf->bssid)) {
                        /* Allocate the configured BSSID. */
                        os_memcpy(hapd->own_addr, conf->bssid, ETH_ALEN);
 
@@ -922,11 +928,18 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
                                           "the radio", conf->iface);
                                return -1;
                        }
+               } else if (hapd->iconf->use_driver_iface_addr) {
+                       addr = NULL;
+               } else {
+                       /* Allocate the next available BSSID. */
+                       do {
+                               inc_byte_array(hapd->own_addr, ETH_ALEN);
+                       } while (mac_in_conf(hapd->iconf, hapd->own_addr));
                }
 
                hapd->interface_added = 1;
                if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
-                                  conf->iface, hapd->own_addr, hapd,
+                                  conf->iface, addr, hapd,
                                   &hapd->drv_priv, force_ifname, if_addr,
                                   conf->bridge[0] ? conf->bridge : NULL,
                                   first == -1)) {
@@ -935,6 +948,9 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
                        hapd->interface_added = 0;
                        return -1;
                }
+
+               if (!addr)
+                       os_memcpy(hapd->own_addr, if_addr, ETH_ALEN);
        }
 
        if (conf->wmm_enabled < 0)
@@ -1027,6 +1043,8 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
                das_conf.time_window = conf->radius_das_time_window;
                das_conf.require_event_timestamp =
                        conf->radius_das_require_event_timestamp;
+               das_conf.require_message_authenticator =
+                       conf->radius_das_require_message_authenticator;
                das_conf.ctx = hapd;
                das_conf.disconnect = hostapd_das_disconnect;
                hapd->radius_das = radius_das_init(&das_conf);
@@ -1514,6 +1532,126 @@ void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
 #endif /* CONFIG_FST */
 
 
+#ifdef NEED_AP_MLME
+static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
+                                                   int ht, int vht)
+{
+       if (!ht && !vht)
+               return NR_CHAN_WIDTH_20;
+       if (!hapd->iconf->secondary_channel)
+               return NR_CHAN_WIDTH_20;
+       if (!vht || hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT)
+               return NR_CHAN_WIDTH_40;
+       if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ)
+               return NR_CHAN_WIDTH_80;
+       if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_160MHZ)
+               return NR_CHAN_WIDTH_160;
+       if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ)
+               return NR_CHAN_WIDTH_80P80;
+       return NR_CHAN_WIDTH_20;
+}
+#endif /* NEED_AP_MLME */
+
+
+static void hostapd_set_own_neighbor_report(struct hostapd_data *hapd)
+{
+#ifdef NEED_AP_MLME
+       u16 capab = hostapd_own_capab_info(hapd);
+       int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
+       int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
+       struct wpa_ssid_value ssid;
+       u8 channel, op_class;
+       int center_freq1 = 0, center_freq2 = 0;
+       enum nr_chan_width width;
+       u32 bssid_info;
+       struct wpabuf *nr;
+
+       if (!(hapd->conf->radio_measurements[0] &
+             WLAN_RRM_CAPS_NEIGHBOR_REPORT))
+               return;
+
+       bssid_info = 3; /* AP is reachable */
+       bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */
+       bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */
+
+       if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT)
+               bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
+
+       bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */
+
+       if (hapd->conf->wmm_enabled) {
+               bssid_info |= NEI_REP_BSSID_INFO_QOS;
+
+               if (hapd->conf->wmm_uapsd &&
+                   (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
+                       bssid_info |= NEI_REP_BSSID_INFO_APSD;
+       }
+
+       if (ht) {
+               bssid_info |= NEI_REP_BSSID_INFO_HT |
+                       NEI_REP_BSSID_INFO_DELAYED_BA;
+
+               /* VHT bit added in IEEE P802.11-REVmc/D4.3 */
+               if (vht)
+                       bssid_info |= NEI_REP_BSSID_INFO_VHT;
+       }
+
+       /* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
+
+       ieee80211_freq_to_channel_ext(hapd->iface->freq,
+                                     hapd->iconf->secondary_channel,
+                                     hapd->iconf->vht_oper_chwidth,
+                                     &op_class, &channel);
+       width = hostapd_get_nr_chan_width(hapd, ht, vht);
+       if (vht) {
+               center_freq1 = ieee80211_chan_to_freq(
+                       NULL, op_class,
+                       hapd->iconf->vht_oper_centr_freq_seg0_idx);
+               if (width == NR_CHAN_WIDTH_80P80)
+                       center_freq2 = ieee80211_chan_to_freq(
+                               NULL, op_class,
+                               hapd->iconf->vht_oper_centr_freq_seg1_idx);
+       } else if (ht) {
+               center_freq1 = hapd->iface->freq +
+                       10 * hapd->iconf->secondary_channel;
+       }
+
+       ssid.ssid_len = hapd->conf->ssid.ssid_len;
+       os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
+
+       /*
+        * Neighbor Report element size = BSSID + BSSID info + op_class + chan +
+        * phy type + wide bandwidth channel subelement.
+        */
+       nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5);
+       if (!nr)
+               return;
+
+       wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN);
+       wpabuf_put_le32(nr, bssid_info);
+       wpabuf_put_u8(nr, op_class);
+       wpabuf_put_u8(nr, channel);
+       wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht));
+
+       /*
+        * Wide Bandwidth Channel subelement may be needed to allow the
+        * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0
+        * Figure 9-301.
+        */
+       wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN);
+       wpabuf_put_u8(nr, 3);
+       wpabuf_put_u8(nr, width);
+       wpabuf_put_u8(nr, center_freq1);
+       wpabuf_put_u8(nr, center_freq2);
+
+       hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci,
+                            hapd->iconf->civic);
+
+       wpabuf_free(nr);
+#endif /* NEED_AP_MLME */
+}
+
+
 static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
                                                 int err)
 {
@@ -1639,7 +1777,6 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
        hostapd_tx_queue_params(iface);
 
        ap_list_init(iface);
-       dl_list_init(&iface->sta_seen);
 
        hostapd_set_acl(hapd);
 
@@ -1699,6 +1836,9 @@ dfs_offload:
        if (iface->interfaces && iface->interfaces->terminate_on_error > 0)
                iface->interfaces->terminate_on_error--;
 
+       for (j = 0; j < iface->num_bss; j++)
+               hostapd_set_own_neighbor_report(iface->bss[j]);
+
        return 0;
 
 fail:
@@ -1860,6 +2000,7 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
        hapd->driver = hapd->iconf->driver;
        hapd->ctrl_sock = -1;
        dl_list_init(&hapd->ctrl_dst);
+       dl_list_init(&hapd->nr_db);
 
        return hapd;
 }
@@ -1867,6 +2008,8 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
 
 static void hostapd_bss_deinit(struct hostapd_data *hapd)
 {
+       if (!hapd)
+               return;
        wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__,
                   hapd->conf->iface);
        hostapd_bss_deinit_no_free(hapd);
@@ -1901,8 +2044,11 @@ void hostapd_interface_deinit(struct hostapd_iface *iface)
        }
 #endif /* CONFIG_FST */
 
-       for (j = iface->num_bss - 1; j >= 0; j--)
+       for (j = iface->num_bss - 1; j >= 0; j--) {
+               if (!iface->bss)
+                       break;
                hostapd_bss_deinit(iface->bss[j]);
+       }
 }
 
 
@@ -1911,6 +2057,8 @@ void hostapd_interface_free(struct hostapd_iface *iface)
        size_t j;
        wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
        for (j = 0; j < iface->num_bss; j++) {
+               if (!iface->bss)
+                       break;
                wpa_printf(MSG_DEBUG, "%s: free hapd %p",
                           __func__, iface->bss[j]);
                os_free(iface->bss[j]);
@@ -1919,6 +2067,20 @@ void hostapd_interface_free(struct hostapd_iface *iface)
 }
 
 
+struct hostapd_iface * hostapd_alloc_iface(void)
+{
+       struct hostapd_iface *hapd_iface;
+
+       hapd_iface = os_zalloc(sizeof(*hapd_iface));
+       if (!hapd_iface)
+               return NULL;
+
+       dl_list_init(&hapd_iface->sta_seen);
+
+       return hapd_iface;
+}
+
+
 /**
  * hostapd_init - Allocate and initialize per-interface data
  * @config_file: Path to the configuration file
@@ -1936,7 +2098,7 @@ struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
        struct hostapd_data *hapd;
        size_t i;
 
-       hapd_iface = os_zalloc(sizeof(*hapd_iface));
+       hapd_iface = hostapd_alloc_iface();
        if (hapd_iface == NULL)
                goto fail;
 
@@ -2272,7 +2434,7 @@ hostapd_iface_alloc(struct hapd_interfaces *interfaces)
                return NULL;
        interfaces->iface = iface;
        hapd_iface = interfaces->iface[interfaces->count] =
-               os_zalloc(sizeof(*hapd_iface));
+               hostapd_alloc_iface();
        if (hapd_iface == NULL) {
                wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for "
                           "the interface", __func__);
@@ -2711,8 +2873,8 @@ const char * hostapd_state_text(enum hostapd_iface_state s)
 void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s)
 {
        wpa_printf(MSG_INFO, "%s: interface state %s->%s",
-                  iface->conf->bss[0]->iface, hostapd_state_text(iface->state),
-                  hostapd_state_text(s));
+                  iface->conf ? iface->conf->bss[0]->iface : "N/A",
+                  hostapd_state_text(iface->state), hostapd_state_text(s));
        iface->state = s;
 }
 
@@ -3080,6 +3242,8 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
        hostapd_enable_iface(iface);
 }
 
+#endif /* NEED_AP_MLME */
+
 
 struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
                                        const char *ifname)
@@ -3100,8 +3264,6 @@ struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
        return NULL;
 }
 
-#endif /* NEED_AP_MLME */
-
 
 void hostapd_periodic_iface(struct hostapd_iface *iface)
 {