wifi: fix enumeration of 6 GHz channels from wiphy

Command NL80211_CMD_GET_WIPHY without any flag only returns channels
in the 2 GHz and 5 GHz bands, for backwards compatibility with old
userspace tools. To get the full list we need to pass attribute
NL80211_ATTR_SPLIT_WIPHY_DUMP (added in Linux 3.9 released in 2013),
and allow the handler to be called multiple times.

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1500
This commit is contained in:
Beniamino Galvani 2024-03-22 10:12:43 +01:00
parent 8ca7433a14
commit 4cd4ab518e
2 changed files with 56 additions and 45 deletions

1
NEWS
View File

@ -15,6 +15,7 @@ USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
visible in nmcli via "nmcli -f all device show $DEV".
* Deprecated 802-11-wireless and 802-11-wired property 'mac-address-blacklist'
and introduced the 'mac-address-denylist' property.
* Fix detection of 6 GHz band capability for WiFi devices
=============================================
NetworkManager-1.46

View File

@ -567,6 +567,7 @@ struct nl80211_device_info {
int phy;
Nl80211Freq *freqs;
int num_freqs;
int num_freqs_alloc;
guint32 freq;
guint32 caps;
gboolean can_scan;
@ -610,7 +611,6 @@ nl80211_wiphy_info_handler(const struct nl_msg *msg, void *arg)
struct nlattr *nl_freq;
int rem_freq;
int rem_band;
guint num_alloc;
#ifdef NL80211_FREQUENCY_ATTR_NO_IR
G_STATIC_ASSERT_EXPR(NL80211_FREQUENCY_ATTR_PASSIVE_SCAN == NL80211_FREQUENCY_ATTR_NO_IR
@ -622,22 +622,16 @@ nl80211_wiphy_info_handler(const struct nl_msg *msg, void *arg)
if (nla_parse_arr(tb, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0)
return NL_SKIP;
if (tb[NL80211_ATTR_WIPHY] == NULL || tb[NL80211_ATTR_WIPHY_BANDS] == NULL)
if (!tb[NL80211_ATTR_WIPHY])
return NL_SKIP;
info->phy = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
if (tb[NL80211_ATTR_WIPHY_FREQ])
info->freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
else
info->freq = 0;
if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) {
if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS])
info->can_scan_ssid = nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) > 0;
} else {
/* old kernel that only had mac80211, so assume it can */
info->can_scan_ssid = TRUE;
}
if (tb[NL80211_ATTR_SUPPORTED_COMMANDS]) {
struct nlattr *nl_cmd;
@ -664,51 +658,57 @@ nl80211_wiphy_info_handler(const struct nl_msg *msg, void *arg)
}
}
/* Read supported frequencies */
num_alloc = 32;
info->num_freqs = 0;
info->freqs = g_new(Nl80211Freq, num_alloc);
if (tb[NL80211_ATTR_WIPHY_BANDS]) {
/* Read supported frequencies */
nla_for_each_nested (nl_band, tb[NL80211_ATTR_WIPHY_BANDS], rem_band) {
if (nla_parse_nested_arr(tb_band, nl_band, NULL) < 0)
return NL_SKIP;
if (!info->freqs) {
info->num_freqs = 0;
info->num_freqs_alloc = 32;
info->freqs = g_new(Nl80211Freq, info->num_freqs_alloc);
}
nla_for_each_nested (nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
Nl80211Freq *f;
nla_for_each_nested (nl_band, tb[NL80211_ATTR_WIPHY_BANDS], rem_band) {
if (nla_parse_nested_arr(tb_band, nl_band, NULL) < 0)
return NL_SKIP;
if (nla_parse_nested_arr(tb_freq, nl_freq, freq_policy) < 0)
if (!tb_band[NL80211_BAND_ATTR_FREQS])
continue;
if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
continue;
nla_for_each_nested (nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
Nl80211Freq *f;
if (info->num_freqs >= num_alloc) {
num_alloc *= 2;
info->freqs = g_renew(Nl80211Freq, info->freqs, num_alloc);
if (nla_parse_nested_arr(tb_freq, nl_freq, freq_policy) < 0)
continue;
if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
continue;
if (info->num_freqs >= info->num_freqs_alloc) {
info->num_freqs_alloc *= 2;
info->freqs = g_renew(Nl80211Freq, info->freqs, info->num_freqs_alloc);
}
f = &info->freqs[info->num_freqs];
*f = (Nl80211Freq){
.freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]),
.disabled = !!tb_freq[NL80211_FREQUENCY_ATTR_DISABLED],
.no_ir = !!tb_freq[NL80211_FREQUENCY_ATTR_NO_IR],
};
info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_VALID;
if (f->freq >= 2401 && f->freq <= 2495)
info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_2GHZ;
if (f->freq >= 5150 && f->freq < 5950)
info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_5GHZ;
if (f->freq >= 5950 && f->freq <= 7125)
info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_6GHZ;
info->num_freqs++;
}
f = &info->freqs[info->num_freqs];
*f = (Nl80211Freq){
.freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]),
.disabled = !!tb_freq[NL80211_FREQUENCY_ATTR_DISABLED],
.no_ir = !!tb_freq[NL80211_FREQUENCY_ATTR_NO_IR],
};
info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_VALID;
if (f->freq >= 2401 && f->freq <= 2495)
info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_2GHZ;
if (f->freq >= 5150 && f->freq < 5950)
info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_5GHZ;
if (f->freq >= 5950 && f->freq <= 7125)
info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_6GHZ;
info->num_freqs++;
}
}
info->freqs = g_renew(Nl80211Freq, info->freqs, info->num_freqs);
/* Read security/encryption support */
if (tb[NL80211_ATTR_CIPHER_SUITES]) {
guint32 *ciphers = nla_data(tb[NL80211_ATTR_CIPHER_SUITES]);
@ -874,7 +874,10 @@ nm_wifi_utils_nl80211_new(struct nl_sock *genl, guint16 genl_family_id, int ifin
self->phy = -1;
msg = nl80211_alloc_msg(self, NL80211_CMD_GET_WIPHY, 0);
msg = nl80211_alloc_msg(self, NL80211_CMD_GET_WIPHY, NLM_F_DUMP);
NLA_PUT_FLAG(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP);
device_info.can_scan_ssid = TRUE;
device_info.self = self;
if (nl80211_send_and_recv(self, msg, nl80211_wiphy_info_handler, &device_info) < 0) {
@ -882,6 +885,10 @@ nm_wifi_utils_nl80211_new(struct nl_sock *genl, guint16 genl_family_id, int ifin
return NULL;
}
if (device_info.freqs) {
device_info.freqs = g_renew(Nl80211Freq, device_info.freqs, device_info.num_freqs);
}
if (!device_info.success) {
_LOGD("NL80211_CMD_GET_WIPHY request indicated failure");
return NULL;
@ -915,4 +922,7 @@ nm_wifi_utils_nl80211_new(struct nl_sock *genl, guint16 genl_family_id, int ifin
_LOGD("using nl80211 for Wi-Fi device control");
return (NMWifiUtils *) g_steal_pointer(&self);
nla_put_failure:
g_return_val_if_reached(NULL);
}