nl80211: Use process_bss_event() for the nl_connect handler

The nl_connect is initialized with the process_bss_event() handler.
However, it is used several times with the default valid handler. As a
result, if a message that is only valid for process_bss_event() is
received while the default handler is used, it will be dropped.

This has been observed in a case where during the 4-way handshake, a
Beacon frame is received on the AP side, which triggers a beacon update,
just before receiving the next EAPOL. When send_and_recv_msgs_owner() is
called for sending the NL80211_CMD_SET_BEACON command, the
NL80211_CMD_CONTROL_PORT_FRAME event is already pending. As a result, it
is received with the default handler, which drops it. Since the EAPOL
frame is dropped, the connection attempt fails.

Fix it by using the process_bss_event() handler when the nl_connect
handler is used.

Signed-off-by: Avraham Stern <avraham.stern@intel.com>
This commit is contained in:
Avraham Stern 2021-02-17 12:14:33 +02:00 committed by Jouni Malinen
parent f7835ac163
commit ab89291928
3 changed files with 39 additions and 34 deletions

View file

@ -531,6 +531,22 @@ static int send_and_recv_msgs_owner(struct wpa_driver_nl80211_data *drv,
} }
static int
send_and_recv_msgs_connect_handle(struct wpa_driver_nl80211_data *drv,
struct nl_msg *msg, struct i802_bss *bss,
int set_owner)
{
struct nl_sock *nl_connect = get_connect_handle(bss);
if (nl_connect)
return send_and_recv_msgs_owner(drv, msg, nl_connect, set_owner,
process_bss_event, bss, NULL,
NULL);
else
return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
}
struct nl_sock * get_connect_handle(struct i802_bss *bss) struct nl_sock * get_connect_handle(struct i802_bss *bss)
{ {
if ((bss->drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) || if ((bss->drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) ||
@ -3542,10 +3558,11 @@ static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params,
int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
const u8 *addr, int cmd, u16 reason_code, const u8 *addr, int cmd, u16 reason_code,
int local_state_change, int local_state_change,
struct nl_sock *nl_connect) struct i802_bss *bss)
{ {
int ret; int ret;
struct nl_msg *msg; struct nl_msg *msg;
struct nl_sock *nl_connect = get_connect_handle(bss);
if (!(msg = nl80211_drv_msg(drv, 0, cmd)) || if (!(msg = nl80211_drv_msg(drv, 0, cmd)) ||
nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code) || nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code) ||
@ -3557,8 +3574,8 @@ int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
} }
if (nl_connect) if (nl_connect)
ret = send_and_recv(drv->global, nl_connect, msg, NULL, NULL, ret = send_and_recv(drv->global, nl_connect, msg,
NULL, NULL); process_bss_event, bss, NULL, NULL);
else else
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) { if (ret) {
@ -3572,7 +3589,7 @@ int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv, static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv,
u16 reason_code, u16 reason_code,
struct nl_sock *nl_connect) struct i802_bss *bss)
{ {
int ret; int ret;
int drv_associated = drv->associated; int drv_associated = drv->associated;
@ -3581,7 +3598,7 @@ static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv,
nl80211_mark_disconnected(drv); nl80211_mark_disconnected(drv);
/* Disconnect command doesn't need BSSID - it uses cached value */ /* Disconnect command doesn't need BSSID - it uses cached value */
ret = wpa_driver_nl80211_mlme(drv, NULL, NL80211_CMD_DISCONNECT, ret = wpa_driver_nl80211_mlme(drv, NULL, NL80211_CMD_DISCONNECT,
reason_code, 0, nl_connect); reason_code, 0, bss);
/* /*
* For locally generated disconnect, supplicant already generates a * For locally generated disconnect, supplicant already generates a
* DEAUTH event, so ignore the event from NL80211. * DEAUTH event, so ignore the event from NL80211.
@ -3604,14 +3621,13 @@ static int wpa_driver_nl80211_deauthenticate(struct i802_bss *bss,
return nl80211_leave_ibss(drv, 1); return nl80211_leave_ibss(drv, 1);
} }
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
return wpa_driver_nl80211_disconnect(drv, reason_code, return wpa_driver_nl80211_disconnect(drv, reason_code, bss);
get_connect_handle(bss));
} }
wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)", wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)",
__func__, MAC2STR(addr), reason_code); __func__, MAC2STR(addr), reason_code);
nl80211_mark_disconnected(drv); nl80211_mark_disconnected(drv);
ret = wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE, ret = wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE,
reason_code, 0, get_connect_handle(bss)); reason_code, 0, bss);
/* /*
* For locally generated deauthenticate, supplicant already generates a * For locally generated deauthenticate, supplicant already generates a
* DEAUTH event, so ignore the event from NL80211. * DEAUTH event, so ignore the event from NL80211.
@ -4760,8 +4776,7 @@ static int wpa_driver_nl80211_set_ap(void *priv,
goto fail; goto fail;
#endif /* CONFIG_FILS */ #endif /* CONFIG_FILS */
ret = send_and_recv_msgs_owner(drv, msg, get_connect_handle(bss), 1, ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 1);
NULL, NULL, NULL, NULL);
if (ret) { if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)", wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
ret, strerror(-ret)); ret, strerror(-ret));
@ -5815,9 +5830,7 @@ static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv,
int ret; int ret;
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_IBSS); msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_IBSS);
ret = send_and_recv_msgs_owner(drv, msg, ret = send_and_recv_msgs_connect_handle(drv, msg, drv->first_bss, 1);
get_connect_handle(drv->first_bss), 1,
NULL, NULL, NULL, NULL);
if (ret) { if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d " wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d "
"(%s)", ret, strerror(-ret)); "(%s)", ret, strerror(-ret));
@ -5949,9 +5962,7 @@ retry:
if (ret < 0) if (ret < 0)
goto fail; goto fail;
ret = send_and_recv_msgs_owner(drv, msg, ret = send_and_recv_msgs_connect_handle(drv, msg, drv->first_bss, 1);
get_connect_handle(drv->first_bss), 1,
NULL, NULL, NULL, NULL);
msg = NULL; msg = NULL;
if (ret) { if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Join IBSS failed: ret=%d (%s)", wpa_printf(MSG_DEBUG, "nl80211: Join IBSS failed: ret=%d (%s)",
@ -6287,7 +6298,7 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
static int wpa_driver_nl80211_try_connect( static int wpa_driver_nl80211_try_connect(
struct wpa_driver_nl80211_data *drv, struct wpa_driver_nl80211_data *drv,
struct wpa_driver_associate_params *params, struct wpa_driver_associate_params *params,
struct nl_sock *nl_connect) struct i802_bss *bss)
{ {
struct nl_msg *msg; struct nl_msg *msg;
enum nl80211_auth_type type; enum nl80211_auth_type type;
@ -6359,8 +6370,7 @@ skip_auth_type:
if (ret) if (ret)
goto fail; goto fail;
ret = send_and_recv_msgs_owner(drv, msg, nl_connect, 1, NULL, ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 1);
NULL, NULL, NULL);
msg = NULL; msg = NULL;
if (ret) { if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d " wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
@ -6384,7 +6394,7 @@ fail:
static int wpa_driver_nl80211_connect( static int wpa_driver_nl80211_connect(
struct wpa_driver_nl80211_data *drv, struct wpa_driver_nl80211_data *drv,
struct wpa_driver_associate_params *params, struct wpa_driver_associate_params *params,
struct nl_sock *nl_connect) struct i802_bss *bss)
{ {
int ret; int ret;
@ -6394,7 +6404,7 @@ static int wpa_driver_nl80211_connect(
else else
os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN); os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN);
ret = wpa_driver_nl80211_try_connect(drv, params, nl_connect); ret = wpa_driver_nl80211_try_connect(drv, params, bss);
if (ret == -EALREADY) { if (ret == -EALREADY) {
/* /*
* cfg80211 does not currently accept new connections if * cfg80211 does not currently accept new connections if
@ -6405,9 +6415,9 @@ static int wpa_driver_nl80211_connect(
"disconnecting before reassociation " "disconnecting before reassociation "
"attempt"); "attempt");
if (wpa_driver_nl80211_disconnect( if (wpa_driver_nl80211_disconnect(
drv, WLAN_REASON_PREV_AUTH_NOT_VALID, nl_connect)) drv, WLAN_REASON_PREV_AUTH_NOT_VALID, bss))
return -1; return -1;
ret = wpa_driver_nl80211_try_connect(drv, params, nl_connect); ret = wpa_driver_nl80211_try_connect(drv, params, bss);
} }
return ret; return ret;
} }
@ -6441,8 +6451,7 @@ static int wpa_driver_nl80211_associate(
else else
bss->use_nl_connect = 0; bss->use_nl_connect = 0;
return wpa_driver_nl80211_connect(drv, params, return wpa_driver_nl80211_connect(drv, params, bss);
get_connect_handle(bss));
} }
nl80211_mark_disconnected(drv); nl80211_mark_disconnected(drv);
@ -6477,9 +6486,7 @@ static int wpa_driver_nl80211_associate(
goto fail; goto fail;
} }
ret = send_and_recv_msgs_owner(drv, msg, ret = send_and_recv_msgs_connect_handle(drv, msg, drv->first_bss, 1);
get_connect_handle(drv->first_bss), 1,
NULL, NULL, NULL, NULL);
msg = NULL; msg = NULL;
if (ret) { if (ret) {
wpa_dbg(drv->ctx, MSG_DEBUG, wpa_dbg(drv->ctx, MSG_DEBUG,
@ -10462,8 +10469,7 @@ static int nl80211_join_mesh(struct i802_bss *bss,
if (nl80211_put_mesh_config(msg, &params->conf) < 0) if (nl80211_put_mesh_config(msg, &params->conf) < 0)
goto fail; goto fail;
ret = send_and_recv_msgs_owner(drv, msg, get_connect_handle(bss), 1, ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 1);
NULL, NULL, NULL, NULL);
msg = NULL; msg = NULL;
if (ret) { if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: mesh join failed: ret=%d (%s)", wpa_printf(MSG_DEBUG, "nl80211: mesh join failed: ret=%d (%s)",
@ -10520,8 +10526,7 @@ static int wpa_driver_nl80211_leave_mesh(void *priv)
wpa_printf(MSG_DEBUG, "nl80211: mesh leave (ifindex=%d)", drv->ifindex); wpa_printf(MSG_DEBUG, "nl80211: mesh leave (ifindex=%d)", drv->ifindex);
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_MESH); msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_MESH);
ret = send_and_recv_msgs_owner(drv, msg, get_connect_handle(bss), 0, ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 0);
NULL, NULL, NULL, NULL);
if (ret) { if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: mesh leave failed: ret=%d (%s)", wpa_printf(MSG_DEBUG, "nl80211: mesh leave failed: ret=%d (%s)",
ret, strerror(-ret)); ret, strerror(-ret));

View file

@ -274,7 +274,7 @@ int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
const u8 *addr, int cmd, u16 reason_code, const u8 *addr, int cmd, u16 reason_code,
int local_state_change, int local_state_change,
struct nl_sock *nl_connect); struct i802_bss *bss);
int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv); int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv);
void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv); void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv);

View file

@ -870,7 +870,7 @@ static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv,
wpa_driver_nl80211_mlme(drv, addr, wpa_driver_nl80211_mlme(drv, addr,
NL80211_CMD_DEAUTHENTICATE, NL80211_CMD_DEAUTHENTICATE,
WLAN_REASON_PREV_AUTH_NOT_VALID, 1, WLAN_REASON_PREV_AUTH_NOT_VALID, 1,
get_connect_handle(drv->first_bss)); drv->first_bss);
} }
} }