diff options
Diffstat (limited to 'src/drivers/driver_nl80211.c')
-rw-r--r-- | src/drivers/driver_nl80211.c | 168 |
1 files changed, 116 insertions, 52 deletions
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 3b7c31c8..75792f34 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -30,7 +30,6 @@ #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_common.h" -#include "l2_packet/l2_packet.h" #include "netlink.h" #include "linux_defines.h" #include "linux_ioctl.h" @@ -438,6 +437,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; @@ -1918,6 +1963,25 @@ static void wpa_driver_nl80211_handle_eapol_tx_status(int sock, } +static int nl80211_init_connect_handle(struct i802_bss *bss) +{ + if (bss->nl_connect) { + wpa_printf(MSG_DEBUG, + "nl80211: Connect handle already created (nl_connect=%p)", + bss->nl_connect); + return -1; + } + + bss->nl_connect = nl_create_handle(bss->nl_cb, "connect"); + if (!bss->nl_connect) + return -1; + nl80211_register_eloop_read(&bss->nl_connect, + wpa_driver_nl80211_event_receive, + bss->nl_cb, 1); + return 0; +} + + static int nl80211_init_bss(struct i802_bss *bss) { bss->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); @@ -1929,6 +1993,8 @@ static int nl80211_init_bss(struct i802_bss *bss) nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, process_bss_event, bss); + nl80211_init_connect_handle(bss); + return 0; } @@ -1937,6 +2003,9 @@ static void nl80211_destroy_bss(struct i802_bss *bss) { nl_cb_put(bss->nl_cb); bss->nl_cb = NULL; + + if (bss->nl_connect) + nl80211_destroy_eloop_handle(&bss->nl_connect, 1); } @@ -2160,25 +2229,6 @@ static int nl80211_register_action_frame(struct i802_bss *bss, } -static int nl80211_init_connect_handle(struct i802_bss *bss) -{ - if (bss->nl_connect) { - wpa_printf(MSG_DEBUG, - "nl80211: Connect handle already created (nl_connect=%p)", - bss->nl_connect); - return -1; - } - - bss->nl_connect = nl_create_handle(bss->nl_cb, "connect"); - if (!bss->nl_connect) - return -1; - nl80211_register_eloop_read(&bss->nl_connect, - wpa_driver_nl80211_event_receive, - bss->nl_cb, 1); - return 0; -} - - static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) { struct wpa_driver_nl80211_data *drv = bss->drv; @@ -2716,8 +2766,6 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, if (drv->vendor_cmd_test_avail) qca_vendor_test(drv); - nl80211_init_connect_handle(bss); - return 0; } @@ -2830,9 +2878,6 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) nl80211_del_p2pdev(bss); } - if (bss->nl_connect) - nl80211_destroy_eloop_handle(&bss->nl_connect, 1); - nl80211_destroy_bss(drv->first_bss); os_free(drv->filter_ssids); @@ -3437,18 +3482,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 +4473,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)); @@ -5285,7 +5327,10 @@ static int wpa_driver_nl80211_hapd_send_eapol( int res; int qos = flags & WPA_STA_WMM; - if (drv->capa.flags & WPA_DRIVER_FLAGS_CONTROL_PORT) + /* For now, disable EAPOL TX over control port in AP mode by default + * since it does not provide TX status notifications. */ + if (drv->control_port_ap && + (drv->capa.flags & WPA_DRIVER_FLAGS_CONTROL_PORT)) return nl80211_tx_control_port(bss, addr, ETH_P_EAPOL, data, data_len, !encrypt); @@ -5452,7 +5497,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 +5631,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 +6034,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 +6104,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 +6149,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, @@ -7242,6 +7287,12 @@ static void *i802_init(struct hostapd_data *hapd, } #endif /* CONFIG_LIBNL3_ROUTE */ + if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) { + wpa_printf(MSG_DEBUG, + "nl80211: Do not open EAPOL RX socket - using control port for RX"); + goto skip_eapol_sock; + } + drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE)); if (drv->eapol_sock < 0) { wpa_printf(MSG_ERROR, "nl80211: socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE) failed: %s", @@ -7254,6 +7305,7 @@ static void *i802_init(struct hostapd_data *hapd, wpa_printf(MSG_INFO, "nl80211: Could not register read socket for eapol"); goto failed; } +skip_eapol_sock: if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, params->own_addr)) @@ -8125,8 +8177,13 @@ static int nl80211_set_param(void *priv, const char *param) drv->test_use_roc_tx = 1; } - if (os_strstr(param, "control_port=0")) + if (os_strstr(param, "control_port=0")) { drv->capa.flags &= ~WPA_DRIVER_FLAGS_CONTROL_PORT; + drv->capa.flags2 &= ~WPA_DRIVER_FLAGS2_CONTROL_PORT_RX; + } + + if (os_strstr(param, "control_port_ap=1")) + drv->control_port_ap = 1; if (os_strstr(param, "full_ap_client_state=0")) drv->capa.flags &= ~WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE; @@ -9500,7 +9557,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); @@ -9955,7 +10017,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)", @@ -10012,7 +10075,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)); |