Add AP channel switch mechanism
[mech_eap.git] / src / ap / drv_callbacks.c
index 5517294..1b69ba8 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / Callback functions for driver wrappers
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -13,6 +13,7 @@
 #include "drivers/driver.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
 #include "crypto/random.h"
 #include "p2p/p2p.h"
 #include "wps/wps.h"
@@ -28,6 +29,7 @@
 #include "ap_drv_ops.h"
 #include "ap_config.h"
 #include "hw_features.h"
+#include "dfs.h"
 
 
 int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
@@ -44,6 +46,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
 #endif /* CONFIG_IEEE80211R */
        u16 reason = WLAN_REASON_UNSPECIFIED;
        u16 status = WLAN_STATUS_SUCCESS;
+       const u8 *p2p_dev_addr = NULL;
 
        if (addr == NULL) {
                /*
@@ -84,6 +87,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
 
        sta = ap_get_sta(hapd, addr);
        if (sta) {
+               ap_sta_no_session_timeout(hapd, sta);
                accounting_sta_stop(hapd, sta);
 
                /*
@@ -106,9 +110,27 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
                wpabuf_free(sta->p2p_ie);
                sta->p2p_ie = ieee802_11_vendor_ie_concat(req_ies, req_ies_len,
                                                          P2P_IE_VENDOR_TYPE);
+               if (sta->p2p_ie)
+                       p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
        }
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_INTERWORKING
+       if (elems.ext_capab && elems.ext_capab_len > 4) {
+               if (elems.ext_capab[4] & 0x01)
+                       sta->qos_map_enabled = 1;
+       }
+#endif /* CONFIG_INTERWORKING */
+
+#ifdef CONFIG_HS20
+       wpabuf_free(sta->hs20_ie);
+       if (elems.hs20 && elems.hs20_len > 4) {
+               sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4,
+                                                elems.hs20_len - 4);
+       } else
+               sta->hs20_ie = NULL;
+#endif /* CONFIG_HS20 */
+
        if (hapd->conf->wpa) {
                if (ie == NULL || ielen == 0) {
 #ifdef CONFIG_WPS
@@ -145,7 +167,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
 
                if (sta->wpa_sm == NULL)
                        sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
-                                                       sta->addr);
+                                                       sta->addr,
+                                                       p2p_dev_addr);
                if (sta->wpa_sm == NULL) {
                        wpa_printf(MSG_ERROR, "Failed to initialize WPA state "
                                   "machine");
@@ -380,10 +403,33 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
        hapd->iconf->channel = channel;
        hapd->iconf->ieee80211n = ht;
        hapd->iconf->secondary_channel = offset;
+
+       if (hapd->iface->csa_in_progress && freq == hapd->iface->cs_freq) {
+               hostapd_cleanup_cs_params(hapd);
+
+               wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED "freq=%d",
+                       freq);
+       }
 #endif /* NEED_AP_MLME */
 }
 
 
+void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
+                                        const u8 *addr, int reason_code)
+{
+       switch (reason_code) {
+       case MAX_CLIENT_REACHED:
+               wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_MAX_STA MACSTR,
+                       MAC2STR(addr));
+               break;
+       case BLOCKED_CLIENT:
+               wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_BLOCKED_STA MACSTR,
+                       MAC2STR(addr));
+               break;
+       }
+}
+
+
 int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
                         const u8 *bssid, const u8 *ie, size_t ie_len,
                         int ssi_signal)
@@ -443,7 +489,7 @@ static void hostapd_notif_auth(struct hostapd_data *hapd,
        if (!sta) {
                sta = ap_sta_add(hapd, rx_auth->peer);
                if (sta == NULL) {
-                       status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
                        goto fail;
                }
        }
@@ -454,7 +500,7 @@ static void hostapd_notif_auth(struct hostapd_data *hapd,
                sta->auth_alg = WLAN_AUTH_FT;
                if (sta->wpa_sm == NULL)
                        sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
-                                                       sta->addr);
+                                                       sta->addr, NULL);
                if (sta->wpa_sm == NULL) {
                        wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA "
                                   "state machine");
@@ -503,13 +549,13 @@ static void hostapd_action_rx(struct hostapd_data *hapd,
                                           action->data + 2);
        }
 #endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211V
+#ifdef CONFIG_WNM
        if (action->category == WLAN_ACTION_WNM) {
                wpa_printf(MSG_DEBUG, "%s: WNM_ACTION length %d",
                           __func__, (int) action->len);
                ieee802_11_rx_wnm_action_ap(hapd, action);
        }
-#endif /* CONFIG_IEEE80211V */
+#endif /* CONFIG_WNM */
 }
 
 
@@ -688,6 +734,116 @@ static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
 }
 
 
