Commit 3713b4e3 authored by Johannes Berg's avatar Johannes Berg

nl80211: allow splitting wiphy information in dumps

The per-wiphy information is getting large, to the point
where with more than the typical number of channels it's
too large and overflows, and userspace can't get any of
the information at all.

To address this (in a way that doesn't require making all
messages bigger) allow userspace to specify that it can
deal with wiphy information split across multiple parts
of the dump, and if it can split up the data. This also
splits up each channel separately so an arbitrary number
of channels can be supported.

Additionally, since GET_WIPHY has the same problem, add
support for filtering the wiphy dump and get information
for a single wiphy only, this allows userspace apps to
use dump in this case to retrieve all data from a single
device.

As userspace needs to know if all this this is supported,
add a global nl80211 feature set and include a bit for
this behaviour in it.

Cc: Dennis H Jensen <dennis.h.jensen@siemens.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 191922cd
...@@ -625,6 +625,10 @@ ...@@ -625,6 +625,10 @@
* %NL80211_ATTR_RADAR_EVENT is used to inform about the type of the * %NL80211_ATTR_RADAR_EVENT is used to inform about the type of the
* event. * event.
* *
* @NL80211_CMD_GET_PROTOCOL_FEATURES: Get global nl80211 protocol features,
* i.e. features for the nl80211 protocol rather than device features.
* Returns the features in the %NL80211_ATTR_PROTOCOL_FEATURES bitmap.
*
* @NL80211_CMD_MAX: highest used command number * @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use * @__NL80211_CMD_AFTER_LAST: internal use
*/ */
...@@ -779,6 +783,8 @@ enum nl80211_commands { ...@@ -779,6 +783,8 @@ enum nl80211_commands {
NL80211_CMD_RADAR_DETECT, NL80211_CMD_RADAR_DETECT,
NL80211_CMD_GET_PROTOCOL_FEATURES,
/* add new commands above here */ /* add new commands above here */
/* used to define NL80211_CMD_MAX below */ /* used to define NL80211_CMD_MAX below */
...@@ -1383,6 +1389,13 @@ enum nl80211_commands { ...@@ -1383,6 +1389,13 @@ enum nl80211_commands {
* advertised to the driver, e.g., to enable TDLS off channel operations * advertised to the driver, e.g., to enable TDLS off channel operations
* and PU-APSD. * and PU-APSD.
* *
* @NL80211_ATTR_PROTOCOL_FEATURES: global nl80211 feature flags, see
* &enum nl80211_protocol_features, the attribute is a u32.
*
* @NL80211_ATTR_SPLIT_WIPHY_DUMP: flag attribute, userspace supports
* receiving the data for a single wiphy split across multiple
* messages, given with wiphy dump message
*
* @NL80211_ATTR_MAX: highest attribute number currently defined * @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use * @__NL80211_ATTR_AFTER_LAST: internal use
*/ */
...@@ -1669,6 +1682,9 @@ enum nl80211_attrs { ...@@ -1669,6 +1682,9 @@ enum nl80211_attrs {
NL80211_ATTR_STA_CAPABILITY, NL80211_ATTR_STA_CAPABILITY,
NL80211_ATTR_STA_EXT_CAPABILITY, NL80211_ATTR_STA_EXT_CAPABILITY,
NL80211_ATTR_PROTOCOL_FEATURES,
NL80211_ATTR_SPLIT_WIPHY_DUMP,
/* add attributes here, update the policy in nl80211.c */ /* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST, __NL80211_ATTR_AFTER_LAST,
...@@ -3619,4 +3635,16 @@ enum nl80211_dfs_state { ...@@ -3619,4 +3635,16 @@ enum nl80211_dfs_state {
NL80211_DFS_AVAILABLE, NL80211_DFS_AVAILABLE,
}; };
/**
* enum enum nl80211_protocol_features - nl80211 protocol features
* @NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP: nl80211 supports splitting
* wiphy dumps (if requested by the application with the attribute
* %NL80211_ATTR_SPLIT_WIPHY_DUMP. Also supported is filtering the
* wiphy dump by %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFINDEX or
* %NL80211_ATTR_WDEV.
*/
enum nl80211_protocol_features {
NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP = 1 << 0,
};
#endif /* __LINUX_NL80211_H */ #endif /* __LINUX_NL80211_H */
...@@ -370,6 +370,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { ...@@ -370,6 +370,7 @@ 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, },
}; };
/* policy for the key attributes */ /* policy for the key attributes */
...@@ -892,30 +893,209 @@ static int nl80211_put_iface_combinations(struct wiphy *wiphy, ...@@ -892,30 +893,209 @@ static int nl80211_put_iface_combinations(struct wiphy *wiphy,
return -ENOBUFS; return -ENOBUFS;
} }
static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flags, #ifdef CONFIG_PM
static int nl80211_send_wowlan(struct sk_buff *msg,
struct cfg80211_registered_device *dev) struct cfg80211_registered_device *dev)
{
struct nlattr *nl_wowlan;
if (!dev->wiphy.wowlan.flags && !dev->wiphy.wowlan.n_patterns)
return 0;
nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
if (!nl_wowlan)
return -ENOBUFS;
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 (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))
return -ENOBUFS;
}
nla_nest_end(msg, nl_wowlan);
return 0;
}
#endif
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;
/* 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;
/* 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;
/* add bitrates */
nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
if (!nl_rates)
return -ENOBUFS;
for (i = 0; i < sband->n_bitrates; i++) {
nl_rate = nla_nest_start(msg, i);
if (!nl_rate)
return -ENOBUFS;
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;
nla_nest_end(msg, nl_rate);
}
nla_nest_end(msg, nl_rates);
return 0;
}
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 (!mgmt_stypes)
return 0;
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_ifs);
nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_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].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);
return 0;
}
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; void *hdr;
struct nlattr *nl_bands, *nl_band; struct nlattr *nl_bands, *nl_band;
struct nlattr *nl_freqs, *nl_freq; struct nlattr *nl_freqs, *nl_freq;
struct nlattr *nl_rates, *nl_rate;
struct nlattr *nl_cmds; struct nlattr *nl_cmds;
enum ieee80211_band band; enum ieee80211_band band;
struct ieee80211_channel *chan; struct ieee80211_channel *chan;
struct ieee80211_rate *rate;
int i; int i;
const struct ieee80211_txrx_stypes *mgmt_stypes = const struct ieee80211_txrx_stypes *mgmt_stypes =
dev->wiphy.mgmt_stypes; dev->wiphy.mgmt_stypes;
long start = 0, start_chan = 0, start_band = 0;
hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY); hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY);
if (!hdr) if (!hdr)
return -1; return -ENOBUFS;
/* allow always using the variables */
if (!split) {
split_start = &start;
band_start = &start_band;
chan_start = &start_chan;
}
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx) || 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_string(msg, NL80211_ATTR_WIPHY_NAME,
wiphy_name(&dev->wiphy)) ||
nla_put_u32(msg, NL80211_ATTR_GENERATION, nla_put_u32(msg, NL80211_ATTR_GENERATION,
cfg80211_rdev_list_generation) || cfg80211_rdev_list_generation))
nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT, goto nla_put_failure;
switch (*split_start) {
case 0:
if (nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
dev->wiphy.retry_short) || dev->wiphy.retry_short) ||
nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG, nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
dev->wiphy.retry_long) || dev->wiphy.retry_long) ||
...@@ -956,6 +1136,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag ...@@ -956,6 +1136,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag
nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP)) nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
goto nla_put_failure; goto nla_put_failure;
(*split_start)++;
if (split)
break;
case 1:
if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES, if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
sizeof(u32) * dev->wiphy.n_cipher_suites, sizeof(u32) * dev->wiphy.n_cipher_suites,
dev->wiphy.cipher_suites)) dev->wiphy.cipher_suites))
...@@ -981,105 +1165,108 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag ...@@ -981,105 +1165,108 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag
goto nla_put_failure; goto nla_put_failure;
if ((dev->wiphy.available_antennas_tx || if ((dev->wiphy.available_antennas_tx ||
dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) { dev->wiphy.available_antennas_rx) &&
dev->ops->get_antenna) {
u32 tx_ant = 0, rx_ant = 0; u32 tx_ant = 0, rx_ant = 0;
int res; int res;
res = rdev_get_antenna(dev, &tx_ant, &rx_ant); res = rdev_get_antenna(dev, &tx_ant, &rx_ant);
if (!res) { if (!res) {
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, if (nla_put_u32(msg,
NL80211_ATTR_WIPHY_ANTENNA_TX,
tx_ant) || tx_ant) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, nla_put_u32(msg,
NL80211_ATTR_WIPHY_ANTENNA_RX,
rx_ant)) rx_ant))
goto nla_put_failure; goto nla_put_failure;
} }
} }
(*split_start)++;
if (split)
break;
case 2:
if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES, if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
dev->wiphy.interface_modes)) dev->wiphy.interface_modes))
goto nla_put_failure; goto nla_put_failure;
(*split_start)++;
if (split)
break;
case 3:
nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS); nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
if (!nl_bands) if (!nl_bands)
goto nla_put_failure; goto nla_put_failure;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) { for (band = *band_start; band < IEEE80211_NUM_BANDS; band++) {
if (!dev->wiphy.bands[band]) struct ieee80211_supported_band *sband;
sband = dev->wiphy.bands[band];
if (!sband)
continue; continue;
nl_band = nla_nest_start(msg, band); nl_band = nla_nest_start(msg, band);
if (!nl_band) if (!nl_band)
goto nla_put_failure; goto nla_put_failure;
/* add HT info */ switch (*chan_start) {
if (dev->wiphy.bands[band]->ht_cap.ht_supported && case 0:
(nla_put(msg, NL80211_BAND_ATTR_HT_MCS_SET, if (nl80211_send_band_rateinfo(msg, sband))
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;
/* 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; goto nla_put_failure;
(*chan_start)++;
if (split)
break;
default:
/* add frequencies */ /* add frequencies */
nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS); nl_freqs = nla_nest_start(
msg, NL80211_BAND_ATTR_FREQS);
if (!nl_freqs) if (!nl_freqs)
goto nla_put_failure; goto nla_put_failure;
for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) { for (i = *chan_start - 1;
i < sband->n_channels;
i++) {
nl_freq = nla_nest_start(msg, i); nl_freq = nla_nest_start(msg, i);
if (!nl_freq) if (!nl_freq)
goto nla_put_failure; goto nla_put_failure;
chan = &dev->wiphy.bands[band]->channels[i]; chan = &sband->channels[i];
if (nl80211_msg_put_channel(msg, chan)) if (nl80211_msg_put_channel(msg, chan))
goto nla_put_failure; goto nla_put_failure;
nla_nest_end(msg, nl_freq); 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_freqs);
/* add bitrates */
nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
if (!nl_rates)
goto nla_put_failure;
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;
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;
nla_nest_end(msg, nl_rate);
} }
nla_nest_end(msg, nl_rates);
nla_nest_end(msg, nl_band); nla_nest_end(msg, nl_band);
if (split) {
/* start again here */
if (*chan_start)
band--;
break;
}
} }
nla_nest_end(msg, nl_bands); nla_nest_end(msg, nl_bands);
if (band < IEEE80211_NUM_BANDS)
*band_start = band + 1;
else
*band_start = 0;
/* 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); nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
if (!nl_cmds) if (!nl_cmds)
goto nla_put_failure; goto nla_put_failure;
...@@ -1163,10 +1350,14 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag ...@@ -1163,10 +1350,14 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag
} }
nla_nest_end(msg, nl_cmds); nla_nest_end(msg, nl_cmds);
(*split_start)++;
if (split)
break;
case 5:
if (dev->ops->remain_on_channel && if (dev->ops->remain_on_channel &&
(dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) && (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&
nla_put_u32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION, nla_put_u32(msg,
NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
dev->wiphy.max_remain_on_channel_duration)) dev->wiphy.max_remain_on_channel_duration))
goto nla_put_failure; goto nla_put_failure;
...@@ -1174,102 +1365,22 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag ...@@ -1174,102 +1365,22 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag
nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK)) nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK))
goto nla_put_failure; goto nla_put_failure;
if (mgmt_stypes) { if (nl80211_send_mgmt_stypes(msg, mgmt_stypes))
u16 stypes;
struct nlattr *nl_ftypes, *nl_ifs;
enum nl80211_iftype ift;
nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
if (!nl_ifs)
goto nla_put_failure; goto nla_put_failure;
(*split_start)++;
for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) { if (split)
nl_ftypes = nla_nest_start(msg, ift); break;
if (!nl_ftypes) case 6:
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))
goto nla_put_failure;
stypes >>= 1;
i++;
}
nla_nest_end(msg, nl_ftypes);
}
nla_nest_end(msg, nl_ifs);
nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
if (!nl_ifs)
goto nla_put_failure;
for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
nl_ftypes = nla_nest_start(msg, ift);
if (!nl_ftypes)
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);
}
#ifdef CONFIG_PM #ifdef CONFIG_PM
if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) { if (nl80211_send_wowlan(msg, dev))
struct nlattr *nl_wowlan;
nl_wowlan = nla_nest_start(msg,
NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
if (!nl_wowlan)
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)))
goto nla_put_failure; goto nla_put_failure;
if (dev->wiphy.wowlan.n_patterns) { (*split_start)++;
struct nl80211_wowlan_pattern_support pat = { if (split)
.max_patterns = dev->wiphy.wowlan.n_patterns, break;
.min_pattern_len = #else
dev->wiphy.wowlan.pattern_min_len, (*split_start)++;
.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))
goto nla_put_failure;
}
nla_nest_end(msg, nl_wowlan);
}
#endif #endif
case 7:
if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES, if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
dev->wiphy.software_iftypes)) dev->wiphy.software_iftypes))
goto nla_put_failure; goto nla_put_failure;
...@@ -1277,6 +1388,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag ...@@ -1277,6 +1388,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag
if (nl80211_put_iface_combinations(&dev->wiphy, msg)) if (nl80211_put_iface_combinations(&dev->wiphy, msg))
goto nla_put_failure; goto nla_put_failure;
(*split_start)++;
if (split)
break;
case 8:
if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) && if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME, nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME,
dev->wiphy.ap_sme_capa)) dev->wiphy.ap_sme_capa))
...@@ -1298,6 +1413,25 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag ...@@ -1298,6 +1413,25 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag
dev->wiphy.max_acl_mac_addrs)) dev->wiphy.max_acl_mac_addrs))
goto nla_put_failure; 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:
/* placeholder */
/* done */
*split_start = 0;
break;
}
return genlmsg_end(msg, hdr); return genlmsg_end(msg, hdr);
nla_put_failure: nla_put_failure:
...@@ -1310,25 +1444,64 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -1310,25 +1444,64 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
int idx = 0, ret; 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;
ret = nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).portid, if (filter_wiphy != -1 && dev->wiphy_idx != filter_wiphy)
cb->nlh->nlmsg_seq, NLM_F_MULTI, continue;
dev); /* 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 (ret < 0) {
/* /*
* If sending the wiphy data didn't fit (ENOBUFS or * If sending the wiphy data didn't fit (ENOBUFS
* EMSGSIZE returned), this SKB is still empty (so * or EMSGSIZE returned), this SKB is still
* it's not too big because another wiphy dataset is * empty (so it's not too big because another
* already in the skb) and we've not tried to adjust * wiphy dataset is already in the skb) and
* the dump allocation yet ... then adjust the alloc * we've not tried to adjust the dump allocation
* size to be bigger, and return 1 but with the empty * yet ... then adjust the alloc size to be
* skb. This results in an empty message being RX'ed * bigger, and return 1 but with the empty skb.
* This results in an empty message being RX'ed
* in userspace, but that is ignored. * in userspace, but that is ignored.
* *
* We can then retry with the larger buffer. * We can then retry with the larger buffer.
...@@ -1343,6 +1516,8 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -1343,6 +1516,8 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
idx--; idx--;
break; break;
} }
} while (cb->args[1] > 0);
break;
} }
mutex_unlock(&cfg80211_mutex); mutex_unlock(&cfg80211_mutex);
...@@ -1360,7 +1535,8 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info) ...@@ -1360,7 +1535,8 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
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;
} }
...@@ -7821,6 +7997,33 @@ static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info) ...@@ -7821,6 +7997,33 @@ 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;
}
#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
...@@ -8497,6 +8700,11 @@ static struct genl_ops nl80211_ops[] = { ...@@ -8497,6 +8700,11 @@ 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,
},
}; };
static struct genl_multicast_group nl80211_mlme_mcgrp = { static struct genl_multicast_group nl80211_mlme_mcgrp = {
...@@ -8524,7 +8732,8 @@ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev) ...@@ -8524,7 +8732,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;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment