hostapd: Add mechanism to track unconnected stations
[mech_eap.git] / src / ap / hostapd.c
index 81e9b2b..e4d7bfc 100644 (file)
@@ -17,6 +17,7 @@
 #include "eap_server/tncs.h"
 #include "eapol_auth/eapol_auth_sm.h"
 #include "eapol_auth/eapol_auth_sm_i.h"
+#include "fst/fst.h"
 #include "hostapd.h"
 #include "authsrv.h"
 #include "sta_info.h"
@@ -82,8 +83,7 @@ static void hostapd_reload_bss(struct hostapd_data *hapd)
                 * Force PSK to be derived again since SSID or passphrase may
                 * have changed.
                 */
-               os_free(ssid->wpa_psk);
-               ssid->wpa_psk = NULL;
+               hostapd_config_clear_wpa_psk(&hapd->conf->ssid.wpa_psk);
        }
        if (hostapd_setup_wpa_psk(hapd->conf)) {
                wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK "
@@ -180,6 +180,7 @@ int hostapd_reload_config(struct hostapd_iface *iface)
                hapd = iface->bss[j];
                hapd->iconf = newconf;
                hapd->iconf->channel = oldconf->channel;
+               hapd->iconf->acs = oldconf->acs;
                hapd->iconf->secondary_channel = oldconf->secondary_channel;
                hapd->iconf->ieee80211n = oldconf->ieee80211n;
                hapd->iconf->ieee80211ac = oldconf->ieee80211ac;
@@ -354,9 +355,30 @@ static void hostapd_cleanup(struct hostapd_data *hapd)
 }
 
 
+static void sta_track_deinit(struct hostapd_iface *iface)
+{
+       struct hostapd_sta_info *info;
+
+       if (!iface->num_sta_seen)
+               return;
+
+       while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info,
+                                    list))) {
+               dl_list_del(&info->list);
+               iface->num_sta_seen--;
+               os_free(info);
+       }
+}
+
+
 static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
 {
        wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+#ifdef CONFIG_IEEE80211N
+#ifdef NEED_AP_MLME
+       hostapd_stop_setup_timers(iface);
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_IEEE80211N */
        hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
        iface->hw_features = NULL;
        os_free(iface->current_rates);
@@ -364,6 +386,7 @@ static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
        os_free(iface->basic_rates);
        iface->basic_rates = NULL;
        ap_list_deinit(iface);
+       sta_track_deinit(iface);
 }
 
 
@@ -794,6 +817,15 @@ static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd,
 }
 
 
+static int hostapd_das_disconnect_pmksa(struct hostapd_data *hapd,
+                                       struct radius_das_attrs *attr)
+{
+       if (!hapd->wpa_auth)
+               return -1;
+       return wpa_auth_radius_das_disconnect_pmksa(hapd->wpa_auth, attr);
+}
+
+
 static enum radius_das_res
 hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr)
 {
@@ -811,6 +843,11 @@ hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr)
                                   "RADIUS DAS: Multiple sessions match - not supported");
                        return RADIUS_DAS_MULTI_SESSION_MATCH;
                }
+               if (hostapd_das_disconnect_pmksa(hapd, attr) == 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "RADIUS DAS: PMKSA cache entry matched");
+                       return RADIUS_DAS_SUCCESS;
+               }
                wpa_printf(MSG_DEBUG, "RADIUS DAS: No matching session found");
                return RADIUS_DAS_SESSION_NOT_FOUND;
        }
@@ -843,7 +880,7 @@ hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr)
 static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
 {
        struct hostapd_bss_config *conf = hapd->conf;
-       u8 ssid[HOSTAPD_MAX_SSID_LEN + 1];
+       u8 ssid[SSID_MAX_LEN + 1];
        int ssid_len, set_ssid;
        char force_ifname[IFNAMSIZ];
        u8 if_addr[ETH_ALEN];
@@ -1345,6 +1382,132 @@ fail:
 }
 
 
+#ifdef CONFIG_FST
+
+static const u8 * fst_hostapd_get_bssid_cb(void *ctx)
+{
+       struct hostapd_data *hapd = ctx;
+
+       return hapd->own_addr;
+}
+
+
+static void fst_hostapd_get_channel_info_cb(void *ctx,
+                                           enum hostapd_hw_mode *hw_mode,
+                                           u8 *channel)
+{
+       struct hostapd_data *hapd = ctx;
+
+       *hw_mode = ieee80211_freq_to_chan(hapd->iface->freq, channel);
+}
+
+
+static void fst_hostapd_set_ies_cb(void *ctx, const struct wpabuf *fst_ies)
+{
+       struct hostapd_data *hapd = ctx;
+
+       if (hapd->iface->fst_ies != fst_ies) {
+               hapd->iface->fst_ies = fst_ies;
+               if (ieee802_11_set_beacon(hapd))
+                       wpa_printf(MSG_WARNING, "FST: Cannot set beacon");
+       }
+}
+
+
+static int fst_hostapd_send_action_cb(void *ctx, const u8 *da,
+                                     struct wpabuf *buf)
+{
+       struct hostapd_data *hapd = ctx;
+
+       return hostapd_drv_send_action(hapd, hapd->iface->freq, 0, da,
+                                      wpabuf_head(buf), wpabuf_len(buf));
+}
+
+
+static const struct wpabuf * fst_hostapd_get_mb_ie_cb(void *ctx, const u8 *addr)
+{
+       struct hostapd_data *hapd = ctx;
+       struct sta_info *sta = ap_get_sta(hapd, addr);
+
+       return sta ? sta->mb_ies : NULL;
+}
+
+
+static void fst_hostapd_update_mb_ie_cb(void *ctx, const u8 *addr,
+                                       const u8 *buf, size_t size)
+{
+       struct hostapd_data *hapd = ctx;
+       struct sta_info *sta = ap_get_sta(hapd, addr);
+
+       if (sta) {
+               struct mb_ies_info info;
+
+               if (!mb_ies_info_by_ies(&info, buf, size)) {
+                       wpabuf_free(sta->mb_ies);
+                       sta->mb_ies = mb_ies_by_info(&info);
+               }
+       }
+}
+
+
+static const u8 * fst_hostapd_get_sta(struct fst_get_peer_ctx **get_ctx,
+                                     Boolean mb_only)
+{
+       struct sta_info *s = (struct sta_info *) *get_ctx;
+
+       if (mb_only) {
+               for (; s && !s->mb_ies; s = s->next)
+                       ;
+       }
+
+       if (s) {
+               *get_ctx = (struct fst_get_peer_ctx *) s->next;
+
+               return s->addr;
+       }
+
+       *get_ctx = NULL;
+       return NULL;
+}
+
+
+static const u8 * fst_hostapd_get_peer_first(void *ctx,
+                                            struct fst_get_peer_ctx **get_ctx,
+                                            Boolean mb_only)
+{
+       struct hostapd_data *hapd = ctx;
+
+       *get_ctx = (struct fst_get_peer_ctx *) hapd->sta_list;
+
+       return fst_hostapd_get_sta(get_ctx, mb_only);
+}
+
+
+static const u8 * fst_hostapd_get_peer_next(void *ctx,
+                                           struct fst_get_peer_ctx **get_ctx,
+                                           Boolean mb_only)
+{
+       return fst_hostapd_get_sta(get_ctx, mb_only);
+}
+
+
+void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
+                               struct fst_wpa_obj *iface_obj)
+{
+       iface_obj->ctx = hapd;
+       iface_obj->get_bssid = fst_hostapd_get_bssid_cb;
+       iface_obj->get_channel_info = fst_hostapd_get_channel_info_cb;
+       iface_obj->set_ies = fst_hostapd_set_ies_cb;
+       iface_obj->send_action = fst_hostapd_send_action_cb;
+       iface_obj->get_mb_ie = fst_hostapd_get_mb_ie_cb;
+       iface_obj->update_mb_ie = fst_hostapd_update_mb_ie_cb;
+       iface_obj->get_peer_first = fst_hostapd_get_peer_first;
+       iface_obj->get_peer_next = fst_hostapd_get_peer_next;
+}
+
+#endif /* CONFIG_FST */
+
+
 /**
  * hostapd_setup_interface_complete - Complete interface setup
  *
@@ -1359,6 +1522,7 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
        size_t j;
        u8 *prev_addr;
        int delay_apply_cfg = 0;
+       int res_dfs_offload = 0;
 
        if (err)
                goto fail;
@@ -1385,6 +1549,23 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
                                        goto fail;
                                return res;
                        }
+               } else {
+                       /* If DFS is offloaded to the driver */
+                       res_dfs_offload = hostapd_handle_dfs_offload(iface);
+                       if (res_dfs_offload <= 0) {
+                               if (res_dfs_offload < 0)
+                                       goto fail;
+                       } else {
+                               wpa_printf(MSG_DEBUG,
+                                          "Proceed with AP/channel setup");
+                               /*
+                                * If this is a DFS channel, move to completing
+                                * AP setup.
+                                */
+                               if (res_dfs_offload == 1)
+                                       goto dfs_offload;
+                               /* Otherwise fall through. */
+                       }
                }
 #endif /* NEED_AP_MLME */
 
@@ -1459,6 +1640,7 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
        hostapd_tx_queue_params(iface);
 
        ap_list_init(iface);
+       dl_list_init(&iface->sta_seen);
 
        hostapd_set_acl(hapd);
 
@@ -1479,6 +1661,35 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
                        goto fail;
        }
 
+       if ((iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+           !res_dfs_offload) {
+               /*
+                * If freq is DFS, and DFS is offloaded to the driver, then wait
+                * for CAC to complete.
+                */
+               wpa_printf(MSG_DEBUG, "%s: Wait for CAC to complete", __func__);
+               return res_dfs_offload;
+       }
+
+#ifdef NEED_AP_MLME
+dfs_offload:
+#endif /* NEED_AP_MLME */
+
+#ifdef CONFIG_FST
+       if (hapd->iconf->fst_cfg.group_id[0]) {
+               struct fst_wpa_obj iface_obj;
+
+               fst_hostapd_fill_iface_obj(hapd, &iface_obj);
+               iface->fst = fst_attach(hapd->conf->iface, hapd->own_addr,
+                                       &iface_obj, &hapd->iconf->fst_cfg);
+               if (!iface->fst) {
+                       wpa_printf(MSG_ERROR, "Could not attach to FST %s",
+                                  hapd->iconf->fst_cfg.group_id);
+                       goto fail;
+               }
+       }
+#endif /* CONFIG_FST */
+
        hostapd_set_state(iface, HAPD_IFACE_ENABLED);
        wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED);
        if (hapd->setup_complete_cb)
