Commit 36995892 authored by David Lin's avatar David Lin Committed by Kalle Valo
Browse files

wifi: mwifiex: add host mlme for client mode



Add host based MLME to enable WPA3 functionalities in client mode.
This feature required a firmware with the corresponding V2 Key API
support. The feature (WPA3) is currently enabled and verified only
on IW416. Also, verified no regression with change when host MLME
is disabled.

Signed-off-by: default avatarDavid Lin <yu-hao.lin@nxp.com>
Reviewed-by: default avatarFrancesco Dolcini <francesco.dolcini@toradex.com>
Acked-by: default avatarBrian Norris <briannorris@chromium.org>
Signed-off-by: default avatarKalle Valo <kvalo@kernel.org>
Link: https://patch.msgid.link/20240704033001.603419-2-yu-hao.lin@nxp.com
parent e8b7d0c6
Loading
Loading
Loading
Loading
+318 −0
Original line number Diff line number Diff line
@@ -268,6 +268,8 @@ mwifiex_cfg80211_update_mgmt_frame_registrations(struct wiphy *wiphy,

	if (mask != priv->mgmt_frame_mask) {
		priv->mgmt_frame_mask = mask;
		if (priv->host_mlme_reg)
			priv->mgmt_frame_mask |= HOST_MLME_MGMT_MASK;
		mwifiex_send_cmd(priv, HostCmd_CMD_MGMT_FRAME_REG,
				 HostCmd_ACT_GEN_SET, 0,
				 &priv->mgmt_frame_mask, false);
@@ -848,6 +850,7 @@ static int mwifiex_deinit_priv_params(struct mwifiex_private *priv)
	struct mwifiex_adapter *adapter = priv->adapter;
	unsigned long flags;

	priv->host_mlme_reg = false;
	priv->mgmt_frame_mask = 0;
	if (mwifiex_send_cmd(priv, HostCmd_CMD_MGMT_FRAME_REG,
			     HostCmd_ACT_GEN_SET, 0,
@@ -3633,6 +3636,9 @@ static int mwifiex_set_rekey_data(struct wiphy *wiphy, struct net_device *dev,
	if (!ISSUPP_FIRMWARE_SUPPLICANT(priv->adapter->fw_cap_info))
		return -EOPNOTSUPP;

	if (priv->adapter->host_mlme_enabled)
		return 0;

	return mwifiex_send_cmd(priv, HostCmd_CMD_GTK_REKEY_OFFLOAD_CFG,
				HostCmd_ACT_GEN_SET, 0, data, true);
}
@@ -4206,6 +4212,305 @@ mwifiex_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev,
	return ret;
}

static int
mwifiex_cfg80211_authenticate(struct wiphy *wiphy,
			      struct net_device *dev,
			      struct cfg80211_auth_request *req)
{
	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
	struct mwifiex_adapter *adapter = priv->adapter;
	struct sk_buff *skb;
	u16 pkt_len, auth_alg;
	int ret;
	struct mwifiex_ieee80211_mgmt *mgmt;
	struct mwifiex_txinfo *tx_info;
	u32 tx_control = 0, pkt_type = PKT_TYPE_MGMT;
	u8 trans = 1, status_code = 0;
	u8 *varptr;

	if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
		mwifiex_dbg(priv->adapter, ERROR, "Interface role is AP\n");
		return -EFAULT;
	}

	if (priv->wdev.iftype != NL80211_IFTYPE_STATION) {
		mwifiex_dbg(priv->adapter, ERROR,
			    "Interface type is not correct (type %d)\n",
			    priv->wdev.iftype);
		return -EINVAL;
	}

	if (priv->auth_alg != WLAN_AUTH_SAE &&
	    (priv->auth_flag & HOST_MLME_AUTH_PENDING)) {
		mwifiex_dbg(priv->adapter, ERROR, "Pending auth on going\n");
		return -EBUSY;
	}

	if (!priv->host_mlme_reg) {
		priv->host_mlme_reg = true;
		priv->mgmt_frame_mask |= HOST_MLME_MGMT_MASK;
		mwifiex_send_cmd(priv, HostCmd_CMD_MGMT_FRAME_REG,
				 HostCmd_ACT_GEN_SET, 0,
				 &priv->mgmt_frame_mask, false);
	}

	switch (req->auth_type) {
	case NL80211_AUTHTYPE_OPEN_SYSTEM:
		auth_alg = WLAN_AUTH_OPEN;
		break;
	case NL80211_AUTHTYPE_SHARED_KEY:
		auth_alg = WLAN_AUTH_SHARED_KEY;
		break;
	case NL80211_AUTHTYPE_FT:
		auth_alg = WLAN_AUTH_FT;
		break;
	case NL80211_AUTHTYPE_NETWORK_EAP:
		auth_alg = WLAN_AUTH_LEAP;
		break;
	case NL80211_AUTHTYPE_SAE:
		auth_alg = WLAN_AUTH_SAE;
		break;
	default:
		mwifiex_dbg(priv->adapter, ERROR,
			    "unsupported auth type=%d\n", req->auth_type);
		return -EOPNOTSUPP;
	}

	if (!priv->auth_flag) {
		ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_SET,
						 req->bss->channel,
						 AUTH_TX_DEFAULT_WAIT_TIME);

		if (!ret) {
			priv->roc_cfg.cookie = get_random_u32() | 1;
			priv->roc_cfg.chan = *req->bss->channel;
		} else {
			return -EFAULT;
		}
	}

	priv->sec_info.authentication_mode = auth_alg;

	mwifiex_cancel_scan(adapter);

	pkt_len = (u16)req->ie_len + req->auth_data_len +
		MWIFIEX_MGMT_HEADER_LEN + MWIFIEX_AUTH_BODY_LEN;
	if (req->auth_data_len >= 4)
		pkt_len -= 4;

	skb = dev_alloc_skb(MWIFIEX_MIN_DATA_HEADER_LEN +
			    MWIFIEX_MGMT_FRAME_HEADER_SIZE +
			    pkt_len + sizeof(pkt_len));
	if (!skb) {
		mwifiex_dbg(priv->adapter, ERROR,
			    "allocate skb failed for management frame\n");
		return -ENOMEM;
	}

	tx_info = MWIFIEX_SKB_TXCB(skb);
	memset(tx_info, 0, sizeof(*tx_info));
	tx_info->bss_num = priv->bss_num;
	tx_info->bss_type = priv->bss_type;
	tx_info->pkt_len = pkt_len;

	skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN +
		    MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len));
	memcpy(skb_push(skb, sizeof(pkt_len)), &pkt_len, sizeof(pkt_len));
	memcpy(skb_push(skb, sizeof(tx_control)),
	       &tx_control, sizeof(tx_control));
	memcpy(skb_push(skb, sizeof(pkt_type)), &pkt_type, sizeof(pkt_type));

	mgmt = (struct mwifiex_ieee80211_mgmt *)skb_put(skb, pkt_len);
	memset(mgmt, 0, pkt_len);
	mgmt->frame_control =
		cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH);
	memcpy(mgmt->da, req->bss->bssid, ETH_ALEN);
	memcpy(mgmt->sa, priv->curr_addr, ETH_ALEN);
	memcpy(mgmt->bssid, req->bss->bssid, ETH_ALEN);
	eth_broadcast_addr(mgmt->addr4);

	if (req->auth_data_len >= 4) {
		if (req->auth_type == NL80211_AUTHTYPE_SAE) {
			__le16 *pos = (__le16 *)req->auth_data;

			trans = le16_to_cpu(pos[0]);
			status_code = le16_to_cpu(pos[1]);
		}
		memcpy((u8 *)(&mgmt->auth.variable), req->auth_data + 4,
		       req->auth_data_len - 4);
		varptr = (u8 *)&mgmt->auth.variable +
			 (req->auth_data_len - 4);
	}

	mgmt->auth.auth_alg = cpu_to_le16(auth_alg);
	mgmt->auth.auth_transaction = cpu_to_le16(trans);
	mgmt->auth.status_code = cpu_to_le16(status_code);

	if (req->ie && req->ie_len) {
		if (!varptr)
			varptr = (u8 *)&mgmt->auth.variable;
		memcpy((u8 *)varptr, req->ie, req->ie_len);
	}

	priv->auth_flag = HOST_MLME_AUTH_PENDING;
	priv->auth_alg = auth_alg;

	skb->priority = WMM_HIGHEST_PRIORITY;
	__net_timestamp(skb);

	mwifiex_dbg(priv->adapter, MSG,
		    "auth: send authentication to %pM\n", req->bss->bssid);

	mwifiex_queue_tx_pkt(priv, skb);

	return 0;
}