+static struct hostapd_channel_data * hostapd_get_mode_channel(
+       struct hostapd_iface *iface, unsigned int freq)
+{
+       int i;
+       struct hostapd_channel_data *chan;
+
+       for (i = 0; i < iface->current_mode->num_channels; i++) {
+               chan = &iface->current_mode->channels[i];
+               if (!chan)
+                       return NULL;
+               if ((unsigned int) chan->freq == freq)
+                       return chan;
+       }
+
+       return NULL;
+}
+
+
+static void hostapd_update_nf(struct hostapd_iface *iface,
+                             struct hostapd_channel_data *chan,
+                             struct freq_survey *survey)
+{
+       if (!iface->chans_surveyed) {
+               chan->min_nf = survey->nf;
+               iface->lowest_nf = survey->nf;
+       } else {
+               if (dl_list_empty(&chan->survey_list))
+                       chan->min_nf = survey->nf;
+               else if (survey->nf < chan->min_nf)
+                       chan->min_nf = survey->nf;
+               if (survey->nf < iface->lowest_nf)
+                       iface->lowest_nf = survey->nf;
+       }
+}
+
+
+static void hostapd_event_get_survey(struct hostapd_data *hapd,
+                                    struct survey_results *survey_results)
+{
+       struct hostapd_iface *iface = hapd->iface;
+       struct freq_survey *survey, *tmp;
+       struct hostapd_channel_data *chan;
+
+       if (dl_list_empty(&survey_results->survey_list)) {
+               wpa_printf(MSG_DEBUG, "No survey data received");
+               return;
+       }
+
+       dl_list_for_each_safe(survey, tmp, &survey_results->survey_list,
+                             struct freq_survey, list) {
+               chan = hostapd_get_mode_channel(iface, survey->freq);
+               if (!chan)
+                       continue;
+               if (chan->flag & HOSTAPD_CHAN_DISABLED)
+                       continue;
+
+               dl_list_del(&survey->list);
+               dl_list_add_tail(&chan->survey_list, &survey->list);
+
+               hostapd_update_nf(iface, chan, survey);
+
+               iface->chans_surveyed++;
+       }
+}
+
+
+#ifdef NEED_AP_MLME
+
+static void hostapd_event_dfs_radar_detected(struct hostapd_data *hapd,
+                                            struct dfs_event *radar)
+{
+       wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq);
+       hostapd_dfs_radar_detected(hapd->iface, radar->freq, radar->ht_enabled,
+                                  radar->chan_offset, radar->chan_width,
+                                  radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_cac_finished(struct hostapd_data *hapd,
+                                          struct dfs_event *radar)
+{
+       wpa_printf(MSG_DEBUG, "DFS CAC finished on %d MHz", radar->freq);
+       hostapd_dfs_complete_cac(hapd->iface, 1, radar->freq, radar->ht_enabled,
+                                radar->chan_offset, radar->chan_width,
+                                radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_cac_aborted(struct hostapd_data *hapd,
+                                         struct dfs_event *radar)
+{
+       wpa_printf(MSG_DEBUG, "DFS CAC aborted on %d MHz", radar->freq);
+       hostapd_dfs_complete_cac(hapd->iface, 0, radar->freq, radar->ht_enabled,
+                                radar->chan_offset, radar->chan_width,
+                                radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_nop_finished(struct hostapd_data *hapd,
+                                          struct dfs_event *radar)
+{
+       wpa_printf(MSG_DEBUG, "DFS NOP finished on %d MHz", radar->freq);
+       hostapd_dfs_nop_finished(hapd->iface, radar->freq, radar->ht_enabled,
+                                radar->chan_offset, radar->chan_width,
+                                radar->cf1, radar->cf2);
+}
+
+#endif /* NEED_AP_MLME */
+
+
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                          union wpa_event_data *data)
 {
@@ -704,6 +860,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
                    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
                        level = MSG_EXCESSIVE;
+               if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+                   WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ)
+                       level = MSG_EXCESSIVE;
        }
 
        wpa_dbg(hapd->msg_ctx, level, "Event %s (%d) received",
@@ -819,6 +978,46 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                                        data->ch_switch.ht_enabled,
                                        data->ch_switch.ch_offset);
                break;
+       case EVENT_CONNECT_FAILED_REASON:
+               if (!data)
+                       break;
+               hostapd_event_connect_failed_reason(
+                       hapd, data->connect_failed_reason.addr,
+                       data->connect_failed_reason.code);
+               break;
+       case EVENT_SURVEY:
+               hostapd_event_get_survey(hapd, &data->survey_results);
+               break;
+#ifdef NEED_AP_MLME
+       case EVENT_DFS_RADAR_DETECTED:
+               if (!data)
+                       break;
+               hostapd_event_dfs_radar_detected(hapd, &data->dfs_event);
+               break;
+       case EVENT_DFS_CAC_FINISHED:
+               if (!data)
+                       break;
+               hostapd_event_dfs_cac_finished(hapd, &data->dfs_event);
+               break;
+       case EVENT_DFS_CAC_ABORTED:
+               if (!data)
+                       break;
+               hostapd_event_dfs_cac_aborted(hapd, &data->dfs_event);
+               break;
+       case EVENT_DFS_NOP_FINISHED:
+               if (!data)
+                       break;
+               hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
+               break;
+       case EVENT_CHANNEL_LIST_CHANGED:
+               /* channel list changed (regulatory?), update channel list */
+               /* TODO: check this. hostapd_get_hw_features() initializes
+                * too much stuff. */
+               /* hostapd_get_hw_features(hapd->iface); */
+               hostapd_channel_list_updated(
+                       hapd->iface, data->channel_list_changed.initiator);
+               break;
+#endif /* NEED_AP_MLME */
        default:
                wpa_printf(MSG_DEBUG, "Unknown event %d", event);
                break;