]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - src/drivers/driver_nl80211.c
nl80211: Resubscribe to nl80211 events on global nl_event socket
[thirdparty/hostap.git] / src / drivers / driver_nl80211.c
index 40e4df8b6859d398516a2e66a6fe371bb2cd5608..2dce242a3170ff34a08a4e68e460f50f0c86a832 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,22 @@ static void nl80211_register_eloop_read(struct nl_handle **handle,
                                        eloop_sock_handler handler,
                                        void *eloop_data)
 {
+#ifdef CONFIG_LIBNL20
+       /*
+        * 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 */
+       }
+#endif /* CONFIG_LIBNL20 */
+
        nl_socket_set_nonblocking(*handle);
        eloop_register_read_sock(nl_socket_get_fd(*handle), handler,
                                 eloop_data, *handle);
@@ -148,6 +164,7 @@ static void nl80211_destroy_eloop_handle(struct nl_handle **handle)
 
 
 static void nl80211_global_deinit(void *priv);
+static void nl80211_check_global(struct nl80211_global *global);
 
 static void wpa_driver_nl80211_deinit(struct i802_bss *bss);
 static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss,
@@ -848,6 +865,7 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv,
                return 1;
 
        if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) {
+               nl80211_check_global(drv->global);
                wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed "
                           "interface");
                wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL);
@@ -936,16 +954,21 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
                   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
 
        if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
+               namebuf[0] = '\0';
                if (if_indextoname(ifi->ifi_index, namebuf) &&
-                   linux_iface_up(drv->global->ioctl_sock,
-                                  drv->first_bss->ifname) > 0) {
+                   linux_iface_up(drv->global->ioctl_sock, namebuf) > 0) {
                        wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
                                   "event since interface %s is up", namebuf);
                        drv->ignore_if_down_event = 0;
                        return;
                }
-               wpa_printf(MSG_DEBUG, "nl80211: Interface down");
-               if (drv->ignore_if_down_event) {
+               wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
+                          namebuf, ifname);
+               if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "nl80211: Not the main interface (%s) - do not indicate interface down",
+                                  drv->first_bss->ifname);
+               } else if (drv->ignore_if_down_event) {
                        wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
                                   "event generated by mode change");
                        drv->ignore_if_down_event = 0;
@@ -968,8 +991,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
 
        if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
                if (if_indextoname(ifi->ifi_index, namebuf) &&
-                   linux_iface_up(drv->global->ioctl_sock,
-                                  drv->first_bss->ifname) == 0) {
+                   linux_iface_up(drv->global->ioctl_sock, namebuf) == 0) {
                        wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
                                   "event since interface %s is down",
                                   namebuf);
@@ -1461,6 +1483,33 @@ err:
 }
 
 
+static void nl80211_check_global(struct nl80211_global *global)
+{
+       struct nl_handle *handle;
+       const char *groups[] = { "scan", "mlme", "regulatory", "vendor", NULL };
+       int ret;
+       unsigned int i;
+
+       /*
+        * Try to re-add memberships to handle case of cfg80211 getting reloaded
+        * and all registration having been cleared.
+        */
+       handle = (void *) (((intptr_t) global->nl_event) ^
+                          ELOOP_SOCKET_INVALID);
+
+       for (i = 0; groups[i]; i++) {
+               ret = nl_get_multicast_id(global, "nl80211", groups[i]);
+               if (ret >= 0)
+                       ret = nl_socket_add_membership(handle, ret);
+               if (ret < 0) {
+                       wpa_printf(MSG_INFO,
+                                  "nl80211: Could not re-add multicast membership for %s events: %d (%s)",
+                                  groups[i], ret, strerror(-ret));
+               }
+       }
+}
+
+
 static void wpa_driver_nl80211_rfkill_blocked(void *ctx)
 {
        wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked");
@@ -1654,6 +1703,7 @@ static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname,
        }
 
        if (drv->global) {
+               nl80211_check_global(drv->global);
                dl_list_add(&drv->global->interfaces, &drv->list);
                drv->in_interface_list = 1;
        }
@@ -2049,6 +2099,60 @@ static int i802_set_iface_flags(struct i802_bss *bss, int up)
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
+static int qca_vendor_test_cmd_handler(struct nl_msg *msg, void *arg)
+{
+       /* struct wpa_driver_nl80211_data *drv = arg; */
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: QCA vendor test command response received");
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+       if (!tb[NL80211_ATTR_VENDOR_DATA]) {
+               wpa_printf(MSG_DEBUG, "nl80211: No vendor data attribute");
+               return NL_SKIP;
+       }
+
+       wpa_hexdump(MSG_DEBUG,
+                   "nl80211: Received QCA vendor test command response",
+                   nla_data(tb[NL80211_ATTR_VENDOR_DATA]),
+                   nla_len(tb[NL80211_ATTR_VENDOR_DATA]));
+
+       return NL_SKIP;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static void qca_vendor_test(struct wpa_driver_nl80211_data *drv)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+       struct nl_msg *msg;
+       struct nlattr *params;
+       int ret;
+
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+                       QCA_NL80211_VENDOR_SUBCMD_TEST) ||
+           !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+           nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_TEST, 123)) {
+               nlmsg_free(msg);
+               return;
+       }
+       nla_nest_end(msg, params);
+
+       ret = send_and_recv_msgs(drv, msg, qca_vendor_test_cmd_handler, drv);
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: QCA vendor test command returned %d (%s)",
+                  ret, strerror(-ret));
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
 static int
 wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
                                   const u8 *set_addr, int first,
