diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 228385457..c72dda381 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -438,6 +438,52 @@ int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, } +/* Use this method to mark that it is necessary to own the connection/interface + * for this operation. + * handle may be set to NULL, to get the same behavior as send_and_recv_msgs(). + * set_owner can be used to mark this socket for receiving control port frames. + */ +static int send_and_recv_msgs_owner(struct wpa_driver_nl80211_data *drv, + struct nl_msg *msg, + struct nl_sock *handle, int set_owner, + int (*valid_handler)(struct nl_msg *, + void *), + void *valid_data) +{ + /* Control port over nl80211 needs the flags and attributes below. + * + * The Linux kernel has initial checks for them (in nl80211.c) like: + * validate_pae_over_nl80211(...) + * or final checks like: + * dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid + * + * Final operations (e.g., disassociate) don't need to set these + * attributes, but they have to be performed on the socket, which has + * the connection owner property set in the kernel. + */ + if ((drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) && + handle && set_owner && + (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_OVER_NL80211) || + nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER) || + nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, ETH_P_PAE) || + nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_PREAUTH))) + return -1; + + return send_and_recv(drv->global, handle ? handle : drv->global->nl, + msg, valid_handler, valid_data); +} + + +struct nl_sock * get_connect_handle(struct i802_bss *bss) +{ + if ((bss->drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) || + bss->use_nl_connect) + return bss->nl_connect; + + return NULL; +} + + struct family_data { const char *group; int id; @@ -3437,18 +3483,14 @@ static int wpa_driver_nl80211_deauthenticate(struct i802_bss *bss, return nl80211_leave_ibss(drv, 1); } if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { - struct nl_sock *nl_connect = NULL; - - if (bss->use_nl_connect) - nl_connect = bss->nl_connect; return wpa_driver_nl80211_disconnect(drv, reason_code, - nl_connect); + get_connect_handle(bss)); } wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)", __func__, MAC2STR(addr), reason_code); nl80211_mark_disconnected(drv); ret = wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE, - reason_code, 0, NULL); + reason_code, 0, get_connect_handle(bss)); /* * For locally generated deauthenticate, supplicant already generates a * DEAUTH event, so ignore the event from NL80211. @@ -4432,7 +4474,8 @@ static int wpa_driver_nl80211_set_ap(void *priv, } #endif /* CONFIG_IEEE80211AX */ - ret = send_and_recv_msgs(drv, msg, NULL, NULL); + ret = send_and_recv_msgs_owner(drv, msg, get_connect_handle(bss), 1, + NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)", ret, strerror(-ret)); @@ -5452,7 +5495,9 @@ static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv, int ret; msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_IBSS); - ret = send_and_recv_msgs(drv, msg, NULL, NULL); + ret = send_and_recv_msgs_owner(drv, msg, + get_connect_handle(drv->first_bss), 1, + NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d " "(%s)", ret, strerror(-ret)); @@ -5584,7 +5629,9 @@ retry: if (ret < 0) goto fail; - ret = send_and_recv_msgs(drv, msg, NULL, NULL); + ret = send_and_recv_msgs_owner(drv, msg, + get_connect_handle(drv->first_bss), 1, + NULL, NULL); msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Join IBSS failed: ret=%d (%s)", @@ -5985,12 +6032,8 @@ skip_auth_type: if (ret) goto fail; - if (nl_connect) - ret = send_and_recv(drv->global, nl_connect, msg, - NULL, (void *) -1); - else - ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1); - + ret = send_and_recv_msgs_owner(drv, msg, nl_connect, 1, NULL, + (void *) -1); msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d " @@ -6059,19 +6102,17 @@ static int wpa_driver_nl80211_associate( if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { enum nl80211_iftype nlmode = params->p2p ? NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION; - struct nl_sock *nl_connect = NULL; if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0) return -1; if (params->key_mgmt_suite == WPA_KEY_MGMT_SAE || - params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE) { - nl_connect = bss->nl_connect; + params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE) bss->use_nl_connect = 1; - } else { + else bss->use_nl_connect = 0; - } - return wpa_driver_nl80211_connect(drv, params, nl_connect); + return wpa_driver_nl80211_connect(drv, params, + get_connect_handle(bss)); } nl80211_mark_disconnected(drv); @@ -6106,7 +6147,9 @@ static int wpa_driver_nl80211_associate( goto fail; } - ret = send_and_recv_msgs(drv, msg, NULL, NULL); + ret = send_and_recv_msgs_owner(drv, msg, + get_connect_handle(drv->first_bss), 1, + NULL, NULL); msg = NULL; if (ret) { wpa_dbg(drv->ctx, MSG_DEBUG, @@ -9502,7 +9545,12 @@ static int nl80211_vendor_cmd(void *priv, unsigned int vendor_id, if (nlmsg_append(msg, (void *) data, data_len, NLMSG_ALIGNTO) < 0) goto fail; - ret = send_and_recv_msgs(drv, msg, cmd_reply_handler, buf); + /* This test vendor_cmd can be used with nl80211 commands that + * need the connect nl_sock, so use the owner-setting variant + * of send_and_recv_msgs(). */ + ret = send_and_recv_msgs_owner(drv, msg, + get_connect_handle(bss), 0, + cmd_reply_handler, buf); if (ret) wpa_printf(MSG_DEBUG, "nl80211: command failed err=%d", ret); @@ -9957,7 +10005,8 @@ static int nl80211_join_mesh(struct i802_bss *bss, if (nl80211_put_mesh_config(msg, ¶ms->conf) < 0) goto fail; - ret = send_and_recv_msgs(drv, msg, NULL, NULL); + ret = send_and_recv_msgs_owner(drv, msg, get_connect_handle(bss), 1, + NULL, NULL); msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: mesh join failed: ret=%d (%s)", @@ -10014,7 +10063,8 @@ static int wpa_driver_nl80211_leave_mesh(void *priv) wpa_printf(MSG_DEBUG, "nl80211: mesh leave (ifindex=%d)", drv->ifindex); msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_MESH); - ret = send_and_recv_msgs(drv, msg, NULL, NULL); + ret = send_and_recv_msgs_owner(drv, msg, get_connect_handle(bss), 0, + NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: mesh leave failed: ret=%d (%s)", ret, strerror(-ret)); diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h index 6e6c87247..538d40364 100644 --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -229,6 +229,7 @@ struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd); int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg, int (*valid_handler)(struct nl_msg *, void *), void *valid_data); +struct nl_sock * get_connect_handle(struct i802_bss *bss); int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, const char *ifname, enum nl80211_iftype iftype, const u8 *addr, int wds, diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c index 04f6bb879..17e8b2c2e 100644 --- a/src/drivers/driver_nl80211_scan.c +++ b/src/drivers/driver_nl80211_scan.c @@ -872,7 +872,7 @@ static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv, wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE, WLAN_REASON_PREV_AUTH_NOT_VALID, 1, - NULL); + get_connect_handle(drv->first_bss)); } }