Preparations for variable length KCK and KEK
[mech_eap.git] / src / drivers / driver_nl80211.c
index 7988cec..ea52575 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Driver interaction with Linux nl80211/cfg80211
- * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2003-2004, Instant802 Networks, Inc.
  * Copyright (c) 2005-2006, Devicescape Software, Inc.
  * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
@@ -132,6 +132,20 @@ static void nl80211_register_eloop_read(struct nl_handle **handle,
                                        eloop_sock_handler handler,
                                        void *eloop_data)
 {
+       /*
+        * libnl uses a pretty small buffer (32 kB that gets converted to 64 kB)
+        * by default. It is possible to hit that limit in some cases where
+        * operations are blocked, e.g., with a burst of Deauthentication frames
+        * to hostapd and STA entry deletion. Try to increase the buffer to make
+        * this less likely to occur.
+        */
+       if (nl_socket_set_buffer_size(*handle, 262144, 0) < 0) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Could not set nl_socket RX buffer size: %s",
+                          strerror(errno));
+               /* continue anyway with the default (smaller) buffer */
+       }
+
        nl_socket_set_nonblocking(*handle);
        eloop_register_read_sock(nl_socket_get_fd(*handle), handler,
                                 eloop_data, *handle);
@@ -2479,7 +2493,10 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
        msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_SET_KEY);
        if (!msg ||
            nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx) ||
-           nla_put_flag(msg, alg == WPA_ALG_IGTK ?
+           nla_put_flag(msg, (alg == WPA_ALG_IGTK ||
+                              alg == WPA_ALG_BIP_GMAC_128 ||
+                              alg == WPA_ALG_BIP_GMAC_256 ||
+                              alg == WPA_ALG_BIP_CMAC_256) ?
                         NL80211_ATTR_KEY_DEFAULT_MGMT :
                         NL80211_ATTR_KEY_DEFAULT))
                goto fail;
@@ -4214,6 +4231,48 @@ static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv,
 }
 
 
+static int nl80211_ht_vht_overrides(struct nl_msg *msg,
+                                   struct wpa_driver_associate_params *params)
+{
+       if (params->disable_ht && nla_put_flag(msg, NL80211_ATTR_DISABLE_HT))
+               return -1;
+
+       if (params->htcaps && params->htcaps_mask) {
+               int sz = sizeof(struct ieee80211_ht_capabilities);
+               wpa_hexdump(MSG_DEBUG, "  * htcaps", params->htcaps, sz);
+               wpa_hexdump(MSG_DEBUG, "  * htcaps_mask",
+                           params->htcaps_mask, sz);
+               if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY, sz,
+                           params->htcaps) ||
+                   nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
+                           params->htcaps_mask))
+                       return -1;
+       }
+
+#ifdef CONFIG_VHT_OVERRIDES
+       if (params->disable_vht) {
+               wpa_printf(MSG_DEBUG, "  * VHT disabled");
+               if (nla_put_flag(msg, NL80211_ATTR_DISABLE_VHT))
+                       return -1;
+       }
+
+       if (params->vhtcaps && params->vhtcaps_mask) {
+               int sz = sizeof(struct ieee80211_vht_capabilities);
+               wpa_hexdump(MSG_DEBUG, "  * vhtcaps", params->vhtcaps, sz);
+               wpa_hexdump(MSG_DEBUG, "  * vhtcaps_mask",
+                           params->vhtcaps_mask, sz);
+               if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY, sz,
+                           params->vhtcaps) ||
+                   nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz,
+                           params->vhtcaps_mask))
+                       return -1;
+       }
+#endif /* CONFIG_VHT_OVERRIDES */
+
+       return 0;
+}
+
+
 static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv,
                                   struct wpa_driver_associate_params *params)
 {
@@ -4274,6 +4333,9 @@ retry:
                        goto fail;
        }
 
+       if (nl80211_ht_vht_overrides(msg, params) < 0)
+               return -1;
+
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
        msg = NULL;
        if (ret) {
@@ -4455,41 +4517,9 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
                        return -1;
        }
 
-       if (params->disable_ht && nla_put_flag(msg, NL80211_ATTR_DISABLE_HT))
+       if (nl80211_ht_vht_overrides(msg, params) < 0)
                return -1;
 