@@ -2135,6 +2239,9 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
                                       drv, drv->ctx);
        }
 
+       if (drv->vendor_cmd_test_avail)
+               qca_vendor_test(drv);
+
        return 0;
 }
 
@@ -2479,7 +2586,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;
@@ -3168,6 +3278,18 @@ static int wpa_driver_nl80211_set_acl(void *priv,
 }
 
 
+static int nl80211_put_beacon_int(struct nl_msg *msg, int beacon_int)
+{
+       if (beacon_int > 0) {
+               wpa_printf(MSG_DEBUG, "  * beacon_int=%d", beacon_int);
+               return nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL,
+                                  beacon_int);
+       }
+
+       return 0;
+}
+
+
 static int wpa_driver_nl80211_set_ap(void *priv,
                                     struct wpa_driver_ap_params *params)
 {
@@ -3182,7 +3304,7 @@ static int wpa_driver_nl80211_set_ap(void *priv,
        u32 suites[10], suite;
        u32 ver;
 
-       beacon_set = bss->beacon_set;
+       beacon_set = params->reenable ? 0 : bss->beacon_set;
 
        wpa_printf(MSG_DEBUG, "nl80211: Set beacon (beacon_set=%d)",
                   beacon_set);
@@ -3203,8 +3325,7 @@ static int wpa_driver_nl80211_set_ap(void *priv,
                    params->head) ||
            nla_put(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len,
                    params->tail) ||
-           nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL,
-                       params->beacon_int) ||
+           nl80211_put_beacon_int(msg, params->beacon_int) ||
            nla_put_u32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period) ||
            nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid))
                goto fail;
@@ -3348,6 +3469,21 @@ static int wpa_driver_nl80211_set_ap(void *priv,
                        goto fail;
        }
 
+#ifdef CONFIG_P2P
+       if (params->p2p_go_ctwindow > 0) {
+               if (drv->p2p_go_ctwindow_supported) {
+                       wpa_printf(MSG_DEBUG, "nl80211: P2P GO ctwindow=%d",
+                                  params->p2p_go_ctwindow);
+                       if (nla_put_u8(msg, NL80211_ATTR_P2P_CTWINDOW,
+                                      params->p2p_go_ctwindow))
+                               goto fail;
+               } else {
+                       wpa_printf(MSG_INFO,
+                                  "nl80211: Driver does not support CTWindow configuration - ignore this parameter");
+               }
+       }
+#endif /* CONFIG_P2P */
+
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
        if (ret) {
                wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
@@ -4203,6 +4339,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)
 {
@@ -4230,16 +4408,10 @@ retry:
        os_memcpy(drv->ssid, params->ssid, params->ssid_len);
        drv->ssid_len = params->ssid_len;
 
-       if (nl80211_put_freq_params(msg, &params->freq) < 0)
+       if (nl80211_put_freq_params(msg, &params->freq) < 0 ||
+           nl80211_put_beacon_int(msg, params->beacon_int))
                goto fail;
 
-       if (params->beacon_int > 0) {
-               wpa_printf(MSG_DEBUG, "  * beacon_int=%d", params->beacon_int);
-               if (nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL,
-                               params->beacon_int))
-                       goto fail;
-       }
-
        ret = nl80211_set_conn_keys(params, msg);
        if (ret)
                goto fail;
@@ -4251,6 +4423,12 @@ retry:
                        goto fail;
        }
 
+       if (params->fixed_freq) {
+               wpa_printf(MSG_DEBUG, "  * fixed_freq");
+               if (nla_put_flag(msg, NL80211_ATTR_FREQ_FIXED))
+                       goto fail;
+       }
+
        if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
            params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
            params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
