Fix IEEE 802.11r key derivation function to match with the standard
[wpasupplicant] / hostapd / beacon.c
index b720489..8ccfa12 100644 (file)
@@ -25,8 +25,9 @@
 #include "wme.h"
 #include "beacon.h"
 #include "hw_features.h"
-#include "driver.h"
+#include "driver_i.h"
 #include "sta_info.h"
+#include "wps_hostapd.h"
 
 
 static u8 ieee802_11_erp_info(struct hostapd_data *hapd)
@@ -95,12 +96,36 @@ static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid)
 }
 
 
+static u8 * hostapd_eid_country_add(u8 *pos, u8 *end, int chan_spacing,
+                                   struct hostapd_channel_data *start,
+                                   struct hostapd_channel_data *prev)
+{
+       if (end - pos < 3)
+               return pos;
+
+       /* first channel number */
+       *pos++ = start->chan;
+       /* number of channels */
+       *pos++ = (prev->chan - start->chan) / chan_spacing + 1;
+       /* maximum transmit power level */
+       *pos++ = start->max_tx_power;
+
+       return pos;
+}
+
+
 static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid,
                                int max_len)
 {
        u8 *pos = eid;
-
-       if (!hapd->iconf->ieee80211d || max_len < 6)
+       u8 *end = eid + max_len;
+       int i;
+       struct hostapd_hw_modes *mode;
+       struct hostapd_channel_data *start, *prev;
+       int chan_spacing = 1;
+
+       if (!hapd->iconf->ieee80211d || max_len < 6 ||
+           hapd->iface->current_mode == NULL)
                return eid;
 
        *pos++ = WLAN_EID_COUNTRY;
@@ -108,8 +133,42 @@ static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid,
        os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */
        pos += 3;
 
-       if ((pos - eid) & 1)
+       mode = hapd->iface->current_mode;
+       if (mode->mode == HOSTAPD_MODE_IEEE80211A)
+               chan_spacing = 4;
+
+       start = prev = NULL;
+       for (i = 0; i < mode->num_channels; i++) {
+               struct hostapd_channel_data *chan = &mode->channels[i];
+               if (chan->flag & HOSTAPD_CHAN_DISABLED)
+                       continue;
+               if (start && prev &&
+                   prev->chan + chan_spacing == chan->chan &&
+                   start->max_tx_power == chan->max_tx_power) {
+                       prev = chan;
+                       continue; /* can use same entry */
+               }
+
+               if (start) {
+                       pos = hostapd_eid_country_add(pos, end, chan_spacing,
+                                                     start, prev);
+                       start = NULL;
+               }
+
+               /* Start new group */
+               start = prev = chan;
+       }
+
+       if (start) {
+               pos = hostapd_eid_country_add(pos, end, chan_spacing,
+                                             start, prev);
+       }
+
+       if ((pos - eid) & 1) {
+               if (end - pos < 1)
+                       return eid;
                *pos++ = 0; /* pad for 16-bit alignment */
+       }
 
        eid[1] = (pos - eid) - 2;
 
@@ -145,6 +204,8 @@ void handle_probe_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt,
        ie = mgmt->u.probe_req.variable;
        ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
 
+       hostapd_wps_probe_req_rx(hapd, mgmt->sa, ie, ie_len);
+
        if (!hapd->iconf->send_probe_response)
                return;
 
@@ -237,12 +298,20 @@ void handle_probe_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt,
 
        pos = hostapd_eid_wpa(hapd, pos, epos - pos, sta);
 
-       /* Wi-Fi Wireless Multimedia Extensions */
-       pos = hostapd_eid_wme(hapd, pos);
+       /* Wi-Fi Alliance WMM */
+       pos = hostapd_eid_wmm(hapd, pos);
 
        pos = hostapd_eid_ht_capabilities_info(hapd, pos);
        pos = hostapd_eid_ht_operation(hapd, pos);
 
+#ifdef CONFIG_WPS
+       if (hapd->conf->wps_state && hapd->wps_probe_resp_ie) {
+               os_memcpy(pos, hapd->wps_probe_resp_ie,
+                         hapd->wps_probe_resp_ie_len);
+               pos += hapd->wps_probe_resp_ie_len;
+       }
+#endif /* CONFIG_WPS */
+
        if (hostapd_send_mgmt_frame(hapd, resp, pos - (u8 *) resp, 0) < 0)
                perror("handle_probe_req: send");
 
@@ -326,29 +395,36 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
        tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE -
                                  tailpos, NULL);
 
-       /* Wi-Fi Wireless Multimedia Extensions */
-       tailpos = hostapd_eid_wme(hapd, tailpos);
+       /* Wi-Fi Alliance WMM */
+       tailpos = hostapd_eid_wmm(hapd, tailpos);
 
 #ifdef CONFIG_IEEE80211N
        if (hapd->iconf->ieee80211n) {
-               u8 *start;
-               start = tailpos;
+               u8 *ht_capab, *ht_oper;
+               ht_capab = tailpos;
                tailpos = hostapd_eid_ht_capabilities_info(hapd, tailpos);
-               if (hostapd_set_ht_capability(hapd->conf->iface, hapd,
-                                             start + 2)) {
+
+               ht_oper = tailpos;
+               tailpos = hostapd_eid_ht_operation(hapd, tailpos);
+
+               if (tailpos > ht_oper && ht_oper > ht_capab &&
+                   hostapd_set_ht_params(hapd->conf->iface, hapd,
+                                         ht_capab + 2, ht_capab[1],
+                                         ht_oper + 2, ht_oper[1])) {
                        wpa_printf(MSG_ERROR, "Could not set HT capabilities "
                                   "for kernel driver");
                }
-
-               start = tailpos;
-               tailpos = hostapd_eid_ht_operation(hapd, tailpos);
-               if (hostapd_set_ht_operation(hapd->conf->iface, hapd,
-                                            start + 2))
-                       wpa_printf(MSG_ERROR, "Could not set HT operation for "
-                                  "kernel driver");
        }
 #endif /* CONFIG_IEEE80211N */
 
+#ifdef CONFIG_WPS
+       if (hapd->conf->wps_state && hapd->wps_beacon_ie) {
+               os_memcpy(tailpos, hapd->wps_beacon_ie,
+                         hapd->wps_beacon_ie_len);
+               tailpos += hapd->wps_beacon_ie_len;
+       }
+#endif /* CONFIG_WPS */
+
        tail_len = tailpos > tail ? tailpos - tail : 0;
 
        if (hostapd_set_beacon(hapd->conf->iface, hapd, (u8 *) head, head_len,
@@ -358,6 +434,10 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
        os_free(tail);
        os_free(head);
 
+       if (hostapd_set_dtim_period(hapd, hapd->conf->dtim_period))
+               wpa_printf(MSG_ERROR, "Could not set DTIM period for kernel "
+                          "driver");
+
        if (hostapd_set_cts_protect(hapd, cts_protection))
                wpa_printf(MSG_ERROR, "Failed to set CTS protect in kernel "
                           "driver");