|
@@ -370,6 +370,14 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
|
|
[NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED },
|
|
[NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED },
|
|
[NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 },
|
|
[NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 },
|
|
[NL80211_ATTR_STA_EXT_CAPABILITY] = { .type = NLA_BINARY, },
|
|
[NL80211_ATTR_STA_EXT_CAPABILITY] = { .type = NLA_BINARY, },
|
|
|
|
+ [NL80211_ATTR_SPLIT_WIPHY_DUMP] = { .type = NLA_FLAG, },
|
|
|
|
+ [NL80211_ATTR_DISABLE_VHT] = { .type = NLA_FLAG },
|
|
|
|
+ [NL80211_ATTR_VHT_CAPABILITY_MASK] = {
|
|
|
|
+ .len = NL80211_VHT_CAPABILITY_LEN,
|
|
|
|
+ },
|
|
|
|
+ [NL80211_ATTR_MDID] = { .type = NLA_U16 },
|
|
|
|
+ [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
|
|
|
|
+ .len = IEEE80211_MAX_DATA_LEN },
|
|
};
|
|
};
|
|
|
|
|
|
/* policy for the key attributes */
|
|
/* policy for the key attributes */
|
|
@@ -539,7 +547,8 @@ static inline void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
|
|
}
|
|
}
|
|
|
|
|
|
static int nl80211_msg_put_channel(struct sk_buff *msg,
|
|
static int nl80211_msg_put_channel(struct sk_buff *msg,
|
|
- struct ieee80211_channel *chan)
|
|
|
|
|
|
+ struct ieee80211_channel *chan,
|
|
|
|
+ bool large)
|
|
{
|
|
{
|
|
if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_FREQ,
|
|
if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_FREQ,
|
|
chan->center_freq))
|
|
chan->center_freq))
|
|
@@ -555,27 +564,36 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,
|
|
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS))
|
|
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
if (chan->flags & IEEE80211_CHAN_RADAR) {
|
|
if (chan->flags & IEEE80211_CHAN_RADAR) {
|
|
- u32 time = elapsed_jiffies_msecs(chan->dfs_state_entered);
|
|
|
|
if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
|
|
if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
- if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_STATE,
|
|
|
|
- chan->dfs_state))
|
|
|
|
|
|
+ if (large) {
|
|
|
|
+ u32 time;
|
|
|
|
+
|
|
|
|
+ time = elapsed_jiffies_msecs(chan->dfs_state_entered);
|
|
|
|
+
|
|
|
|
+ if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_STATE,
|
|
|
|
+ chan->dfs_state))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+ if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_TIME,
|
|
|
|
+ time))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (large) {
|
|
|
|
+ if ((chan->flags & IEEE80211_CHAN_NO_HT40MINUS) &&
|
|
|
|
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_MINUS))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+ if ((chan->flags & IEEE80211_CHAN_NO_HT40PLUS) &&
|
|
|
|
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_PLUS))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+ if ((chan->flags & IEEE80211_CHAN_NO_80MHZ) &&
|
|
|
|
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_80MHZ))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
- if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_TIME, time))
|
|
|
|
|
|
+ if ((chan->flags & IEEE80211_CHAN_NO_160MHZ) &&
|
|
|
|
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_160MHZ))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
}
|
|
}
|
|
- if ((chan->flags & IEEE80211_CHAN_NO_HT40MINUS) &&
|
|
|
|
- nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_MINUS))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
- if ((chan->flags & IEEE80211_CHAN_NO_HT40PLUS) &&
|
|
|
|
- nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_PLUS))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
- if ((chan->flags & IEEE80211_CHAN_NO_80MHZ) &&
|
|
|
|
- nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_80MHZ))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
- if ((chan->flags & IEEE80211_CHAN_NO_160MHZ) &&
|
|
|
|
- nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_160MHZ))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
|
|
if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
|
|
DBM_TO_MBM(chan->max_power)))
|
|
DBM_TO_MBM(chan->max_power)))
|
|
@@ -851,7 +869,8 @@ nla_put_failure:
|
|
}
|
|
}
|
|
|
|
|
|
static int nl80211_put_iface_combinations(struct wiphy *wiphy,
|
|
static int nl80211_put_iface_combinations(struct wiphy *wiphy,
|
|
- struct sk_buff *msg)
|
|
|
|
|
|
+ struct sk_buff *msg,
|
|
|
|
+ bool large)
|
|
{
|
|
{
|
|
struct nlattr *nl_combis;
|
|
struct nlattr *nl_combis;
|
|
int i, j;
|
|
int i, j;
|
|
@@ -900,7 +919,8 @@ static int nl80211_put_iface_combinations(struct wiphy *wiphy,
|
|
nla_put_u32(msg, NL80211_IFACE_COMB_MAXNUM,
|
|
nla_put_u32(msg, NL80211_IFACE_COMB_MAXNUM,
|
|
c->max_interfaces))
|
|
c->max_interfaces))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
- if (nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
|
|
|
|
|
|
+ if (large &&
|
|
|
|
+ nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
|
|
c->radar_detect_widths))
|
|
c->radar_detect_widths))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
|
|
|
|
@@ -954,426 +974,571 @@ static int nl80211_send_wowlan_tcp_caps(struct cfg80211_registered_device *rdev,
|
|
nla_nest_end(msg, nl_tcp);
|
|
nla_nest_end(msg, nl_tcp);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
-#endif
|
|
|
|
|
|
|
|
-static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flags,
|
|
|
|
- struct cfg80211_registered_device *dev)
|
|
|
|
|
|
+static int nl80211_send_wowlan(struct sk_buff *msg,
|
|
|
|
+ struct cfg80211_registered_device *dev,
|
|
|
|
+ bool large)
|
|
{
|
|
{
|
|
- void *hdr;
|
|
|
|
- struct nlattr *nl_bands, *nl_band;
|
|
|
|
- struct nlattr *nl_freqs, *nl_freq;
|
|
|
|
- struct nlattr *nl_rates, *nl_rate;
|
|
|
|
- struct nlattr *nl_cmds;
|
|
|
|
- enum ieee80211_band band;
|
|
|
|
- struct ieee80211_channel *chan;
|
|
|
|
- struct ieee80211_rate *rate;
|
|
|
|
- int i;
|
|
|
|
- const struct ieee80211_txrx_stypes *mgmt_stypes =
|
|
|
|
- dev->wiphy.mgmt_stypes;
|
|
|
|
|
|
+ struct nlattr *nl_wowlan;
|
|
|
|
|
|
- hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY);
|
|
|
|
- if (!hdr)
|
|
|
|
- return -1;
|
|
|
|
|
|
+ if (!dev->wiphy.wowlan.flags && !dev->wiphy.wowlan.n_patterns)
|
|
|
|
+ return 0;
|
|
|
|
|
|
- if (nla_put_u32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx) ||
|
|
|
|
- nla_put_string(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)) ||
|
|
|
|
- nla_put_u32(msg, NL80211_ATTR_GENERATION,
|
|
|
|
- cfg80211_rdev_list_generation) ||
|
|
|
|
- nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
|
|
|
|
- dev->wiphy.retry_short) ||
|
|
|
|
- nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
|
|
|
|
- dev->wiphy.retry_long) ||
|
|
|
|
- nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
|
|
|
|
- dev->wiphy.frag_threshold) ||
|
|
|
|
- nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
|
|
|
|
- dev->wiphy.rts_threshold) ||
|
|
|
|
- nla_put_u8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
|
|
|
|
- dev->wiphy.coverage_class) ||
|
|
|
|
- nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
|
|
|
|
- dev->wiphy.max_scan_ssids) ||
|
|
|
|
- nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
|
|
|
|
- dev->wiphy.max_sched_scan_ssids) ||
|
|
|
|
- nla_put_u16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
|
|
|
|
- dev->wiphy.max_scan_ie_len) ||
|
|
|
|
- nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
|
|
|
|
- dev->wiphy.max_sched_scan_ie_len) ||
|
|
|
|
- nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS,
|
|
|
|
- dev->wiphy.max_match_sets))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
|
|
|
|
+ if (!nl_wowlan)
|
|
|
|
+ return -ENOBUFS;
|
|
|
|
|
|
- if ((dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&
|
|
|
|
- nla_put_flag(msg, NL80211_ATTR_SUPPORT_IBSS_RSN))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
- if ((dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
|
|
|
|
- nla_put_flag(msg, NL80211_ATTR_SUPPORT_MESH_AUTH))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
- if ((dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
|
|
|
|
- nla_put_flag(msg, NL80211_ATTR_SUPPORT_AP_UAPSD))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
- if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
|
|
|
|
- nla_put_flag(msg, NL80211_ATTR_ROAM_SUPPORT))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
- if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
|
|
|
|
- nla_put_flag(msg, NL80211_ATTR_TDLS_SUPPORT))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
- if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
|
|
|
|
- nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ if (((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) &&
|
|
|
|
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
|
|
|
|
+ ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) &&
|
|
|
|
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
|
|
|
|
+ ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) &&
|
|
|
|
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
|
|
|
|
+ ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
|
|
|
|
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) ||
|
|
|
|
+ ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
|
|
|
|
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
|
|
|
|
+ ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
|
|
|
|
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
|
|
|
|
+ ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
|
|
|
|
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
|
|
|
|
+ ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
|
|
|
|
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
|
|
|
|
+ return -ENOBUFS;
|
|
|
|
|
|
- if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
|
|
|
|
- sizeof(u32) * dev->wiphy.n_cipher_suites,
|
|
|
|
- dev->wiphy.cipher_suites))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ if (dev->wiphy.wowlan.n_patterns) {
|
|
|
|
+ struct nl80211_wowlan_pattern_support pat = {
|
|
|
|
+ .max_patterns = dev->wiphy.wowlan.n_patterns,
|
|
|
|
+ .min_pattern_len = dev->wiphy.wowlan.pattern_min_len,
|
|
|
|
+ .max_pattern_len = dev->wiphy.wowlan.pattern_max_len,
|
|
|
|
+ .max_pkt_offset = dev->wiphy.wowlan.max_pkt_offset,
|
|
|
|
+ };
|
|
|
|
|
|
- if (nla_put_u8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
|
|
|
|
- dev->wiphy.max_num_pmkids))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
|
|
|
|
+ sizeof(pat), &pat))
|
|
|
|
+ return -ENOBUFS;
|
|
|
|
+ }
|
|
|
|
|
|
- if ((dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
|
|
|
|
- nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ if (large && nl80211_send_wowlan_tcp_caps(dev, msg))
|
|
|
|
+ return -ENOBUFS;
|
|
|
|
|
|
- if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
|
|
|
|
- dev->wiphy.available_antennas_tx) ||
|
|
|
|
- nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
|
|
|
|
- dev->wiphy.available_antennas_rx))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ nla_nest_end(msg, nl_wowlan);
|
|
|
|
|
|
- if ((dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) &&
|
|
|
|
- nla_put_u32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD,
|
|
|
|
- dev->wiphy.probe_resp_offload))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+#endif
|
|
|
|
|
|
- if ((dev->wiphy.available_antennas_tx ||
|
|
|
|
- dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) {
|
|
|
|
- u32 tx_ant = 0, rx_ant = 0;
|
|
|
|
- int res;
|
|
|
|
- res = rdev_get_antenna(dev, &tx_ant, &rx_ant);
|
|
|
|
- if (!res) {
|
|
|
|
- if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX,
|
|
|
|
- tx_ant) ||
|
|
|
|
- nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX,
|
|
|
|
- rx_ant))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+static int nl80211_send_band_rateinfo(struct sk_buff *msg,
|
|
|
|
+ struct ieee80211_supported_band *sband)
|
|
|
|
+{
|
|
|
|
+ struct nlattr *nl_rates, *nl_rate;
|
|
|
|
+ struct ieee80211_rate *rate;
|
|
|
|
+ int i;
|
|
|
|
|
|
- if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
|
|
|
|
- dev->wiphy.interface_modes))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ /* add HT info */
|
|
|
|
+ if (sband->ht_cap.ht_supported &&
|
|
|
|
+ (nla_put(msg, NL80211_BAND_ATTR_HT_MCS_SET,
|
|
|
|
+ sizeof(sband->ht_cap.mcs),
|
|
|
|
+ &sband->ht_cap.mcs) ||
|
|
|
|
+ nla_put_u16(msg, NL80211_BAND_ATTR_HT_CAPA,
|
|
|
|
+ sband->ht_cap.cap) ||
|
|
|
|
+ nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
|
|
|
|
+ sband->ht_cap.ampdu_factor) ||
|
|
|
|
+ nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
|
|
|
|
+ sband->ht_cap.ampdu_density)))
|
|
|
|
+ return -ENOBUFS;
|
|
|
|
|
|
- nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
|
|
|
|
- if (!nl_bands)
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ /* add VHT info */
|
|
|
|
+ if (sband->vht_cap.vht_supported &&
|
|
|
|
+ (nla_put(msg, NL80211_BAND_ATTR_VHT_MCS_SET,
|
|
|
|
+ sizeof(sband->vht_cap.vht_mcs),
|
|
|
|
+ &sband->vht_cap.vht_mcs) ||
|
|
|
|
+ nla_put_u32(msg, NL80211_BAND_ATTR_VHT_CAPA,
|
|
|
|
+ sband->vht_cap.cap)))
|
|
|
|
+ return -ENOBUFS;
|
|
|
|
|
|
- for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
|
|
|
|
- if (!dev->wiphy.bands[band])
|
|
|
|
- continue;
|
|
|
|
|
|
+ /* add bitrates */
|
|
|
|
+ nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
|
|
|
|
+ if (!nl_rates)
|
|
|
|
+ return -ENOBUFS;
|
|
|
|
|
|
- nl_band = nla_nest_start(msg, band);
|
|
|
|
- if (!nl_band)
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ for (i = 0; i < sband->n_bitrates; i++) {
|
|
|
|
+ nl_rate = nla_nest_start(msg, i);
|
|
|
|
+ if (!nl_rate)
|
|
|
|
+ return -ENOBUFS;
|
|
|
|
|
|
- /* add HT info */
|
|
|
|
- if (dev->wiphy.bands[band]->ht_cap.ht_supported &&
|
|
|
|
- (nla_put(msg, NL80211_BAND_ATTR_HT_MCS_SET,
|
|
|
|
- sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
|
|
|
|
- &dev->wiphy.bands[band]->ht_cap.mcs) ||
|
|
|
|
- nla_put_u16(msg, NL80211_BAND_ATTR_HT_CAPA,
|
|
|
|
- dev->wiphy.bands[band]->ht_cap.cap) ||
|
|
|
|
- nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
|
|
|
|
- dev->wiphy.bands[band]->ht_cap.ampdu_factor) ||
|
|
|
|
- nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
|
|
|
|
- dev->wiphy.bands[band]->ht_cap.ampdu_density)))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ rate = &sband->bitrates[i];
|
|
|
|
+ if (nla_put_u32(msg, NL80211_BITRATE_ATTR_RATE,
|
|
|
|
+ rate->bitrate))
|
|
|
|
+ return -ENOBUFS;
|
|
|
|
+ if ((rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
|
|
|
|
+ nla_put_flag(msg,
|
|
|
|
+ NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE))
|
|
|
|
+ return -ENOBUFS;
|
|
|
|
|
|
- /* add VHT info */
|
|
|
|
- if (dev->wiphy.bands[band]->vht_cap.vht_supported &&
|
|
|
|
- (nla_put(msg, NL80211_BAND_ATTR_VHT_MCS_SET,
|
|
|
|
- sizeof(dev->wiphy.bands[band]->vht_cap.vht_mcs),
|
|
|
|
- &dev->wiphy.bands[band]->vht_cap.vht_mcs) ||
|
|
|
|
- nla_put_u32(msg, NL80211_BAND_ATTR_VHT_CAPA,
|
|
|
|
- dev->wiphy.bands[band]->vht_cap.cap)))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ nla_nest_end(msg, nl_rate);
|
|
|
|
+ }
|
|
|
|
|
|
- /* add frequencies */
|
|
|
|
- nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
|
|
|
|
- if (!nl_freqs)
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ nla_nest_end(msg, nl_rates);
|
|
|
|
|
|
- for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
|
|
|
|
- nl_freq = nla_nest_start(msg, i);
|
|
|
|
- if (!nl_freq)
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
|
|
- chan = &dev->wiphy.bands[band]->channels[i];
|
|
|
|
|
|
+static int
|
|
|
|
+nl80211_send_mgmt_stypes(struct sk_buff *msg,
|
|
|
|
+ const struct ieee80211_txrx_stypes *mgmt_stypes)
|
|
|
|
+{
|
|
|
|
+ u16 stypes;
|
|
|
|
+ struct nlattr *nl_ftypes, *nl_ifs;
|
|
|
|
+ enum nl80211_iftype ift;
|
|
|
|
+ int i;
|
|
|
|
|
|
- if (nl80211_msg_put_channel(msg, chan))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ if (!mgmt_stypes)
|
|
|
|
+ return 0;
|
|
|
|
|
|
- nla_nest_end(msg, nl_freq);
|
|
|
|
|
|
+ nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
|
|
|
|
+ if (!nl_ifs)
|
|
|
|
+ return -ENOBUFS;
|
|
|
|
+
|
|
|
|
+ for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
|
|
|
|
+ nl_ftypes = nla_nest_start(msg, ift);
|
|
|
|
+ if (!nl_ftypes)
|
|
|
|
+ return -ENOBUFS;
|
|
|
|
+ i = 0;
|
|
|
|
+ stypes = mgmt_stypes[ift].tx;
|
|
|
|
+ while (stypes) {
|
|
|
|
+ if ((stypes & 1) &&
|
|
|
|
+ nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
|
|
|
|
+ (i << 4) | IEEE80211_FTYPE_MGMT))
|
|
|
|
+ return -ENOBUFS;
|
|
|
|
+ stypes >>= 1;
|
|
|
|
+ i++;
|
|
}
|
|
}
|
|
|
|
+ nla_nest_end(msg, nl_ftypes);
|
|
|
|
+ }
|
|
|
|
|
|
- nla_nest_end(msg, nl_freqs);
|
|
|
|
|
|
+ nla_nest_end(msg, nl_ifs);
|
|
|
|
|
|
- /* add bitrates */
|
|
|
|
- nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
|
|
|
|
- if (!nl_rates)
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
|
|
|
|
+ if (!nl_ifs)
|
|
|
|
+ return -ENOBUFS;
|
|
|
|
|
|
- for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
|
|
|
|
- nl_rate = nla_nest_start(msg, i);
|
|
|
|
- if (!nl_rate)
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
|
|
|
|
+ nl_ftypes = nla_nest_start(msg, ift);
|
|
|
|
+ if (!nl_ftypes)
|
|
|
|
+ return -ENOBUFS;
|
|
|
|
+ i = 0;
|
|
|
|
+ stypes = mgmt_stypes[ift].rx;
|
|
|
|
+ while (stypes) {
|
|
|
|
+ if ((stypes & 1) &&
|
|
|
|
+ nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
|
|
|
|
+ (i << 4) | IEEE80211_FTYPE_MGMT))
|
|
|
|
+ return -ENOBUFS;
|
|
|
|
+ stypes >>= 1;
|
|
|
|
+ i++;
|
|
|
|
+ }
|
|
|
|
+ nla_nest_end(msg, nl_ftypes);
|
|
|
|
+ }
|
|
|
|
+ nla_nest_end(msg, nl_ifs);
|
|
|
|
|
|
- rate = &dev->wiphy.bands[band]->bitrates[i];
|
|
|
|
- if (nla_put_u32(msg, NL80211_BITRATE_ATTR_RATE,
|
|
|
|
- rate->bitrate))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
- if ((rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
|
|
|
|
- nla_put_flag(msg,
|
|
|
|
- NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
|
|
- nla_nest_end(msg, nl_rate);
|
|
|
|
- }
|
|
|
|
|
|
+static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
|
|
|
|
+ struct sk_buff *msg, u32 portid, u32 seq,
|
|
|
|
+ int flags, bool split, long *split_start,
|
|
|
|
+ long *band_start, long *chan_start)
|
|
|
|
+{
|
|
|
|
+ void *hdr;
|
|
|
|
+ struct nlattr *nl_bands, *nl_band;
|
|
|
|
+ struct nlattr *nl_freqs, *nl_freq;
|
|
|
|
+ struct nlattr *nl_cmds;
|
|
|
|
+ enum ieee80211_band band;
|
|
|
|
+ struct ieee80211_channel *chan;
|
|
|
|
+ int i;
|
|
|
|
+ const struct ieee80211_txrx_stypes *mgmt_stypes =
|
|
|
|
+ dev->wiphy.mgmt_stypes;
|
|
|
|
+ long start = 0, start_chan = 0, start_band = 0;
|
|
|
|
+ u32 features;
|
|
|
|
|
|
- nla_nest_end(msg, nl_rates);
|
|
|
|
|
|
+ hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY);
|
|
|
|
+ if (!hdr)
|
|
|
|
+ return -ENOBUFS;
|
|
|
|
|
|
- nla_nest_end(msg, nl_band);
|
|
|
|
|
|
+ /* allow always using the variables */
|
|
|
|
+ if (!split) {
|
|
|
|
+ split_start = &start;
|
|
|
|
+ band_start = &start_band;
|
|
|
|
+ chan_start = &start_chan;
|
|
}
|
|
}
|
|
- nla_nest_end(msg, nl_bands);
|
|
|
|
|
|
|
|
- nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
|
|
|
|
- if (!nl_cmds)
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx) ||
|
|
|
|
+ nla_put_string(msg, NL80211_ATTR_WIPHY_NAME,
|
|
|
|
+ wiphy_name(&dev->wiphy)) ||
|
|
|
|
+ nla_put_u32(msg, NL80211_ATTR_GENERATION,
|
|
|
|
+ cfg80211_rdev_list_generation))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+
|
|
|
|
+ switch (*split_start) {
|
|
|
|
+ case 0:
|
|
|
|
+ if (nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
|
|
|
|
+ dev->wiphy.retry_short) ||
|
|
|
|
+ nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
|
|
|
|
+ dev->wiphy.retry_long) ||
|
|
|
|
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
|
|
|
|
+ dev->wiphy.frag_threshold) ||
|
|
|
|
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
|
|
|
|
+ dev->wiphy.rts_threshold) ||
|
|
|
|
+ nla_put_u8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
|
|
|
|
+ dev->wiphy.coverage_class) ||
|
|
|
|
+ nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
|
|
|
|
+ dev->wiphy.max_scan_ssids) ||
|
|
|
|
+ nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
|
|
|
|
+ dev->wiphy.max_sched_scan_ssids) ||
|
|
|
|
+ nla_put_u16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
|
|
|
|
+ dev->wiphy.max_scan_ie_len) ||
|
|
|
|
+ nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
|
|
|
|
+ dev->wiphy.max_sched_scan_ie_len) ||
|
|
|
|
+ nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS,
|
|
|
|
+ dev->wiphy.max_match_sets))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
|
|
- i = 0;
|
|
|
|
-#define CMD(op, n) \
|
|
|
|
- do { \
|
|
|
|
- if (dev->ops->op) { \
|
|
|
|
- i++; \
|
|
|
|
- if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \
|
|
|
|
- goto nla_put_failure; \
|
|
|
|
- } \
|
|
|
|
- } while (0)
|
|
|
|
-
|
|
|
|
- CMD(add_virtual_intf, NEW_INTERFACE);
|
|
|
|
- CMD(change_virtual_intf, SET_INTERFACE);
|
|
|
|
- CMD(add_key, NEW_KEY);
|
|
|
|
- CMD(start_ap, START_AP);
|
|
|
|
- CMD(add_station, NEW_STATION);
|
|
|
|
- CMD(add_mpath, NEW_MPATH);
|
|
|
|
- CMD(update_mesh_config, SET_MESH_CONFIG);
|
|
|
|
- CMD(change_bss, SET_BSS);
|
|
|
|
- CMD(auth, AUTHENTICATE);
|
|
|
|
- CMD(assoc, ASSOCIATE);
|
|
|
|
- CMD(deauth, DEAUTHENTICATE);
|
|
|
|
- CMD(disassoc, DISASSOCIATE);
|
|
|
|
- CMD(join_ibss, JOIN_IBSS);
|
|
|
|
- CMD(join_mesh, JOIN_MESH);
|
|
|
|
- CMD(set_pmksa, SET_PMKSA);
|
|
|
|
- CMD(del_pmksa, DEL_PMKSA);
|
|
|
|
- CMD(flush_pmksa, FLUSH_PMKSA);
|
|
|
|
- if (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
|
|
|
|
- CMD(remain_on_channel, REMAIN_ON_CHANNEL);
|
|
|
|
- CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
|
|
|
|
- CMD(mgmt_tx, FRAME);
|
|
|
|
- CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
|
|
|
|
- if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
|
|
|
|
- i++;
|
|
|
|
- if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS))
|
|
|
|
|
|
+ if ((dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&
|
|
|
|
+ nla_put_flag(msg, NL80211_ATTR_SUPPORT_IBSS_RSN))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
- }
|
|
|
|
- if (dev->ops->set_monitor_channel || dev->ops->start_ap ||
|
|
|
|
- dev->ops->join_mesh) {
|
|
|
|
- i++;
|
|
|
|
- if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL))
|
|
|
|
|
|
+ if ((dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
|
|
|
|
+ nla_put_flag(msg, NL80211_ATTR_SUPPORT_MESH_AUTH))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
- }
|
|
|
|
- CMD(set_wds_peer, SET_WDS_PEER);
|
|
|
|
- if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
|
|
|
|
- CMD(tdls_mgmt, TDLS_MGMT);
|
|
|
|
- CMD(tdls_oper, TDLS_OPER);
|
|
|
|
- }
|
|
|
|
- if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
|
|
|
|
- CMD(sched_scan_start, START_SCHED_SCAN);
|
|
|
|
- CMD(probe_client, PROBE_CLIENT);
|
|
|
|
- CMD(set_noack_map, SET_NOACK_MAP);
|
|
|
|
- if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
|
|
|
|
- i++;
|
|
|
|
- if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS))
|
|
|
|
|
|
+ if ((dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
|
|
|
|
+ nla_put_flag(msg, NL80211_ATTR_SUPPORT_AP_UAPSD))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+ if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
|
|
|
|
+ nla_put_flag(msg, NL80211_ATTR_ROAM_SUPPORT))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+ if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
|
|
|
|
+ nla_put_flag(msg, NL80211_ATTR_TDLS_SUPPORT))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+ if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
|
|
|
|
+ nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
- }
|
|
|
|
- CMD(start_p2p_device, START_P2P_DEVICE);
|
|
|
|
- CMD(set_mcast_rate, SET_MCAST_RATE);
|
|
|
|
|
|
|
|
-#ifdef CONFIG_NL80211_TESTMODE
|
|
|
|
- CMD(testmode_cmd, TESTMODE);
|
|
|
|
-#endif
|
|
|
|
|
|
+ (*split_start)++;
|
|
|
|
+ if (split)
|
|
|
|
+ break;
|
|
|
|
+ case 1:
|
|
|
|
+ if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
|
|
|
|
+ sizeof(u32) * dev->wiphy.n_cipher_suites,
|
|
|
|
+ dev->wiphy.cipher_suites))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
|
|
-#undef CMD
|
|
|
|
|
|
+ if (nla_put_u8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
|
|
|
|
+ dev->wiphy.max_num_pmkids))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
|
|
- if (dev->ops->connect || dev->ops->auth) {
|
|
|
|
- i++;
|
|
|
|
- if (nla_put_u32(msg, i, NL80211_CMD_CONNECT))
|
|
|
|
|
|
+ if ((dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
|
|
|
|
+ nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
- }
|
|
|
|
|
|
|
|
- if (dev->ops->disconnect || dev->ops->deauth) {
|
|
|
|
- i++;
|
|
|
|
- if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT))
|
|
|
|
|
|
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
|
|
|
|
+ dev->wiphy.available_antennas_tx) ||
|
|
|
|
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
|
|
|
|
+ dev->wiphy.available_antennas_rx))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
- }
|
|
|
|
|
|
|
|
- nla_nest_end(msg, nl_cmds);
|
|
|
|
|
|
+ if ((dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) &&
|
|
|
|
+ nla_put_u32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD,
|
|
|
|
+ dev->wiphy.probe_resp_offload))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
|
|
- if (dev->ops->remain_on_channel &&
|
|
|
|
- (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&
|
|
|
|
- nla_put_u32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
|
|
|
|
- dev->wiphy.max_remain_on_channel_duration))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ if ((dev->wiphy.available_antennas_tx ||
|
|
|
|
+ dev->wiphy.available_antennas_rx) &&
|
|
|
|
+ dev->ops->get_antenna) {
|
|
|
|
+ u32 tx_ant = 0, rx_ant = 0;
|
|
|
|
+ int res;
|
|
|
|
+ res = rdev_get_antenna(dev, &tx_ant, &rx_ant);
|
|
|
|
+ if (!res) {
|
|
|
|
+ if (nla_put_u32(msg,
|
|
|
|
+ NL80211_ATTR_WIPHY_ANTENNA_TX,
|
|
|
|
+ tx_ant) ||
|
|
|
|
+ nla_put_u32(msg,
|
|
|
|
+ NL80211_ATTR_WIPHY_ANTENNA_RX,
|
|
|
|
+ rx_ant))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
- if ((dev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) &&
|
|
|
|
- nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ (*split_start)++;
|
|
|
|
+ if (split)
|
|
|
|
+ break;
|
|
|
|
+ case 2:
|
|
|
|
+ if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
|
|
|
|
+ dev->wiphy.interface_modes))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+ (*split_start)++;
|
|
|
|
+ if (split)
|
|
|
|
+ break;
|
|
|
|
+ case 3:
|
|
|
|
+ nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
|
|
|
|
+ if (!nl_bands)
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
|
|
- if (mgmt_stypes) {
|
|
|
|
- u16 stypes;
|
|
|
|
- struct nlattr *nl_ftypes, *nl_ifs;
|
|
|
|
- enum nl80211_iftype ift;
|
|
|
|
|
|
+ for (band = *band_start; band < IEEE80211_NUM_BANDS; band++) {
|
|
|
|
+ struct ieee80211_supported_band *sband;
|
|
|
|
|
|
- nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
|
|
|
|
- if (!nl_ifs)
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ sband = dev->wiphy.bands[band];
|
|
|
|
+
|
|
|
|
+ if (!sband)
|
|
|
|
+ continue;
|
|
|
|
|
|
- for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
|
|
|
|
- nl_ftypes = nla_nest_start(msg, ift);
|
|
|
|
- if (!nl_ftypes)
|
|
|
|
|
|
+ nl_band = nla_nest_start(msg, band);
|
|
|
|
+ if (!nl_band)
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
- i = 0;
|
|
|
|
- stypes = mgmt_stypes[ift].tx;
|
|
|
|
- while (stypes) {
|
|
|
|
- if ((stypes & 1) &&
|
|
|
|
- nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
|
|
|
|
- (i << 4) | IEEE80211_FTYPE_MGMT))
|
|
|
|
|
|
+
|
|
|
|
+ switch (*chan_start) {
|
|
|
|
+ case 0:
|
|
|
|
+ if (nl80211_send_band_rateinfo(msg, sband))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
- stypes >>= 1;
|
|
|
|
- i++;
|
|
|
|
|
|
+ (*chan_start)++;
|
|
|
|
+ if (split)
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ /* add frequencies */
|
|
|
|
+ nl_freqs = nla_nest_start(
|
|
|
|
+ msg, NL80211_BAND_ATTR_FREQS);
|
|
|
|
+ if (!nl_freqs)
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+
|
|
|
|
+ for (i = *chan_start - 1;
|
|
|
|
+ i < sband->n_channels;
|
|
|
|
+ i++) {
|
|
|
|
+ nl_freq = nla_nest_start(msg, i);
|
|
|
|
+ if (!nl_freq)
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+
|
|
|
|
+ chan = &sband->channels[i];
|
|
|
|
+
|
|
|
|
+ if (nl80211_msg_put_channel(msg, chan,
|
|
|
|
+ split))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+
|
|
|
|
+ nla_nest_end(msg, nl_freq);
|
|
|
|
+ if (split)
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ if (i < sband->n_channels)
|
|
|
|
+ *chan_start = i + 2;
|
|
|
|
+ else
|
|
|
|
+ *chan_start = 0;
|
|
|
|
+ nla_nest_end(msg, nl_freqs);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ nla_nest_end(msg, nl_band);
|
|
|
|
+
|
|
|
|
+ if (split) {
|
|
|
|
+ /* start again here */
|
|
|
|
+ if (*chan_start)
|
|
|
|
+ band--;
|
|
|
|
+ break;
|
|
}
|
|
}
|
|
- nla_nest_end(msg, nl_ftypes);
|
|
|
|
}
|
|
}
|
|
|
|
+ nla_nest_end(msg, nl_bands);
|
|
|
|
|
|
- nla_nest_end(msg, nl_ifs);
|
|
|
|
|
|
+ if (band < IEEE80211_NUM_BANDS)
|
|
|
|
+ *band_start = band + 1;
|
|
|
|
+ else
|
|
|
|
+ *band_start = 0;
|
|
|
|
|
|
- nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
|
|
|
|
- if (!nl_ifs)
|
|
|
|
|
|
+ /* if bands & channels are done, continue outside */
|
|
|
|
+ if (*band_start == 0 && *chan_start == 0)
|
|
|
|
+ (*split_start)++;
|
|
|
|
+ if (split)
|
|
|
|
+ break;
|
|
|
|
+ case 4:
|
|
|
|
+ nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
|
|
|
|
+ if (!nl_cmds)
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
|
|
|
|
- for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
|
|
|
|
- nl_ftypes = nla_nest_start(msg, ift);
|
|
|
|
- if (!nl_ftypes)
|
|
|
|
|
|
+ i = 0;
|
|
|
|
+#define CMD(op, n) \
|
|
|
|
+ do { \
|
|
|
|
+ if (dev->ops->op) { \
|
|
|
|
+ i++; \
|
|
|
|
+ if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \
|
|
|
|
+ goto nla_put_failure; \
|
|
|
|
+ } \
|
|
|
|
+ } while (0)
|
|
|
|
+
|
|
|
|
+ CMD(add_virtual_intf, NEW_INTERFACE);
|
|
|
|
+ CMD(change_virtual_intf, SET_INTERFACE);
|
|
|
|
+ CMD(add_key, NEW_KEY);
|
|
|
|
+ CMD(start_ap, START_AP);
|
|
|
|
+ CMD(add_station, NEW_STATION);
|
|
|
|
+ CMD(add_mpath, NEW_MPATH);
|
|
|
|
+ CMD(update_mesh_config, SET_MESH_CONFIG);
|
|
|
|
+ CMD(change_bss, SET_BSS);
|
|
|
|
+ CMD(auth, AUTHENTICATE);
|
|
|
|
+ CMD(assoc, ASSOCIATE);
|
|
|
|
+ CMD(deauth, DEAUTHENTICATE);
|
|
|
|
+ CMD(disassoc, DISASSOCIATE);
|
|
|
|
+ CMD(join_ibss, JOIN_IBSS);
|
|
|
|
+ CMD(join_mesh, JOIN_MESH);
|
|
|
|
+ CMD(set_pmksa, SET_PMKSA);
|
|
|
|
+ CMD(del_pmksa, DEL_PMKSA);
|
|
|
|
+ CMD(flush_pmksa, FLUSH_PMKSA);
|
|
|
|
+ if (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
|
|
|
|
+ CMD(remain_on_channel, REMAIN_ON_CHANNEL);
|
|
|
|
+ CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
|
|
|
|
+ CMD(mgmt_tx, FRAME);
|
|
|
|
+ CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
|
|
|
|
+ if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
|
|
|
|
+ i++;
|
|
|
|
+ if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
- i = 0;
|
|
|
|
- stypes = mgmt_stypes[ift].rx;
|
|
|
|
- while (stypes) {
|
|
|
|
- if ((stypes & 1) &&
|
|
|
|
- nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
|
|
|
|
- (i << 4) | IEEE80211_FTYPE_MGMT))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
- stypes >>= 1;
|
|
|
|
- i++;
|
|
|
|
- }
|
|
|
|
- nla_nest_end(msg, nl_ftypes);
|
|
|
|
}
|
|
}
|
|
- nla_nest_end(msg, nl_ifs);
|
|
|
|
- }
|
|
|
|
|
|
+ if (dev->ops->set_monitor_channel || dev->ops->start_ap ||
|
|
|
|
+ dev->ops->join_mesh) {
|
|
|
|
+ i++;
|
|
|
|
+ if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+ }
|
|
|
|
+ CMD(set_wds_peer, SET_WDS_PEER);
|
|
|
|
+ if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
|
|
|
|
+ CMD(tdls_mgmt, TDLS_MGMT);
|
|
|
|
+ CMD(tdls_oper, TDLS_OPER);
|
|
|
|
+ }
|
|
|
|
+ if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
|
|
|
|
+ CMD(sched_scan_start, START_SCHED_SCAN);
|
|
|
|
+ CMD(probe_client, PROBE_CLIENT);
|
|
|
|
+ CMD(set_noack_map, SET_NOACK_MAP);
|
|
|
|
+ if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
|
|
|
|
+ i++;
|
|
|
|
+ if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+ }
|
|
|
|
+ CMD(start_p2p_device, START_P2P_DEVICE);
|
|
|
|
+ CMD(set_mcast_rate, SET_MCAST_RATE);
|
|
|
|
|
|
-#ifdef CONFIG_PM
|
|
|
|
- if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) {
|
|
|
|
- struct nlattr *nl_wowlan;
|
|
|
|
|
|
+#ifdef CONFIG_NL80211_TESTMODE
|
|
|
|
+ CMD(testmode_cmd, TESTMODE);
|
|
|
|
+#endif
|
|
|
|
|
|
- nl_wowlan = nla_nest_start(msg,
|
|
|
|
- NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
|
|
|
|
- if (!nl_wowlan)
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+#undef CMD
|
|
|
|
|
|
- if (((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) &&
|
|
|
|
- nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
|
|
|
|
- ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) &&
|
|
|
|
- nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
|
|
|
|
- ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) &&
|
|
|
|
- nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
|
|
|
|
- ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
|
|
|
|
- nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) ||
|
|
|
|
- ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
|
|
|
|
- nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
|
|
|
|
- ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
|
|
|
|
- nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
|
|
|
|
- ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
|
|
|
|
- nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
|
|
|
|
- ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
|
|
|
|
- nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
- if (dev->wiphy.wowlan.n_patterns) {
|
|
|
|
- struct nl80211_wowlan_pattern_support pat = {
|
|
|
|
- .max_patterns = dev->wiphy.wowlan.n_patterns,
|
|
|
|
- .min_pattern_len =
|
|
|
|
- dev->wiphy.wowlan.pattern_min_len,
|
|
|
|
- .max_pattern_len =
|
|
|
|
- dev->wiphy.wowlan.pattern_max_len,
|
|
|
|
- .max_pkt_offset =
|
|
|
|
- dev->wiphy.wowlan.max_pkt_offset,
|
|
|
|
- };
|
|
|
|
- if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
|
|
|
|
- sizeof(pat), &pat))
|
|
|
|
|
|
+ if (dev->ops->connect || dev->ops->auth) {
|
|
|
|
+ i++;
|
|
|
|
+ if (nla_put_u32(msg, i, NL80211_CMD_CONNECT))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (dev->ops->disconnect || dev->ops->deauth) {
|
|
|
|
+ i++;
|
|
|
|
+ if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
}
|
|
}
|
|
|
|
|
|
- if (nl80211_send_wowlan_tcp_caps(dev, msg))
|
|
|
|
|
|
+ nla_nest_end(msg, nl_cmds);
|
|
|
|
+ (*split_start)++;
|
|
|
|
+ if (split)
|
|
|
|
+ break;
|
|
|
|
+ case 5:
|
|
|
|
+ if (dev->ops->remain_on_channel &&
|
|
|
|
+ (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&
|
|
|
|
+ nla_put_u32(msg,
|
|
|
|
+ NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
|
|
|
|
+ dev->wiphy.max_remain_on_channel_duration))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+
|
|
|
|
+ if ((dev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) &&
|
|
|
|
+ nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
|
|
|
|
- nla_nest_end(msg, nl_wowlan);
|
|
|
|
- }
|
|
|
|
|
|
+ if (nl80211_send_mgmt_stypes(msg, mgmt_stypes))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+ (*split_start)++;
|
|
|
|
+ if (split)
|
|
|
|
+ break;
|
|
|
|
+ case 6:
|
|
|
|
+#ifdef CONFIG_PM
|
|
|
|
+ if (nl80211_send_wowlan(msg, dev, split))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+ (*split_start)++;
|
|
|
|
+ if (split)
|
|
|
|
+ break;
|
|
|
|
+#else
|
|
|
|
+ (*split_start)++;
|
|
#endif
|
|
#endif
|
|
|
|
+ case 7:
|
|
|
|
+ if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
|
|
|
|
+ dev->wiphy.software_iftypes))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
|
|
- if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
|
|
|
|
- dev->wiphy.software_iftypes))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ if (nl80211_put_iface_combinations(&dev->wiphy, msg, split))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
|
|
- if (nl80211_put_iface_combinations(&dev->wiphy, msg))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ (*split_start)++;
|
|
|
|
+ if (split)
|
|
|
|
+ break;
|
|
|
|
+ case 8:
|
|
|
|
+ if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
|
|
|
|
+ nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME,
|
|
|
|
+ dev->wiphy.ap_sme_capa))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
|
|
- if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
|
|
|
|
- nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME,
|
|
|
|
- dev->wiphy.ap_sme_capa))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ features = dev->wiphy.features;
|
|
|
|
+ /*
|
|
|
|
+ * We can only add the per-channel limit information if the
|
|
|
|
+ * dump is split, otherwise it makes it too big. Therefore
|
|
|
|
+ * only advertise it in that case.
|
|
|
|
+ */
|
|
|
|
+ if (split)
|
|
|
|
+ features |= NL80211_FEATURE_ADVERTISE_CHAN_LIMITS;
|
|
|
|
+ if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS, features))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
|
|
- if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS,
|
|
|
|
- dev->wiphy.features))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ if (dev->wiphy.ht_capa_mod_mask &&
|
|
|
|
+ nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK,
|
|
|
|
+ sizeof(*dev->wiphy.ht_capa_mod_mask),
|
|
|
|
+ dev->wiphy.ht_capa_mod_mask))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
|
|
- if (dev->wiphy.ht_capa_mod_mask &&
|
|
|
|
- nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK,
|
|
|
|
- sizeof(*dev->wiphy.ht_capa_mod_mask),
|
|
|
|
- dev->wiphy.ht_capa_mod_mask))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME &&
|
|
|
|
+ dev->wiphy.max_acl_mac_addrs &&
|
|
|
|
+ nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX,
|
|
|
|
+ dev->wiphy.max_acl_mac_addrs))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
|
|
- if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME &&
|
|
|
|
- dev->wiphy.max_acl_mac_addrs &&
|
|
|
|
- nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX,
|
|
|
|
- dev->wiphy.max_acl_mac_addrs))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ /*
|
|
|
|
+ * Any information below this point is only available to
|
|
|
|
+ * applications that can deal with it being split. This
|
|
|
|
+ * helps ensure that newly added capabilities don't break
|
|
|
|
+ * older tools by overrunning their buffers.
|
|
|
|
+ *
|
|
|
|
+ * We still increment split_start so that in the split
|
|
|
|
+ * case we'll continue with more data in the next round,
|
|
|
|
+ * but break unconditionally so unsplit data stops here.
|
|
|
|
+ */
|
|
|
|
+ (*split_start)++;
|
|
|
|
+ break;
|
|
|
|
+ case 9:
|
|
|
|
+ if (dev->wiphy.extended_capabilities &&
|
|
|
|
+ (nla_put(msg, NL80211_ATTR_EXT_CAPA,
|
|
|
|
+ dev->wiphy.extended_capabilities_len,
|
|
|
|
+ dev->wiphy.extended_capabilities) ||
|
|
|
|
+ nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK,
|
|
|
|
+ dev->wiphy.extended_capabilities_len,
|
|
|
|
+ dev->wiphy.extended_capabilities_mask)))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
|
|
- if (dev->wiphy.extended_capabilities &&
|
|
|
|
- (nla_put(msg, NL80211_ATTR_EXT_CAPA,
|
|
|
|
- dev->wiphy.extended_capabilities_len,
|
|
|
|
- dev->wiphy.extended_capabilities) ||
|
|
|
|
- nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK,
|
|
|
|
- dev->wiphy.extended_capabilities_len,
|
|
|
|
- dev->wiphy.extended_capabilities_mask)))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ if (dev->wiphy.vht_capa_mod_mask &&
|
|
|
|
+ nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK,
|
|
|
|
+ sizeof(*dev->wiphy.vht_capa_mod_mask),
|
|
|
|
+ dev->wiphy.vht_capa_mod_mask))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
|
|
|
|
+ /* done */
|
|
|
|
+ *split_start = 0;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
return genlmsg_end(msg, hdr);
|
|
return genlmsg_end(msg, hdr);
|
|
|
|
|
|
nla_put_failure:
|
|
nla_put_failure:
|
|
@@ -1383,22 +1548,83 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag
|
|
|
|
|
|
static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
|
|
static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
|
|
{
|
|
{
|
|
- int idx = 0;
|
|
|
|
|
|
+ int idx = 0, ret;
|
|
int start = cb->args[0];
|
|
int start = cb->args[0];
|
|
struct cfg80211_registered_device *dev;
|
|
struct cfg80211_registered_device *dev;
|
|
|
|
+ s64 filter_wiphy = -1;
|
|
|
|
+ bool split = false;
|
|
|
|
+ struct nlattr **tb = nl80211_fam.attrbuf;
|
|
|
|
+ int res;
|
|
|
|
|
|
mutex_lock(&cfg80211_mutex);
|
|
mutex_lock(&cfg80211_mutex);
|
|
|
|
+ res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
|
|
|
|
+ tb, nl80211_fam.maxattr, nl80211_policy);
|
|
|
|
+ if (res == 0) {
|
|
|
|
+ split = tb[NL80211_ATTR_SPLIT_WIPHY_DUMP];
|
|
|
|
+ if (tb[NL80211_ATTR_WIPHY])
|
|
|
|
+ filter_wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
|
|
|
|
+ if (tb[NL80211_ATTR_WDEV])
|
|
|
|
+ filter_wiphy = nla_get_u64(tb[NL80211_ATTR_WDEV]) >> 32;
|
|
|
|
+ if (tb[NL80211_ATTR_IFINDEX]) {
|
|
|
|
+ struct net_device *netdev;
|
|
|
|
+ int ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
|
|
|
|
+
|
|
|
|
+ netdev = dev_get_by_index(sock_net(skb->sk), ifidx);
|
|
|
|
+ if (!netdev) {
|
|
|
|
+ mutex_unlock(&cfg80211_mutex);
|
|
|
|
+ return -ENODEV;
|
|
|
|
+ }
|
|
|
|
+ if (netdev->ieee80211_ptr) {
|
|
|
|
+ dev = wiphy_to_dev(
|
|
|
|
+ netdev->ieee80211_ptr->wiphy);
|
|
|
|
+ filter_wiphy = dev->wiphy_idx;
|
|
|
|
+ }
|
|
|
|
+ dev_put(netdev);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
list_for_each_entry(dev, &cfg80211_rdev_list, list) {
|
|
list_for_each_entry(dev, &cfg80211_rdev_list, list) {
|
|
if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
|
|
if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
|
|
continue;
|
|
continue;
|
|
if (++idx <= start)
|
|
if (++idx <= start)
|
|
continue;
|
|
continue;
|
|
- if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).portid,
|
|
|
|
- cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
|
|
|
- dev) < 0) {
|
|
|
|
- idx--;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
|
|
+ if (filter_wiphy != -1 && dev->wiphy_idx != filter_wiphy)
|
|
|
|
+ continue;
|
|
|
|
+ /* attempt to fit multiple wiphy data chunks into the skb */
|
|
|
|
+ do {
|
|
|
|
+ ret = nl80211_send_wiphy(dev, skb,
|
|
|
|
+ NETLINK_CB(cb->skb).portid,
|
|
|
|
+ cb->nlh->nlmsg_seq,
|
|
|
|
+ NLM_F_MULTI,
|
|
|
|
+ split, &cb->args[1],
|
|
|
|
+ &cb->args[2],
|
|
|
|
+ &cb->args[3]);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ /*
|
|
|
|
+ * If sending the wiphy data didn't fit (ENOBUFS
|
|
|
|
+ * or EMSGSIZE returned), this SKB is still
|
|
|
|
+ * empty (so it's not too big because another
|
|
|
|
+ * wiphy dataset is already in the skb) and
|
|
|
|
+ * we've not tried to adjust the dump allocation
|
|
|
|
+ * yet ... then adjust the alloc size to be
|
|
|
|
+ * bigger, and return 1 but with the empty skb.
|
|
|
|
+ * This results in an empty message being RX'ed
|
|
|
|
+ * in userspace, but that is ignored.
|
|
|
|
+ *
|
|
|
|
+ * We can then retry with the larger buffer.
|
|
|
|
+ */
|
|
|
|
+ if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
|
|
|
|
+ !skb->len &&
|
|
|
|
+ cb->min_dump_alloc < 4096) {
|
|
|
|
+ cb->min_dump_alloc = 4096;
|
|
|
|
+ mutex_unlock(&cfg80211_mutex);
|
|
|
|
+ return 1;
|
|
|
|
+ }
|
|
|
|
+ idx--;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ } while (cb->args[1] > 0);
|
|
|
|
+ break;
|
|
}
|
|
}
|
|
mutex_unlock(&cfg80211_mutex);
|
|
mutex_unlock(&cfg80211_mutex);
|
|
|
|
|
|
@@ -1412,11 +1638,12 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
|
|
struct sk_buff *msg;
|
|
struct sk_buff *msg;
|
|
struct cfg80211_registered_device *dev = info->user_ptr[0];
|
|
struct cfg80211_registered_device *dev = info->user_ptr[0];
|
|
|
|
|
|
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
|
|
|
+ msg = nlmsg_new(4096, GFP_KERNEL);
|
|
if (!msg)
|
|
if (!msg)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
|
|
|
- if (nl80211_send_wiphy(msg, info->snd_portid, info->snd_seq, 0, dev) < 0) {
|
|
|
|
|
|
+ if (nl80211_send_wiphy(dev, msg, info->snd_portid, info->snd_seq, 0,
|
|
|
|
+ false, NULL, NULL, NULL) < 0) {
|
|
nlmsg_free(msg);
|
|
nlmsg_free(msg);
|
|
return -ENOBUFS;
|
|
return -ENOBUFS;
|
|
}
|
|
}
|
|
@@ -3023,6 +3250,7 @@ static int parse_station_flags(struct genl_info *info,
|
|
sta_flags = nla_data(nla);
|
|
sta_flags = nla_data(nla);
|
|
params->sta_flags_mask = sta_flags->mask;
|
|
params->sta_flags_mask = sta_flags->mask;
|
|
params->sta_flags_set = sta_flags->set;
|
|
params->sta_flags_set = sta_flags->set;
|
|
|
|
+ params->sta_flags_set &= params->sta_flags_mask;
|
|
if ((params->sta_flags_mask |
|
|
if ((params->sta_flags_mask |
|
|
params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
|
|
params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
@@ -3376,6 +3604,136 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
|
|
return genlmsg_reply(msg, info);
|
|
return genlmsg_reply(msg, info);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+int cfg80211_check_station_change(struct wiphy *wiphy,
|
|
|
|
+ struct station_parameters *params,
|
|
|
|
+ enum cfg80211_station_type statype)
|
|
|
|
+{
|
|
|
|
+ if (params->listen_interval != -1)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ if (params->aid)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ /* When you run into this, adjust the code below for the new flag */
|
|
|
|
+ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7);
|
|
|
|
+
|
|
|
|
+ switch (statype) {
|
|
|
|
+ case CFG80211_STA_MESH_PEER_KERNEL:
|
|
|
|
+ case CFG80211_STA_MESH_PEER_USER:
|
|
|
|
+ /*
|
|
|
|
+ * No ignoring the TDLS flag here -- the userspace mesh
|
|
|
|
+ * code doesn't have the bug of including TDLS in the
|
|
|
|
+ * mask everywhere.
|
|
|
|
+ */
|
|
|
|
+ if (params->sta_flags_mask &
|
|
|
|
+ ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
|
|
|
|
+ BIT(NL80211_STA_FLAG_MFP) |
|
|
|
|
+ BIT(NL80211_STA_FLAG_AUTHORIZED)))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ break;
|
|
|
|
+ case CFG80211_STA_TDLS_PEER_SETUP:
|
|
|
|
+ case CFG80211_STA_TDLS_PEER_ACTIVE:
|
|
|
|
+ if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ /* ignore since it can't change */
|
|
|
|
+ params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ /* disallow mesh-specific things */
|
|
|
|
+ if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ if (params->local_pm)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (statype != CFG80211_STA_TDLS_PEER_SETUP &&
|
|
|
|
+ statype != CFG80211_STA_TDLS_PEER_ACTIVE) {
|
|
|
|
+ /* TDLS can't be set, ... */
|
|
|
|
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ /*
|
|
|
|
+ * ... but don't bother the driver with it. This works around
|
|
|
|
+ * a hostapd/wpa_supplicant issue -- it always includes the
|
|
|
|
+ * TLDS_PEER flag in the mask even for AP mode.
|
|
|
|
+ */
|
|
|
|
+ params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (statype != CFG80211_STA_TDLS_PEER_SETUP) {
|
|
|
|
+ /* reject other things that can't change */
|
|
|
|
+ if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ if (params->sta_modify_mask & STATION_PARAM_APPLY_CAPABILITY)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ if (params->supported_rates)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ if (params->ext_capab || params->ht_capa || params->vht_capa)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (statype != CFG80211_STA_AP_CLIENT) {
|
|
|
|
+ if (params->vlan)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ switch (statype) {
|
|
|
|
+ case CFG80211_STA_AP_MLME_CLIENT:
|
|
|
|
+ /* Use this only for authorizing/unauthorizing a station */
|
|
|
|
+ if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
+ break;
|
|
|
|
+ case CFG80211_STA_AP_CLIENT:
|
|
|
|
+ /* accept only the listed bits */
|
|
|
|
+ if (params->sta_flags_mask &
|
|
|
|
+ ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
|
|
|
|
+ BIT(NL80211_STA_FLAG_AUTHENTICATED) |
|
|
|
|
+ BIT(NL80211_STA_FLAG_ASSOCIATED) |
|
|
|
|
+ BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
|
|
|
|
+ BIT(NL80211_STA_FLAG_WME) |
|
|
|
|
+ BIT(NL80211_STA_FLAG_MFP)))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ /* but authenticated/associated only if driver handles it */
|
|
|
|
+ if (!(wiphy->features & NL80211_FEATURE_FULL_AP_CLIENT_STATE) &&
|
|
|
|
+ params->sta_flags_mask &
|
|
|
|
+ (BIT(NL80211_STA_FLAG_AUTHENTICATED) |
|
|
|
|
+ BIT(NL80211_STA_FLAG_ASSOCIATED)))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ break;
|
|
|
|
+ case CFG80211_STA_IBSS:
|
|
|
|
+ case CFG80211_STA_AP_STA:
|
|
|
|
+ /* reject any changes other than AUTHORIZED */
|
|
|
|
+ if (params->sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ break;
|
|
|
|
+ case CFG80211_STA_TDLS_PEER_SETUP:
|
|
|
|
+ /* reject any changes other than AUTHORIZED or WME */
|
|
|
|
+ if (params->sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
|
|
|
|
+ BIT(NL80211_STA_FLAG_WME)))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ /* force (at least) rates when authorizing */
|
|
|
|
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED) &&
|
|
|
|
+ !params->supported_rates)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ break;
|
|
|
|
+ case CFG80211_STA_TDLS_PEER_ACTIVE:
|
|
|
|
+ /* reject any changes */
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ case CFG80211_STA_MESH_PEER_KERNEL:
|
|
|
|
+ if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ break;
|
|
|
|
+ case CFG80211_STA_MESH_PEER_USER:
|
|
|
|
+ if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL(cfg80211_check_station_change);
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Get vlan interface making sure it is running and on the right wiphy.
|
|
* Get vlan interface making sure it is running and on the right wiphy.
|
|
*/
|
|
*/
|
|
@@ -3398,6 +3756,13 @@ static struct net_device *get_vlan(struct genl_info *info,
|
|
goto error;
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (v->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
|
|
|
|
+ v->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
|
|
|
|
+ v->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto error;
|
|
|
|
+ }
|
|
|
|
+
|
|
if (!netif_running(v)) {
|
|
if (!netif_running(v)) {
|
|
ret = -ENETDOWN;
|
|
ret = -ENETDOWN;
|
|
goto error;
|
|
goto error;
|
|
@@ -3415,21 +3780,13 @@ nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = {
|
|
[NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
|
|
[NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
|
|
};
|
|
};
|
|
|
|
|
|
-static int nl80211_set_station_tdls(struct genl_info *info,
|
|
|
|
- struct station_parameters *params)
|
|
|
|
|
|
+static int nl80211_parse_sta_wme(struct genl_info *info,
|
|
|
|
+ struct station_parameters *params)
|
|
{
|
|
{
|
|
struct nlattr *tb[NL80211_STA_WME_MAX + 1];
|
|
struct nlattr *tb[NL80211_STA_WME_MAX + 1];
|
|
struct nlattr *nla;
|
|
struct nlattr *nla;
|
|
int err;
|
|
int err;
|
|
|
|
|
|
- /* Dummy STA entry gets updated once the peer capabilities are known */
|
|
|
|
- if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
|
|
|
|
- params->ht_capa =
|
|
|
|
- nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
|
|
|
|
- if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
|
|
|
|
- params->vht_capa =
|
|
|
|
- nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
|
|
|
|
-
|
|
|
|
/* parse WME attributes if present */
|
|
/* parse WME attributes if present */
|
|
if (!info->attrs[NL80211_ATTR_STA_WME])
|
|
if (!info->attrs[NL80211_ATTR_STA_WME])
|
|
return 0;
|
|
return 0;
|
|
@@ -3457,18 +3814,34 @@ static int nl80211_set_station_tdls(struct genl_info *info,
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int nl80211_set_station_tdls(struct genl_info *info,
|
|
|
|
+ struct station_parameters *params)
|
|
|
|
+{
|
|
|
|
+ /* Dummy STA entry gets updated once the peer capabilities are known */
|
|
|
|
+ if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
|
|
|
|
+ params->ht_capa =
|
|
|
|
+ nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
|
|
|
|
+ if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
|
|
|
|
+ params->vht_capa =
|
|
|
|
+ nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
|
|
|
|
+
|
|
|
|
+ return nl80211_parse_sta_wme(info, params);
|
|
|
|
+}
|
|
|
|
+
|
|
static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
|
|
static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
{
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
- int err;
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
struct net_device *dev = info->user_ptr[1];
|
|
struct station_parameters params;
|
|
struct station_parameters params;
|
|
- u8 *mac_addr = NULL;
|
|
|
|
|
|
+ u8 *mac_addr;
|
|
|
|
+ int err;
|
|
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
|
|
params.listen_interval = -1;
|
|
params.listen_interval = -1;
|
|
- params.plink_state = -1;
|
|
|
|
|
|
+
|
|
|
|
+ if (!rdev->ops->change_station)
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_STA_AID])
|
|
if (info->attrs[NL80211_ATTR_STA_AID])
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
@@ -3501,152 +3874,62 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
|
|
if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
|
|
if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
- if (!rdev->ops->change_station)
|
|
|
|
- return -EOPNOTSUPP;
|
|
|
|
-
|
|
|
|
if (parse_station_flags(info, dev->ieee80211_ptr->iftype, ¶ms))
|
|
if (parse_station_flags(info, dev->ieee80211_ptr->iftype, ¶ms))
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
- if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
|
|
|
|
|
|
+ if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) {
|
|
params.plink_action =
|
|
params.plink_action =
|
|
- nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
|
|
|
|
-
|
|
|
|
- if (info->attrs[NL80211_ATTR_STA_PLINK_STATE])
|
|
|
|
- params.plink_state =
|
|
|
|
- nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
|
|
|
|
-
|
|
|
|
- if (info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]) {
|
|
|
|
- enum nl80211_mesh_power_mode pm = nla_get_u32(
|
|
|
|
- info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]);
|
|
|
|
-
|
|
|
|
- if (pm <= NL80211_MESH_POWER_UNKNOWN ||
|
|
|
|
- pm > NL80211_MESH_POWER_MAX)
|
|
|
|
- return -EINVAL;
|
|
|
|
-
|
|
|
|
- params.local_pm = pm;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- switch (dev->ieee80211_ptr->iftype) {
|
|
|
|
- case NL80211_IFTYPE_AP:
|
|
|
|
- case NL80211_IFTYPE_AP_VLAN:
|
|
|
|
- case NL80211_IFTYPE_P2P_GO:
|
|
|
|
- /* disallow mesh-specific things */
|
|
|
|
- if (params.plink_action)
|
|
|
|
- return -EINVAL;
|
|
|
|
- if (params.local_pm)
|
|
|
|
- return -EINVAL;
|
|
|
|
-
|
|
|
|
- /* TDLS can't be set, ... */
|
|
|
|
- if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
|
|
|
|
- return -EINVAL;
|
|
|
|
- /*
|
|
|
|
- * ... but don't bother the driver with it. This works around
|
|
|
|
- * a hostapd/wpa_supplicant issue -- it always includes the
|
|
|
|
- * TLDS_PEER flag in the mask even for AP mode.
|
|
|
|
- */
|
|
|
|
- params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
|
|
|
|
-
|
|
|
|
- /* accept only the listed bits */
|
|
|
|
- if (params.sta_flags_mask &
|
|
|
|
- ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
|
|
|
|
- BIT(NL80211_STA_FLAG_AUTHENTICATED) |
|
|
|
|
- BIT(NL80211_STA_FLAG_ASSOCIATED) |
|
|
|
|
- BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
|
|
|
|
- BIT(NL80211_STA_FLAG_WME) |
|
|
|
|
- BIT(NL80211_STA_FLAG_MFP)))
|
|
|
|
- return -EINVAL;
|
|
|
|
-
|
|
|
|
- /* but authenticated/associated only if driver handles it */
|
|
|
|
- if (!(rdev->wiphy.features &
|
|
|
|
- NL80211_FEATURE_FULL_AP_CLIENT_STATE) &&
|
|
|
|
- params.sta_flags_mask &
|
|
|
|
- (BIT(NL80211_STA_FLAG_AUTHENTICATED) |
|
|
|
|
- BIT(NL80211_STA_FLAG_ASSOCIATED)))
|
|
|
|
- return -EINVAL;
|
|
|
|
-
|
|
|
|
- /* reject other things that can't change */
|
|
|
|
- if (params.supported_rates)
|
|
|
|
- return -EINVAL;
|
|
|
|
- if (info->attrs[NL80211_ATTR_STA_CAPABILITY])
|
|
|
|
|
|
+ nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
|
|
|
|
+ if (params.plink_action >= NUM_NL80211_PLINK_ACTIONS)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
- if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY])
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (info->attrs[NL80211_ATTR_STA_PLINK_STATE]) {
|
|
|
|
+ params.plink_state =
|
|
|
|
+ nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
|
|
|
|
+ if (params.plink_state >= NUM_NL80211_PLINK_STATES)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
- if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
|
|
|
|
- info->attrs[NL80211_ATTR_VHT_CAPABILITY])
|
|
|
|
|
|
+ params.sta_modify_mask |= STATION_PARAM_APPLY_PLINK_STATE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]) {
|
|
|
|
+ enum nl80211_mesh_power_mode pm = nla_get_u32(
|
|
|
|
+ info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]);
|
|
|
|
+
|
|
|
|
+ if (pm <= NL80211_MESH_POWER_UNKNOWN ||
|
|
|
|
+ pm > NL80211_MESH_POWER_MAX)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
- /* must be last in here for error handling */
|
|
|
|
- params.vlan = get_vlan(info, rdev);
|
|
|
|
- if (IS_ERR(params.vlan))
|
|
|
|
- return PTR_ERR(params.vlan);
|
|
|
|
- break;
|
|
|
|
|
|
+ params.local_pm = pm;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Include parameters for TDLS peer (will check later) */
|
|
|
|
+ err = nl80211_set_station_tdls(info, ¶ms);
|
|
|
|
+ if (err)
|
|
|
|
+ return err;
|
|
|
|
+
|
|
|
|
+ params.vlan = get_vlan(info, rdev);
|
|
|
|
+ if (IS_ERR(params.vlan))
|
|
|
|
+ return PTR_ERR(params.vlan);
|
|
|
|
+
|
|
|
|
+ switch (dev->ieee80211_ptr->iftype) {
|
|
|
|
+ case NL80211_IFTYPE_AP:
|
|
|
|
+ case NL80211_IFTYPE_AP_VLAN:
|
|
|
|
+ case NL80211_IFTYPE_P2P_GO:
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
|
case NL80211_IFTYPE_STATION:
|
|
case NL80211_IFTYPE_STATION:
|
|
- /*
|
|
|
|
- * Don't allow userspace to change the TDLS_PEER flag,
|
|
|
|
- * but silently ignore attempts to change it since we
|
|
|
|
- * don't have state here to verify that it doesn't try
|
|
|
|
- * to change the flag.
|
|
|
|
- */
|
|
|
|
- params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
|
|
|
|
- /* Include parameters for TDLS peer (driver will check) */
|
|
|
|
- err = nl80211_set_station_tdls(info, ¶ms);
|
|
|
|
- if (err)
|
|
|
|
- return err;
|
|
|
|
- /* disallow things sta doesn't support */
|
|
|
|
- if (params.plink_action)
|
|
|
|
- return -EINVAL;
|
|
|
|
- if (params.local_pm)
|
|
|
|
- return -EINVAL;
|
|
|
|
- /* reject any changes other than AUTHORIZED or WME (for TDLS) */
|
|
|
|
- if (params.sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
|
|
|
|
- BIT(NL80211_STA_FLAG_WME)))
|
|
|
|
- return -EINVAL;
|
|
|
|
- break;
|
|
|
|
case NL80211_IFTYPE_ADHOC:
|
|
case NL80211_IFTYPE_ADHOC:
|
|
- /* disallow things sta doesn't support */
|
|
|
|
- if (params.plink_action)
|
|
|
|
- return -EINVAL;
|
|
|
|
- if (params.local_pm)
|
|
|
|
- return -EINVAL;
|
|
|
|
- if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
|
|
|
|
- info->attrs[NL80211_ATTR_VHT_CAPABILITY])
|
|
|
|
- return -EINVAL;
|
|
|
|
- /* reject any changes other than AUTHORIZED */
|
|
|
|
- if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
|
|
|
|
- return -EINVAL;
|
|
|
|
- break;
|
|
|
|
case NL80211_IFTYPE_MESH_POINT:
|
|
case NL80211_IFTYPE_MESH_POINT:
|
|
- /* disallow things mesh doesn't support */
|
|
|
|
- if (params.vlan)
|
|
|
|
- return -EINVAL;
|
|
|
|
- if (params.supported_rates)
|
|
|
|
- return -EINVAL;
|
|
|
|
- if (info->attrs[NL80211_ATTR_STA_CAPABILITY])
|
|
|
|
- return -EINVAL;
|
|
|
|
- if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY])
|
|
|
|
- return -EINVAL;
|
|
|
|
- if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
|
|
|
|
- info->attrs[NL80211_ATTR_VHT_CAPABILITY])
|
|
|
|
- return -EINVAL;
|
|
|
|
- /*
|
|
|
|
- * No special handling for TDLS here -- the userspace
|
|
|
|
- * mesh code doesn't have this bug.
|
|
|
|
- */
|
|
|
|
- if (params.sta_flags_mask &
|
|
|
|
- ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
|
|
|
|
- BIT(NL80211_STA_FLAG_MFP) |
|
|
|
|
- BIT(NL80211_STA_FLAG_AUTHORIZED)))
|
|
|
|
- return -EINVAL;
|
|
|
|
break;
|
|
break;
|
|
default:
|
|
default:
|
|
- return -EOPNOTSUPP;
|
|
|
|
|
|
+ err = -EOPNOTSUPP;
|
|
|
|
+ goto out_put_vlan;
|
|
}
|
|
}
|
|
|
|
|
|
- /* be aware of params.vlan when changing code here */
|
|
|
|
-
|
|
|
|
|
|
+ /* driver will call cfg80211_check_station_change() */
|
|
err = rdev_change_station(rdev, dev, mac_addr, ¶ms);
|
|
err = rdev_change_station(rdev, dev, mac_addr, ¶ms);
|
|
|
|
|
|
|
|
+ out_put_vlan:
|
|
if (params.vlan)
|
|
if (params.vlan)
|
|
dev_put(params.vlan);
|
|
dev_put(params.vlan);
|
|
|
|
|
|
@@ -3663,6 +3946,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
|
|
|
|
+ if (!rdev->ops->add_station)
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
+
|
|
if (!info->attrs[NL80211_ATTR_MAC])
|
|
if (!info->attrs[NL80211_ATTR_MAC])
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
@@ -3708,50 +3994,32 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
|
|
params.vht_capa =
|
|
params.vht_capa =
|
|
nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
|
|
nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
|
|
|
|
|
|
- if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
|
|
|
|
|
|
+ if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) {
|
|
params.plink_action =
|
|
params.plink_action =
|
|
- nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
|
|
|
|
|
|
+ nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
|
|
|
|
+ if (params.plink_action >= NUM_NL80211_PLINK_ACTIONS)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
|
|
- if (!rdev->ops->add_station)
|
|
|
|
- return -EOPNOTSUPP;
|
|
|
|
|
|
+ err = nl80211_parse_sta_wme(info, ¶ms);
|
|
|
|
+ if (err)
|
|
|
|
+ return err;
|
|
|
|
|
|
if (parse_station_flags(info, dev->ieee80211_ptr->iftype, ¶ms))
|
|
if (parse_station_flags(info, dev->ieee80211_ptr->iftype, ¶ms))
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
+ /* When you run into this, adjust the code below for the new flag */
|
|
|
|
+ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7);
|
|
|
|
+
|
|
switch (dev->ieee80211_ptr->iftype) {
|
|
switch (dev->ieee80211_ptr->iftype) {
|
|
case NL80211_IFTYPE_AP:
|
|
case NL80211_IFTYPE_AP:
|
|
case NL80211_IFTYPE_AP_VLAN:
|
|
case NL80211_IFTYPE_AP_VLAN:
|
|
case NL80211_IFTYPE_P2P_GO:
|
|
case NL80211_IFTYPE_P2P_GO:
|
|
- /* parse WME attributes if sta is WME capable */
|
|
|
|
- if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
|
|
|
|
- (params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)) &&
|
|
|
|
- info->attrs[NL80211_ATTR_STA_WME]) {
|
|
|
|
- struct nlattr *tb[NL80211_STA_WME_MAX + 1];
|
|
|
|
- struct nlattr *nla;
|
|
|
|
-
|
|
|
|
- nla = info->attrs[NL80211_ATTR_STA_WME];
|
|
|
|
- err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla,
|
|
|
|
- nl80211_sta_wme_policy);
|
|
|
|
- if (err)
|
|
|
|
- return err;
|
|
|
|
-
|
|
|
|
- if (tb[NL80211_STA_WME_UAPSD_QUEUES])
|
|
|
|
- params.uapsd_queues =
|
|
|
|
- nla_get_u8(tb[NL80211_STA_WME_UAPSD_QUEUES]);
|
|
|
|
- if (params.uapsd_queues &
|
|
|
|
- ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
|
|
|
|
- return -EINVAL;
|
|
|
|
-
|
|
|
|
- if (tb[NL80211_STA_WME_MAX_SP])
|
|
|
|
- params.max_sp =
|
|
|
|
- nla_get_u8(tb[NL80211_STA_WME_MAX_SP]);
|
|
|
|
|
|
+ /* ignore WME attributes if iface/sta is not capable */
|
|
|
|
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) ||
|
|
|
|
+ !(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)))
|
|
|
|
+ params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
|
|
|
|
|
|
- if (params.max_sp &
|
|
|
|
- ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
|
|
|
|
- return -EINVAL;
|
|
|
|
-
|
|
|
|
- params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
|
|
|
|
- }
|
|
|
|
/* TDLS peers cannot be added */
|
|
/* TDLS peers cannot be added */
|
|
if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
|
|
if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
@@ -3772,6 +4040,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
|
|
return PTR_ERR(params.vlan);
|
|
return PTR_ERR(params.vlan);
|
|
break;
|
|
break;
|
|
case NL80211_IFTYPE_MESH_POINT:
|
|
case NL80211_IFTYPE_MESH_POINT:
|
|
|
|
+ /* ignore uAPSD data */
|
|
|
|
+ params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
|
|
|
|
+
|
|
/* associated is disallowed */
|
|
/* associated is disallowed */
|
|
if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED))
|
|
if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED))
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
@@ -3780,8 +4051,14 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
break;
|
|
break;
|
|
case NL80211_IFTYPE_STATION:
|
|
case NL80211_IFTYPE_STATION:
|
|
- /* associated is disallowed */
|
|
|
|
- if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED))
|
|
|
|
|
|
+ case NL80211_IFTYPE_P2P_CLIENT:
|
|
|
|
+ /* ignore uAPSD data */
|
|
|
|
+ params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
|
|
|
|
+
|
|
|
|
+ /* these are disallowed */
|
|
|
|
+ if (params.sta_flags_mask &
|
|
|
|
+ (BIT(NL80211_STA_FLAG_ASSOCIATED) |
|
|
|
|
+ BIT(NL80211_STA_FLAG_AUTHENTICATED)))
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
/* Only TDLS peers can be added */
|
|
/* Only TDLS peers can be added */
|
|
if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
|
|
if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
|
|
@@ -3792,6 +4069,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
|
|
/* ... with external setup is supported */
|
|
/* ... with external setup is supported */
|
|
if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
|
|
if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
|
|
return -EOPNOTSUPP;
|
|
return -EOPNOTSUPP;
|
|
|
|
+ /*
|
|
|
|
+ * Older wpa_supplicant versions always mark the TDLS peer
|
|
|
|
+ * as authorized, but it shouldn't yet be.
|
|
|
|
+ */
|
|
|
|
+ params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_AUTHORIZED);
|
|
break;
|
|
break;
|
|
default:
|
|
default:
|
|
return -EOPNOTSUPP;
|
|
return -EOPNOTSUPP;
|
|
@@ -4336,6 +4618,7 @@ static const struct nla_policy
|
|
[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
|
|
[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
|
|
[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
|
|
[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
|
|
[NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
|
|
[NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
|
|
|
|
+ [NL80211_MESH_SETUP_USERSPACE_MPM] = { .type = NLA_FLAG },
|
|
[NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
|
|
[NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
|
|
.len = IEEE80211_MAX_DATA_LEN },
|
|
.len = IEEE80211_MAX_DATA_LEN },
|
|
[NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG },
|
|
[NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG },
|
|
@@ -4474,6 +4757,7 @@ do { \
|
|
static int nl80211_parse_mesh_setup(struct genl_info *info,
|
|
static int nl80211_parse_mesh_setup(struct genl_info *info,
|
|
struct mesh_setup *setup)
|
|
struct mesh_setup *setup)
|
|
{
|
|
{
|
|
|
|
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1];
|
|
struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1];
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MESH_SETUP])
|
|
if (!info->attrs[NL80211_ATTR_MESH_SETUP])
|
|
@@ -4510,8 +4794,14 @@ static int nl80211_parse_mesh_setup(struct genl_info *info,
|
|
setup->ie = nla_data(ieattr);
|
|
setup->ie = nla_data(ieattr);
|
|
setup->ie_len = nla_len(ieattr);
|
|
setup->ie_len = nla_len(ieattr);
|
|
}
|
|
}
|
|
|
|
+ if (tb[NL80211_MESH_SETUP_USERSPACE_MPM] &&
|
|
|
|
+ !(rdev->wiphy.features & NL80211_FEATURE_USERSPACE_MPM))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ setup->user_mpm = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_MPM]);
|
|
setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]);
|
|
setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]);
|
|
setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]);
|
|
setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]);
|
|
|
|
+ if (setup->is_secure)
|
|
|
|
+ setup->user_mpm = true;
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -5706,14 +5996,10 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
{
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
struct net_device *dev = info->user_ptr[1];
|
|
struct net_device *dev = info->user_ptr[1];
|
|
- struct cfg80211_crypto_settings crypto;
|
|
|
|
struct ieee80211_channel *chan;
|
|
struct ieee80211_channel *chan;
|
|
- const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
|
|
|
|
- int err, ssid_len, ie_len = 0;
|
|
|
|
- bool use_mfp = false;
|
|
|
|
- u32 flags = 0;
|
|
|
|
- struct ieee80211_ht_cap *ht_capa = NULL;
|
|
|
|
- struct ieee80211_ht_cap *ht_capa_mask = NULL;
|
|
|
|
|
|
+ struct cfg80211_assoc_request req = {};
|
|
|
|
+ const u8 *bssid, *ssid;
|
|
|
|
+ int err, ssid_len = 0;
|
|
|
|
|
|
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
|
|
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
@@ -5741,41 +6027,58 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
|
|
ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
|
|
ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_IE]) {
|
|
if (info->attrs[NL80211_ATTR_IE]) {
|
|
- ie = nla_data(info->attrs[NL80211_ATTR_IE]);
|
|
|
|
- ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
|
|
|
|
|
|
+ req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
|
|
|
|
+ req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
|
|
}
|
|
}
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_USE_MFP]) {
|
|
if (info->attrs[NL80211_ATTR_USE_MFP]) {
|
|
enum nl80211_mfp mfp =
|
|
enum nl80211_mfp mfp =
|
|
nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
|
|
nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
|
|
if (mfp == NL80211_MFP_REQUIRED)
|
|
if (mfp == NL80211_MFP_REQUIRED)
|
|
- use_mfp = true;
|
|
|
|
|
|
+ req.use_mfp = true;
|
|
else if (mfp != NL80211_MFP_NO)
|
|
else if (mfp != NL80211_MFP_NO)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_PREV_BSSID])
|
|
if (info->attrs[NL80211_ATTR_PREV_BSSID])
|
|
- prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
|
|
|
|
|
|
+ req.prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
|
|
|
|
|
|
if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
|
|
if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
|
|
- flags |= ASSOC_REQ_DISABLE_HT;
|
|
|
|
|
|
+ req.flags |= ASSOC_REQ_DISABLE_HT;
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
|
|
if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
|
|
- ht_capa_mask =
|
|
|
|
- nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]);
|
|
|
|
|
|
+ memcpy(&req.ht_capa_mask,
|
|
|
|
+ nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
|
|
|
|
+ sizeof(req.ht_capa_mask));
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
|
|
if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
|
|
- if (!ht_capa_mask)
|
|
|
|
|
|
+ if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
- ht_capa = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
|
|
|
|
|
|
+ memcpy(&req.ht_capa,
|
|
|
|
+ nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
|
|
|
|
+ sizeof(req.ht_capa));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_VHT]))
|
|
|
|
+ req.flags |= ASSOC_REQ_DISABLE_VHT;
|
|
|
|
+
|
|
|
|
+ if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
|
|
|
|
+ memcpy(&req.vht_capa_mask,
|
|
|
|
+ nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]),
|
|
|
|
+ sizeof(req.vht_capa_mask));
|
|
|
|
+
|
|
|
|
+ if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) {
|
|
|
|
+ if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ memcpy(&req.vht_capa,
|
|
|
|
+ nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]),
|
|
|
|
+ sizeof(req.vht_capa));
|
|
}
|
|
}
|
|
|
|
|
|
- err = nl80211_crypto_settings(rdev, info, &crypto, 1);
|
|
|
|
|
|
+ err = nl80211_crypto_settings(rdev, info, &req.crypto, 1);
|
|
if (!err)
|
|
if (!err)
|
|
- err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
|
|
|
|
- ssid, ssid_len, ie, ie_len, use_mfp,
|
|
|
|
- &crypto, flags, ht_capa,
|
|
|
|
- ht_capa_mask);
|
|
|
|
|
|
+ err = cfg80211_mlme_assoc(rdev, dev, chan, bssid,
|
|
|
|
+ ssid, ssid_len, &req);
|
|
|
|
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
@@ -6355,6 +6658,24 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
|
|
sizeof(connect.ht_capa));
|
|
sizeof(connect.ht_capa));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_VHT]))
|
|
|
|
+ connect.flags |= ASSOC_REQ_DISABLE_VHT;
|
|
|
|
+
|
|
|
|
+ if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
|
|
|
|
+ memcpy(&connect.vht_capa_mask,
|
|
|
|
+ nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]),
|
|
|
|
+ sizeof(connect.vht_capa_mask));
|
|
|
|
+
|
|
|
|
+ if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) {
|
|
|
|
+ if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) {
|
|
|
|
+ kfree(connkeys);
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+ memcpy(&connect.vht_capa,
|
|
|
|
+ nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]),
|
|
|
|
+ sizeof(connect.vht_capa));
|
|
|
|
+ }
|
|
|
|
+
|
|
err = cfg80211_connect(rdev, dev, &connect, connkeys);
|
|
err = cfg80211_connect(rdev, dev, &connect, connkeys);
|
|
if (err)
|
|
if (err)
|
|
kfree(connkeys);
|
|
kfree(connkeys);
|
|
@@ -7128,6 +7449,9 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (setup.user_mpm)
|
|
|
|
+ cfg.auto_open_plinks = false;
|
|
|
|
+
|
|
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
|
|
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
|
|
err = nl80211_parse_chandef(rdev, info, &setup.chandef);
|
|
err = nl80211_parse_chandef(rdev, info, &setup.chandef);
|
|
if (err)
|
|
if (err)
|
|
@@ -7327,7 +7651,8 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
if (nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) >
|
|
if (nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) >
|
|
- rdev->wiphy.wowlan.tcp->data_interval_max)
|
|
|
|
|
|
+ rdev->wiphy.wowlan.tcp->data_interval_max ||
|
|
|
|
+ nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) == 0)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
wake_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]);
|
|
wake_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]);
|
|
@@ -7823,6 +8148,54 @@ static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int nl80211_get_protocol_features(struct sk_buff *skb,
|
|
|
|
+ struct genl_info *info)
|
|
|
|
+{
|
|
|
|
+ void *hdr;
|
|
|
|
+ struct sk_buff *msg;
|
|
|
|
+
|
|
|
|
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
|
+ if (!msg)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
|
|
|
|
+ NL80211_CMD_GET_PROTOCOL_FEATURES);
|
|
|
|
+ if (!hdr)
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+
|
|
|
|
+ if (nla_put_u32(msg, NL80211_ATTR_PROTOCOL_FEATURES,
|
|
|
|
+ NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+
|
|
|
|
+ genlmsg_end(msg, hdr);
|
|
|
|
+ return genlmsg_reply(msg, info);
|
|
|
|
+
|
|
|
|
+ nla_put_failure:
|
|
|
|
+ kfree_skb(msg);
|
|
|
|
+ return -ENOBUFS;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int nl80211_update_ft_ies(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
+{
|
|
|
|
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
+ struct cfg80211_update_ft_ies_params ft_params;
|
|
|
|
+ struct net_device *dev = info->user_ptr[1];
|
|
|
|
+
|
|
|
|
+ if (!rdev->ops->update_ft_ies)
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
+
|
|
|
|
+ if (!info->attrs[NL80211_ATTR_MDID] ||
|
|
|
|
+ !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ memset(&ft_params, 0, sizeof(ft_params));
|
|
|
|
+ ft_params.md = nla_get_u16(info->attrs[NL80211_ATTR_MDID]);
|
|
|
|
+ ft_params.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
|
|
|
|
+ ft_params.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
|
|
|
|
+
|
|
|
|
+ return rdev_update_ft_ies(rdev, dev, &ft_params);
|
|
|
|
+}
|
|
|
|
+
|
|
#define NL80211_FLAG_NEED_WIPHY 0x01
|
|
#define NL80211_FLAG_NEED_WIPHY 0x01
|
|
#define NL80211_FLAG_NEED_NETDEV 0x02
|
|
#define NL80211_FLAG_NEED_NETDEV 0x02
|
|
#define NL80211_FLAG_NEED_RTNL 0x04
|
|
#define NL80211_FLAG_NEED_RTNL 0x04
|
|
@@ -8499,6 +8872,19 @@ static struct genl_ops nl80211_ops[] = {
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
NL80211_FLAG_NEED_RTNL,
|
|
NL80211_FLAG_NEED_RTNL,
|
|
},
|
|
},
|
|
|
|
+ {
|
|
|
|
+ .cmd = NL80211_CMD_GET_PROTOCOL_FEATURES,
|
|
|
|
+ .doit = nl80211_get_protocol_features,
|
|
|
|
+ .policy = nl80211_policy,
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ .cmd = NL80211_CMD_UPDATE_FT_IES,
|
|
|
|
+ .doit = nl80211_update_ft_ies,
|
|
|
|
+ .policy = nl80211_policy,
|
|
|
|
+ .flags = GENL_ADMIN_PERM,
|
|
|
|
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
|
|
+ NL80211_FLAG_NEED_RTNL,
|
|
|
|
+ },
|
|
};
|
|
};
|
|
|
|
|
|
static struct genl_multicast_group nl80211_mlme_mcgrp = {
|
|
static struct genl_multicast_group nl80211_mlme_mcgrp = {
|
|
@@ -8526,7 +8912,8 @@ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
|
|
if (!msg)
|
|
if (!msg)
|
|
return;
|
|
return;
|
|
|
|
|
|
- if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
|
|
|
|
|
|
+ if (nl80211_send_wiphy(rdev, msg, 0, 0, 0,
|
|
|
|
+ false, NULL, NULL, NULL) < 0) {
|
|
nlmsg_free(msg);
|
|
nlmsg_free(msg);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
@@ -8850,21 +9237,31 @@ void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
|
|
NL80211_CMD_DISASSOCIATE, gfp);
|
|
NL80211_CMD_DISASSOCIATE, gfp);
|
|
}
|
|
}
|
|
|
|
|
|
-void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev,
|
|
|
|
- struct net_device *netdev, const u8 *buf,
|
|
|
|
- size_t len, gfp_t gfp)
|
|
|
|
|
|
+void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf,
|
|
|
|
+ size_t len)
|
|
{
|
|
{
|
|
- nl80211_send_mlme_event(rdev, netdev, buf, len,
|
|
|
|
- NL80211_CMD_UNPROT_DEAUTHENTICATE, gfp);
|
|
|
|
|
|
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
+ struct wiphy *wiphy = wdev->wiphy;
|
|
|
|
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
|
|
|
|
+
|
|
|
|
+ trace_cfg80211_send_unprot_deauth(dev);
|
|
|
|
+ nl80211_send_mlme_event(rdev, dev, buf, len,
|
|
|
|
+ NL80211_CMD_UNPROT_DEAUTHENTICATE, GFP_ATOMIC);
|
|
}
|
|
}
|
|
|
|
+EXPORT_SYMBOL(cfg80211_send_unprot_deauth);
|
|
|
|
|
|
-void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev,
|
|
|
|
- struct net_device *netdev, const u8 *buf,
|
|
|
|
- size_t len, gfp_t gfp)
|
|
|
|
|
|
+void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf,
|
|
|
|
+ size_t len)
|
|
{
|
|
{
|
|
- nl80211_send_mlme_event(rdev, netdev, buf, len,
|
|
|
|
- NL80211_CMD_UNPROT_DISASSOCIATE, gfp);
|
|
|
|
|
|
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
+ struct wiphy *wiphy = wdev->wiphy;
|
|
|
|
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
|
|
|
|
+
|
|
|
|
+ trace_cfg80211_send_unprot_disassoc(dev);
|
|
|
|
+ nl80211_send_mlme_event(rdev, dev, buf, len,
|
|
|
|
+ NL80211_CMD_UNPROT_DISASSOCIATE, GFP_ATOMIC);
|
|
}
|
|
}
|
|
|
|
+EXPORT_SYMBOL(cfg80211_send_unprot_disassoc);
|
|
|
|
|
|
static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
|
|
static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
|
|
struct net_device *netdev, int cmd,
|
|
struct net_device *netdev, int cmd,
|
|
@@ -9067,14 +9464,19 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
|
|
nlmsg_free(msg);
|
|
nlmsg_free(msg);
|
|
}
|
|
}
|
|
|
|
|
|
-void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
|
|
|
|
- struct net_device *netdev,
|
|
|
|
- const u8 *macaddr, const u8* ie, u8 ie_len,
|
|
|
|
- gfp_t gfp)
|
|
|
|
|
|
+void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,
|
|
|
|
+ const u8* ie, u8 ie_len, gfp_t gfp)
|
|
{
|
|
{
|
|
|
|
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
|
|
struct sk_buff *msg;
|
|
struct sk_buff *msg;
|
|
void *hdr;
|
|
void *hdr;
|
|
|
|
|
|
|
|
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT))
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ trace_cfg80211_notify_new_peer_candidate(dev, addr);
|
|
|
|
+
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
|
if (!msg)
|
|
if (!msg)
|
|
return;
|
|
return;
|
|
@@ -9086,8 +9488,8 @@ void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
|
|
}
|
|
}
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
|
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_MAC, ETH_ALEN, macaddr) ||
|
|
|
|
|
|
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
|
|
|
|
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
|
|
(ie_len && ie &&
|
|
(ie_len && ie &&
|
|
nla_put(msg, NL80211_ATTR_IE, ie_len , ie)))
|
|
nla_put(msg, NL80211_ATTR_IE, ie_len , ie)))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
@@ -9102,6 +9504,7 @@ void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
|
|
genlmsg_cancel(msg, hdr);
|
|
genlmsg_cancel(msg, hdr);
|
|
nlmsg_free(msg);
|
|
nlmsg_free(msg);
|
|
}
|
|
}
|
|
|
|
+EXPORT_SYMBOL(cfg80211_notify_new_peer_candidate);
|
|
|
|
|
|
void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
|
|
void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
|
|
struct net_device *netdev, const u8 *addr,
|
|
struct net_device *netdev, const u8 *addr,
|
|
@@ -9170,7 +9573,7 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
|
|
nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
|
|
nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
|
|
if (!nl_freq)
|
|
if (!nl_freq)
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
- if (nl80211_msg_put_channel(msg, channel_before))
|
|
|
|
|
|
+ if (nl80211_msg_put_channel(msg, channel_before, false))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
nla_nest_end(msg, nl_freq);
|
|
nla_nest_end(msg, nl_freq);
|
|
|
|
|
|
@@ -9178,7 +9581,7 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
|
|
nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
|
|
nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
|
|
if (!nl_freq)
|
|
if (!nl_freq)
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
- if (nl80211_msg_put_channel(msg, channel_after))
|
|
|
|
|
|
+ if (nl80211_msg_put_channel(msg, channel_after, false))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
nla_nest_end(msg, nl_freq);
|
|
nla_nest_end(msg, nl_freq);
|
|
|
|
|
|
@@ -9240,31 +9643,42 @@ static void nl80211_send_remain_on_chan_event(
|
|
nlmsg_free(msg);
|
|
nlmsg_free(msg);
|
|
}
|
|
}
|
|
|
|
|
|
-void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
|
|
|
|
- struct wireless_dev *wdev, u64 cookie,
|
|
|
|
- struct ieee80211_channel *chan,
|
|
|
|
- unsigned int duration, gfp_t gfp)
|
|
|
|
|
|
+void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie,
|
|
|
|
+ struct ieee80211_channel *chan,
|
|
|
|
+ unsigned int duration, gfp_t gfp)
|
|
{
|
|
{
|
|
|
|
+ struct wiphy *wiphy = wdev->wiphy;
|
|
|
|
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
|
|
|
|
+
|
|
|
|
+ trace_cfg80211_ready_on_channel(wdev, cookie, chan, duration);
|
|
nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
|
|
nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
|
|
rdev, wdev, cookie, chan,
|
|
rdev, wdev, cookie, chan,
|
|
duration, gfp);
|
|
duration, gfp);
|
|
}
|
|
}
|
|
|
|
+EXPORT_SYMBOL(cfg80211_ready_on_channel);
|
|
|
|
|
|
-void nl80211_send_remain_on_channel_cancel(
|
|
|
|
- struct cfg80211_registered_device *rdev,
|
|
|
|
- struct wireless_dev *wdev,
|
|
|
|
- u64 cookie, struct ieee80211_channel *chan, gfp_t gfp)
|
|
|
|
|
|
+void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie,
|
|
|
|
+ struct ieee80211_channel *chan,
|
|
|
|
+ gfp_t gfp)
|
|
{
|
|
{
|
|
|
|
+ struct wiphy *wiphy = wdev->wiphy;
|
|
|
|
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
|
|
|
|
+
|
|
|
|
+ trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan);
|
|
nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
|
|
nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
|
|
rdev, wdev, cookie, chan, 0, gfp);
|
|
rdev, wdev, cookie, chan, 0, gfp);
|
|
}
|
|
}
|
|
|
|
+EXPORT_SYMBOL(cfg80211_remain_on_channel_expired);
|
|
|
|
|
|
-void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
|
|
|
|
- struct net_device *dev, const u8 *mac_addr,
|
|
|
|
- struct station_info *sinfo, gfp_t gfp)
|
|
|
|
|
|
+void cfg80211_new_sta(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_dev(wiphy);
|
|
struct sk_buff *msg;
|
|
struct sk_buff *msg;
|
|
|
|
|
|
|
|
+ trace_cfg80211_new_sta(dev, mac_addr, sinfo);
|
|
|
|
+
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
|
if (!msg)
|
|
if (!msg)
|
|
return;
|
|
return;
|
|
@@ -9278,14 +9692,17 @@ void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
|
|
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
|
|
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
|
|
nl80211_mlme_mcgrp.id, gfp);
|
|
nl80211_mlme_mcgrp.id, gfp);
|
|
}
|
|
}
|
|
|
|
+EXPORT_SYMBOL(cfg80211_new_sta);
|
|
|
|
|
|
-void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
|
|
|
|
- struct net_device *dev, const u8 *mac_addr,
|
|
|
|
- gfp_t gfp)
|
|
|
|
|
|
+void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)
|
|
{
|
|
{
|
|
|
|
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
|
|
|
|
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
|
|
struct sk_buff *msg;
|
|
struct sk_buff *msg;
|
|
void *hdr;
|
|
void *hdr;
|
|
|
|
|
|
|
|
+ trace_cfg80211_del_sta(dev, mac_addr);
|
|
|
|
+
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
|
if (!msg)
|
|
if (!msg)
|
|
return;
|
|
return;
|
|
@@ -9310,12 +9727,14 @@ void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
|
|
genlmsg_cancel(msg, hdr);
|
|
genlmsg_cancel(msg, hdr);
|
|
nlmsg_free(msg);
|
|
nlmsg_free(msg);
|
|
}
|
|
}
|
|
|
|
+EXPORT_SYMBOL(cfg80211_del_sta);
|
|
|
|
|
|
-void nl80211_send_conn_failed_event(struct cfg80211_registered_device *rdev,
|
|
|
|
- struct net_device *dev, const u8 *mac_addr,
|
|
|
|
- enum nl80211_connect_failed_reason reason,
|
|
|
|
- gfp_t gfp)
|
|
|
|
|
|
+void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
|
|
|
|
+ enum nl80211_connect_failed_reason reason,
|
|
|
|
+ gfp_t gfp)
|
|
{
|
|
{
|
|
|
|
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
|
|
|
|
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
|
|
struct sk_buff *msg;
|
|
struct sk_buff *msg;
|
|
void *hdr;
|
|
void *hdr;
|
|
|
|
|
|
@@ -9344,6 +9763,7 @@ void nl80211_send_conn_failed_event(struct cfg80211_registered_device *rdev,
|
|
genlmsg_cancel(msg, hdr);
|
|
genlmsg_cancel(msg, hdr);
|
|
nlmsg_free(msg);
|
|
nlmsg_free(msg);
|
|
}
|
|
}
|
|
|
|
+EXPORT_SYMBOL(cfg80211_conn_failed);
|
|
|
|
|
|
static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
|
|
static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
|
|
const u8 *addr, gfp_t gfp)
|
|
const u8 *addr, gfp_t gfp)
|
|
@@ -9388,19 +9808,47 @@ static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
-bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp)
|
|
|
|
|
|
+bool cfg80211_rx_spurious_frame(struct net_device *dev,
|
|
|
|
+ const u8 *addr, gfp_t gfp)
|
|
{
|
|
{
|
|
- return __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME,
|
|
|
|
- addr, gfp);
|
|
|
|
|
|
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
+ bool ret;
|
|
|
|
+
|
|
|
|
+ trace_cfg80211_rx_spurious_frame(dev, addr);
|
|
|
|
+
|
|
|
|
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
|
|
|
|
+ wdev->iftype != NL80211_IFTYPE_P2P_GO)) {
|
|
|
|
+ trace_cfg80211_return_bool(false);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ ret = __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME,
|
|
|
|
+ addr, gfp);
|
|
|
|
+ trace_cfg80211_return_bool(ret);
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
+EXPORT_SYMBOL(cfg80211_rx_spurious_frame);
|
|
|
|
|
|
-bool nl80211_unexpected_4addr_frame(struct net_device *dev,
|
|
|
|
- const u8 *addr, gfp_t gfp)
|
|
|
|
|
|
+bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev,
|
|
|
|
+ const u8 *addr, gfp_t gfp)
|
|
{
|
|
{
|
|
- return __nl80211_unexpected_frame(dev,
|
|
|
|
- NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
|
|
|
|
- addr, gfp);
|
|
|
|
|
|
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
+ bool ret;
|
|
|
|
+
|
|
|
|
+ trace_cfg80211_rx_unexpected_4addr_frame(dev, addr);
|
|
|
|
+
|
|
|
|
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
|
|
|
|
+ wdev->iftype != NL80211_IFTYPE_P2P_GO &&
|
|
|
|
+ wdev->iftype != NL80211_IFTYPE_AP_VLAN)) {
|
|
|
|
+ trace_cfg80211_return_bool(false);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ ret = __nl80211_unexpected_frame(dev,
|
|
|
|
+ NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
|
|
|
|
+ addr, gfp);
|
|
|
|
+ trace_cfg80211_return_bool(ret);
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
+EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame);
|
|
|
|
|
|
int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
|
|
int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
|
|
struct wireless_dev *wdev, u32 nlportid,
|
|
struct wireless_dev *wdev, u32 nlportid,
|
|
@@ -9440,15 +9888,17 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
|
|
return -ENOBUFS;
|
|
return -ENOBUFS;
|
|
}
|
|
}
|
|
|
|
|
|
-void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
|
|
|
|
- struct wireless_dev *wdev, u64 cookie,
|
|
|
|
- const u8 *buf, size_t len, bool ack,
|
|
|
|
- gfp_t gfp)
|
|
|
|
|
|
+void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
|
|
|
|
+ const u8 *buf, size_t len, bool ack, gfp_t gfp)
|
|
{
|
|
{
|
|
|
|
+ struct wiphy *wiphy = wdev->wiphy;
|
|
|
|
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
|
|
struct net_device *netdev = wdev->netdev;
|
|
struct net_device *netdev = wdev->netdev;
|
|
struct sk_buff *msg;
|
|
struct sk_buff *msg;
|
|
void *hdr;
|
|
void *hdr;
|
|
|
|
|
|
|
|
+ trace_cfg80211_mgmt_tx_status(wdev, cookie, ack);
|
|
|
|
+
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
|
if (!msg)
|
|
if (!msg)
|
|
return;
|
|
return;
|
|
@@ -9476,17 +9926,21 @@ void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
|
|
genlmsg_cancel(msg, hdr);
|
|
genlmsg_cancel(msg, hdr);
|
|
nlmsg_free(msg);
|
|
nlmsg_free(msg);
|
|
}
|
|
}
|
|
|
|
+EXPORT_SYMBOL(cfg80211_mgmt_tx_status);
|
|
|
|
|
|
-void
|
|
|
|
-nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
|
|
|
|
- struct net_device *netdev,
|
|
|
|
- enum nl80211_cqm_rssi_threshold_event rssi_event,
|
|
|
|
- gfp_t gfp)
|
|
|
|
|
|
+void cfg80211_cqm_rssi_notify(struct net_device *dev,
|
|
|
|
+ enum nl80211_cqm_rssi_threshold_event rssi_event,
|
|
|
|
+ gfp_t gfp)
|
|
{
|
|
{
|
|
|
|
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
+ struct wiphy *wiphy = wdev->wiphy;
|
|
|
|
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
|
|
struct sk_buff *msg;
|
|
struct sk_buff *msg;
|
|
struct nlattr *pinfoattr;
|
|
struct nlattr *pinfoattr;
|
|
void *hdr;
|
|
void *hdr;
|
|
|
|
|
|
|
|
+ trace_cfg80211_cqm_rssi_notify(dev, rssi_event);
|
|
|
|
+
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
|
if (!msg)
|
|
if (!msg)
|
|
return;
|
|
return;
|
|
@@ -9498,7 +9952,7 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
|
|
}
|
|
}
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
|
- nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
|
|
|
|
|
|
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
|
|
|
|
pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
|
|
pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
|
|
@@ -9521,10 +9975,11 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
|
|
genlmsg_cancel(msg, hdr);
|
|
genlmsg_cancel(msg, hdr);
|
|
nlmsg_free(msg);
|
|
nlmsg_free(msg);
|
|
}
|
|
}
|
|
|
|
+EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
|
|
|
|
|
|
-void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
|
|
|
|
- struct net_device *netdev, const u8 *bssid,
|
|
|
|
- const u8 *replay_ctr, gfp_t gfp)
|
|
|
|
|
|
+static void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
|
|
|
|
+ struct net_device *netdev, const u8 *bssid,
|
|
|
|
+ const u8 *replay_ctr, gfp_t gfp)
|
|
{
|
|
{
|
|
struct sk_buff *msg;
|
|
struct sk_buff *msg;
|
|
struct nlattr *rekey_attr;
|
|
struct nlattr *rekey_attr;
|
|
@@ -9566,9 +10021,22 @@ void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
|
|
nlmsg_free(msg);
|
|
nlmsg_free(msg);
|
|
}
|
|
}
|
|
|
|
|
|
-void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
|
|
|
|
- struct net_device *netdev, int index,
|
|
|
|
- const u8 *bssid, bool preauth, gfp_t gfp)
|
|
|
|
|
|
+void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
|
|
|
|
+ const u8 *replay_ctr, gfp_t gfp)
|
|
|
|
+{
|
|
|
|
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
+ struct wiphy *wiphy = wdev->wiphy;
|
|
|
|
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
|
|
|
|
+
|
|
|
|
+ trace_cfg80211_gtk_rekey_notify(dev, bssid);
|
|
|
|
+ nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp);
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL(cfg80211_gtk_rekey_notify);
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
|
|
|
|
+ struct net_device *netdev, int index,
|
|
|
|
+ const u8 *bssid, bool preauth, gfp_t gfp)
|
|
{
|
|
{
|
|
struct sk_buff *msg;
|
|
struct sk_buff *msg;
|
|
struct nlattr *attr;
|
|
struct nlattr *attr;
|
|
@@ -9611,9 +10079,22 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
|
|
nlmsg_free(msg);
|
|
nlmsg_free(msg);
|
|
}
|
|
}
|
|
|
|
|
|
-void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
|
|
|
|
- struct net_device *netdev,
|
|
|
|
- struct cfg80211_chan_def *chandef, gfp_t gfp)
|
|
|
|
|
|
+void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
|
|
|
|
+ const u8 *bssid, bool preauth, gfp_t gfp)
|
|
|
|
+{
|
|
|
|
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
+ struct wiphy *wiphy = wdev->wiphy;
|
|
|
|
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
|
|
|
|
+
|
|
|
|
+ trace_cfg80211_pmksa_candidate_notify(dev, index, bssid, preauth);
|
|
|
|
+ nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
|
|
|
|
+
|
|
|
|
+static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
|
|
|
|
+ struct net_device *netdev,
|
|
|
|
+ struct cfg80211_chan_def *chandef,
|
|
|
|
+ gfp_t gfp)
|
|
{
|
|
{
|
|
struct sk_buff *msg;
|
|
struct sk_buff *msg;
|
|
void *hdr;
|
|
void *hdr;
|
|
@@ -9645,11 +10126,36 @@ void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
|
|
nlmsg_free(msg);
|
|
nlmsg_free(msg);
|
|
}
|
|
}
|
|
|
|
|
|
-void
|
|
|
|
-nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
|
|
|
|
- struct net_device *netdev, const u8 *peer,
|
|
|
|
- u32 num_packets, u32 rate, u32 intvl, gfp_t gfp)
|
|
|
|
|
|
+void cfg80211_ch_switch_notify(struct net_device *dev,
|
|
|
|
+ struct cfg80211_chan_def *chandef)
|
|
|
|
+{
|
|
|
|
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
+ struct wiphy *wiphy = wdev->wiphy;
|
|
|
|
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
|
|
|
|
+
|
|
|
|
+ trace_cfg80211_ch_switch_notify(dev, chandef);
|
|
|
|
+
|
|
|
|
+ wdev_lock(wdev);
|
|
|
|
+
|
|
|
|
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
|
|
|
|
+ wdev->iftype != NL80211_IFTYPE_P2P_GO))
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ wdev->channel = chandef->chan;
|
|
|
|
+ nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
|
|
|
|
+out:
|
|
|
|
+ wdev_unlock(wdev);
|
|
|
|
+ return;
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL(cfg80211_ch_switch_notify);
|
|
|
|
+
|
|
|
|
+void cfg80211_cqm_txe_notify(struct net_device *dev,
|
|
|
|
+ const u8 *peer, u32 num_packets,
|
|
|
|
+ u32 rate, u32 intvl, gfp_t gfp)
|
|
{
|
|
{
|
|
|
|
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
+ struct wiphy *wiphy = wdev->wiphy;
|
|
|
|
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
|
|
struct sk_buff *msg;
|
|
struct sk_buff *msg;
|
|
struct nlattr *pinfoattr;
|
|
struct nlattr *pinfoattr;
|
|
void *hdr;
|
|
void *hdr;
|
|
@@ -9665,7 +10171,7 @@ nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
|
|
}
|
|
}
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
|
- nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
|
|
|
|
|
|
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
|
|
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
|
|
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
|
|
|
|
@@ -9694,6 +10200,7 @@ nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
|
|
genlmsg_cancel(msg, hdr);
|
|
genlmsg_cancel(msg, hdr);
|
|
nlmsg_free(msg);
|
|
nlmsg_free(msg);
|
|
}
|
|
}
|
|
|
|
+EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
|
|
|
|
|
|
void
|
|
void
|
|
nl80211_radar_notify(struct cfg80211_registered_device *rdev,
|
|
nl80211_radar_notify(struct cfg80211_registered_device *rdev,
|
|
@@ -9746,15 +10253,18 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
|
|
nlmsg_free(msg);
|
|
nlmsg_free(msg);
|
|
}
|
|
}
|
|
|
|
|
|
-void
|
|
|
|
-nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
|
|
|
|
- struct net_device *netdev, const u8 *peer,
|
|
|
|
- u32 num_packets, gfp_t gfp)
|
|
|
|
|
|
+void cfg80211_cqm_pktloss_notify(struct net_device *dev,
|
|
|
|
+ const u8 *peer, u32 num_packets, gfp_t gfp)
|
|
{
|
|
{
|
|
|
|
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
+ struct wiphy *wiphy = wdev->wiphy;
|
|
|
|
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
|
|
struct sk_buff *msg;
|
|
struct sk_buff *msg;
|
|
struct nlattr *pinfoattr;
|
|
struct nlattr *pinfoattr;
|
|
void *hdr;
|
|
void *hdr;
|
|
|
|
|
|
|
|
+ trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);
|
|
|
|
+
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
|
if (!msg)
|
|
if (!msg)
|
|
return;
|
|
return;
|
|
@@ -9766,7 +10276,7 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
|
|
}
|
|
}
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
|
- nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
|
|
|
|
|
|
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
|
|
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
|
|
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
|
|
|
|
@@ -9789,6 +10299,7 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
|
|
genlmsg_cancel(msg, hdr);
|
|
genlmsg_cancel(msg, hdr);
|
|
nlmsg_free(msg);
|
|
nlmsg_free(msg);
|
|
}
|
|
}
|
|
|
|
+EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
|
|
|
|
|
|
void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
|
|
void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
|
|
u64 cookie, bool acked, gfp_t gfp)
|
|
u64 cookie, bool acked, gfp_t gfp)
|
|
@@ -10075,6 +10586,50 @@ static struct notifier_block nl80211_netlink_notifier = {
|
|
.notifier_call = nl80211_netlink_notify,
|
|
.notifier_call = nl80211_netlink_notify,
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+void cfg80211_ft_event(struct net_device *netdev,
|
|
|
|
+ struct cfg80211_ft_event_params *ft_event)
|
|
|
|
+{
|
|
|
|
+ struct wiphy *wiphy = netdev->ieee80211_ptr->wiphy;
|
|
|
|
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
|
|
|
|
+ struct sk_buff *msg;
|
|
|
|
+ void *hdr;
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ trace_cfg80211_ft_event(wiphy, netdev, ft_event);
|
|
|
|
+
|
|
|
|
+ if (!ft_event->target_ap)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
|
+ if (!msg)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FT_EVENT);
|
|
|
|
+ if (!hdr) {
|
|
|
|
+ nlmsg_free(msg);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
|
|
|
|
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
|
|
|
|
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, ft_event->target_ap);
|
|
|
|
+ if (ft_event->ies)
|
|
|
|
+ nla_put(msg, NL80211_ATTR_IE, ft_event->ies_len, ft_event->ies);
|
|
|
|
+ if (ft_event->ric_ies)
|
|
|
|
+ nla_put(msg, NL80211_ATTR_IE_RIC, ft_event->ric_ies_len,
|
|
|
|
+ ft_event->ric_ies);
|
|
|
|
+
|
|
|
|
+ err = genlmsg_end(msg, hdr);
|
|
|
|
+ if (err < 0) {
|
|
|
|
+ nlmsg_free(msg);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
|
|
|
|
+ nl80211_mlme_mcgrp.id, GFP_KERNEL);
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL(cfg80211_ft_event);
|
|
|
|
+
|
|
/* initialisation/exit functions */
|
|
/* initialisation/exit functions */
|
|
|
|
|
|
int nl80211_init(void)
|
|
int nl80211_init(void)
|