-       if (params->htcaps && params->htcaps_mask) {
-               int sz = sizeof(struct ieee80211_ht_capabilities);
-               wpa_hexdump(MSG_DEBUG, "  * htcaps", params->htcaps, sz);
-               wpa_hexdump(MSG_DEBUG, "  * htcaps_mask",
-                           params->htcaps_mask, sz);
-               if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY, sz,
-                           params->htcaps) ||
-                   nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
-                           params->htcaps_mask))
-                       return -1;
-       }
-
-#ifdef CONFIG_VHT_OVERRIDES
-       if (params->disable_vht) {
-               wpa_printf(MSG_DEBUG, "  * VHT disabled");
-               if (nla_put_flag(msg, NL80211_ATTR_DISABLE_VHT))
-                       return -1;
-       }
-
-       if (params->vhtcaps && params->vhtcaps_mask) {
-               int sz = sizeof(struct ieee80211_vht_capabilities);
-               wpa_hexdump(MSG_DEBUG, "  * vhtcaps", params->vhtcaps, sz);
-               wpa_hexdump(MSG_DEBUG, "  * vhtcaps_mask",
-                           params->vhtcaps_mask, sz);
-               if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY, sz,
-                           params->vhtcaps) ||
-                   nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz,
-                           params->vhtcaps_mask))
-                       return -1;
-       }
-#endif /* CONFIG_VHT_OVERRIDES */
-
        if (params->p2p)
                wpa_printf(MSG_DEBUG, "  * P2P group");
 
@@ -5219,6 +5249,8 @@ static int i802_get_inact_sec(void *priv, const u8 *addr)
 
        data.inactive_msec = (unsigned long) -1;
        ret = i802_read_sta_data(priv, &data, addr);
+       if (ret == -ENOENT)
+               return -ENOENT;
        if (ret || data.inactive_msec == (unsigned long) -1)
                return -1;
        return data.inactive_msec / 1000;
@@ -6749,7 +6781,8 @@ static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq)
 }
 
 
-static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck,
+static void nl80211_set_rekey_info(void *priv, const u8 *kek, size_t kek_len,
+                                  const u8 *kck, size_t kck_len,
                                   const u8 *replay_ctr)
 {
        struct i802_bss *bss = priv;
@@ -6764,8 +6797,8 @@ static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck,
        wpa_printf(MSG_DEBUG, "nl80211: Set rekey offload");
        if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_REKEY_OFFLOAD)) ||
            !(replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA)) ||
-           nla_put(msg, NL80211_REKEY_DATA_KEK, NL80211_KEK_LEN, kek) ||
-           nla_put(msg, NL80211_REKEY_DATA_KCK, NL80211_KCK_LEN, kck) ||
+           nla_put(msg, NL80211_REKEY_DATA_KEK, kek_len, kek) ||
+           nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck) ||
            nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN,
                    replay_ctr)) {
                nl80211_nlmsg_clear(msg);
@@ -7797,6 +7830,19 @@ static int wpa_driver_nl80211_init_mesh(void *priv)
 }
 
 
+static int nl80211_put_mesh_id(struct nl_msg *msg, const u8 *mesh_id,
+                              size_t mesh_id_len)
+{
+       if (mesh_id) {
+               wpa_hexdump_ascii(MSG_DEBUG, "  * Mesh ID (SSID)",
+                                 mesh_id, mesh_id_len);
+               return nla_put(msg, NL80211_ATTR_MESH_ID, mesh_id_len, mesh_id);
+       }
+
+       return 0;
+}
+
+
 static int
 wpa_driver_nl80211_join_mesh(void *priv,
                             struct wpa_driver_mesh_join_params *params)
@@ -7805,24 +7851,16 @@ wpa_driver_nl80211_join_mesh(void *priv,
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
        struct nlattr *container;
-       int ret = 0;
+       int ret = -1;
+       u32 timeout;
 
        wpa_printf(MSG_DEBUG, "nl80211: mesh join (ifindex=%d)", drv->ifindex);
        msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_MESH);
        if (!msg ||
            nl80211_put_freq_params(msg, &params->freq) ||
-           nl80211_put_basic_rates(msg, params->basic_rates))
-               goto fail;
-
-       if (params->meshid) {
-               wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
-                                 params->meshid, params->meshid_len);
-               if (nla_put(msg, NL80211_ATTR_MESH_ID, params->meshid_len,
-                           params->meshid))
-                       goto fail;
-       }
-
-       if (nl80211_put_beacon_int(msg, params->beacon_int))
+           nl80211_put_basic_rates(msg, params->basic_rates) ||
+           nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) ||
+           nl80211_put_beacon_int(msg, params->beacon_int))
                goto fail;
 
        wpa_printf(MSG_DEBUG, "  * flags=%08X", params->flags);
@@ -7862,6 +7900,22 @@ wpa_driver_nl80211_join_mesh(void *priv,
            nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
                        params->max_peer_links))
                goto fail;
+
+       /*
+        * Set NL80211_MESHCONF_PLINK_TIMEOUT even if user mpm is used because
+        * the timer could disconnect stations even in that case.
+        *
+        * Set 0xffffffff instead of 0 because NL80211_MESHCONF_PLINK_TIMEOUT
+        * does not allow 0.
+        */
+       timeout = params->conf.peer_link_timeout;
+       if ((params->flags & WPA_DRIVER_MESH_FLAG_USER_MPM) || timeout == 0)
+               timeout = 0xffffffff;
+       if (nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT, timeout)) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to set PLINK_TIMEOUT");
+               goto fail;
+       }
+
        nla_nest_end(msg, container);
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);