static int
mwifiex_cfg80211_associate(struct wiphy *wiphy, struct net_device *dev,
			   struct cfg80211_assoc_request *req)
{
	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
	struct mwifiex_adapter *adapter = priv->adapter;
	int ret;
	struct cfg80211_ssid req_ssid;
	const u8 *ssid_ie;

	if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA) {
		mwifiex_dbg(adapter, ERROR,
			    "%s: reject infra assoc request in non-STA role\n",
			    dev->name);
		return -EINVAL;
	}

	if (test_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags) ||
	    test_bit(MWIFIEX_IS_CMD_TIMEDOUT, &adapter->work_flags)) {
		mwifiex_dbg(adapter, ERROR,
			    "%s: Ignore association.\t"
			    "Card removed or FW in bad state\n",
			    dev->name);
		return -EFAULT;
	}

	if (priv->auth_alg == WLAN_AUTH_SAE)
		priv->auth_flag = HOST_MLME_AUTH_DONE;

	if (priv->auth_flag && !(priv->auth_flag & HOST_MLME_AUTH_DONE))
		return -EBUSY;

	if (priv->roc_cfg.cookie) {
		ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_REMOVE,
						 &priv->roc_cfg.chan, 0);
		if (!ret)
			memset(&priv->roc_cfg, 0,
			       sizeof(struct mwifiex_roc_cfg));
		else
			return -EFAULT;
	}

	if (!mwifiex_stop_bg_scan(priv))
		cfg80211_sched_scan_stopped_locked(priv->wdev.wiphy, 0);

	memset(&req_ssid, 0, sizeof(struct cfg80211_ssid));
	rcu_read_lock();
	ssid_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);

	if (!ssid_ie)
		goto ssid_err;

	req_ssid.ssid_len = ssid_ie[1];
	if (req_ssid.ssid_len > IEEE80211_MAX_SSID_LEN) {
		mwifiex_dbg(adapter, ERROR, "invalid SSID - aborting\n");
		goto ssid_err;
	}

	memcpy(req_ssid.ssid, ssid_ie + 2, req_ssid.ssid_len);
	if (!req_ssid.ssid_len || req_ssid.ssid[0] < 0x20) {
		mwifiex_dbg(adapter, ERROR, "invalid SSID - aborting\n");
		goto ssid_err;
	}
	rcu_read_unlock();

	/* As this is new association, clear locally stored
	 * keys and security related flags
	 */
	priv->sec_info.wpa_enabled = false;
	priv->sec_info.wpa2_enabled = false;
	priv->wep_key_curr_index = 0;
	priv->sec_info.encryption_mode = 0;
	priv->sec_info.is_authtype_auto = 0;
	if (mwifiex_set_encode(priv, NULL, NULL, 0, 0, NULL, 1)) {
		mwifiex_dbg(priv->adapter, ERROR, "deleting the crypto keys\n");
		return -EFAULT;
	}

	if (req->crypto.n_ciphers_pairwise)
		priv->sec_info.encryption_mode =
			req->crypto.ciphers_pairwise[0];

	if (req->crypto.cipher_group)
		priv->sec_info.encryption_mode = req->crypto.cipher_group;

	if (req->ie)
		mwifiex_set_gen_ie(priv, req->ie, req->ie_len);

	memcpy(priv->cfg_bssid, req->bss->bssid, ETH_ALEN);

	mwifiex_dbg(adapter, MSG,
		    "assoc: send association to %pM\n", req->bss->bssid);

	cfg80211_ref_bss(adapter->wiphy, req->bss);
	ret = mwifiex_bss_start(priv, req->bss, &req_ssid);
	if (ret) {
		priv->auth_flag = 0;
		priv->auth_alg = WLAN_AUTH_NONE;
		eth_zero_addr(priv->cfg_bssid);
	}

	if (priv->assoc_rsp_size) {
		priv->req_bss = req->bss;
		adapter->assoc_resp_received = true;
		queue_work(adapter->host_mlme_workqueue,
			   &adapter->host_mlme_work);
	}

	cfg80211_put_bss(priv->adapter->wiphy, req->bss);

	return 0;

