diff options
Diffstat (limited to 'drivers/net/wireless/ath/wil6210/txrx.c')
-rw-r--r-- | drivers/net/wireless/ath/wil6210/txrx.c | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index b6253fc57991..cb13652491ad 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -725,6 +725,182 @@ static void wil_get_netif_rx_params(struct sk_buff *skb, int *cid, } /* + * Check if skb is ptk eapol key message + * + * returns a pointer to the start of the eapol key structure, NULL + * if frame is not PTK eapol key + */ +static struct wil_eapol_key *wil_is_ptk_eapol_key(struct wil6210_priv *wil, + struct sk_buff *skb) +{ + u8 *buf; + const struct wil_1x_hdr *hdr; + struct wil_eapol_key *key; + u16 key_info; + int len = skb->len; + + if (!skb_mac_header_was_set(skb)) { + wil_err(wil, "mac header was not set\n"); + return NULL; + } + + len -= skb_mac_offset(skb); + + if (len < sizeof(struct ethhdr) + sizeof(struct wil_1x_hdr) + + sizeof(struct wil_eapol_key)) + return NULL; + + buf = skb_mac_header(skb) + sizeof(struct ethhdr); + + hdr = (const struct wil_1x_hdr *)buf; + if (hdr->type != WIL_1X_TYPE_EAPOL_KEY) + return NULL; + + key = (struct wil_eapol_key *)(buf + sizeof(struct wil_1x_hdr)); + if (key->type != WIL_EAPOL_KEY_TYPE_WPA && + key->type != WIL_EAPOL_KEY_TYPE_RSN) + return NULL; + + key_info = be16_to_cpu(key->key_info); + if (!(key_info & WIL_KEY_INFO_KEY_TYPE)) /* check if pairwise */ + return NULL; + + return key; +} + +static bool wil_skb_is_eap_3(struct wil6210_priv *wil, struct sk_buff *skb) +{ + struct wil_eapol_key *key; + u16 key_info; + + key = wil_is_ptk_eapol_key(wil, skb); + if (!key) + return false; + + key_info = be16_to_cpu(key->key_info); + if (key_info & (WIL_KEY_INFO_MIC | + WIL_KEY_INFO_ENCR_KEY_DATA)) { + /* 3/4 of 4-Way Handshake */ + wil_dbg_misc(wil, "EAPOL key message 3\n"); + return true; + } + /* 1/4 of 4-Way Handshake */ + wil_dbg_misc(wil, "EAPOL key message 1\n"); + + return false; +} + +static bool wil_skb_is_eap_4(struct wil6210_priv *wil, struct sk_buff *skb) +{ + struct wil_eapol_key *key; + u32 *nonce, i; + + key = wil_is_ptk_eapol_key(wil, skb); + if (!key) + return false; + + nonce = (u32 *)key->key_nonce; + for (i = 0; i < WIL_EAP_NONCE_LEN / sizeof(u32); i++, nonce++) { + if (*nonce != 0) { + /* message 2/4 */ + wil_dbg_misc(wil, "EAPOL key message 2\n"); + return false; + } + } + wil_dbg_misc(wil, "EAPOL key message 4\n"); + + return true; +} + +void wil_enable_tx_key_worker(struct work_struct *work) +{ + struct wil6210_vif *vif = container_of(work, + struct wil6210_vif, enable_tx_key_worker); + struct wil6210_priv *wil = vif_to_wil(vif); + int rc, cid; + + rtnl_lock(); + if (vif->ptk_rekey_state != WIL_REKEY_WAIT_M4_SENT) { + wil_dbg_misc(wil, "Invalid rekey state = %d\n", + vif->ptk_rekey_state); + rtnl_unlock(); + return; + } + + cid = wil_find_cid_by_idx(wil, vif->mid, 0); + if (!wil_cid_valid(wil, cid)) { + wil_err(wil, "Invalid cid = %d\n", cid); + rtnl_unlock(); + return; + } + + wil_dbg_misc(wil, "Apply PTK key after eapol was sent out\n"); + rc = wmi_add_cipher_key(vif, 0, wil->sta[cid].addr, 0, NULL, + WMI_KEY_USE_APPLY_PTK); + + vif->ptk_rekey_state = WIL_REKEY_IDLE; + rtnl_unlock(); + + if (rc) + wil_err(wil, "Apply PTK key failed %d\n", rc); +} + +void wil_tx_complete_handle_eapol(struct wil6210_vif *vif, struct sk_buff *skb) +{ + struct wil6210_priv *wil = vif_to_wil(vif); + struct wireless_dev *wdev = vif_to_wdev(vif); + bool q = false; + + if (wdev->iftype != NL80211_IFTYPE_STATION || + !test_bit(WMI_FW_CAPABILITY_SPLIT_REKEY, wil->fw_capabilities)) + return; + + /* check if skb is an EAP message 4/4 */ + if (!wil_skb_is_eap_4(wil, skb)) + return; + + spin_lock_bh(&wil->eap_lock); + switch (vif->ptk_rekey_state) { + case WIL_REKEY_IDLE: + /* ignore idle state, can happen due to M4 retransmission */ + break; + case WIL_REKEY_M3_RECEIVED: + vif->ptk_rekey_state = WIL_REKEY_IDLE; + break; + case WIL_REKEY_WAIT_M4_SENT: + q = true; + break; + default: + wil_err(wil, "Unknown rekey state = %d", + vif->ptk_rekey_state); + } + spin_unlock_bh(&wil->eap_lock); + + if (q) { + q = queue_work(wil->wmi_wq, &vif->enable_tx_key_worker); + wil_dbg_misc(wil, "queue_work of enable_tx_key_worker -> %d\n", + q); + } +} + +static void wil_rx_handle_eapol(struct wil6210_vif *vif, struct sk_buff *skb) +{ + struct wil6210_priv *wil = vif_to_wil(vif); + struct wireless_dev *wdev = vif_to_wdev(vif); + + if (wdev->iftype != NL80211_IFTYPE_STATION || + !test_bit(WMI_FW_CAPABILITY_SPLIT_REKEY, wil->fw_capabilities)) + return; + + /* check if skb is a EAP message 3/4 */ + if (!wil_skb_is_eap_3(wil, skb)) + return; + + if (vif->ptk_rekey_state == WIL_REKEY_IDLE) + vif->ptk_rekey_state = WIL_REKEY_M3_RECEIVED; +} + +/* * Pass Rx packet to the netif. Update statistics. * Called in softirq context (NAPI poll). */ @@ -796,6 +972,10 @@ void wil_netif_rx(struct sk_buff *skb, struct net_device *ndev, int cid, if (skb) { /* deliver to local stack */ skb->protocol = eth_type_trans(skb, ndev); skb->dev = ndev; + + if (skb->protocol == cpu_to_be16(ETH_P_PAE)) + wil_rx_handle_eapol(vif, skb); + if (gro) rc = napi_gro_receive(&wil->napi_rx, skb); else @@ -2332,6 +2512,10 @@ int wil_tx_complete(struct wil6210_vif *vif, int ringid) if (stats) stats->tx_errors++; } + + if (skb->protocol == cpu_to_be16(ETH_P_PAE)) + wil_tx_complete_handle_eapol(vif, skb); + wil_consume_skb(skb, d->dma.error == 0); } memset(ctx, 0, sizeof(*ctx)); |