// SPDX-License-Identifier: GPL-2.0
/*
* cfg80211 MLME SAP interface
*
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
* Copyright (c) 2015 Intel Deutschland GmbH
* Copyright (C) 2019-2020, 2022 Intel Corporation
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/etherdevice.h>
#include <linux/netdevice.h>
#include <linux/nl80211.h>
#include <linux/slab.h>
#include <linux/wireless.h>
#include <net/cfg80211.h>
#include <net/iw_handler.h>
#include "core.h"
#include "nl80211.h"
#include "rdev-ops.h"
void cfg80211_rx_assoc_resp(struct net_device *dev,
struct cfg80211_rx_assoc_resp *data)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)data->buf;
struct cfg80211_connect_resp_params cr = {
.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED,
.req_ie = data->req_ies,
.req_ie_len = data->req_ies_len,
.resp_ie = mgmt->u.assoc_resp.variable,
.resp_ie_len = data->len -
offsetof(struct ieee80211_mgmt,
u.assoc_resp.variable),
.status = le16_to_cpu(mgmt->u.assoc_resp.status_code),
.ap_mld_addr = data->ap_mld_addr,
};
unsigned int link_id;
for (link_id = 0; link_id < ARRAY_SIZE(data->links); link_id++) {
cr.links[link_id].status = data->links[link_id].status;
WARN_ON_ONCE(cr.links[link_id].status != WLAN_STATUS_SUCCESS &&
(!cr.ap_mld_addr || !cr.links[link_id].bss));
cr.links[link_id].bss = data->links[link_id].bss;
if (!cr.links[link_id].bss)
continue;
cr.links[link_id].bssid = data->links[link_id].bss->bssid;
cr.links[link_id].addr = data->links[link_id].addr;
/* need to have local link addresses for MLO connections */
WARN_ON(cr.ap_mld_addr && !cr.links[link_id].addr);
BUG_ON(!cr.links[link_id].bss->channel);
if (cr.links[link_id].bss->channel->band == NL80211_BAND_S1GHZ) {
WARN_ON(link_id);
cr.resp_ie = (u8 *)&mgmt->u.s1g_assoc_resp.variable;
cr.resp_ie_len = data->len -
offsetof(struct ieee80211_mgmt,
u.s1g_assoc_resp.variable);
}
if (cr.ap_mld_addr)
cr.valid_links |= BIT(link_id);
}
trace_cfg80211_send_rx_assoc(dev, data);
/*
* This is a bit of a hack, we don't notify userspace of
* a (re-)association reply if we tried to send a reassoc
* and got a reject -- we only try again with an assoc
* frame instead of reassoc.
*/
if (cfg80211_sme_rx_assoc_resp(wdev, cr.status)) {
for (link_id = 0; link_id < ARRAY_SIZE(data->links); link_id++) {
struct cfg80211_bss *bss = data->links[link_id].bss;
if (!bss)
continue;
cfg80211_unhold_bss(bss_from_pub(bss));
cfg80211_put_bss(wiphy, bss);
}
return;
}
nl80211_send_rx_assoc(rdev, dev, data);
/* update current_bss etc., consumes the bss reference */
__cfg80211_connect_result(dev, &cr, cr.status == WLAN_STATUS_SUCCESS);
}
EXPORT_SYMBOL(cfg80211_rx_assoc_resp);
static void cfg80211_process_auth(struct wireless_dev *wdev,
const u8 *buf, size_t len)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
nl80211_send_rx_auth(rdev, wdev->netdev, buf, len, GFP_KERNEL);
cfg80211_sme_rx_auth(wdev, buf, len);
}
static void cfg80211_process_deauth(struct wireless_dev *wdev,
const u8 *buf, size_t len,
bool reconnect)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);