ssid_err:
	rcu_read_unlock();
	return -EFAULT;
}

static int
mwifiex_cfg80211_deauthenticate(struct wiphy *wiphy,
				struct net_device *dev,
				struct cfg80211_deauth_request *req)
{
	return mwifiex_cfg80211_disconnect(wiphy, dev, req->reason_code);
}

static int
mwifiex_cfg80211_disassociate(struct wiphy *wiphy,
			      struct net_device *dev,
			      struct cfg80211_disassoc_request *req)
{
	return mwifiex_cfg80211_disconnect(wiphy, dev, req->reason_code);
}

static int
mwifiex_cfg80211_probe_client(struct wiphy *wiphy,
			      struct net_device *dev, const u8 *peer,
			      u64 *cookie)
{
	/* hostapd looks for NL80211_CMD_PROBE_CLIENT support; otherwise,
	 * it requires monitor-mode support (which mwifiex doesn't support).
	 * Provide fake probe_client support to work around this.
	 */
	return -EOPNOTSUPP;
}

/* station cfg80211 operations */
static struct cfg80211_ops mwifiex_cfg80211_ops = {
	.add_virtual_intf = mwifiex_add_virtual_intf,
@@ -4351,6 +4656,16 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
			    "%s: creating new wiphy\n", __func__);
		return -ENOMEM;
	}
	if (adapter->host_mlme_enabled) {
		mwifiex_cfg80211_ops.auth = mwifiex_cfg80211_authenticate;
		mwifiex_cfg80211_ops.assoc = mwifiex_cfg80211_associate;
		mwifiex_cfg80211_ops.deauth = mwifiex_cfg80211_deauthenticate;
		mwifiex_cfg80211_ops.disassoc = mwifiex_cfg80211_disassociate;
		mwifiex_cfg80211_ops.disconnect = NULL;
		mwifiex_cfg80211_ops.connect = NULL;
		mwifiex_cfg80211_ops.probe_client =
			mwifiex_cfg80211_probe_client;
	}
	wiphy->max_scan_ssids = MWIFIEX_MAX_SSID_LIST_LENGTH;
	wiphy->max_scan_ie_len = MWIFIEX_MAX_VSIE_LEN;
	wiphy->mgmt_stypes = mwifiex_mgmt_stypes;
