diff options
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/core.h | 13 | ||||
-rw-r--r-- | net/wireless/mlme.c | 8 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 168 | ||||
-rw-r--r-- | net/wireless/nl80211.h | 3 | ||||
-rw-r--r-- | net/wireless/pmsr.c | 2 | ||||
-rw-r--r-- | net/wireless/reg.c | 77 | ||||
-rw-r--r-- | net/wireless/reg.h | 2 | ||||
-rw-r--r-- | net/wireless/scan.c | 595 | ||||
-rw-r--r-- | net/wireless/util.c | 6 | ||||
-rw-r--r-- | net/wireless/wext-compat.c | 2 |
10 files changed, 731 insertions, 145 deletions
diff --git a/net/wireless/core.h b/net/wireless/core.h index f6b40563dc63..84d36ca7a7ab 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -3,7 +3,7 @@ * Wireless configuration interface internals. * * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> - * Copyright (C) 2018 Intel Corporation + * Copyright (C) 2018-2019 Intel Corporation */ #ifndef __NET_WIRELESS_CORE_H #define __NET_WIRELESS_CORE_H @@ -182,12 +182,23 @@ static inline struct cfg80211_internal_bss *bss_from_pub(struct cfg80211_bss *pu static inline void cfg80211_hold_bss(struct cfg80211_internal_bss *bss) { atomic_inc(&bss->hold); + if (bss->pub.transmitted_bss) { + bss = container_of(bss->pub.transmitted_bss, + struct cfg80211_internal_bss, pub); + atomic_inc(&bss->hold); + } } static inline void cfg80211_unhold_bss(struct cfg80211_internal_bss *bss) { int r = atomic_dec_return(&bss->hold); WARN_ON(r < 0); + if (bss->pub.transmitted_bss) { + bss = container_of(bss->pub.transmitted_bss, + struct cfg80211_internal_bss, pub); + r = atomic_dec_return(&bss->hold); + WARN_ON(r < 0); + } } diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 1615e503f8e3..f9462010575f 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -21,7 +21,8 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, - const u8 *buf, size_t len, int uapsd_queues) + const u8 *buf, size_t len, int uapsd_queues, + const u8 *req_ies, size_t req_ies_len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; @@ -33,6 +34,8 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, cr.status = (int)le16_to_cpu(mgmt->u.assoc_resp.status_code); cr.bssid = mgmt->bssid; cr.bss = bss; + cr.req_ie = req_ies; + cr.req_ie_len = req_ies_len; cr.resp_ie = mgmt->u.assoc_resp.variable; cr.resp_ie_len = len - offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); @@ -52,7 +55,8 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, return; } - nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL, uapsd_queues); + nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL, uapsd_queues, + req_ies, req_ies_len); /* update current_bss etc., consumes the bss reference */ __cfg80211_connect_result(dev, &cr, cr.status == WLAN_STATUS_SUCCESS); } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d91a408db113..25a9e3b5c154 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4,7 +4,7 @@ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018 Intel Corporation + * Copyright (C) 2018-2019 Intel Corporation */ #include <linux/if.h> @@ -203,29 +203,17 @@ cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info) static int validate_ie_attr(const struct nlattr *attr, struct netlink_ext_ack *extack) { - const u8 *pos; - int len; + const u8 *data = nla_data(attr); + unsigned int len = nla_len(attr); + const struct element *elem; - pos = nla_data(attr); - len = nla_len(attr); - - while (len) { - u8 elemlen; - - if (len < 2) - goto error; - len -= 2; - - elemlen = pos[1]; - if (elemlen > len) - goto error; - - len -= elemlen; - pos += 2 + elemlen; + for_each_element(elem, data, len) { + /* nothing */ } - return 0; -error: + if (for_each_element_completed(elem, data, len)) + return 0; + NL_SET_ERR_MSG_ATTR(extack, attr, "malformed information elements"); return -EINVAL; } @@ -259,15 +247,13 @@ nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1] = { static const struct nla_policy nl80211_pmsr_req_data_policy[NL80211_PMSR_TYPE_MAX + 1] = { [NL80211_PMSR_TYPE_FTM] = - NLA_POLICY_NESTED(NL80211_PMSR_FTM_REQ_ATTR_MAX, - nl80211_pmsr_ftm_req_attr_policy), + NLA_POLICY_NESTED(nl80211_pmsr_ftm_req_attr_policy), }; static const struct nla_policy nl80211_pmsr_req_attr_policy[NL80211_PMSR_REQ_ATTR_MAX + 1] = { [NL80211_PMSR_REQ_ATTR_DATA] = - NLA_POLICY_NESTED(NL80211_PMSR_TYPE_MAX, - nl80211_pmsr_req_data_policy), + NLA_POLICY_NESTED(nl80211_pmsr_req_data_policy), [NL80211_PMSR_REQ_ATTR_GET_AP_TSF] = { .type = NLA_FLAG }, }; @@ -280,8 +266,7 @@ nl80211_psmr_peer_attr_policy[NL80211_PMSR_PEER_ATTR_MAX + 1] = { */ [NL80211_PMSR_PEER_ATTR_CHAN] = { .type = NLA_NESTED }, [NL80211_PMSR_PEER_ATTR_REQ] = - NLA_POLICY_NESTED(NL80211_PMSR_REQ_ATTR_MAX, - nl80211_pmsr_req_attr_policy), + NLA_POLICY_NESTED(nl80211_pmsr_req_attr_policy), [NL80211_PMSR_PEER_ATTR_RESP] = { .type = NLA_REJECT }, }; @@ -292,8 +277,7 @@ nl80211_pmsr_attr_policy[NL80211_PMSR_ATTR_MAX + 1] = { [NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR] = { .type = NLA_REJECT }, [NL80211_PMSR_ATTR_TYPE_CAPA] = { .type = NLA_REJECT }, [NL80211_PMSR_ATTR_PEERS] = - NLA_POLICY_NESTED_ARRAY(NL80211_PMSR_PEER_ATTR_MAX, - nl80211_psmr_peer_attr_policy), + NLA_POLICY_NESTED_ARRAY(nl80211_psmr_peer_attr_policy), }; const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { @@ -555,8 +539,8 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { }, [NL80211_ATTR_TIMEOUT] = NLA_POLICY_MIN(NLA_U32, 1), [NL80211_ATTR_PEER_MEASUREMENTS] = - NLA_POLICY_NESTED(NL80211_PMSR_ATTR_MAX, - nl80211_pmsr_attr_policy), + NLA_POLICY_NESTED(nl80211_pmsr_attr_policy), + [NL80211_ATTR_AIRTIME_WEIGHT] = NLA_POLICY_MIN(NLA_U16, 1), }; /* policy for the key attributes */ @@ -2278,6 +2262,15 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, if (nl80211_send_pmsr_capa(rdev, msg)) goto nla_put_failure; + state->split_start++; + break; + case 15: + if (rdev->wiphy.akm_suites && + nla_put(msg, NL80211_ATTR_AKM_SUITES, + sizeof(u32) * rdev->wiphy.n_akm_suites, + rdev->wiphy.akm_suites)) + goto nla_put_failure; + /* done */ state->split_start = 0; break; @@ -4540,6 +4533,9 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) nl80211_calculate_ap_params(¶ms); + if (info->attrs[NL80211_ATTR_EXTERNAL_AUTH_SUPPORT]) + params.flags |= AP_SETTINGS_EXTERNAL_AUTH_SUPPORT; + wdev_lock(wdev); err = rdev_start_ap(rdev, dev, ¶ms); if (!err) { @@ -4851,6 +4847,11 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, PUT_SINFO(PLID, plid, u16); PUT_SINFO(PLINK_STATE, plink_state, u8); PUT_SINFO_U64(RX_DURATION, rx_duration); + PUT_SINFO_U64(TX_DURATION, tx_duration); + + if (wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) + PUT_SINFO(AIRTIME_WEIGHT, airtime_weight, u16); switch (rdev->wiphy.signal_type) { case CFG80211_SIGNAL_TYPE_MBM: @@ -5470,6 +5471,15 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) nla_get_u8(info->attrs[NL80211_ATTR_OPMODE_NOTIF]); } + if (info->attrs[NL80211_ATTR_AIRTIME_WEIGHT]) + params.airtime_weight = + nla_get_u16(info->attrs[NL80211_ATTR_AIRTIME_WEIGHT]); + + if (params.airtime_weight && + !wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) + return -EOPNOTSUPP; + /* Include parameters for TDLS peer (will check later) */ err = nl80211_set_station_tdls(info, ¶ms); if (err) @@ -5598,6 +5608,15 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) params.plink_action = nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); + if (info->attrs[NL80211_ATTR_AIRTIME_WEIGHT]) + params.airtime_weight = + nla_get_u16(info->attrs[NL80211_ATTR_AIRTIME_WEIGHT]); + + if (params.airtime_weight && + !wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) + return -EOPNOTSUPP; + err = nl80211_parse_sta_channel_info(info, ¶ms); if (err) return err; @@ -5803,7 +5822,13 @@ static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq, pinfo->discovery_timeout)) || ((pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES) && nla_put_u8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES, - pinfo->discovery_retries))) + pinfo->discovery_retries)) || + ((pinfo->filled & MPATH_INFO_HOP_COUNT) && + nla_put_u8(msg, NL80211_MPATH_INFO_HOP_COUNT, + pinfo->hop_count)) || + ((pinfo->filled & MPATH_INFO_PATH_CHANGE) && + nla_put_u32(msg, NL80211_MPATH_INFO_PATH_CHANGE, + pinfo->path_change_count))) goto nla_put_failure; nla_nest_end(msg, pinfoattr); @@ -9281,6 +9306,7 @@ struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy, struct wireless_dev *wdev, enum nl80211_commands cmd, enum nl80211_attrs attr, + unsigned int portid, int vendor_event_idx, int approxlen, gfp_t gfp) { @@ -9304,7 +9330,7 @@ struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy, return NULL; } - return __cfg80211_alloc_vendor_skb(rdev, wdev, approxlen, 0, 0, + return __cfg80211_alloc_vendor_skb(rdev, wdev, approxlen, portid, 0, cmd, attr, info, gfp); } EXPORT_SYMBOL(__cfg80211_alloc_event_skb); @@ -9313,6 +9339,7 @@ void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp) { struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; void *hdr = ((void **)skb->cb)[1]; + struct nlmsghdr *nlhdr = nlmsg_hdr(skb); struct nlattr *data = ((void **)skb->cb)[2]; enum nl80211_multicast_groups mcgrp = NL80211_MCGRP_TESTMODE; @@ -9322,11 +9349,16 @@ void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp) nla_nest_end(skb, data); genlmsg_end(skb, hdr); - if (data->nla_type == NL80211_ATTR_VENDOR_DATA) - mcgrp = NL80211_MCGRP_VENDOR; + if (nlhdr->nlmsg_pid) { + genlmsg_unicast(wiphy_net(&rdev->wiphy), skb, + nlhdr->nlmsg_pid); + } else { + if (data->nla_type == NL80211_ATTR_VENDOR_DATA) + mcgrp = NL80211_MCGRP_VENDOR; - genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), skb, 0, - mcgrp, gfp); + genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), + skb, 0, mcgrp, gfp); + } } EXPORT_SYMBOL(__cfg80211_send_event_skb); @@ -9857,7 +9889,10 @@ static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info) } if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && + !(dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP && + wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_AP_PMKSA_CACHING))) return -EOPNOTSUPP; switch (info->genlhdr->cmd) { @@ -12708,6 +12743,17 @@ int cfg80211_vendor_cmd_reply(struct sk_buff *skb) } EXPORT_SYMBOL_GPL(cfg80211_vendor_cmd_reply); +unsigned int cfg80211_vendor_cmd_get_sender(struct wiphy *wiphy) +{ + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + + if (WARN_ON(!rdev->cur_cmd_info)) + return 0; + + return rdev->cur_cmd_info->snd_portid; +} +EXPORT_SYMBOL_GPL(cfg80211_vendor_cmd_get_sender); + static int nl80211_set_qos_map(struct sk_buff *skb, struct genl_info *info) { @@ -13047,7 +13093,9 @@ static int nl80211_external_auth(struct sk_buff *skb, struct genl_info *info) if (!rdev->ops->external_auth) return -EOPNOTSUPP; - if (!info->attrs[NL80211_ATTR_SSID]) + if (!info->attrs[NL80211_ATTR_SSID] && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) return -EINVAL; if (!info->attrs[NL80211_ATTR_BSSID]) @@ -13058,18 +13106,24 @@ static int nl80211_external_auth(struct sk_buff *skb, struct genl_info *info) memset(¶ms, 0, sizeof(params)); - params.ssid.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); - if (params.ssid.ssid_len == 0 || - params.ssid.ssid_len > IEEE80211_MAX_SSID_LEN) - return -EINVAL; - memcpy(params.ssid.ssid, nla_data(info->attrs[NL80211_ATTR_SSID]), - params.ssid.ssid_len); + if (info->attrs[NL80211_ATTR_SSID]) { + params.ssid.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + if (params.ssid.ssid_len == 0 || + params.ssid.ssid_len > IEEE80211_MAX_SSID_LEN) + return -EINVAL; + memcpy(params.ssid.ssid, + nla_data(info->attrs[NL80211_ATTR_SSID]), + params.ssid.ssid_len); + } memcpy(params.bssid, nla_data(info->attrs[NL80211_ATTR_BSSID]), ETH_ALEN); params.status = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]); + if (info->attrs[NL80211_ATTR_PMKID]) + params.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]); + return rdev_external_auth(rdev, dev, ¶ms); } @@ -14455,12 +14509,13 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, enum nl80211_commands cmd, gfp_t gfp, - int uapsd_queues) + int uapsd_queues, const u8 *req_ies, + size_t req_ies_len) { struct sk_buff *msg; void *hdr; - msg = nlmsg_new(100 + len, gfp); + msg = nlmsg_new(100 + len + req_ies_len, gfp); if (!msg) return; @@ -14472,7 +14527,9 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || - nla_put(msg, NL80211_ATTR_FRAME, len, buf)) + nla_put(msg, NL80211_ATTR_FRAME, len, buf) || + (req_ies && + nla_put(msg, NL80211_ATTR_REQ_IE, req_ies_len, req_ies))) goto nla_put_failure; if (uapsd_queues >= 0) { @@ -14503,15 +14560,17 @@ void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, size_t len, gfp_t gfp) { nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_AUTHENTICATE, gfp, -1); + NL80211_CMD_AUTHENTICATE, gfp, -1, NULL, 0); } void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, - size_t len, gfp_t gfp, int uapsd_queues) + size_t len, gfp_t gfp, int uapsd_queues, + const u8 *req_ies, size_t req_ies_len) { nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_ASSOCIATE, gfp, uapsd_queues); + NL80211_CMD_ASSOCIATE, gfp, uapsd_queues, + req_ies, req_ies_len); } void nl80211_send_deauth(struct cfg80211_registered_device *rdev, @@ -14519,7 +14578,7 @@ void nl80211_send_deauth(struct cfg80211_registered_device *rdev, size_t len, gfp_t gfp) { nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_DEAUTHENTICATE, gfp, -1); + NL80211_CMD_DEAUTHENTICATE, gfp, -1, NULL, 0); } void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, @@ -14527,7 +14586,7 @@ void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, size_t len, gfp_t gfp) { nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_DISASSOCIATE, gfp, -1); + NL80211_CMD_DISASSOCIATE, gfp, -1, NULL, 0); } void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf, @@ -14548,7 +14607,8 @@ void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf, cmd = NL80211_CMD_UNPROT_DISASSOCIATE; trace_cfg80211_rx_unprot_mlme_mgmt(dev, buf, len); - nl80211_send_mlme_event(rdev, dev, buf, len, cmd, GFP_ATOMIC, -1); + nl80211_send_mlme_event(rdev, dev, buf, len, cmd, GFP_ATOMIC, -1, + NULL, 0); } EXPORT_SYMBOL(cfg80211_rx_unprot_mlme_mgmt); diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 531c82dcba6b..a41e94a49a89 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -67,7 +67,8 @@ void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp, - int uapsd_queues); + int uapsd_queues, + const u8 *req_ies, size_t req_ies_len); void nl80211_send_deauth(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp); diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c index 0216ab555249..5e2ab01d325c 100644 --- a/net/wireless/pmsr.c +++ b/net/wireless/pmsr.c @@ -257,7 +257,7 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info) goto out_err; } else { memcpy(req->mac_addr, wdev_address(wdev), ETH_ALEN); - memset(req->mac_addr_mask, 0xff, ETH_ALEN); + eth_broadcast_addr(req->mac_addr_mask); } idx = 0; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index dd58b9909ac9..2f1bf91eb226 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -5,7 +5,7 @@ * Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com> * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2017 Intel Deutschland GmbH - * Copyright (C) 2018 Intel Corporation + * Copyright (C) 2018 - 2019 Intel Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -131,7 +131,8 @@ static spinlock_t reg_indoor_lock; /* Used to track the userspace process controlling the indoor setting */ static u32 reg_is_indoor_portid; -static void restore_regulatory_settings(bool reset_user); +static void restore_regulatory_settings(bool reset_user, bool cached); +static void print_regdomain(const struct ieee80211_regdomain *rd); static const struct ieee80211_regdomain *get_cfg80211_regdom(void) { @@ -263,6 +264,7 @@ static const struct ieee80211_regdomain *cfg80211_world_regdom = static char *ieee80211_regdom = "00"; static char user_alpha2[2]; +static const struct ieee80211_regdomain *cfg80211_user_regdom; module_param(ieee80211_regdom, charp, 0444); MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); @@ -445,6 +447,15 @@ reg_copy_regd(const struct ieee80211_regdomain *src_regd) return regd; } +static void cfg80211_save_user_regdom(const struct ieee80211_regdomain *rd) +{ + ASSERT_RTNL(); + + if (!IS_ERR(cfg80211_user_regdom)) + kfree(cfg80211_user_regdom); + cfg80211_user_regdom = reg_copy_regd(rd); +} + struct reg_regdb_apply_request { struct list_head list; const struct ieee80211_regdomain *regdom; @@ -510,7 +521,7 @@ static void crda_timeout_work(struct work_struct *work) pr_debug("Timeout while waiting for CRDA to reply, restoring regulatory settings\n"); rtnl_lock(); reg_crda_timeouts++; - restore_regulatory_settings(true); + restore_regulatory_settings(true, false); rtnl_unlock(); } @@ -1044,7 +1055,7 @@ static void regdb_fw_cb(const struct firmware *fw, void *context) } if (restore) - restore_regulatory_settings(true); + restore_regulatory_settings(true, false); rtnl_unlock(); @@ -2729,9 +2740,7 @@ static void notify_self_managed_wiphys(struct regulatory_request *request) list_for_each_entry(rdev, &cfg80211_rdev_list, list) { wiphy = &rdev->wiphy; if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED && - request->initiator == NL80211_REGDOM_SET_BY_USER && - request->user_reg_hint_type == - NL80211_USER_REG_HINT_CELL_BASE) + request->initiator == NL80211_REGDOM_SET_BY_USER) reg_call_notifier(wiphy, request); } } @@ -3119,7 +3128,7 @@ static void restore_custom_reg_settings(struct wiphy *wiphy) * keep their own regulatory domain on wiphy->regd so that does does * not need to be remembered. */ -static void restore_regulatory_settings(bool reset_user) +static void restore_regulatory_settings(bool reset_user, bool cached) { char alpha2[2]; char world_alpha2[2]; @@ -3178,15 +3187,41 @@ static void restore_regulatory_settings(bool reset_user) restore_custom_reg_settings(&rdev->wiphy); } - regulatory_hint_core(world_alpha2); + if (cached && (!is_an_alpha2(alpha2) || + !IS_ERR_OR_NULL(cfg80211_user_regdom))) { + reset_regdomains(false, cfg80211_world_regdom); + update_all_wiphy_regulatory(NL80211_REGDOM_SET_BY_CORE); + print_regdomain(get_cfg80211_regdom()); + nl80211_send_reg_change_event(&core_request_world); + reg_set_request_processed(); - /* - * This restores the ieee80211_regdom module parameter - * preference or the last user requested regulatory - * settings, user regulatory settings takes precedence. - */ - if (is_an_alpha2(alpha2)) - regulatory_hint_user(alpha2, NL80211_USER_REG_HINT_USER); + if (is_an_alpha2(alpha2) && + !regulatory_hint_user(alpha2, NL80211_USER_REG_HINT_USER)) { + struct regulatory_request *ureq; + + spin_lock(®_requests_lock); + ureq = list_last_entry(®_requests_list, + struct regulatory_request, + list); + list_del(&ureq->list); + spin_unlock(®_requests_lock); + + notify_self_managed_wiphys(ureq); + reg_update_last_request(ureq); + set_regdom(reg_copy_regd(cfg80211_user_regdom), + REGD_SOURCE_CACHED); + } + } else { + regulatory_hint_core(world_alpha2); + + /* + * This restores the ieee80211_regdom module parameter + * preference or the last user requested regulatory + * settings, user regulatory settings takes precedence. + */ + if (is_an_alpha2(alpha2)) + regulatory_hint_user(alpha2, NL80211_USER_REG_HINT_USER); + } spin_lock(®_requests_lock); list_splice_tail_init(&tmp_reg_req_list, ®_requests_list); @@ -3246,7 +3281,7 @@ void regulatory_hint_disconnect(void) } pr_debug("All devices are disconnected, going to restore regulatory settings\n"); - restore_regulatory_settings(false); + restore_regulatory_settings(false, true); } static bool freq_is_chan_12_13_14(u32 freq) @@ -3563,6 +3598,9 @@ int set_regdom(const struct ieee80211_regdomain *rd, bool user_reset = false; int r; + if (IS_ERR_OR_NULL(rd)) + return -ENODATA; + if (!reg_is_valid_request(rd->alpha2)) { kfree(rd); return -EINVAL; @@ -3579,6 +3617,7 @@ int set_regdom(const struct ieee80211_regdomain *rd, r = reg_set_rd_core(rd); break; case NL80211_REGDOM_SET_BY_USER: + cfg80211_save_user_regdom(rd); r = reg_set_rd_user(rd, lr); user_reset = true; break; @@ -3601,7 +3640,7 @@ int set_regdom(const struct ieee80211_regdomain *rd, break; default: /* Back to world regulatory in case of errors */ - restore_regulatory_settings(user_reset); + restore_regulatory_settings(user_reset, false); } kfree(rd); @@ -3937,6 +3976,8 @@ void regulatory_exit(void) if (!IS_ERR_OR_NULL(regdb)) kfree(regdb); + if (!IS_ERR_OR_NULL(cfg80211_user_regdom)) + kfree(cfg80211_user_regdom); free_regdb_keyring(); } diff --git a/net/wireless/reg.h b/net/wireless/reg.h index 9ceeb5f3a7cb..504133d76de4 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -5,6 +5,7 @@ /* * Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com> + * Copyright (C) 2019 Intel Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -22,6 +23,7 @@ enum ieee80211_regd_source { REGD_SOURCE_INTERNAL_DB, REGD_SOURCE_CRDA, + REGD_SOURCE_CACHED, }; extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 5123667f4569..287518c6caa4 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -5,6 +5,7 @@ * Copyright 2008 Johannes Berg <johannes@sipsolutions.net> * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2016 Intel Deutschland GmbH + * Copyright (C) 2018-2019 Intel Corporation */ #include <linux/kernel.h> #include <linux/slab.h> @@ -109,6 +110,12 @@ static inline void bss_ref_get(struct cfg80211_registered_device *rdev, pub); bss->refcount++; } + if (bss->pub.transmitted_bss) { + bss = container_of(bss->pub.transmitted_bss, + struct cfg80211_internal_bss, + pub); + bss->refcount++; + } } static inline void bss_ref_put(struct cfg80211_registered_device *rdev, @@ -125,6 +132,18 @@ static inline void bss_ref_put(struct cfg80211_registered_device *rdev, if (hbss->refcount == 0) bss_free(hbss); } + + if (bss->pub.transmitted_bss) { + struct cfg80211_internal_bss *tbss; + + tbss = container_of(bss->pub.transmitted_bss, + struct cfg80211_internal_bss, + pub); + tbss->refcount--; + if (tbss->refcount == 0) + bss_free(tbss); + } + bss->refcount--; if (bss->refcount == 0) bss_free(bss); @@ -150,6 +169,7 @@ static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *rdev, } list_del_init(&bss->list); + list_del_init(&bss->pub.nontrans_list); rb_erase(&bss->rbn, &rdev->bss_tree); rdev->bss_entries--; WARN_ONCE((rdev->bss_entries == 0) ^ list_empty(&rdev->bss_list), @@ -159,6 +179,162 @@ static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *rdev, return true; } +static size_t cfg80211_gen_new_ie(const u8 *ie, size_t ielen, + const u8 *subelement, size_t subie_len, + u8 *new_ie, gfp_t gfp) +{ + u8 *pos, *tmp; + const u8 *tmp_old, *tmp_new; + u8 *sub_copy; + + /* copy subelement as we need to change its content to + * mark an ie after it is processed. + */ + sub_copy = kmalloc(subie_len, gfp); + if (!sub_copy) + return 0; + memcpy(sub_copy, subelement, subie_len); + + pos = &new_ie[0]; + + /* set new ssid */ + tmp_new = cfg80211_find_ie(WLAN_EID_SSID, sub_copy, subie_len); + if (tmp_new) { + memcpy(pos, tmp_new, tmp_new[1] + 2); + pos += (tmp_new[1] + 2); + } + + /* go through IEs in ie (skip SSID) and subelement, + * merge them into new_ie + */ + tmp_old = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen); + tmp_old = (tmp_old) ? tmp_old + tmp_old[1] + 2 : ie; + + while (tmp_old + tmp_old[1] + 2 - ie <= ielen) { + if (tmp_old[0] == 0) { + tmp_old++; + continue; + } + + if (tmp_old[0] == WLAN_EID_EXTENSION) + tmp = (u8 *)cfg80211_find_ext_ie(tmp_old[2], sub_copy, + subie_len); + else + tmp = (u8 *)cfg80211_find_ie(tmp_old[0], sub_copy, + subie_len); + + if (!tmp) { + /* ie in old ie but not in subelement */ + if (tmp_old[0] != WLAN_EID_MULTIPLE_BSSID) { + memcpy(pos, tmp_old, tmp_old[1] + 2); + pos += tmp_old[1] + 2; + } + } else { + /* ie in transmitting ie also in subelement, + * copy from subelement and flag the ie in subelement + * as copied (by setting eid field to WLAN_EID_SSID, + * which is skipped anyway). + * For vendor ie, compare OUI + type + subType to + * determine if they are the same ie. + */ + if (tmp_old[0] == WLAN_EID_VENDOR_SPECIFIC) { + if (!memcmp(tmp_old + 2, tmp + 2, 5)) { + /* same vendor ie, copy from + * subelement + */ + memcpy(pos, tmp, tmp[1] + 2); + pos += tmp[1] + 2; + tmp[0] = WLAN_EID_SSID; + } else { + memcpy(pos, tmp_old, tmp_old[1] + 2); + pos += tmp_old[1] + 2; + } + } else { + /* copy ie from subelement into new ie */ + memcpy(pos, tmp, tmp[1] + 2); + pos += tmp[1] + 2; + tmp[0] = WLAN_EID_SSID; + } + } + + if (tmp_old + tmp_old[1] + 2 - ie == ielen) + break; + + tmp_old += tmp_old[1] + 2; + } + + /* go through subelement again to check if there is any ie not + * copied to new ie, skip ssid, capability, bssid-index ie + */ + tmp_new = sub_copy; + while (tmp_new + tmp_new[1] + 2 - sub_copy <= subie_len) { + if (!(tmp_new[0] == WLAN_EID_NON_TX_BSSID_CAP || + tmp_new[0] == WLAN_EID_SSID || + tmp_new[0] == WLAN_EID_MULTI_BSSID_IDX)) { + memcpy(pos, tmp_new, tmp_new[1] + 2); + pos += tmp_new[1] + 2; + } + if (tmp_new + tmp_new[1] + 2 - sub_copy == subie_len) + break; + tmp_new += tmp_new[1] + 2; + } + + kfree(sub_copy); + return pos - new_ie; +} + +static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, + const u8 *ssid, size_t ssid_len) +{ + const struct cfg80211_bss_ies *ies; + const u8 *ssidie; + + if (bssid && !ether_addr_equal(a->bssid, bssid)) + return false; + + if (!ssid) + return true; + + ies = rcu_access_pointer(a->ies); + if (!ies) + return false; + ssidie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len); + if (!ssidie) + return false; + if (ssidie[1] != ssid_len) + return false; + return memcmp(ssidie + 2, ssid, ssid_len) == 0; +} + +static int +cfg80211_add_nontrans_list(struct cfg80211_bss *trans_bss, + struct cfg80211_bss *nontrans_bss) +{ + const u8 *ssid; + size_t ssid_len; + struct cfg80211_bss *bss = NULL; + + rcu_read_lock(); + ssid = ieee80211_bss_get_ie(nontrans_bss, WLAN_EID_SSID); + if (!ssid) { + rcu_read_unlock(); + return -EINVAL; + } + ssid_len = ssid[1]; + ssid = ssid + 2; + rcu_read_unlock(); + + /* check if nontrans_bss is in the list */ + list_for_each_entry(bss, &trans_bss->nontrans_list, nontrans_list) { + if (is_bss(bss, nontrans_bss->bssid, ssid, ssid_len)) + return 0; + } + + /* add to the list */ + list_add_tail(&nontrans_bss->nontrans_list, &trans_bss->nontrans_list); + return 0; +} + static void __cfg80211_bss_expire(struct cfg80211_registered_device *rdev, unsigned long expire_time) { @@ -480,73 +656,43 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *rdev) __cfg80211_bss_expire(rdev, jiffies - IEEE80211_SCAN_RESULT_EXPIRE); } -const u8 *cfg80211_find_ie_match(u8 eid, const u8 *ies, int len, - const u8 *match, int match_len, - int match_offset) +const struct element * +cfg80211_find_elem_match(u8 eid, const u8 *ies, unsigned int len, + const u8 *match, unsigned int match_len, + unsigned int match_offset) { - /* match_offset can't be smaller than 2, unless match_len is - * zero, in which case match_offset must be zero as well. - */ - if (WARN_ON((match_len && match_offset < 2) || - (!match_len && match_offset))) - return NULL; + const struct element *elem; - while (len >= 2 && len >= ies[1] + 2) { - if ((ies[0] == eid) && - (ies[1] + 2 >= match_offset + match_len) && - !memcmp(ies + match_offset, match, match_len)) - return ies; - - len -= ies[1] + 2; - ies += ies[1] + 2; + for_each_element_id(elem, eid, ies, len) { + if (elem->datalen >= match_offset + match_len && + !memcmp(elem->data + match_offset, match, match_len)) + return elem; } return NULL; } -EXPORT_SYMBOL(cfg80211_find_ie_match); +EXPORT_SYMBOL(cfg80211_find_elem_match); -const u8 *cfg80211_find_vendor_ie(unsigned int oui, int oui_type, - const u8 *ies, int len) +const struct element *cfg80211_find_vendor_elem(unsigned int oui, int oui_type, + const u8 *ies, + unsigned int len) { - const u8 *ie; + const struct element *elem; u8 match[] = { oui >> 16, oui >> 8, oui, oui_type }; int match_len = (oui_type < 0) ? 3 : sizeof(match); if (WARN_ON(oui_type > 0xff)) return NULL; - ie = cfg80211_find_ie_match(WLAN_EID_VENDOR_SPECIFIC, ies, len, - match, match_len, 2); + elem = cfg80211_find_elem_match(WLAN_EID_VENDOR_SPECIFIC, ies, len, + match, match_len, 0); - if (ie && (ie[1] < 4)) + if (!elem || elem->datalen < 4) return NULL; - return ie; -} -EXPORT_SYMBOL(cfg80211_find_vendor_ie); - -static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, - const u8 *ssid, size_t ssid_len) -{ - const struct cfg80211_bss_ies *ies; - const u8 *ssidie; - - if (bssid && !ether_addr_equal(a->bssid, bssid)) - return false; - - if (!ssid) - return true; - - ies = rcu_access_pointer(a->ies); - if (!ies) - return false; - ssidie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len); - if (!ssidie) - return false; - if (ssidie[1] != ssid_len) - return false; - return memcmp(ssidie + 2, ssid, ssid_len) == 0; + return elem; } +EXPORT_SYMBOL(cfg80211_find_vendor_elem); /** * enum bss_compare_mode - BSS compare mode @@ -882,6 +1028,12 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *rdev, return true; } +struct cfg80211_non_tx_bss { + struct cfg80211_bss *tx_bss; + u8 max_bssid_indicator; + u8 bssid_index; +}; + /* Returned bss is reference counted and must be cleaned up appropriately. */ static struct cfg80211_internal_bss * cfg80211_bss_update(struct cfg80211_registered_device *rdev, @@ -985,6 +1137,8 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev, memcpy(found->pub.chain_signal, tmp->pub.chain_signal, IEEE80211_MAX_CHAINS); ether_addr_copy(found->parent_bssid, tmp->parent_bssid); + found->pub.max_bssid_indicator = tmp->pub.max_bssid_indicator; + found->pub.bssid_index = tmp->pub.bssid_index; } else { struct cfg80211_internal_bss *new; struct cfg80211_internal_bss *hidden; @@ -1009,6 +1163,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev, memcpy(new, tmp, sizeof(*new)); new->refcount = 1; INIT_LIST_HEAD(&new->hidden_list); + INIT_LIST_HEAD(&new->pub.nontrans_list); if (rcu_access_pointer(tmp->pub.proberesp_ies)) { hidden = rb_find_bss(rdev, tmp, BSS_CMP_HIDE_ZLEN); @@ -1042,6 +1197,17 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev, goto drop; } + /* This must be before the call to bss_ref_get */ + if (tmp->pub.transmitted_bss) { + struct cfg80211_internal_bss *pbss = + container_of(tmp->pub.transmitted_bss, + struct cfg80211_internal_bss, + pub); + + new->pub.transmitted_bss = tmp->pub.transmitted_bss; + bss_ref_get(rdev, pbss); + } + list_add_tail(&new->list, &rdev->bss_list); rdev->bss_entries++; rb_insert_bss(rdev, new); @@ -1130,14 +1296,16 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen, } /* Returned bss is reference counted and must be cleaned up appropriately. */ -struct cfg80211_bss * -cfg80211_inform_bss_data(struct wiphy *wiphy, - struct cfg80211_inform_bss *data, - enum cfg80211_bss_frame_type ftype, - const u8 *bssid, u64 tsf, u16 capability, - u16 beacon_interval, const u8 *ie, size_t ielen, - gfp_t gfp) +static struct cfg80211_bss * +cfg80211_inform_single_bss_data(struct wiphy *wiphy, + struct cfg80211_inform_bss *data, + enum cfg80211_bss_frame_type ftype, + const u8 *bssid, u64 tsf, u16 capability, + u16 beacon_interval, const u8 *ie, size_t ielen, + struct cfg80211_non_tx_bss *non_tx_data, + gfp_t gfp) { + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct cfg80211_bss_ies *ies; struct ieee80211_channel *channel; struct cfg80211_internal_bss tmp = {}, *res; @@ -1163,6 +1331,11 @@ cfg80211_inform_bss_data(struct wiphy *wiphy, tmp.pub.beacon_interval = beacon_interval; tmp.pub.capability = capability; tmp.ts_boottime = data->boottime_ns; + if (non_tx_data) { + tmp.pub.transmitted_bss = non_tx_data->tx_bss; + tmp.pub.bssid_index = non_tx_data->bssid_index; + tmp.pub.max_bssid_indicator = non_tx_data->max_bssid_indicator; + } /* * If we do not know here whether the IEs are from a Beacon or Probe @@ -1209,19 +1382,247 @@ cfg80211_inform_bss_data(struct wiphy *wiphy, regulatory_hint_found_beacon(wiphy, channel, gfp); } + if (non_tx_data && non_tx_data->tx_bss) { + /* this is a nontransmitting bss, we need to add it to + * transmitting bss' list if it is not there + */ + if (cfg80211_add_nontrans_list(non_tx_data->tx_bss, + &res->pub)) { + if (__cfg80211_unlink_bss(rdev, res)) + rdev->bss_generation++; + } + } + trace_cfg80211_return_bss(&res->pub); /* cfg80211_bss_update gives us a referenced result */ return &res->pub; } -EXPORT_SYMBOL(cfg80211_inform_bss_data); -/* cfg80211_inform_bss_width_frame helper */ +static void cfg80211_parse_mbssid_data(struct wiphy *wiphy, + struct cfg80211_inform_bss *data, + enum cfg80211_bss_frame_type ftype, + const u8 *bssid, u64 tsf, + u16 beacon_interval, const u8 *ie, + size_t ielen, + struct cfg80211_non_tx_bss *non_tx_data, + gfp_t gfp) +{ + const u8 *mbssid_index_ie; + const struct element *elem, *sub; + size_t new_ie_len; + u8 new_bssid[ETH_ALEN]; + u8 *new_ie; + u16 capability; + struct cfg80211_bss *bss; + + if (!non_tx_data) + return; + if (!cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen)) + return; + if (!wiphy->support_mbssid) + return; + if (wiphy->support_only_he_mbssid && + !cfg80211_find_ext_ie(WLAN_EID_EXT_HE_CAPABILITY, ie, ielen)) + return; + + new_ie = kmalloc(IEEE80211_MAX_DATA_LEN, gfp); + if (!new_ie) + return; + + for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, ie, ielen) { + if (elem->datalen < 4) + continue; + for_each_element(sub, elem->data + 1, elem->datalen - 1) { + if (sub->id != 0 || sub->datalen < 4) { + /* not a valid BSS profile */ + continue; + } + + if (sub->data[0] != WLAN_EID_NON_TX_BSSID_CAP || + sub->data[1] != 2) { + /* The first element within the Nontransmitted + * BSSID Profile is not the Nontransmitted + * BSSID Capability element. + */ + continue; + } + + /* found a Nontransmitted BSSID Profile */ + mbssid_index_ie = cfg80211_find_ie + (WLAN_EID_MULTI_BSSID_IDX, + sub->data, sub->datalen); + if (!mbssid_index_ie || mbssid_index_ie[1] < 1 || + mbssid_index_ie[2] == 0) { + /* No valid Multiple BSSID-Index element */ + continue; + } + + non_tx_data->bssid_index = mbssid_index_ie[2]; + non_tx_data->max_bssid_indicator = elem->data[0]; + + cfg80211_gen_new_bssid(bssid, + non_tx_data->max_bssid_indicator, + non_tx_data->bssid_index, + new_bssid); + memset(new_ie, 0, IEEE80211_MAX_DATA_LEN); + new_ie_len = cfg80211_gen_new_ie(ie, ielen, sub->data, + sub->datalen, new_ie, + gfp); + if (!new_ie_len) + continue; + + capability = get_unaligned_le16(sub->data + 2); + bss = cfg80211_inform_single_bss_data(wiphy, data, + ftype, + new_bssid, tsf, + capability, + beacon_interval, + new_ie, + new_ie_len, + non_tx_data, + gfp); + if (!bss) + break; + cfg80211_put_bss(wiphy, bss); + } + } + + kfree(new_ie); +} + struct cfg80211_bss * -cfg80211_inform_bss_frame_data(struct wiphy *wiphy, - struct cfg80211_inform_bss *data, - struct ieee80211_mgmt *mgmt, size_t len, - gfp_t gfp) +cfg80211_inform_bss_data(struct wiphy *wiphy, + struct cfg80211_inform_bss *data, + enum cfg80211_bss_frame_type ftype, + const u8 *bssid, u64 tsf, u16 capability, + u16 beacon_interval, const u8 *ie, size_t ielen, + gfp_t gfp) +{ + struct cfg80211_bss *res; + struct cfg80211_non_tx_bss non_tx_data; + + res = cfg80211_inform_single_bss_data(wiphy, data, ftype, bssid, tsf, + capability, beacon_interval, ie, + ielen, NULL, gfp); + non_tx_data.tx_bss = res; + cfg80211_parse_mbssid_data(wiphy, data, ftype, bssid, tsf, + beacon_interval, ie, ielen, &non_tx_data, + gfp); + return res; +} +EXPORT_SYMBOL(cfg80211_inform_bss_data); + +static void +cfg80211_parse_mbssid_frame_data(struct wiphy *wiphy, + struct cfg80211_inform_bss *data, + struct ieee80211_mgmt *mgmt, size_t len, + struct cfg80211_non_tx_bss *non_tx_data, + gfp_t gfp) +{ + enum cfg80211_bss_frame_type ftype; + const u8 *ie = mgmt->u.probe_resp.variable; + size_t ielen = len - offsetof(struct ieee80211_mgmt, + u.probe_resp.variable); + ftype = ieee80211_is_beacon(mgmt->frame_control) ? + CFG80211_BSS_FTYPE_BEACON : CFG80211_BSS_FTYPE_PRESP; + + cfg80211_parse_mbssid_data(wiphy, data, ftype, mgmt->bssid, + le64_to_cpu(mgmt->u.probe_resp.timestamp), + le16_to_cpu(mgmt->u.probe_resp.beacon_int), + ie, ielen, non_tx_data, gfp); +} + +static void +cfg80211_update_notlisted_nontrans(struct wiphy *wiphy, + struct cfg80211_bss *nontrans_bss, + struct ieee80211_mgmt *mgmt, size_t len, + gfp_t gfp) +{ + u8 *ie, *new_ie, *pos; + const u8 *nontrans_ssid, *trans_ssid, *mbssid; + size_t ielen = len - offsetof(struct ieee80211_mgmt, + u.probe_resp.variable); + size_t new_ie_len; + struct cfg80211_bss_ies *new_ies; + const struct cfg80211_bss_ies *old; + u8 cpy_len; + + ie = mgmt->u.probe_resp.variable; + + new_ie_len = ielen; + trans_ssid = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen); + if (!trans_ssid) + return; + new_ie_len -= trans_ssid[1]; + mbssid = cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen); + if (!mbssid) + return; + new_ie_len -= mbssid[1]; + rcu_read_lock(); + nontrans_ssid = ieee80211_bss_get_ie(nontrans_bss, WLAN_EID_SSID); + if (!nontrans_ssid) { + rcu_read_unlock(); + return; + } + new_ie_len += nontrans_ssid[1]; + rcu_read_unlock(); + + /* generate new ie for nontrans BSS + * 1. replace SSID with nontrans BSS' SSID + * 2. skip MBSSID IE + */ + new_ie = kzalloc(new_ie_len, gfp); + if (!new_ie) + return; + new_ies = kzalloc(sizeof(*new_ies) + new_ie_len, gfp); + if (!new_ies) + goto out_free; + + pos = new_ie; + + /* copy the nontransmitted SSID */ + cpy_len = nontrans_ssid[1] + 2; + memcpy(pos, nontrans_ssid, cpy_len); + pos += cpy_len; + /* copy the IEs between SSID and MBSSID */ + cpy_len = trans_ssid[1] + 2; + memcpy(pos, (trans_ssid + cpy_len), (mbssid - (trans_ssid + cpy_len))); + pos += (mbssid - (trans_ssid + cpy_len)); + /* copy the IEs after MBSSID */ + cpy_len = mbssid[1] + 2; + memcpy(pos, mbssid + cpy_len, ((ie + ielen) - (mbssid + cpy_len))); + + /* update ie */ + new_ies->len = new_ie_len; + new_ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); + new_ies->from_beacon = ieee80211_is_beacon(mgmt->frame_control); + memcpy(new_ies->data, new_ie, new_ie_len); + if (ieee80211_is_probe_resp(mgmt->frame_control)) { + old = rcu_access_pointer(nontrans_bss->proberesp_ies); + rcu_assign_pointer(nontrans_bss->proberesp_ies, new_ies); + rcu_assign_pointer(nontrans_bss->ies, new_ies); + if (old) + kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head); + } else { + old = rcu_access_pointer(nontrans_bss->beacon_ies); + rcu_assign_pointer(nontrans_bss->beacon_ies, new_ies); + rcu_assign_pointer(nontrans_bss->ies, new_ies); + if (old) + kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head); + } + +out_free: + kfree(new_ie); +} + +/* cfg80211_inform_bss_width_frame helper */ +static struct cfg80211_bss * +cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy, + struct cfg80211_inform_bss *data, + struct ieee80211_mgmt *mgmt, size_t len, + struct cfg80211_non_tx_bss *non_tx_data, + gfp_t gfp) { struct cfg80211_internal_bss tmp = {}, *res; struct cfg80211_bss_ies *ies; @@ -1279,6 +1680,11 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy, tmp.pub.chains = data->chains; memcpy(tmp.pub.chain_signal, data->chain_signal, IEEE80211_MAX_CHAINS); ether_addr_copy(tmp.parent_bssid, data->parent_bssid); + if (non_tx_data) { + tmp.pub.transmitted_bss = non_tx_data->tx_bss; + tmp.pub.bssid_index = non_tx_data->bssid_index; + tmp.pub.max_bssid_indicator = non_tx_data->max_bssid_indicator; + } signal_valid = abs(data->chan->center_freq - channel->center_freq) <= wiphy->max_adj_channel_rssi_comp; @@ -1300,6 +1706,53 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy, /* cfg80211_bss_update gives us a referenced result */ return &res->pub; } + +struct cfg80211_bss * +cfg80211_inform_bss_frame_data(struct wiphy *wiphy, + struct cfg80211_inform_bss *data, + struct ieee80211_mgmt *mgmt, size_t len, + gfp_t gfp) +{ + struct cfg80211_bss *res, *tmp_bss; + const u8 *ie = mgmt->u.probe_resp.variable; + const struct cfg80211_bss_ies *ies1, *ies2; + size_t ielen = len - offsetof(struct ieee80211_mgmt, + u.probe_resp.variable); + struct cfg80211_non_tx_bss non_tx_data; + + res = cfg80211_inform_single_bss_frame_data(wiphy, data, mgmt, + len, NULL, gfp); + if (!res || !wiphy->support_mbssid || + !cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen)) + return res; + if (wiphy->support_only_he_mbssid && + !cfg80211_find_ext_ie(WLAN_EID_EXT_HE_CAPABILITY, ie, ielen)) + return res; + + non_tx_data.tx_bss = res; + /* process each non-transmitting bss */ + cfg80211_parse_mbssid_frame_data(wiphy, data, mgmt, len, + &non_tx_data, gfp); + + /* check if the res has other nontransmitting bss which is not + * in MBSSID IE + */ + ies1 = rcu_access_pointer(res->ies); + + /* go through nontrans_list, if the timestamp of the BSS is + * earlier than the timestamp of the transmitting BSS then + * update it + */ + list_for_each_entry(tmp_bss, &res->nontrans_list, + nontrans_list) { + ies2 = rcu_access_pointer(tmp_bss->ies); + if (ies2->tsf < ies1->tsf) + cfg80211_update_notlisted_nontrans(wiphy, tmp_bss, + mgmt, len, gfp); + } + + return res; +} EXPORT_SYMBOL(cfg80211_inform_bss_frame_data); void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) @@ -1337,7 +1790,8 @@ EXPORT_SYMBOL(cfg80211_put_bss); void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); - struct cfg80211_internal_bss *bss; + struct cfg80211_internal_bss *bss, *tmp1; + struct cfg80211_bss *nontrans_bss, *tmp; if (WARN_ON(!pub)) return; @@ -1345,10 +1799,21 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) bss = container_of(pub, struct cfg80211_internal_bss, pub); spin_lock_bh(&rdev->bss_lock); - if (!list_empty(&bss->list)) { - if (__cfg80211_unlink_bss(rdev, bss)) + if (list_empty(&bss->list)) + goto out; + + list_for_each_entry_safe(nontrans_bss, tmp, + &pub->nontrans_list, + nontrans_list) { + tmp1 = container_of(nontrans_bss, + struct cfg80211_internal_bss, pub); + if (__cfg80211_unlink_bss(rdev, tmp1)) rdev->bss_generation++; } + + if (__cfg80211_unlink_bss(rdev, bss)) + rdev->bss_generation++; +out: spin_unlock_bh(&rdev->bss_lock); } EXPORT_SYMBOL(cfg80211_unlink_bss); diff --git a/net/wireless/util.c b/net/wireless/util.c index ec30e3732c7b..e4b8db5e81ec 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -789,7 +789,7 @@ out: } EXPORT_SYMBOL(cfg80211_classify8021d); -const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie) +const struct element *ieee80211_bss_get_elem(struct cfg80211_bss *bss, u8 id) { const struct cfg80211_bss_ies *ies; @@ -797,9 +797,9 @@ const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie) if (!ies) return NULL; - return cfg80211_find_ie(ie, ies->data, ies->len); + return cfg80211_find_elem(id, ies->data, ies->len); } -EXPORT_SYMBOL(ieee80211_bss_get_ie); +EXPORT_SYMBOL(ieee80211_bss_get_elem); void cfg80211_upload_connect_keys(struct wireless_dev *wdev) { diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 06943d9c9835..d522787c7354 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -1337,6 +1337,7 @@ static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev) wstats.qual.qual = sig + 110; break; } + /* fall through */ case CFG80211_SIGNAL_TYPE_UNSPEC: if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_SIGNAL)) { wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED; @@ -1345,6 +1346,7 @@ static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev) wstats.qual.qual = sinfo.signal; break; } + /* fall through */ default: wstats.qual.updated |= IW_QUAL_LEVEL_INVALID; wstats.qual.updated |= IW_QUAL_QUAL_INVALID; |