@@ -4269,6 +4447,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) {
@@ -4396,7 +4577,8 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
            params->key_mgmt_suite == WPA_KEY_MGMT_OSEN ||
            params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
            params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
-           params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+           params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
+           params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
                int mgmt = WLAN_AKM_SUITE_PSK;
 
                switch (params->key_mgmt_suite) {
@@ -4424,6 +4606,9 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
                case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
                        mgmt = WLAN_AKM_SUITE_8021X_SUITE_B;
                        break;
+               case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+                       mgmt = WLAN_AKM_SUITE_8021X_SUITE_B_192;
+                       break;
                case WPA_KEY_MGMT_PSK:
                default:
                        mgmt = WLAN_AKM_SUITE_PSK;
@@ -4450,41 +4635,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");
 
@@ -5214,6 +5367,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;
@@ -6744,7 +6899,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;
@@ -6759,8 +6915,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);
@@ -6823,6 +6979,7 @@ static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr,
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
+       int ret;
 
        if (!drv->poll_command_supported) {
                nl80211_send_null_frame(bss, own_addr, addr, qos);
@@ -6835,7 +6992,12 @@ static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr,
                return;
        }
 
-       send_and_recv_msgs(drv, msg, NULL, NULL);
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Client probe request for "
+                          MACSTR " failed: ret=%d (%s)",
+                          MAC2STR(addr), ret, strerror(-ret));
+       }
 }
 
 
@@ -7792,72 +7954,36 @@ static int wpa_driver_nl80211_init_mesh(void *priv)
 }
 
 
-static int
-wpa_driver_nl80211_join_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 nl80211_join_mesh(struct i802_bss *bss,
                             struct wpa_driver_mesh_join_params *params)
 {
-       struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
        struct nlattr *container;
-       int ret = 0;
+       int ret = -1;
 
        wpa_printf(MSG_DEBUG, "nl80211: mesh join (ifindex=%d)", drv->ifindex);
        msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_MESH);
-       if (!msg)
-               goto fail;
-       if (params->freq) {
-               wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
-               if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq))
-                       goto fail;
-       }
-
-       if (params->ht_mode) {
-               unsigned int ht_value;
-               char *ht_mode = "";
-
-               switch (params->ht_mode) {
-               default:
-               case CHAN_NO_HT:
-                       ht_value = NL80211_CHAN_NO_HT;
-                       ht_mode = "NOHT";
-                       break;
-               case CHAN_HT20:
-                       ht_value = NL80211_CHAN_HT20;
-                       ht_mode = "HT20";
-                       break;
-               case CHAN_HT40PLUS:
-                       ht_value = NL80211_CHAN_HT40PLUS;
-                       ht_mode = "HT40+";
-                       break;
-               case CHAN_HT40MINUS:
-                       ht_value = NL80211_CHAN_HT40MINUS;
-                       ht_mode = "HT40-";
-                       break;
-               }
-               wpa_printf(MSG_DEBUG, "  * ht_mode=%s", ht_mode);
-               if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, ht_value))
-                       goto fail;
-       }
-
-       if (nl80211_put_basic_rates(msg, params->basic_rates))
+       if (!msg ||
+           nl80211_put_freq_params(msg, &params->freq) ||
+           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;
 
-       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 (params->beacon_int > 0) {
-               wpa_printf(MSG_DEBUG, "  * beacon_int=%d", params->beacon_int);
-               if (nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL,
-                               params->beacon_int))
-                       goto fail;
-       }
-
        wpa_printf(MSG_DEBUG, "  * flags=%08X", params->flags);
 
        container = nla_nest_start(msg, NL80211_ATTR_MESH_SETUP);
@@ -7895,6 +8021,17 @@ 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.
+        */
+       if (nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT,
+                       params->conf.peer_link_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);
@@ -7905,7 +8042,7 @@ wpa_driver_nl80211_join_mesh(void *priv,
                goto fail;
        }
        ret = 0;
-       bss->freq = params->freq;
+       bss->freq = params->freq.freq;
        wpa_printf(MSG_DEBUG, "nl80211: mesh join request send successfully");
 
 fail:
@@ -7914,6 +8051,37 @@ fail:
 }
 
 
+static int
+wpa_driver_nl80211_join_mesh(void *priv,
+                            struct wpa_driver_mesh_join_params *params)
+{
+       struct i802_bss *bss = priv;
+       int ret, timeout;
+
+       timeout = params->conf.peer_link_timeout;
+
+       /* Disable kernel inactivity timer */
+       if (params->flags & WPA_DRIVER_MESH_FLAG_USER_MPM)
+               params->conf.peer_link_timeout = 0;
+
+       ret = nl80211_join_mesh(bss, params);
+       if (ret == -EINVAL && params->conf.peer_link_timeout == 0) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Mesh join retry for peer_link_timeout");
+               /*
+                * Old kernel does not support setting
+                * NL80211_MESHCONF_PLINK_TIMEOUT to zero, so set 60 seconds
+                * into future from peer_link_timeout.
+                */
+               params->conf.peer_link_timeout = timeout + 60;
+               ret = nl80211_join_mesh(priv, params);
+       }
+
+       params->conf.peer_link_timeout = timeout;
+       return ret;
+}
+
+
 static int wpa_driver_nl80211_leave_mesh(void *priv)
 {
        struct i802_bss *bss = priv;