@@ -4434,6 +4749,9 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
			   NL80211_FEATURE_LOW_PRIORITY_SCAN |
			   NL80211_FEATURE_NEED_OBSS_SCAN;

	if (adapter->host_mlme_enabled)
		wiphy->features |= NL80211_FEATURE_SAE;

	if (ISSUPP_ADHOC_ENABLED(adapter->fw_cap_info))
		wiphy->features |= NL80211_FEATURE_HT_IBSS;

+25 −0
Original line number Diff line number Diff line
@@ -924,6 +924,24 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)
	return ret;
}

void mwifiex_process_assoc_resp(struct mwifiex_adapter *adapter)
{
	struct cfg80211_rx_assoc_resp_data assoc_resp = {
		.uapsd_queues = -1,
	};
	struct mwifiex_private *priv =
		mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);

	if (priv->assoc_rsp_size) {
		assoc_resp.links[0].bss = priv->req_bss;
		assoc_resp.buf = priv->assoc_rsp_buf;
		assoc_resp.len = priv->assoc_rsp_size;
		cfg80211_rx_assoc_resp(priv->netdev,
				       &assoc_resp);
		priv->assoc_rsp_size = 0;
	}
}

/*
 * This function handles the timeout of command sending.
 *
@@ -1672,6 +1690,13 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv,
	if (adapter->fw_api_ver == MWIFIEX_FW_V15)
		adapter->scan_chan_gap_enabled = true;

	if (adapter->key_api_major_ver != KEY_API_VER_MAJOR_V2)
		adapter->host_mlme_enabled = false;

	mwifiex_dbg(adapter, MSG, "host_mlme: %s, key_api: %d\n",
		    adapter->host_mlme_enabled ? "enable" : "disable",
		    adapter->key_api_major_ver);

	return 0;
}

+23 −0
Original line number Diff line number Diff line
@@ -31,6 +31,29 @@
						 *   + sizeof(tx_control)
						 */

#define FRMCTL_LEN                2
#define DURATION_LEN              2
#define SEQCTL_LEN                2
/* special FW 4 address management header */
#define MWIFIEX_MGMT_HEADER_LEN   (FRMCTL_LEN + DURATION_LEN + ETH_ALEN + \
				   ETH_ALEN + ETH_ALEN + SEQCTL_LEN + ETH_ALEN)