@@ -1495,6 +1706,12 @@ fail:
        wpa_printf(MSG_ERROR, "Interface initialization failed");
        hostapd_set_state(iface, HAPD_IFACE_DISABLED);
        wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+#ifdef CONFIG_FST
+       if (iface->fst) {
+               fst_detach(iface->fst);
+               iface->fst = NULL;
+       }
+#endif /* CONFIG_FST */
        if (iface->interfaces && iface->interfaces->terminate_on_error)
                eloop_terminate();
        return -1;
@@ -1570,6 +1787,7 @@ static void hostapd_bss_deinit(struct hostapd_data *hapd)
        wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__,
                   hapd->conf->iface);
        hostapd_bss_deinit_no_free(hapd);
+       wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
        hostapd_cleanup(hapd);
 }
 
@@ -1582,6 +1800,8 @@ void hostapd_interface_deinit(struct hostapd_iface *iface)
        if (iface == NULL)
                return;
 
+       hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+
 #ifdef CONFIG_IEEE80211N
 #ifdef NEED_AP_MLME
        hostapd_stop_setup_timers(iface);
@@ -1591,6 +1811,13 @@ void hostapd_interface_deinit(struct hostapd_iface *iface)
        eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
        iface->wait_channel_update = 0;
 
+#ifdef CONFIG_FST
+       if (iface->fst) {
+               fst_detach(iface->fst);
+               iface->fst = NULL;
+       }
+#endif /* CONFIG_FST */
+
        for (j = iface->num_bss - 1; j >= 0; j--)
                hostapd_bss_deinit(iface->bss[j]);
 }
@@ -1977,7 +2204,7 @@ hostapd_iface_alloc(struct hapd_interfaces *interfaces)
 
 static struct hostapd_config *
 hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname,
-                    const char *ctrl_iface)
+                    const char *ctrl_iface, const char *driver)
 {
        struct hostapd_bss_config *bss;
        struct hostapd_config *conf;
@@ -1990,6 +2217,21 @@ hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname,
                return NULL;
        }
 
+       if (driver) {
+               int j;
+
+               for (j = 0; wpa_drivers[j]; j++) {
+                       if (os_strcmp(driver, wpa_drivers[j]->name) == 0) {
+                               conf->driver = wpa_drivers[j];
+                               goto skip;
+                       }
+               }
+
+               wpa_printf(MSG_ERROR,
+                          "Invalid/unknown driver '%s' - registering the default driver",
+                          driver);
+       }
+
        conf->driver = wpa_drivers[0];
        if (conf->driver == NULL) {
                wpa_printf(MSG_ERROR, "No driver wrappers registered!");
@@ -1997,6 +2239,7 @@ hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname,
                return NULL;
        }
 
+skip:
        bss = conf->last_bss = conf->bss[0];
 
        os_strlcpy(bss->iface, ifname, sizeof(bss->iface));
@@ -2157,8 +2400,14 @@ int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf)
                if (conf && conf->bss)
                        os_strlcpy(conf->bss[0]->iface, buf,
                                   sizeof(conf->bss[0]->iface));
-       } else
-               conf = hostapd_config_alloc(interfaces, buf, ptr);
+       } else {
+               char *driver = os_strchr(ptr, ' ');
+
+               if (driver)
+                       *driver++ = '\0';
+               conf = hostapd_config_alloc(interfaces, buf, ptr, driver);
+       }
+
        if (conf == NULL || conf->bss == NULL) {
                wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
                           "for configuration", __func__);
@@ -2670,4 +2919,43 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
        hostapd_enable_iface(iface);
 }
 
+
+struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
+                                       const char *ifname)
+{
+       size_t i, j;
+
+       for (i = 0; i < interfaces->count; i++) {
+               struct hostapd_iface *iface = interfaces->iface[i];
+
+               for (j = 0; j < iface->num_bss; j++) {
+                       struct hostapd_data *hapd = iface->bss[j];
+
+                       if (os_strcmp(ifname, hapd->conf->iface) == 0)
+                               return hapd;
+               }
+       }
+
+       return NULL;
+}
+
 #endif /* NEED_AP_MLME */
+
+
+void hostapd_periodic_iface(struct hostapd_iface *iface)
+{
+       size_t i;
+
+       ap_list_timer(iface);
+
+       for (i = 0; i < iface->num_bss; i++) {
+               struct hostapd_data *hapd = iface->bss[i];
+
+               if (!hapd->started)
+                       continue;
+
+#ifndef CONFIG_NO_RADIUS
+               hostapd_acl_expire(hapd);
+#endif /* CONFIG_NO_RADIUS */
+       }
+}