|
@@ -59,13 +59,13 @@ enum nl80211_multicast_groups {
|
|
|
};
|
|
|
|
|
|
static const struct genl_multicast_group nl80211_mcgrps[] = {
|
|
|
- [NL80211_MCGRP_CONFIG] = { .name = "config", },
|
|
|
- [NL80211_MCGRP_SCAN] = { .name = "scan", },
|
|
|
- [NL80211_MCGRP_REGULATORY] = { .name = "regulatory", },
|
|
|
- [NL80211_MCGRP_MLME] = { .name = "mlme", },
|
|
|
- [NL80211_MCGRP_VENDOR] = { .name = "vendor", },
|
|
|
+ [NL80211_MCGRP_CONFIG] = { .name = NL80211_MULTICAST_GROUP_CONFIG },
|
|
|
+ [NL80211_MCGRP_SCAN] = { .name = NL80211_MULTICAST_GROUP_SCAN },
|
|
|
+ [NL80211_MCGRP_REGULATORY] = { .name = NL80211_MULTICAST_GROUP_REG },
|
|
|
+ [NL80211_MCGRP_MLME] = { .name = NL80211_MULTICAST_GROUP_MLME },
|
|
|
+ [NL80211_MCGRP_VENDOR] = { .name = NL80211_MULTICAST_GROUP_VENDOR },
|
|
|
#ifdef CONFIG_NL80211_TESTMODE
|
|
|
- [NL80211_MCGRP_TESTMODE] = { .name = "testmode", }
|
|
|
+ [NL80211_MCGRP_TESTMODE] = { .name = NL80211_MULTICAST_GROUP_TESTMODE }
|
|
|
#endif
|
|
|
};
|
|
|
|
|
@@ -396,6 +396,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
|
|
|
[NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 },
|
|
|
[NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 },
|
|
|
[NL80211_ATTR_MAC_MASK] = { .len = ETH_ALEN },
|
|
|
+ [NL80211_ATTR_WIPHY_SELF_MANAGED_REG] = { .type = NLA_FLAG },
|
|
|
};
|
|
|
|
|
|
/* policy for the key attributes */
|
|
@@ -1087,6 +1088,11 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
|
|
|
return -ENOBUFS;
|
|
|
}
|
|
|
|
|
|
+ if ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_NET_DETECT) &&
|
|
|
+ nla_put_u32(msg, NL80211_WOWLAN_TRIG_NET_DETECT,
|
|
|
+ rdev->wiphy.wowlan->max_nd_match_sets))
|
|
|
+ return -ENOBUFS;
|
|
|
+
|
|
|
if (large && nl80211_send_wowlan_tcp_caps(rdev, msg))
|
|
|
return -ENOBUFS;
|
|
|
|
|
@@ -1701,6 +1707,15 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
|
|
|
rdev->wiphy.max_num_csa_counters))
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
+ if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
|
|
|
+ nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG))
|
|
|
+ goto nla_put_failure;
|
|
|
+
|
|
|
+ if (nla_put(msg, NL80211_ATTR_EXT_FEATURES,
|
|
|
+ sizeof(rdev->wiphy.ext_features),
|
|
|
+ rdev->wiphy.ext_features))
|
|
|
+ goto nla_put_failure;
|
|
|
+
|
|
|
/* done */
|
|
|
state->split_start = 0;
|
|
|
break;
|
|
@@ -3563,6 +3578,7 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
|
|
|
struct nlattr *rate;
|
|
|
u32 bitrate;
|
|
|
u16 bitrate_compat;
|
|
|
+ enum nl80211_attrs rate_flg;
|
|
|
|
|
|
rate = nla_nest_start(msg, attr);
|
|
|
if (!rate)
|
|
@@ -3579,12 +3595,36 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
|
|
|
nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat))
|
|
|
return false;
|
|
|
|
|
|
+ switch (info->bw) {
|
|
|
+ case RATE_INFO_BW_5:
|
|
|
+ rate_flg = NL80211_RATE_INFO_5_MHZ_WIDTH;
|
|
|
+ break;
|
|
|
+ case RATE_INFO_BW_10:
|
|
|
+ rate_flg = NL80211_RATE_INFO_10_MHZ_WIDTH;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ WARN_ON(1);
|
|
|
+ /* fall through */
|
|
|
+ case RATE_INFO_BW_20:
|
|
|
+ rate_flg = 0;
|
|
|
+ break;
|
|
|
+ case RATE_INFO_BW_40:
|
|
|
+ rate_flg = NL80211_RATE_INFO_40_MHZ_WIDTH;
|
|
|
+ break;
|
|
|
+ case RATE_INFO_BW_80:
|
|
|
+ rate_flg = NL80211_RATE_INFO_80_MHZ_WIDTH;
|
|
|
+ break;
|
|
|
+ case RATE_INFO_BW_160:
|
|
|
+ rate_flg = NL80211_RATE_INFO_160_MHZ_WIDTH;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (rate_flg && nla_put_flag(msg, rate_flg))
|
|
|
+ return false;
|
|
|
+
|
|
|
if (info->flags & RATE_INFO_FLAGS_MCS) {
|
|
|
if (nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs))
|
|
|
return false;
|
|
|
- if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH &&
|
|
|
- nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH))
|
|
|
- return false;
|
|
|
if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
|
|
|
nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
|
|
|
return false;
|
|
@@ -3593,18 +3633,6 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
|
|
|
return false;
|
|
|
if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_NSS, info->nss))
|
|
|
return false;
|
|
|
- if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH &&
|
|
|
- nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH))
|
|
|
- return false;
|
|
|
- if (info->flags & RATE_INFO_FLAGS_80_MHZ_WIDTH &&
|
|
|
- nla_put_flag(msg, NL80211_RATE_INFO_80_MHZ_WIDTH))
|
|
|
- return false;
|
|
|
- if (info->flags & RATE_INFO_FLAGS_80P80_MHZ_WIDTH &&
|
|
|
- nla_put_flag(msg, NL80211_RATE_INFO_80P80_MHZ_WIDTH))
|
|
|
- return false;
|
|
|
- if (info->flags & RATE_INFO_FLAGS_160_MHZ_WIDTH &&
|
|
|
- nla_put_flag(msg, NL80211_RATE_INFO_160_MHZ_WIDTH))
|
|
|
- return false;
|
|
|
if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
|
|
|
nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
|
|
|
return false;
|
|
@@ -3640,8 +3668,8 @@ static bool nl80211_put_signal(struct sk_buff *msg, u8 mask, s8 *signal,
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
|
|
|
- int flags,
|
|
|
+static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
|
|
|
+ u32 seq, int flags,
|
|
|
struct cfg80211_registered_device *rdev,
|
|
|
struct net_device *dev,
|
|
|
const u8 *mac_addr, struct station_info *sinfo)
|
|
@@ -3649,7 +3677,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
|
|
|
void *hdr;
|
|
|
struct nlattr *sinfoattr, *bss_param;
|
|
|
|
|
|
- hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_STATION);
|
|
|
+ hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
|
|
|
if (!hdr)
|
|
|
return -1;
|
|
|
|
|
@@ -3661,115 +3689,77 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
|
|
|
sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
|
|
|
if (!sinfoattr)
|
|
|
goto nla_put_failure;
|
|
|
- if ((sinfo->filled & STATION_INFO_CONNECTED_TIME) &&
|
|
|
- nla_put_u32(msg, NL80211_STA_INFO_CONNECTED_TIME,
|
|
|
- sinfo->connected_time))
|
|
|
- goto nla_put_failure;
|
|
|
- if ((sinfo->filled & STATION_INFO_INACTIVE_TIME) &&
|
|
|
- nla_put_u32(msg, NL80211_STA_INFO_INACTIVE_TIME,
|
|
|
- sinfo->inactive_time))
|
|
|
- goto nla_put_failure;
|
|
|
- if ((sinfo->filled & (STATION_INFO_RX_BYTES |
|
|
|
- STATION_INFO_RX_BYTES64)) &&
|
|
|
+
|
|
|
+#define PUT_SINFO(attr, memb, type) do { \
|
|
|
+ if (sinfo->filled & BIT(NL80211_STA_INFO_ ## attr) && \
|
|
|
+ nla_put_ ## type(msg, NL80211_STA_INFO_ ## attr, \
|
|
|
+ sinfo->memb)) \
|
|
|
+ goto nla_put_failure; \
|
|
|
+ } while (0)
|
|
|
+
|
|
|
+ PUT_SINFO(CONNECTED_TIME, connected_time, u32);
|
|
|
+ PUT_SINFO(INACTIVE_TIME, inactive_time, u32);
|
|
|
+
|
|
|
+ if (sinfo->filled & (BIT(NL80211_STA_INFO_RX_BYTES) |
|
|
|
+ BIT(NL80211_STA_INFO_RX_BYTES64)) &&
|
|
|
nla_put_u32(msg, NL80211_STA_INFO_RX_BYTES,
|
|
|
(u32)sinfo->rx_bytes))
|
|
|
goto nla_put_failure;
|
|
|
- if ((sinfo->filled & (STATION_INFO_TX_BYTES |
|
|
|
- STATION_INFO_TX_BYTES64)) &&
|
|
|
+
|
|
|
+ if (sinfo->filled & (BIT(NL80211_STA_INFO_TX_BYTES) |
|
|
|
+ BIT(NL80211_STA_INFO_TX_BYTES64)) &&
|
|
|
nla_put_u32(msg, NL80211_STA_INFO_TX_BYTES,
|
|
|
(u32)sinfo->tx_bytes))
|
|
|
goto nla_put_failure;
|
|
|
- if ((sinfo->filled & STATION_INFO_RX_BYTES64) &&
|
|
|
- nla_put_u64(msg, NL80211_STA_INFO_RX_BYTES64,
|
|
|
- sinfo->rx_bytes))
|
|
|
- goto nla_put_failure;
|
|
|
- if ((sinfo->filled & STATION_INFO_TX_BYTES64) &&
|
|
|
- nla_put_u64(msg, NL80211_STA_INFO_TX_BYTES64,
|
|
|
- sinfo->tx_bytes))
|
|
|
- goto nla_put_failure;
|
|
|
- if ((sinfo->filled & STATION_INFO_LLID) &&
|
|
|
- nla_put_u16(msg, NL80211_STA_INFO_LLID, sinfo->llid))
|
|
|
- goto nla_put_failure;
|
|
|
- if ((sinfo->filled & STATION_INFO_PLID) &&
|
|
|
- nla_put_u16(msg, NL80211_STA_INFO_PLID, sinfo->plid))
|
|
|
- goto nla_put_failure;
|
|
|
- if ((sinfo->filled & STATION_INFO_PLINK_STATE) &&
|
|
|
- nla_put_u8(msg, NL80211_STA_INFO_PLINK_STATE,
|
|
|
- sinfo->plink_state))
|
|
|
- goto nla_put_failure;
|
|
|
+
|
|
|
+ PUT_SINFO(RX_BYTES64, rx_bytes, u64);
|
|
|
+ PUT_SINFO(TX_BYTES64, tx_bytes, u64);
|
|
|
+ PUT_SINFO(LLID, llid, u16);
|
|
|
+ PUT_SINFO(PLID, plid, u16);
|
|
|
+ PUT_SINFO(PLINK_STATE, plink_state, u8);
|
|
|
+
|
|
|
switch (rdev->wiphy.signal_type) {
|
|
|
case CFG80211_SIGNAL_TYPE_MBM:
|
|
|
- if ((sinfo->filled & STATION_INFO_SIGNAL) &&
|
|
|
- nla_put_u8(msg, NL80211_STA_INFO_SIGNAL,
|
|
|
- sinfo->signal))
|
|
|
- goto nla_put_failure;
|
|
|
- if ((sinfo->filled & STATION_INFO_SIGNAL_AVG) &&
|
|
|
- nla_put_u8(msg, NL80211_STA_INFO_SIGNAL_AVG,
|
|
|
- sinfo->signal_avg))
|
|
|
- goto nla_put_failure;
|
|
|
+ PUT_SINFO(SIGNAL, signal, u8);
|
|
|
+ PUT_SINFO(SIGNAL_AVG, signal_avg, u8);
|
|
|
break;
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
- if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL) {
|
|
|
+ if (sinfo->filled & BIT(NL80211_STA_INFO_CHAIN_SIGNAL)) {
|
|
|
if (!nl80211_put_signal(msg, sinfo->chains,
|
|
|
sinfo->chain_signal,
|
|
|
NL80211_STA_INFO_CHAIN_SIGNAL))
|
|
|
goto nla_put_failure;
|
|
|
}
|
|
|
- if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL_AVG) {
|
|
|
+ if (sinfo->filled & BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) {
|
|
|
if (!nl80211_put_signal(msg, sinfo->chains,
|
|
|
sinfo->chain_signal_avg,
|
|
|
NL80211_STA_INFO_CHAIN_SIGNAL_AVG))
|
|
|
goto nla_put_failure;
|
|
|
}
|
|
|
- if (sinfo->filled & STATION_INFO_TX_BITRATE) {
|
|
|
+ if (sinfo->filled & BIT(NL80211_STA_INFO_TX_BITRATE)) {
|
|
|
if (!nl80211_put_sta_rate(msg, &sinfo->txrate,
|
|
|
NL80211_STA_INFO_TX_BITRATE))
|
|
|
goto nla_put_failure;
|
|
|
}
|
|
|
- if (sinfo->filled & STATION_INFO_RX_BITRATE) {
|
|
|
+ if (sinfo->filled & BIT(NL80211_STA_INFO_RX_BITRATE)) {
|
|
|
if (!nl80211_put_sta_rate(msg, &sinfo->rxrate,
|
|
|
NL80211_STA_INFO_RX_BITRATE))
|
|
|
goto nla_put_failure;
|
|
|
}
|
|
|
- if ((sinfo->filled & STATION_INFO_RX_PACKETS) &&
|
|
|
- nla_put_u32(msg, NL80211_STA_INFO_RX_PACKETS,
|
|
|
- sinfo->rx_packets))
|
|
|
- goto nla_put_failure;
|
|
|
- if ((sinfo->filled & STATION_INFO_TX_PACKETS) &&
|
|
|
- nla_put_u32(msg, NL80211_STA_INFO_TX_PACKETS,
|
|
|
- sinfo->tx_packets))
|
|
|
- goto nla_put_failure;
|
|
|
- if ((sinfo->filled & STATION_INFO_TX_RETRIES) &&
|
|
|
- nla_put_u32(msg, NL80211_STA_INFO_TX_RETRIES,
|
|
|
- sinfo->tx_retries))
|
|
|
- goto nla_put_failure;
|
|
|
- if ((sinfo->filled & STATION_INFO_TX_FAILED) &&
|
|
|
- nla_put_u32(msg, NL80211_STA_INFO_TX_FAILED,
|
|
|
- sinfo->tx_failed))
|
|
|
- goto nla_put_failure;
|
|
|
- if ((sinfo->filled & STATION_INFO_EXPECTED_THROUGHPUT) &&
|
|
|
- nla_put_u32(msg, NL80211_STA_INFO_EXPECTED_THROUGHPUT,
|
|
|
- sinfo->expected_throughput))
|
|
|
- goto nla_put_failure;
|
|
|
- if ((sinfo->filled & STATION_INFO_BEACON_LOSS_COUNT) &&
|
|
|
- nla_put_u32(msg, NL80211_STA_INFO_BEACON_LOSS,
|
|
|
- sinfo->beacon_loss_count))
|
|
|
- goto nla_put_failure;
|
|
|
- if ((sinfo->filled & STATION_INFO_LOCAL_PM) &&
|
|
|
- nla_put_u32(msg, NL80211_STA_INFO_LOCAL_PM,
|
|
|
- sinfo->local_pm))
|
|
|
- goto nla_put_failure;
|
|
|
- if ((sinfo->filled & STATION_INFO_PEER_PM) &&
|
|
|
- nla_put_u32(msg, NL80211_STA_INFO_PEER_PM,
|
|
|
- sinfo->peer_pm))
|
|
|
- goto nla_put_failure;
|
|
|
- if ((sinfo->filled & STATION_INFO_NONPEER_PM) &&
|
|
|
- nla_put_u32(msg, NL80211_STA_INFO_NONPEER_PM,
|
|
|
- sinfo->nonpeer_pm))
|
|
|
- goto nla_put_failure;
|
|
|
- if (sinfo->filled & STATION_INFO_BSS_PARAM) {
|
|
|
+
|
|
|
+ PUT_SINFO(RX_PACKETS, rx_packets, u32);
|
|
|
+ PUT_SINFO(TX_PACKETS, tx_packets, u32);
|
|
|
+ PUT_SINFO(TX_RETRIES, tx_retries, u32);
|
|
|
+ PUT_SINFO(TX_FAILED, tx_failed, u32);
|
|
|
+ PUT_SINFO(EXPECTED_THROUGHPUT, expected_throughput, u32);
|
|
|
+ PUT_SINFO(BEACON_LOSS, beacon_loss_count, u32);
|
|
|
+ PUT_SINFO(LOCAL_PM, local_pm, u32);
|
|
|
+ PUT_SINFO(PEER_PM, peer_pm, u32);
|
|
|
+ PUT_SINFO(NONPEER_PM, nonpeer_pm, u32);
|
|
|
+
|
|
|
+ if (sinfo->filled & BIT(NL80211_STA_INFO_BSS_PARAM)) {
|
|
|
bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
|
|
|
if (!bss_param)
|
|
|
goto nla_put_failure;
|
|
@@ -3788,18 +3778,62 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
|
|
|
|
|
|
nla_nest_end(msg, bss_param);
|
|
|
}
|
|
|
- if ((sinfo->filled & STATION_INFO_STA_FLAGS) &&
|
|
|
+ if ((sinfo->filled & BIT(NL80211_STA_INFO_STA_FLAGS)) &&
|
|
|
nla_put(msg, NL80211_STA_INFO_STA_FLAGS,
|
|
|
sizeof(struct nl80211_sta_flag_update),
|
|
|
&sinfo->sta_flags))
|
|
|
goto nla_put_failure;
|
|
|
- if ((sinfo->filled & STATION_INFO_T_OFFSET) &&
|
|
|
- nla_put_u64(msg, NL80211_STA_INFO_T_OFFSET,
|
|
|
- sinfo->t_offset))
|
|
|
- goto nla_put_failure;
|
|
|
+
|
|
|
+ PUT_SINFO(T_OFFSET, t_offset, u64);
|
|
|
+ PUT_SINFO(RX_DROP_MISC, rx_dropped_misc, u64);
|
|
|
+ PUT_SINFO(BEACON_RX, rx_beacon, u64);
|
|
|
+ PUT_SINFO(BEACON_SIGNAL_AVG, rx_beacon_signal_avg, u8);
|
|
|
+
|
|
|
+#undef PUT_SINFO
|
|
|
+
|
|
|
+ if (sinfo->filled & BIT(NL80211_STA_INFO_TID_STATS)) {
|
|
|
+ struct nlattr *tidsattr;
|
|
|
+ int tid;
|
|
|
+
|
|
|
+ tidsattr = nla_nest_start(msg, NL80211_STA_INFO_TID_STATS);
|
|
|
+ if (!tidsattr)
|
|
|
+ goto nla_put_failure;
|
|
|
+
|
|
|
+ for (tid = 0; tid < IEEE80211_NUM_TIDS + 1; tid++) {
|
|
|
+ struct cfg80211_tid_stats *tidstats;
|
|
|
+ struct nlattr *tidattr;
|
|
|
+
|
|
|
+ tidstats = &sinfo->pertid[tid];
|
|
|
+
|
|
|
+ if (!tidstats->filled)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ tidattr = nla_nest_start(msg, tid + 1);
|
|
|
+ if (!tidattr)
|
|
|
+ goto nla_put_failure;
|
|
|
+
|
|
|
+#define PUT_TIDVAL(attr, memb, type) do { \
|
|
|
+ if (tidstats->filled & BIT(NL80211_TID_STATS_ ## attr) && \
|
|
|
+ nla_put_ ## type(msg, NL80211_TID_STATS_ ## attr, \
|
|
|
+ tidstats->memb)) \
|
|
|
+ goto nla_put_failure; \
|
|
|
+ } while (0)
|
|
|
+
|
|
|
+ PUT_TIDVAL(RX_MSDU, rx_msdu, u64);
|
|
|
+ PUT_TIDVAL(TX_MSDU, tx_msdu, u64);
|
|
|
+ PUT_TIDVAL(TX_MSDU_RETRIES, tx_msdu_retries, u64);
|
|
|
+ PUT_TIDVAL(TX_MSDU_FAILED, tx_msdu_failed, u64);
|
|
|
+
|
|
|
+#undef PUT_TIDVAL
|
|
|
+ nla_nest_end(msg, tidattr);
|
|
|
+ }
|
|
|
+
|
|
|
+ nla_nest_end(msg, tidsattr);
|
|
|
+ }
|
|
|
+
|
|
|
nla_nest_end(msg, sinfoattr);
|
|
|
|
|
|
- if ((sinfo->filled & STATION_INFO_ASSOC_REQ_IES) &&
|
|
|
+ if (sinfo->assoc_req_ies_len &&
|
|
|
nla_put(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len,
|
|
|
sinfo->assoc_req_ies))
|
|
|
goto nla_put_failure;
|
|
@@ -3844,7 +3878,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
|
|
|
if (err)
|
|
|
goto out_err;
|
|
|
|
|
|
- if (nl80211_send_station(skb,
|
|
|
+ if (nl80211_send_station(skb, NL80211_CMD_NEW_STATION,
|
|
|
NETLINK_CB(cb->skb).portid,
|
|
|
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
|
|
rdev, wdev->netdev, mac_addr,
|
|
@@ -3891,7 +3925,8 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
|
|
|
if (!msg)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
- if (nl80211_send_station(msg, info->snd_portid, info->snd_seq, 0,
|
|
|
+ if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION,
|
|
|
+ info->snd_portid, info->snd_seq, 0,
|
|
|
rdev, dev, mac_addr, &sinfo) < 0) {
|
|
|
nlmsg_free(msg);
|
|
|
return -ENOBUFS;
|
|
@@ -5327,42 +5362,20 @@ static int nl80211_update_mesh_config(struct sk_buff *skb,
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
-static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
|
|
|
+static int nl80211_put_regdom(const struct ieee80211_regdomain *regdom,
|
|
|
+ struct sk_buff *msg)
|
|
|
{
|
|
|
- const struct ieee80211_regdomain *regdom;
|
|
|
- struct sk_buff *msg;
|
|
|
- void *hdr = NULL;
|
|
|
struct nlattr *nl_reg_rules;
|
|
|
unsigned int i;
|
|
|
|
|
|
- if (!cfg80211_regdomain)
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
- if (!msg)
|
|
|
- return -ENOBUFS;
|
|
|
-
|
|
|
- hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
|
|
|
- NL80211_CMD_GET_REG);
|
|
|
- if (!hdr)
|
|
|
- goto put_failure;
|
|
|
-
|
|
|
- if (reg_last_request_cell_base() &&
|
|
|
- nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
|
|
|
- NL80211_USER_REG_HINT_CELL_BASE))
|
|
|
- goto nla_put_failure;
|
|
|
-
|
|
|
- rcu_read_lock();
|
|
|
- regdom = rcu_dereference(cfg80211_regdomain);
|
|
|
-
|
|
|
if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, regdom->alpha2) ||
|
|
|
(regdom->dfs_region &&
|
|
|
nla_put_u8(msg, NL80211_ATTR_DFS_REGION, regdom->dfs_region)))
|
|
|
- goto nla_put_failure_rcu;
|
|
|
+ goto nla_put_failure;
|
|
|
|
|
|
nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
|
|
|
if (!nl_reg_rules)
|
|
|
- goto nla_put_failure_rcu;
|
|
|
+ goto nla_put_failure;
|
|
|
|
|
|
for (i = 0; i < regdom->n_reg_rules; i++) {
|
|
|
struct nlattr *nl_reg_rule;
|
|
@@ -5377,7 +5390,7 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
|
|
nl_reg_rule = nla_nest_start(msg, i);
|
|
|
if (!nl_reg_rule)
|
|
|
- goto nla_put_failure_rcu;
|
|
|
+ goto nla_put_failure;
|
|
|
|
|
|
max_bandwidth_khz = freq_range->max_bandwidth_khz;
|
|
|
if (!max_bandwidth_khz)
|
|
@@ -5398,13 +5411,74 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
|
|
|
power_rule->max_eirp) ||
|
|
|
nla_put_u32(msg, NL80211_ATTR_DFS_CAC_TIME,
|
|
|
reg_rule->dfs_cac_ms))
|
|
|
- goto nla_put_failure_rcu;
|
|
|
+ goto nla_put_failure;
|
|
|
|
|
|
nla_nest_end(msg, nl_reg_rule);
|
|
|
}
|
|
|
- rcu_read_unlock();
|
|
|
|
|
|
nla_nest_end(msg, nl_reg_rules);
|
|
|
+ return 0;
|
|
|
+
|
|
|
+nla_put_failure:
|
|
|
+ return -EMSGSIZE;
|
|
|
+}
|
|
|
+
|
|
|
+static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info)
|
|
|
+{
|
|
|
+ const struct ieee80211_regdomain *regdom = NULL;
|
|
|
+ struct cfg80211_registered_device *rdev;
|
|
|
+ struct wiphy *wiphy = NULL;
|
|
|
+ struct sk_buff *msg;
|
|
|
+ void *hdr;
|
|
|
+
|
|
|
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
+ if (!msg)
|
|
|
+ return -ENOBUFS;
|
|
|
+
|
|
|
+ hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
|
|
|
+ NL80211_CMD_GET_REG);
|
|
|
+ if (!hdr)
|
|
|
+ goto put_failure;
|
|
|
+
|
|
|
+ if (info->attrs[NL80211_ATTR_WIPHY]) {
|
|
|
+ bool self_managed;
|
|
|
+
|
|
|
+ rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
|
|
|
+ if (IS_ERR(rdev)) {
|
|
|
+ nlmsg_free(msg);
|
|
|
+ return PTR_ERR(rdev);
|
|
|
+ }
|
|
|
+
|
|
|
+ wiphy = &rdev->wiphy;
|
|
|
+ self_managed = wiphy->regulatory_flags &
|
|
|
+ REGULATORY_WIPHY_SELF_MANAGED;
|
|
|
+ regdom = get_wiphy_regdom(wiphy);
|
|
|
+
|
|
|
+ /* a self-managed-reg device must have a private regdom */
|
|
|
+ if (WARN_ON(!regdom && self_managed)) {
|
|
|
+ nlmsg_free(msg);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (regdom &&
|
|
|
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)))
|
|
|
+ goto nla_put_failure;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!wiphy && reg_last_request_cell_base() &&
|
|
|
+ nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
|
|
|
+ NL80211_USER_REG_HINT_CELL_BASE))
|
|
|
+ goto nla_put_failure;
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+
|
|
|
+ if (!regdom)
|
|
|
+ regdom = rcu_dereference(cfg80211_regdomain);
|
|
|
+
|
|
|
+ if (nl80211_put_regdom(regdom, msg))
|
|
|
+ goto nla_put_failure_rcu;
|
|
|
+
|
|
|
+ rcu_read_unlock();
|
|
|
|
|
|
genlmsg_end(msg, hdr);
|
|
|
return genlmsg_reply(msg, info);
|
|
@@ -5418,6 +5492,83 @@ put_failure:
|
|
|
return -EMSGSIZE;
|
|
|
}
|
|
|
|
|
|
+static int nl80211_send_regdom(struct sk_buff *msg, struct netlink_callback *cb,
|
|
|
+ u32 seq, int flags, struct wiphy *wiphy,
|
|
|
+ const struct ieee80211_regdomain *regdom)
|
|
|
+{
|
|
|
+ void *hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).portid, seq, flags,
|
|
|
+ NL80211_CMD_GET_REG);
|
|
|
+
|
|
|
+ if (!hdr)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ genl_dump_check_consistent(cb, hdr, &nl80211_fam);
|
|
|
+
|
|
|
+ if (nl80211_put_regdom(regdom, msg))
|
|
|
+ goto nla_put_failure;
|
|
|
+
|
|
|
+ if (!wiphy && reg_last_request_cell_base() &&
|
|
|
+ nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
|
|
|
+ NL80211_USER_REG_HINT_CELL_BASE))
|
|
|
+ goto nla_put_failure;
|
|
|
+
|
|
|
+ if (wiphy &&
|
|
|
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)))
|
|
|
+ goto nla_put_failure;
|
|
|
+
|
|
|
+ if (wiphy && wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
|
|
|
+ nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG))
|
|
|
+ goto nla_put_failure;
|
|
|
+
|
|
|
+ return genlmsg_end(msg, hdr);
|
|
|
+
|
|
|
+nla_put_failure:
|
|
|
+ genlmsg_cancel(msg, hdr);
|
|
|
+ return -EMSGSIZE;
|
|
|
+}
|
|
|
+
|
|
|
+static int nl80211_get_reg_dump(struct sk_buff *skb,
|
|
|
+ struct netlink_callback *cb)
|
|
|
+{
|
|
|
+ const struct ieee80211_regdomain *regdom = NULL;
|
|
|
+ struct cfg80211_registered_device *rdev;
|
|
|
+ int err, reg_idx, start = cb->args[2];
|
|
|
+
|
|
|
+ rtnl_lock();
|
|
|
+
|
|
|
+ if (cfg80211_regdomain && start == 0) {
|
|
|
+ err = nl80211_send_regdom(skb, cb, cb->nlh->nlmsg_seq,
|
|
|
+ NLM_F_MULTI, NULL,
|
|
|
+ rtnl_dereference(cfg80211_regdomain));
|
|
|
+ if (err < 0)
|
|
|
+ goto out_err;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* the global regdom is idx 0 */
|
|
|
+ reg_idx = 1;
|
|
|
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
|
|
|
+ regdom = get_wiphy_regdom(&rdev->wiphy);
|
|
|
+ if (!regdom)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (++reg_idx <= start)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ err = nl80211_send_regdom(skb, cb, cb->nlh->nlmsg_seq,
|
|
|
+ NLM_F_MULTI, &rdev->wiphy, regdom);
|
|
|
+ if (err < 0) {
|
|
|
+ reg_idx--;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ cb->args[2] = reg_idx;
|
|
|
+ err = skb->len;
|
|
|
+out_err:
|
|
|
+ rtnl_unlock();
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
|
|
|
{
|
|
|
struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
|
|
@@ -6002,7 +6153,7 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
|
|
|
}
|
|
|
|
|
|
/* there was no other matchset, so the RSSI one is alone */
|
|
|
- if (i == 0)
|
|
|
+ if (i == 0 && n_match_sets)
|
|
|
request->match_sets[0].rssi_thold = default_match_rssi;
|
|
|
|
|
|
request->min_rssi_thold = INT_MAX;
|
|
@@ -6069,6 +6220,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
+ struct cfg80211_sched_scan_request *sched_scan_req;
|
|
|
int err;
|
|
|
|
|
|
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
|
|
@@ -6078,27 +6230,32 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
|
|
|
if (rdev->sched_scan_req)
|
|
|
return -EINPROGRESS;
|
|
|
|
|
|
- rdev->sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev,
|
|
|
- info->attrs);
|
|
|
- err = PTR_ERR_OR_ZERO(rdev->sched_scan_req);
|
|
|
+ sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev,
|
|
|
+ info->attrs);
|
|
|
+
|
|
|
+ err = PTR_ERR_OR_ZERO(sched_scan_req);
|
|
|
if (err)
|
|
|
goto out_err;
|
|
|
|
|
|
- err = rdev_sched_scan_start(rdev, dev, rdev->sched_scan_req);
|
|
|
+ err = rdev_sched_scan_start(rdev, dev, sched_scan_req);
|
|
|
if (err)
|
|
|
goto out_free;
|
|
|
|
|
|
- rdev->sched_scan_req->dev = dev;
|
|
|
- rdev->sched_scan_req->wiphy = &rdev->wiphy;
|
|
|
+ sched_scan_req->dev = dev;
|
|
|
+ sched_scan_req->wiphy = &rdev->wiphy;
|
|
|
+
|
|
|
+ if (info->attrs[NL80211_ATTR_SOCKET_OWNER])
|
|
|
+ sched_scan_req->owner_nlportid = info->snd_portid;
|
|
|
+
|
|
|
+ rcu_assign_pointer(rdev->sched_scan_req, sched_scan_req);
|
|
|
|
|
|
nl80211_send_sched_scan(rdev, dev,
|
|
|
NL80211_CMD_START_SCHED_SCAN);
|
|
|
return 0;
|
|
|
|
|
|
out_free:
|
|
|
- kfree(rdev->sched_scan_req);
|
|
|
+ kfree(sched_scan_req);
|
|
|
out_err:
|
|
|
- rdev->sched_scan_req = NULL;
|
|
|
return err;
|
|
|
}
|
|
|
|
|
@@ -6481,12 +6638,17 @@ static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb)
|
|
|
}
|
|
|
|
|
|
static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq,
|
|
|
- int flags, struct net_device *dev,
|
|
|
- struct survey_info *survey)
|
|
|
+ int flags, struct net_device *dev,
|
|
|
+ bool allow_radio_stats,
|
|
|
+ struct survey_info *survey)
|
|
|
{
|
|
|
void *hdr;
|
|
|
struct nlattr *infoattr;
|
|
|
|
|
|
+ /* skip radio stats if userspace didn't request them */
|
|
|
+ if (!survey->channel && !allow_radio_stats)
|
|
|
+ return 0;
|
|
|
+
|
|
|
hdr = nl80211hdr_put(msg, portid, seq, flags,
|
|
|
NL80211_CMD_NEW_SURVEY_RESULTS);
|
|
|
if (!hdr)
|
|
@@ -6499,7 +6661,8 @@ static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq,
|
|
|
if (!infoattr)
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
- if (nla_put_u32(msg, NL80211_SURVEY_INFO_FREQUENCY,
|
|
|
+ if (survey->channel &&
|
|
|
+ nla_put_u32(msg, NL80211_SURVEY_INFO_FREQUENCY,
|
|
|
survey->channel->center_freq))
|
|
|
goto nla_put_failure;
|
|
|
|
|
@@ -6509,25 +6672,29 @@ static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq,
|
|
|
if ((survey->filled & SURVEY_INFO_IN_USE) &&
|
|
|
nla_put_flag(msg, NL80211_SURVEY_INFO_IN_USE))
|
|
|
goto nla_put_failure;
|
|
|
- if ((survey->filled & SURVEY_INFO_CHANNEL_TIME) &&
|
|
|
- nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME,
|
|
|
- survey->channel_time))
|
|
|
+ if ((survey->filled & SURVEY_INFO_TIME) &&
|
|
|
+ nla_put_u64(msg, NL80211_SURVEY_INFO_TIME,
|
|
|
+ survey->time))
|
|
|
+ goto nla_put_failure;
|
|
|
+ if ((survey->filled & SURVEY_INFO_TIME_BUSY) &&
|
|
|
+ nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_BUSY,
|
|
|
+ survey->time_busy))
|
|
|
goto nla_put_failure;
|
|
|
- if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY) &&
|
|
|
- nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
|
|
|
- survey->channel_time_busy))
|
|
|
+ if ((survey->filled & SURVEY_INFO_TIME_EXT_BUSY) &&
|
|
|
+ nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_EXT_BUSY,
|
|
|
+ survey->time_ext_busy))
|
|
|
goto nla_put_failure;
|
|
|
- if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) &&
|
|
|
- nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
|
|
|
- survey->channel_time_ext_busy))
|
|
|
+ if ((survey->filled & SURVEY_INFO_TIME_RX) &&
|
|
|
+ nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_RX,
|
|
|
+ survey->time_rx))
|
|
|
goto nla_put_failure;
|
|
|
- if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_RX) &&
|
|
|
- nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
|
|
|
- survey->channel_time_rx))
|
|
|
+ if ((survey->filled & SURVEY_INFO_TIME_TX) &&
|
|
|
+ nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_TX,
|
|
|
+ survey->time_tx))
|
|
|
goto nla_put_failure;
|
|
|
- if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_TX) &&
|
|
|
- nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
|
|
|
- survey->channel_time_tx))
|
|
|
+ if ((survey->filled & SURVEY_INFO_TIME_SCAN) &&
|
|
|
+ nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_SCAN,
|
|
|
+ survey->time_scan))
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
nla_nest_end(msg, infoattr);
|
|
@@ -6539,19 +6706,22 @@ static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq,
|
|
|
return -EMSGSIZE;
|
|
|
}
|
|
|
|
|
|
-static int nl80211_dump_survey(struct sk_buff *skb,
|
|
|
- struct netlink_callback *cb)
|
|
|
+static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb)
|
|
|
{
|
|
|
struct survey_info survey;
|
|
|
struct cfg80211_registered_device *rdev;
|
|
|
struct wireless_dev *wdev;
|
|
|
int survey_idx = cb->args[2];
|
|
|
int res;
|
|
|
+ bool radio_stats;
|
|
|
|
|
|
res = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
|
|
|
if (res)
|
|
|
return res;
|
|
|
|
|
|
+ /* prepare_wdev_dump parsed the attributes */
|
|
|
+ radio_stats = nl80211_fam.attrbuf[NL80211_ATTR_SURVEY_RADIO_STATS];
|
|
|
+
|
|
|
if (!wdev->netdev) {
|
|
|
res = -EINVAL;
|
|
|
goto out_err;
|
|
@@ -6569,13 +6739,9 @@ static int nl80211_dump_survey(struct sk_buff *skb,
|
|
|
if (res)
|
|
|
goto out_err;
|
|
|
|
|
|
- /* Survey without a channel doesn't make sense */
|
|
|
- if (!survey.channel) {
|
|
|
- res = -EINVAL;
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- if (survey.channel->flags & IEEE80211_CHAN_DISABLED) {
|
|
|
+ /* don't send disabled channels, but do send non-channel data */
|
|
|
+ if (survey.channel &&
|
|
|
+ survey.channel->flags & IEEE80211_CHAN_DISABLED) {
|
|
|
survey_idx++;
|
|
|
continue;
|
|
|
}
|
|
@@ -6583,7 +6749,7 @@ static int nl80211_dump_survey(struct sk_buff *skb,
|
|
|
if (nl80211_send_survey(skb,
|
|
|
NETLINK_CB(cb->skb).portid,
|
|
|
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
|
|
- wdev->netdev, &survey) < 0)
|
|
|
+ wdev->netdev, radio_stats, &survey) < 0)
|
|
|
goto out;
|
|
|
survey_idx++;
|
|
|
}
|
|
@@ -8599,6 +8765,48 @@ static int nl80211_send_wowlan_tcp(struct sk_buff *msg,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int nl80211_send_wowlan_nd(struct sk_buff *msg,
|
|
|
+ struct cfg80211_sched_scan_request *req)
|
|
|
+{
|
|
|
+ struct nlattr *nd, *freqs, *matches, *match;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (!req)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ nd = nla_nest_start(msg, NL80211_WOWLAN_TRIG_NET_DETECT);
|
|
|
+ if (!nd)
|
|
|
+ return -ENOBUFS;
|
|
|
+
|
|
|
+ if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, req->interval))
|
|
|
+ return -ENOBUFS;
|
|
|
+
|
|
|
+ freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
|
|
|
+ if (!freqs)
|
|
|
+ return -ENOBUFS;
|
|
|
+
|
|
|
+ for (i = 0; i < req->n_channels; i++)
|
|
|
+ nla_put_u32(msg, i, req->channels[i]->center_freq);
|
|
|
+
|
|
|
+ nla_nest_end(msg, freqs);
|
|
|
+
|
|
|
+ if (req->n_match_sets) {
|
|
|
+ matches = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH);
|
|
|
+ for (i = 0; i < req->n_match_sets; i++) {
|
|
|
+ match = nla_nest_start(msg, i);
|
|
|
+ nla_put(msg, NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
|
|
|
+ req->match_sets[i].ssid.ssid_len,
|
|
|
+ req->match_sets[i].ssid.ssid);
|
|
|
+ nla_nest_end(msg, match);
|
|
|
+ }
|
|
|
+ nla_nest_end(msg, matches);
|
|
|
+ }
|
|
|
+
|
|
|
+ nla_nest_end(msg, nd);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
|
|
|
{
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
@@ -8656,6 +8864,11 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
|
|
|
rdev->wiphy.wowlan_config->tcp))
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
+ if (nl80211_send_wowlan_nd(
|
|
|
+ msg,
|
|
|
+ rdev->wiphy.wowlan_config->nd_config))
|
|
|
+ goto nla_put_failure;
|
|
|
+
|
|
|
nla_nest_end(msg, nl_wowlan);
|
|
|
}
|
|
|
|
|
@@ -10225,7 +10438,8 @@ static const struct genl_ops nl80211_ops[] = {
|
|
|
},
|
|
|
{
|
|
|
.cmd = NL80211_CMD_GET_REG,
|
|
|
- .doit = nl80211_get_reg,
|
|
|
+ .doit = nl80211_get_reg_do,
|
|
|
+ .dumpit = nl80211_get_reg_dump,
|
|
|
.policy = nl80211_policy,
|
|
|
.internal_flags = NL80211_FLAG_NEED_RTNL,
|
|
|
/* can be retrieved by unprivileged users */
|
|
@@ -10939,25 +11153,9 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
|
|
|
NL80211_MCGRP_SCAN, GFP_KERNEL);
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * This can happen on global regulatory changes or device specific settings
|
|
|
- * based on custom world regulatory domains.
|
|
|
- */
|
|
|
-void nl80211_send_reg_change_event(struct regulatory_request *request)
|
|
|
+static bool nl80211_reg_change_event_fill(struct sk_buff *msg,
|
|
|
+ struct regulatory_request *request)
|
|
|
{
|
|
|
- struct sk_buff *msg;
|
|
|
- void *hdr;
|
|
|
-
|
|
|
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
- if (!msg)
|
|
|
- return;
|
|
|
-
|
|
|
- hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
|
|
|
- if (!hdr) {
|
|
|
- nlmsg_free(msg);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
/* Userspace can always count this one always being set */
|
|
|
if (nla_put_u8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator))
|
|
|
goto nla_put_failure;
|
|
@@ -10983,8 +11181,46 @@ void nl80211_send_reg_change_event(struct regulatory_request *request)
|
|
|
goto nla_put_failure;
|
|
|
}
|
|
|
|
|
|
- if (request->wiphy_idx != WIPHY_IDX_INVALID &&
|
|
|
- nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx))
|
|
|
+ if (request->wiphy_idx != WIPHY_IDX_INVALID) {
|
|
|
+ struct wiphy *wiphy = wiphy_idx_to_wiphy(request->wiphy_idx);
|
|
|
+
|
|
|
+ if (wiphy &&
|
|
|
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx))
|
|
|
+ goto nla_put_failure;
|
|
|
+
|
|
|
+ if (wiphy &&
|
|
|
+ wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
|
|
|
+ nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG))
|
|
|
+ goto nla_put_failure;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+
|
|
|
+nla_put_failure:
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * This can happen on global regulatory changes or device specific settings
|
|
|
+ * based on custom regulatory domains.
|
|
|
+ */
|
|
|
+void nl80211_common_reg_change_event(enum nl80211_commands cmd_id,
|
|
|
+ struct regulatory_request *request)
|
|
|
+{
|
|
|
+ struct sk_buff *msg;
|
|
|
+ void *hdr;
|
|
|
+
|
|
|
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
+ if (!msg)
|
|
|
+ return;
|
|
|
+
|
|
|
+ hdr = nl80211hdr_put(msg, 0, 0, 0, cmd_id);
|
|
|
+ if (!hdr) {
|
|
|
+ nlmsg_free(msg);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (nl80211_reg_change_event_fill(msg, request) == false)
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
genlmsg_end(msg, hdr);
|
|
@@ -11523,7 +11759,7 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
|
|
|
if (!msg)
|
|
|
return;
|
|
|
|
|
|
- if (nl80211_send_station(msg, 0, 0, 0,
|
|
|
+ if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION, 0, 0, 0,
|
|
|
rdev, dev, mac_addr, sinfo) < 0) {
|
|
|
nlmsg_free(msg);
|
|
|
return;
|
|
@@ -11534,12 +11770,16 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
|
|
|
}
|
|
|
EXPORT_SYMBOL(cfg80211_new_sta);
|
|
|
|
|
|
-void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)
|
|
|
+void cfg80211_del_sta_sinfo(struct net_device *dev, const u8 *mac_addr,
|
|
|
+ struct station_info *sinfo, gfp_t gfp)
|
|
|
{
|
|
|
struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
|
|
struct sk_buff *msg;
|
|
|
- void *hdr;
|
|
|
+ struct station_info empty_sinfo = {};
|
|
|
+
|
|
|
+ if (!sinfo)
|
|
|
+ sinfo = &empty_sinfo;
|
|
|
|
|
|
trace_cfg80211_del_sta(dev, mac_addr);
|
|
|
|
|
@@ -11547,27 +11787,16 @@ void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)
|
|
|
if (!msg)
|
|
|
return;
|
|
|
|
|
|
- hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION);
|
|
|
- if (!hdr) {
|
|
|
+ if (nl80211_send_station(msg, NL80211_CMD_DEL_STATION, 0, 0, 0,
|
|
|
+ rdev, dev, mac_addr, sinfo) < 0) {
|
|
|
nlmsg_free(msg);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
|
|
|
- nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr))
|
|
|
- goto nla_put_failure;
|
|
|
-
|
|
|
- genlmsg_end(msg, hdr);
|
|
|
-
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
|
|
NL80211_MCGRP_MLME, gfp);
|
|
|
- return;
|
|
|
-
|
|
|
- nla_put_failure:
|
|
|
- genlmsg_cancel(msg, hdr);
|
|
|
- nlmsg_free(msg);
|
|
|
}
|
|
|
-EXPORT_SYMBOL(cfg80211_del_sta);
|
|
|
+EXPORT_SYMBOL(cfg80211_del_sta_sinfo);
|
|
|
|
|
|
void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
|
|
|
enum nl80211_connect_failed_reason reason,
|
|
@@ -12471,6 +12700,13 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
|
|
|
|
|
|
list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
|
|
|
bool schedule_destroy_work = false;
|
|
|
+ bool schedule_scan_stop = false;
|
|
|
+ struct cfg80211_sched_scan_request *sched_scan_req =
|
|
|
+ rcu_dereference(rdev->sched_scan_req);
|
|
|
+
|
|
|
+ if (sched_scan_req && notify->portid &&
|
|
|
+ sched_scan_req->owner_nlportid == notify->portid)
|
|
|
+ schedule_scan_stop = true;
|
|
|
|
|
|
list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) {
|
|
|
cfg80211_mlme_unregister_socket(wdev, notify->portid);
|
|
@@ -12501,6 +12737,12 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
|
|
|
spin_unlock(&rdev->destroy_list_lock);
|
|
|
schedule_work(&rdev->destroy_work);
|
|
|
}
|
|
|
+ } else if (schedule_scan_stop) {
|
|
|
+ sched_scan_req->owner_nlportid = 0;
|
|
|
+
|
|
|
+ if (rdev->ops->sched_scan_stop &&
|
|
|
+ rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
|
|
|
+ schedule_work(&rdev->sched_scan_stop_wk);
|
|
|
}
|
|
|
}
|
|
|
|