#define AUTH_ALG_LEN              2
#define AUTH_TRANSACTION_LEN      2
#define AUTH_STATUS_LEN           2
#define MWIFIEX_AUTH_BODY_LEN     (AUTH_ALG_LEN + AUTH_TRANSACTION_LEN + \
				   AUTH_STATUS_LEN)

#define HOST_MLME_AUTH_PENDING    BIT(0)
#define HOST_MLME_AUTH_DONE       BIT(1)

#define HOST_MLME_MGMT_MASK       (BIT(IEEE80211_STYPE_AUTH >> 4) | \
				   BIT(IEEE80211_STYPE_DEAUTH >> 4) | \
				   BIT(IEEE80211_STYPE_DISASSOC >> 4))
#define AUTH_TX_DEFAULT_WAIT_TIME 2400

#define WLAN_AUTH_NONE            0xFFFF

#define MWIFIEX_MAX_TX_BASTREAM_SUPPORTED	2
#define MWIFIEX_MAX_RX_BASTREAM_SUPPORTED	16
#define MWIFIEX_MAX_TDLS_PEER_SUPPORTED 8
+33 −0
Original line number Diff line number Diff line
@@ -210,6 +210,8 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define TLV_TYPE_RANDOM_MAC         (PROPRIETARY_TLV_BASE_ID + 236)
#define TLV_TYPE_CHAN_ATTR_CFG      (PROPRIETARY_TLV_BASE_ID + 237)
#define TLV_TYPE_MAX_CONN           (PROPRIETARY_TLV_BASE_ID + 279)
#define TLV_TYPE_HOST_MLME          (PROPRIETARY_TLV_BASE_ID + 307)
#define TLV_TYPE_SAE_PWE_MODE       (PROPRIETARY_TLV_BASE_ID + 339)

#define MWIFIEX_TX_DATA_BUF_SIZE_2K        2048

@@ -744,6 +746,25 @@ struct uap_rxpd {
	u8 flags;
} __packed;

struct mwifiex_auth {
	__le16 auth_alg;
	__le16 auth_transaction;
	__le16 status_code;
	/* possibly followed by Challenge text */
	u8 variable[];
} __packed;

struct mwifiex_ieee80211_mgmt {
	__le16 frame_control;
	__le16 duration;
	u8 da[ETH_ALEN];
	u8 sa[ETH_ALEN];
	u8 bssid[ETH_ALEN];
	__le16 seq_ctrl;
	u8 addr4[ETH_ALEN];
	struct mwifiex_auth auth;
} __packed;

struct mwifiex_fw_chan_stats {
	u8 chan_num;
	u8 bandcfg;
@@ -803,6 +824,11 @@ struct mwifiex_ie_types_ssid_param_set {
	u8 ssid[];
} __packed;

struct mwifiex_ie_types_host_mlme {
	struct mwifiex_ie_types_header header;
	u8 host_mlme;
} __packed;

struct mwifiex_ie_types_num_probes {
	struct mwifiex_ie_types_header header;
	__le16 num_probes;
@@ -906,6 +932,13 @@ struct mwifiex_ie_types_tdls_idle_timeout {
	__le16 value;
} __packed;

#define MWIFIEX_AUTHTYPE_SAE 6

struct mwifiex_ie_types_sae_pwe_mode {
	struct mwifiex_ie_types_header header;
	u8 pwe[];
} __packed;

struct mwifiex_ie_types_rsn_param_set {
	struct mwifiex_ie_types_header header;
	u8 rsn_ie[];
+6 −0
Original line number Diff line number Diff line
@@ -81,6 +81,9 @@ int mwifiex_init_priv(struct mwifiex_private *priv)
	priv->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR;
	priv->data_avg_factor = DEFAULT_DATA_AVG_FACTOR;

	priv->auth_flag = 0;
	priv->auth_alg = WLAN_AUTH_NONE;

	priv->sec_info.wep_enabled = 0;
	priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM;
	priv->sec_info.encryption_mode = 0;
@@ -220,6 +223,9 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter)
	adapter->cmd_resp_received = false;
	adapter->event_received = false;
	adapter->data_received = false;
	adapter->assoc_resp_received = false;
	adapter->priv_link_lost = NULL;
	adapter->host_mlme_link_lost = false;

	clear_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags);

Loading