From 038659e7c6b385065cb223872771ac437ef70b62 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Sat, 2 May 2009 00:37:17 -0400 Subject: cfg80211: Process regulatory max bandwidth checks for HT40 We are not correctly listening to the regulatory max bandwidth settings. To actually make use of it we need to redesign things a bit. This patch does the work for that. We do this to so we can obey to regulatory rules accordingly for use of HT40. We end up dealing with HT40 by having two passes for each channel. The first check will see if a 20 MHz channel fits into the channel's center freq on a given frequency range. We check for a 20 MHz banwidth channel as that is the maximum an individual channel will use, at least for now. The first pass will go ahead and check if the regulatory rule for that given center of frequency allows 40 MHz bandwidths and we use this to determine whether or not the channel supports HT40 or not. So to support HT40 you'll need at a regulatory rule that allows you to use 40 MHz channels but you're channel must also be enabled and support 20 MHz by itself. The second pass is done after we do the regulatory checks over an device's supported channel list. On each channel we'll check if the control channel and the extension both: o exist o are enabled o regulatory allows 40 MHz bandwidth on its frequency range This work allows allows us to idependently check for HT40- and HT40+. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/reg.c | 201 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 159 insertions(+), 42 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 537af62ec42b..d897528ee7fc 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -48,12 +48,6 @@ static struct regulatory_request *last_request; /* To trigger userspace events */ static struct platform_device *reg_pdev; -/* Keep the ordering from large to small */ -static u32 supported_bandwidths[] = { - MHZ_TO_KHZ(40), - MHZ_TO_KHZ(20), -}; - /* * Central wireless core regulatory domains, we only need two, * the current one and a world regulatory domain in case we have no @@ -435,19 +429,20 @@ static bool is_valid_rd(const struct ieee80211_regdomain *rd) return true; } -/* Returns value in KHz */ -static u32 freq_max_bandwidth(const struct ieee80211_freq_range *freq_range, - u32 freq) +static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range, + u32 center_freq_khz, + u32 bw_khz) { - unsigned int i; - for (i = 0; i < ARRAY_SIZE(supported_bandwidths); i++) { - u32 start_freq_khz = freq - supported_bandwidths[i]/2; - u32 end_freq_khz = freq + supported_bandwidths[i]/2; - if (start_freq_khz >= freq_range->start_freq_khz && - end_freq_khz <= freq_range->end_freq_khz) - return supported_bandwidths[i]; - } - return 0; + u32 start_freq_khz, end_freq_khz; + + start_freq_khz = center_freq_khz - (bw_khz/2); + end_freq_khz = center_freq_khz + (bw_khz/2); + + if (start_freq_khz >= freq_range->start_freq_khz && + end_freq_khz <= freq_range->end_freq_khz) + return true; + + return false; } /** @@ -847,14 +842,17 @@ static u32 map_regdom_flags(u32 rd_flags) static int freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, - u32 *bandwidth, + u32 desired_bw_khz, const struct ieee80211_reg_rule **reg_rule, const struct ieee80211_regdomain *custom_regd) { int i; bool band_rule_found = false; const struct ieee80211_regdomain *regd; - u32 max_bandwidth = 0; + bool bw_fits = false; + + if (!desired_bw_khz) + desired_bw_khz = MHZ_TO_KHZ(20); regd = custom_regd ? custom_regd : cfg80211_regdomain; @@ -887,38 +885,54 @@ static int freq_reg_info_regd(struct wiphy *wiphy, if (!band_rule_found) band_rule_found = freq_in_rule_band(fr, center_freq); - max_bandwidth = freq_max_bandwidth(fr, center_freq); + bw_fits = reg_does_bw_fit(fr, + center_freq, + desired_bw_khz); - if (max_bandwidth && *bandwidth <= max_bandwidth) { + if (band_rule_found && bw_fits) { *reg_rule = rr; - *bandwidth = max_bandwidth; - break; + return 0; } } if (!band_rule_found) return -ERANGE; - return !max_bandwidth; + return -EINVAL; } EXPORT_SYMBOL(freq_reg_info); -int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 *bandwidth, - const struct ieee80211_reg_rule **reg_rule) +int freq_reg_info(struct wiphy *wiphy, + u32 center_freq, + u32 desired_bw_khz, + const struct ieee80211_reg_rule **reg_rule) { assert_cfg80211_lock(); - return freq_reg_info_regd(wiphy, center_freq, - bandwidth, reg_rule, NULL); + return freq_reg_info_regd(wiphy, + center_freq, + desired_bw_khz, + reg_rule, + NULL); } +/* + * Note that right now we assume the desired channel bandwidth + * is always 20 MHz for each individual channel (HT40 uses 20 MHz + * per channel, the primary and the extension channel). To support + * smaller custom bandwidths such as 5 MHz or 10 MHz we'll need a + * new ieee80211_channel.target_bw and re run the regulatory check + * on the wiphy with the target_bw specified. Then we can simply use + * that below for the desired_bw_khz below. + */ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, unsigned int chan_idx) { int r; - u32 flags; - u32 max_bandwidth = 0; + u32 flags, bw_flags = 0; + u32 desired_bw_khz = MHZ_TO_KHZ(20); const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_power_rule *power_rule = NULL; + const struct ieee80211_freq_range *freq_range = NULL; struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; struct wiphy *request_wiphy = NULL; @@ -933,8 +947,10 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, flags = chan->orig_flags; - r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), - &max_bandwidth, ®_rule); + r = freq_reg_info(wiphy, + MHZ_TO_KHZ(chan->center_freq), + desired_bw_khz, + ®_rule); if (r) { /* @@ -977,6 +993,10 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, } power_rule = ®_rule->power_rule; + freq_range = ®_rule->freq_range; + + if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) + bw_flags = IEEE80211_CHAN_NO_HT40; if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && request_wiphy && request_wiphy == wiphy && @@ -987,19 +1007,19 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, * settings */ chan->flags = chan->orig_flags = - map_regdom_flags(reg_rule->flags); + map_regdom_flags(reg_rule->flags) | bw_flags; chan->max_antenna_gain = chan->orig_mag = (int) MBI_TO_DBI(power_rule->max_antenna_gain); - chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth); + chan->max_bandwidth = KHZ_TO_MHZ(desired_bw_khz); chan->max_power = chan->orig_mpwr = (int) MBM_TO_DBM(power_rule->max_eirp); return; } - chan->flags = flags | map_regdom_flags(reg_rule->flags); + chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); chan->max_antenna_gain = min(chan->orig_mag, (int) MBI_TO_DBI(power_rule->max_antenna_gain)); - chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth); + chan->max_bandwidth = KHZ_TO_MHZ(desired_bw_khz); if (chan->orig_mpwr) chan->max_power = min(chan->orig_mpwr, (int) MBM_TO_DBM(power_rule->max_eirp)); @@ -1156,6 +1176,93 @@ static void reg_process_beacons(struct wiphy *wiphy) wiphy_update_beacon_reg(wiphy); } +static bool is_ht40_not_allowed(struct ieee80211_channel *chan) +{ + if (!chan) + return true; + if (chan->flags & IEEE80211_CHAN_DISABLED) + return true; + /* This would happen when regulatory rules disallow HT40 completely */ + if (IEEE80211_CHAN_NO_HT40 == (chan->flags & (IEEE80211_CHAN_NO_HT40))) + return true; + return false; +} + +static void reg_process_ht_flags_channel(struct wiphy *wiphy, + enum ieee80211_band band, + unsigned int chan_idx) +{ + struct ieee80211_supported_band *sband; + struct ieee80211_channel *channel; + struct ieee80211_channel *channel_before = NULL, *channel_after = NULL; + unsigned int i; + + assert_cfg80211_lock(); + + sband = wiphy->bands[band]; + BUG_ON(chan_idx >= sband->n_channels); + channel = &sband->channels[chan_idx]; + + if (is_ht40_not_allowed(channel)) { + channel->flags |= IEEE80211_CHAN_NO_HT40; + return; + } + + /* + * We need to ensure the extension channels exist to + * be able to use HT40- or HT40+, this finds them (or not) + */ + for (i = 0; i < sband->n_channels; i++) { + struct ieee80211_channel *c = &sband->channels[i]; + if (c->center_freq == (channel->center_freq - 20)) + channel_before = c; + if (c->center_freq == (channel->center_freq + 20)) + channel_after = c; + } + + /* + * Please note that this assumes target bandwidth is 20 MHz, + * if that ever changes we also need to change the below logic + * to include that as well. + */ + if (is_ht40_not_allowed(channel_before)) + channel->flags |= IEEE80211_CHAN_NO_FAT_BELOW; + else + channel->flags &= ~IEEE80211_CHAN_NO_FAT_BELOW; + + if (is_ht40_not_allowed(channel_after)) + channel->flags |= IEEE80211_CHAN_NO_FAT_ABOVE; + else + channel->flags &= ~IEEE80211_CHAN_NO_FAT_ABOVE; +} + +static void reg_process_ht_flags_band(struct wiphy *wiphy, + enum ieee80211_band band) +{ + unsigned int i; + struct ieee80211_supported_band *sband; + + BUG_ON(!wiphy->bands[band]); + sband = wiphy->bands[band]; + + for (i = 0; i < sband->n_channels; i++) + reg_process_ht_flags_channel(wiphy, band, i); +} + +static void reg_process_ht_flags(struct wiphy *wiphy) +{ + enum ieee80211_band band; + + if (!wiphy) + return; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (wiphy->bands[band]) + reg_process_ht_flags_band(wiphy, band); + } + +} + void wiphy_update_regulatory(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) { @@ -1169,6 +1276,7 @@ void wiphy_update_regulatory(struct wiphy *wiphy, } out: reg_process_beacons(wiphy); + reg_process_ht_flags(wiphy); if (wiphy->reg_notifier) wiphy->reg_notifier(wiphy, last_request); } @@ -1179,9 +1287,11 @@ static void handle_channel_custom(struct wiphy *wiphy, const struct ieee80211_regdomain *regd) { int r; - u32 max_bandwidth = 0; + u32 desired_bw_khz = MHZ_TO_KHZ(20); + u32 bw_flags = 0; const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_power_rule *power_rule = NULL; + const struct ieee80211_freq_range *freq_range = NULL; struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; @@ -1191,8 +1301,11 @@ static void handle_channel_custom(struct wiphy *wiphy, BUG_ON(chan_idx >= sband->n_channels); chan = &sband->channels[chan_idx]; - r = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), - &max_bandwidth, ®_rule, regd); + r = freq_reg_info_regd(wiphy, + MHZ_TO_KHZ(chan->center_freq), + desired_bw_khz, + ®_rule, + regd); if (r) { chan->flags = IEEE80211_CHAN_DISABLED; @@ -1200,10 +1313,14 @@ static void handle_channel_custom(struct wiphy *wiphy, } power_rule = ®_rule->power_rule; + freq_range = ®_rule->freq_range; + + if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) + bw_flags = IEEE80211_CHAN_NO_HT40; - chan->flags |= map_regdom_flags(reg_rule->flags); + chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags; chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); - chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth); + chan->max_bandwidth = KHZ_TO_MHZ(desired_bw_khz); chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); } -- cgit v1.2.3 From 689da1b3b8b37ff41e79f3fb973c06cdfeef12e5 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Sat, 2 May 2009 00:37:18 -0400 Subject: wireless: rename IEEE80211_CHAN_NO_FAT_* to HT40-/+ This is more consistent with our nl80211 naming convention for HT40-/+. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-core.c | 4 ++-- drivers/net/wireless/iwlwifi/iwl-eeprom.c | 8 ++++---- include/net/cfg80211.h | 10 +++++----- net/mac80211/mlme.c | 4 ++-- net/wireless/reg.c | 8 ++++---- 5 files changed, 17 insertions(+), 17 deletions(-) (limited to 'net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 5393fb3f452c..6c1b171d92be 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -603,10 +603,10 @@ static u8 iwl_is_channel_extension(struct iwl_priv *priv, if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE) return !(ch_info->fat_extension_channel & - IEEE80211_CHAN_NO_FAT_ABOVE); + IEEE80211_CHAN_NO_HT40PLUS); else if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_BELOW) return !(ch_info->fat_extension_channel & - IEEE80211_CHAN_NO_FAT_BELOW); + IEEE80211_CHAN_NO_HT40MINUS); return 0; } diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c index 401438aec19c..b400bd510fc5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.c @@ -481,8 +481,8 @@ int iwl_init_channel_map(struct iwl_priv *priv) /* First write that fat is not enabled, and then enable * one by one */ ch_info->fat_extension_channel = - (IEEE80211_CHAN_NO_FAT_ABOVE | - IEEE80211_CHAN_NO_FAT_BELOW); + (IEEE80211_CHAN_NO_HT40PLUS | + IEEE80211_CHAN_NO_HT40MINUS); if (!(is_channel_valid(ch_info))) { IWL_DEBUG_INFO(priv, "Ch. %d Flags %x [%sGHz] - " @@ -561,7 +561,7 @@ int iwl_init_channel_map(struct iwl_priv *priv) fat_extension_chan = 0; else fat_extension_chan = - IEEE80211_CHAN_NO_FAT_BELOW; + IEEE80211_CHAN_NO_HT40MINUS; /* Set up driver's info for lower half */ iwl_set_fat_chan_info(priv, ieeeband, @@ -573,7 +573,7 @@ int iwl_init_channel_map(struct iwl_priv *priv) iwl_set_fat_chan_info(priv, ieeeband, (eeprom_ch_index[ch] + 4), &(eeprom_ch_info[ch]), - IEEE80211_CHAN_NO_FAT_ABOVE); + IEEE80211_CHAN_NO_HT40PLUS); } } diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 282d58d52fce..7391ad10c8c0 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -56,9 +56,9 @@ enum ieee80211_band { * on this channel. * @IEEE80211_CHAN_NO_IBSS: IBSS is not allowed on this channel. * @IEEE80211_CHAN_RADAR: Radar detection is required on this channel. - * @IEEE80211_CHAN_NO_FAT_ABOVE: extension channel above this channel + * @IEEE80211_CHAN_NO_HT40PLUS: extension channel above this channel * is not permitted. - * @IEEE80211_CHAN_NO_FAT_BELOW: extension channel below this channel + * @IEEE80211_CHAN_NO_HT40MINUS: extension channel below this channel * is not permitted. */ enum ieee80211_channel_flags { @@ -66,12 +66,12 @@ enum ieee80211_channel_flags { IEEE80211_CHAN_PASSIVE_SCAN = 1<<1, IEEE80211_CHAN_NO_IBSS = 1<<2, IEEE80211_CHAN_RADAR = 1<<3, - IEEE80211_CHAN_NO_FAT_ABOVE = 1<<4, - IEEE80211_CHAN_NO_FAT_BELOW = 1<<5, + IEEE80211_CHAN_NO_HT40PLUS = 1<<4, + IEEE80211_CHAN_NO_HT40MINUS = 1<<5, }; #define IEEE80211_CHAN_NO_HT40 \ - (IEEE80211_CHAN_NO_FAT_ABOVE | IEEE80211_CHAN_NO_FAT_BELOW) + (IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS) /** * struct ieee80211_channel - channel definition diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index ae030688771f..da582b643b9a 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -349,13 +349,13 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: - if (flags & IEEE80211_CHAN_NO_FAT_ABOVE) { + if (flags & IEEE80211_CHAN_NO_HT40PLUS) { cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; cap &= ~IEEE80211_HT_CAP_SGI_40; } break; case IEEE80211_HT_PARAM_CHA_SEC_BELOW: - if (flags & IEEE80211_CHAN_NO_FAT_BELOW) { + if (flags & IEEE80211_CHAN_NO_HT40MINUS) { cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; cap &= ~IEEE80211_HT_CAP_SGI_40; } diff --git a/net/wireless/reg.c b/net/wireless/reg.c index d897528ee7fc..48db569d4c6b 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1226,14 +1226,14 @@ static void reg_process_ht_flags_channel(struct wiphy *wiphy, * to include that as well. */ if (is_ht40_not_allowed(channel_before)) - channel->flags |= IEEE80211_CHAN_NO_FAT_BELOW; + channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; else - channel->flags &= ~IEEE80211_CHAN_NO_FAT_BELOW; + channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; if (is_ht40_not_allowed(channel_after)) - channel->flags |= IEEE80211_CHAN_NO_FAT_ABOVE; + channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; else - channel->flags &= ~IEEE80211_CHAN_NO_FAT_ABOVE; + channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; } static void reg_process_ht_flags_band(struct wiphy *wiphy, -- cgit v1.2.3 From 294196ab22c91da974ba1f40d0a7cdcb0b3e6bc3 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Sat, 2 May 2009 00:37:20 -0400 Subject: cfg80211: check allowed channel type upon userspace requests Thanks to nl80211 userspace can be very specific upon device configuration. Before processing the request for the new HT40 channel types (HT40- or HT40+) we need to ensure we can use them regulatory-wise. This wasn't required with wireless extensions as specifying the channel type wasn't not available and configuration was done towards the end implicitly upon association or reception of beacons from the AP. For the new nl80211 we have to check this when configuring the interfaces explicitly. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ade40d503bf0..0e22b5f5880f 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -492,7 +492,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; struct ieee80211_channel *chan; struct ieee80211_sta_ht_cap *ht_cap; - u32 freq, sec_freq; + u32 freq; if (!rdev->ops->set_channel) { result = -EOPNOTSUPP; @@ -518,33 +518,28 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) goto bad_res; - if (channel_type == NL80211_CHAN_HT40MINUS) - sec_freq = freq - 20; - else if (channel_type == NL80211_CHAN_HT40PLUS) - sec_freq = freq + 20; - else - sec_freq = 0; - - ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; - - /* no HT capabilities */ - if (channel_type != NL80211_CHAN_NO_HT && - !ht_cap->ht_supported) + if (channel_type == NL80211_CHAN_HT40MINUS && + (chan->flags & IEEE80211_CHAN_NO_HT40MINUS)) + goto bad_res; + else if (channel_type == NL80211_CHAN_HT40PLUS && + (chan->flags & IEEE80211_CHAN_NO_HT40PLUS)) goto bad_res; - if (sec_freq) { - struct ieee80211_channel *schan; + /* + * At this point we know if that if HT40 was requested + * we are allowed to use it and the extension channel + * exists. + */ - /* no 40 MHz capabilities */ + ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; + + /* no HT capabilities or intolerant */ + if (channel_type != NL80211_CHAN_NO_HT) { + if (!ht_cap->ht_supported) + goto bad_res; if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)) goto bad_res; - - schan = ieee80211_get_channel(&rdev->wiphy, sec_freq); - - /* Secondary channel not allowed */ - if (!schan || schan->flags & IEEE80211_CHAN_DISABLED) - goto bad_res; } result = rdev->ops->set_channel(&rdev->wiphy, chan, -- cgit v1.2.3 From 1ac61302dcd18880e28c29e5728cd4d0efeb5366 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Sat, 2 May 2009 00:37:21 -0400 Subject: mac80211/cfg80211: move wiphy specific debugfs entries to cfg80211 This moves the cfg80211 specific stuff to new cfg80211 debugfs entries. Non-mac80211 will also get these entries now. There were only 4 which we take: rts_threshold fragmentation_threshold short_retry_limit long_retry_limit Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/mac80211/debugfs.c | 16 ----------- net/mac80211/ieee80211_i.h | 4 --- net/wireless/Kconfig | 8 ++++++ net/wireless/Makefile | 1 + net/wireless/core.c | 5 ++++ net/wireless/core.h | 11 ++++++++ net/wireless/debugfs.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++ net/wireless/debugfs.h | 14 ++++++++++ 8 files changed, 109 insertions(+), 20 deletions(-) create mode 100644 net/wireless/debugfs.c create mode 100644 net/wireless/debugfs.h (limited to 'net/wireless') diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index e7682fe1c590..11c72311f35b 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -52,14 +52,6 @@ static const struct file_operations name## _ops = { \ DEBUGFS_READONLY_FILE(frequency, 20, "%d", local->hw.conf.channel->center_freq); -DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d", - local->hw.wiphy->rts_threshold); -DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d", - local->hw.wiphy->frag_threshold); -DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d", - local->hw.wiphy->retry_short); -DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d", - local->hw.wiphy->retry_long); DEBUGFS_READONLY_FILE(total_ps_buffered, 20, "%d", local->total_ps_buffered); DEBUGFS_READONLY_FILE(wep_iv, 20, "%#08x", @@ -303,10 +295,6 @@ void debugfs_hw_add(struct ieee80211_local *local) local->debugfs.keys = debugfs_create_dir("keys", phyd); DEBUGFS_ADD(frequency); - DEBUGFS_ADD(rts_threshold); - DEBUGFS_ADD(fragmentation_threshold); - DEBUGFS_ADD(short_retry_limit); - DEBUGFS_ADD(long_retry_limit); DEBUGFS_ADD(total_ps_buffered); DEBUGFS_ADD(wep_iv); DEBUGFS_ADD(tsf); @@ -359,10 +347,6 @@ void debugfs_hw_add(struct ieee80211_local *local) void debugfs_hw_del(struct ieee80211_local *local) { DEBUGFS_DEL(frequency); - DEBUGFS_DEL(rts_threshold); - DEBUGFS_DEL(fragmentation_threshold); - DEBUGFS_DEL(short_retry_limit); - DEBUGFS_DEL(long_retry_limit); DEBUGFS_DEL(total_ps_buffered); DEBUGFS_DEL(wep_iv); DEBUGFS_DEL(tsf); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 9d1514727f6e..3241e76eece7 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -758,10 +758,6 @@ struct ieee80211_local { struct dentry *rcdir; struct dentry *rcname; struct dentry *frequency; - struct dentry *rts_threshold; - struct dentry *fragmentation_threshold; - struct dentry *short_retry_limit; - struct dentry *long_retry_limit; struct dentry *total_ps_buffered; struct dentry *wep_iv; struct dentry *tsf; diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index 3c3bc9e579ed..45005497c634 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -10,6 +10,14 @@ config CFG80211_REG_DEBUG If unsure, say N. +config CFG80211_DEBUGFS + bool "cfg80211 DebugFS entries" + depends on CFG80211 && DEBUG_FS + ---help--- + You can enable this if you want to debugfs entries for cfg80211. + + If unsure, say N. + config WIRELESS_OLD_REGULATORY bool "Old wireless static regulatory definitions" default n diff --git a/net/wireless/Makefile b/net/wireless/Makefile index 14ea01c4a103..f78c4832a9ca 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o ibss.o +cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/wireless/core.c b/net/wireless/core.c index 47c20eb0c04d..b6ef5337c85f 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -17,6 +17,7 @@ #include "nl80211.h" #include "core.h" #include "sysfs.h" +#include "debugfs.h" /* name for sysfs, %d is appended */ #define PHY_NAME "phy" @@ -375,6 +376,8 @@ int wiphy_register(struct wiphy *wiphy) nl80211_send_reg_change_event(&request); } + cfg80211_debugfs_drv_add(drv); + res = 0; out_unlock: mutex_unlock(&cfg80211_mutex); @@ -405,6 +408,8 @@ void wiphy_unregister(struct wiphy *wiphy) /* unlock again before freeing */ mutex_unlock(&drv->mtx); + cfg80211_debugfs_drv_del(drv); + /* If this device got a regulatory hint tell core its * free to listen now to a new shiny device regulatory hint */ reg_device_remove(wiphy); diff --git a/net/wireless/core.h b/net/wireless/core.h index f14b6c5f4221..930a80124dbf 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include "reg.h" @@ -50,6 +51,16 @@ struct cfg80211_registered_device { struct cfg80211_scan_request *scan_req; /* protected by RTNL */ unsigned long suspend_at; +#ifdef CONFIG_MAC80211_DEBUGFS + /* Debugfs entries */ + struct wiphy_debugfsdentries { + struct dentry *rts_threshold; + struct dentry *fragmentation_threshold; + struct dentry *short_retry_limit; + struct dentry *long_retry_limit; + } debugfs; +#endif + /* must be last because of the way we do wiphy_priv(), * and it should at least be aligned to NETDEV_ALIGN */ struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN))); diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c new file mode 100644 index 000000000000..3211e0e9fe4c --- /dev/null +++ b/net/wireless/debugfs.c @@ -0,0 +1,70 @@ +/* + * cfg80211 debugfs + * + * Copyright 2009 Luis R. Rodriguez + * Copyright 2007 Johannes Berg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "core.h" +#include "debugfs.h" + +static int cfg80211_open_file_generic(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +#define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...) \ +static ssize_t name## _read(struct file *file, char __user *userbuf, \ + size_t count, loff_t *ppos) \ +{ \ + struct wiphy *wiphy= file->private_data; \ + char buf[buflen]; \ + int res; \ + \ + res = scnprintf(buf, buflen, fmt "\n", ##value); \ + return simple_read_from_buffer(userbuf, count, ppos, buf, res); \ +} \ + \ +static const struct file_operations name## _ops = { \ + .read = name## _read, \ + .open = cfg80211_open_file_generic, \ +}; + +DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d", + wiphy->rts_threshold) +DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d", + wiphy->frag_threshold); +DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d", + wiphy->retry_short) +DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d", + wiphy->retry_long); + +#define DEBUGFS_ADD(name) \ + drv->debugfs.name = debugfs_create_file(#name, S_IRUGO, phyd, \ + &drv->wiphy, &name## _ops); +#define DEBUGFS_DEL(name) \ + debugfs_remove(drv->debugfs.name); \ + drv->debugfs.name = NULL; + +void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv) +{ + struct dentry *phyd = drv->wiphy.debugfsdir; + + DEBUGFS_ADD(rts_threshold); + DEBUGFS_ADD(fragmentation_threshold); + DEBUGFS_ADD(short_retry_limit); + DEBUGFS_ADD(long_retry_limit); +} + +void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv) +{ + DEBUGFS_DEL(rts_threshold); + DEBUGFS_DEL(fragmentation_threshold); + DEBUGFS_DEL(short_retry_limit); + DEBUGFS_DEL(long_retry_limit); +} diff --git a/net/wireless/debugfs.h b/net/wireless/debugfs.h new file mode 100644 index 000000000000..c226983ae66b --- /dev/null +++ b/net/wireless/debugfs.h @@ -0,0 +1,14 @@ +#ifndef __CFG80211_DEBUGFS_H +#define __CFG80211_DEBUGFS_H + +#ifdef CONFIG_CFG80211_DEBUGFS +void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv); +void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv); +#else +static inline +void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv) {} +static inline +void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv) {} +#endif + +#endif /* __CFG80211_DEBUGFS_H */ -- cgit v1.2.3 From 80a3511d70e8fc7ed3fe4417d7b0bf6c3f642f64 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Sat, 2 May 2009 00:39:30 -0400 Subject: cfg80211: add debugfs HT40 allow map Here's a screenshot of what this looks like with ath9k: mcgrof@pogo /debug/ieee80211/phy0 $ cat ht40allow_map 2412 HT40 + 2417 HT40 + 2422 HT40 + 2427 HT40 + 2432 HT40 -+ 2437 HT40 -+ 2442 HT40 -+ 2447 HT40 - 2452 HT40 - 2457 HT40 - 2462 HT40 - 2467 Disabled 2472 Disabled 2484 Disabled 5180 HT40 + 5200 HT40 -+ 5220 HT40 -+ 5240 HT40 -+ 5260 HT40 -+ 5280 HT40 -+ 5300 HT40 -+ 5320 HT40 - 5500 HT40 + 5520 HT40 -+ 5540 HT40 -+ 5560 HT40 -+ 5580 HT40 -+ 5600 HT40 -+ 5620 HT40 -+ 5640 HT40 -+ 5660 HT40 -+ 5680 HT40 -+ 5700 HT40 - 5745 HT40 + 5765 HT40 -+ 5785 HT40 -+ 5805 HT40 -+ 5825 HT40 - Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/core.h | 1 + net/wireless/debugfs.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) (limited to 'net/wireless') diff --git a/net/wireless/core.h b/net/wireless/core.h index 930a80124dbf..88f234c8f6b3 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -58,6 +58,7 @@ struct cfg80211_registered_device { struct dentry *fragmentation_threshold; struct dentry *short_retry_limit; struct dentry *long_retry_limit; + struct dentry *ht40allow_map; } debugfs; #endif diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c index 3211e0e9fe4c..679ddfcec1ee 100644 --- a/net/wireless/debugfs.c +++ b/net/wireless/debugfs.c @@ -44,6 +44,65 @@ DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d", DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d", wiphy->retry_long); +static int ht_print_chan(struct ieee80211_channel *chan, + char *buf, int buf_size, int offset) +{ + if (WARN_ON(offset > buf_size)) + return 0; + + if (chan->flags & IEEE80211_CHAN_DISABLED) + return snprintf(buf + offset, + buf_size - offset, + "%d Disabled\n", + chan->center_freq); + + return snprintf(buf + offset, + buf_size - offset, + "%d HT40 %c%c\n", + chan->center_freq, + (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ? ' ' : '-', + (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ? ' ' : '+'); +} + +static ssize_t ht40allow_map_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wiphy *wiphy = file->private_data; + char *buf; + unsigned int offset = 0, buf_size = PAGE_SIZE, i, r; + enum ieee80211_band band; + struct ieee80211_supported_band *sband; + + buf = kzalloc(buf_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&cfg80211_mutex); + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + sband = wiphy->bands[band]; + if (!sband) + continue; + for (i = 0; i < sband->n_channels; i++) + offset += ht_print_chan(&sband->channels[i], + buf, buf_size, offset); + } + + mutex_unlock(&cfg80211_mutex); + + r = simple_read_from_buffer(user_buf, count, ppos, buf, offset); + + kfree(buf); + + return r; +} + +static const struct file_operations ht40allow_map_ops = { + .read = ht40allow_map_read, + .open = cfg80211_open_file_generic, +}; + #define DEBUGFS_ADD(name) \ drv->debugfs.name = debugfs_create_file(#name, S_IRUGO, phyd, \ &drv->wiphy, &name## _ops); @@ -59,6 +118,7 @@ void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv) DEBUGFS_ADD(fragmentation_threshold); DEBUGFS_ADD(short_retry_limit); DEBUGFS_ADD(long_retry_limit); + DEBUGFS_ADD(ht40allow_map); } void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv) @@ -67,4 +127,5 @@ void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv) DEBUGFS_DEL(fragmentation_threshold); DEBUGFS_DEL(short_retry_limit); DEBUGFS_DEL(long_retry_limit); + DEBUGFS_DEL(ht40allow_map); } -- cgit v1.2.3 From 9f26a952210e44691f784b77bf1f83a500d63f58 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 15 May 2009 12:38:32 +0300 Subject: nl80211: Validate NL80211_ATTR_KEY_SEQ length Validate RSC (NL80211_ATTR_KEY_SEQ) length in nl80211/cfg80211 instead of having to do this in all the drivers. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- net/mac80211/key.c | 6 +++--- net/wireless/nl80211.c | 1 + net/wireless/util.c | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) (limited to 'net/wireless') diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 827ea8e6ee0a..ce267565e180 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -320,7 +320,7 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, case ALG_TKIP: key->conf.iv_len = TKIP_IV_LEN; key->conf.icv_len = TKIP_ICV_LEN; - if (seq && seq_len == 6) { + if (seq) { for (i = 0; i < NUM_RX_DATA_QUEUES; i++) { key->u.tkip.rx[i].iv32 = get_unaligned_le32(&seq[2]); @@ -332,7 +332,7 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, case ALG_CCMP: key->conf.iv_len = CCMP_HDR_LEN; key->conf.icv_len = CCMP_MIC_LEN; - if (seq && seq_len == CCMP_PN_LEN) { + if (seq) { for (i = 0; i < NUM_RX_DATA_QUEUES; i++) for (j = 0; j < CCMP_PN_LEN; j++) key->u.ccmp.rx_pn[i][j] = @@ -342,7 +342,7 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, case ALG_AES_CMAC: key->conf.iv_len = 0; key->conf.icv_len = sizeof(struct ieee80211_mmie); - if (seq && seq_len == 6) + if (seq) for (j = 0; j < 6; j++) key->u.aes_cmac.rx_pn[j] = seq[6 - j - 1]; break; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0e22b5f5880f..1cf57f53a283 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -77,6 +77,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 }, [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 }, [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG }, + [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 }, [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 }, [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 }, diff --git a/net/wireless/util.c b/net/wireless/util.c index beb226e78cd7..b94c8604ad7c 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -181,5 +181,20 @@ int cfg80211_validate_key_settings(struct key_params *params, int key_idx, return -EINVAL; } + if (params->seq) { + switch (params->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + /* These ciphers do not use key sequence */ + return -EINVAL; + case WLAN_CIPHER_SUITE_TKIP: + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_AES_CMAC: + if (params->seq_len != 6) + return -EINVAL; + break; + } + } + return 0; } -- cgit v1.2.3 From 3dcf670baf3d3a9bfc752e59d0b1a8d886230750 Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Sat, 16 May 2009 23:13:46 +0100 Subject: cfg80211: mark ops as pointer to const This allows drivers to mark their cfg80211_ops tables const. Signed-off-by: David Kilroy Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 2 +- net/wireless/core.c | 2 +- net/wireless/core.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'net/wireless') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7391ad10c8c0..09a823520e58 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1073,7 +1073,7 @@ static inline const char *wiphy_name(struct wiphy *wiphy) * The returned pointer must be assigned to each netdev's * ieee80211_ptr for proper operation. */ -struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv); +struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv); /** * wiphy_register - register a wiphy with cfg80211 diff --git a/net/wireless/core.c b/net/wireless/core.c index b6ef5337c85f..a5dbea1da476 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -229,7 +229,7 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, /* exported functions */ -struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv) +struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) { static int wiphy_counter; diff --git a/net/wireless/core.h b/net/wireless/core.h index 88f234c8f6b3..9511c2be83d8 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -16,7 +16,7 @@ #include "reg.h" struct cfg80211_registered_device { - struct cfg80211_ops *ops; + const struct cfg80211_ops *ops; struct list_head list; /* we hold this mutex during any call so that * we cannot do multiple calls at once, and also -- cgit v1.2.3 From e3da574a0ddd3e90a1e2b788b84b94bc17a75172 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 18 May 2009 19:56:36 +0200 Subject: cfg80211: allow wext to remove keys that don't exist Some applications using wireless extensions expect to be able to remove a key that doesn't exist. One example is wpa_supplicant which doesn't actually change behaviour when running into an error while trying to do that, but it prints an error message which users interpret as wpa_supplicant having problems. The safe thing to do is not change the behaviour of wireless extensions any more, so when the driver reports -ENOENT let the wext bridge code return success to userspace. To guarantee this, also document that drivers should return -ENOENT when the key doesn't exist. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 5 +++-- net/wireless/wext-compat.c | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'net/wireless') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 81a3bfa45471..389f1d20adf4 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -781,10 +781,11 @@ enum wiphy_params_flags { * @get_key: get information about the key with the given parameters. * @mac_addr will be %NULL when requesting information for a group * key. All pointers given to the @callback function need not be valid - * after it returns. + * after it returns. This function should return an error if it is + * not possible to retrieve the key, -ENOENT if it doesn't exist. * * @del_key: remove a key given the @mac_addr (%NULL for a group key) - * and @key_index + * and @key_index, return -ENOENT if the key doesn't exist. * * @set_default_key: set the default key on an interface * diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index f98090b90fbf..711e00a0c9b5 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -504,6 +504,13 @@ static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, else if (idx == wdev->wext.default_mgmt_key) wdev->wext.default_mgmt_key = -1; } + /* + * Applications using wireless extensions expect to be + * able to delete keys that don't exist, so allow that. + */ + if (err == -ENOENT) + return 0; + return err; } else { if (addr) -- cgit v1.2.3 From 4776c6e7f66f853011bc1fd6fe37fa63f0b6982c Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 13 May 2009 17:04:39 -0400 Subject: cfg80211: return immediately if num reg rules > NL80211_MAX_SUPP_REG_RULES This has no functional change except we save a kfree(rd) and allows us to clean this code up a bit after this. We do avoid an unnecessary kfree(NULL) but calling that was OK too. Cc: stable@kernel.org Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/wireless') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 1cf57f53a283..52a877998ff7 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2567,7 +2567,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) rem_reg_rules) { num_rules++; if (num_rules > NL80211_MAX_SUPP_REG_RULES) - goto bad_reg; + return -EINVAL; } if (!reg_is_valid_request(alpha2)) -- cgit v1.2.3 From d0e18f833d23afefb6751a21d14a2cd71d2d4d66 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 13 May 2009 17:04:40 -0400 Subject: cfg80211: cleanup return calls on nl80211_set_reg() This has no functional change, but it will make the race fix easier to spot in my next patch. Cc: stable@kernel.org Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 52a877998ff7..66e0fb6a6420 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2570,15 +2570,19 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } - if (!reg_is_valid_request(alpha2)) - return -EINVAL; + if (!reg_is_valid_request(alpha2)) { + r = -EINVAL; + goto bad_reg; + } size_of_regd = sizeof(struct ieee80211_regdomain) + (num_rules * sizeof(struct ieee80211_reg_rule)); rd = kzalloc(size_of_regd, GFP_KERNEL); - if (!rd) - return -ENOMEM; + if (!rd) { + r = -ENOMEM; + goto bad_reg; + } rd->n_reg_rules = num_rules; rd->alpha2[0] = alpha2[0]; @@ -2595,8 +2599,10 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) rule_idx++; - if (rule_idx > NL80211_MAX_SUPP_REG_RULES) + if (rule_idx > NL80211_MAX_SUPP_REG_RULES) { + r = -EINVAL; goto bad_reg; + } } BUG_ON(rule_idx != num_rules); @@ -2604,11 +2610,12 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) mutex_lock(&cfg80211_mutex); r = set_regdom(rd); mutex_unlock(&cfg80211_mutex); + return r; bad_reg: kfree(rd); - return -EINVAL; + return r; } static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) -- cgit v1.2.3 From 61405e97788b1bc4e7c5be5b4ec04a73fc11bac2 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 13 May 2009 17:04:41 -0400 Subject: cfg80211: fix in nl80211_set_reg() There is a race on access to last_request and its alpha2 through reg_is_valid_request() and us possibly processing first another regulatory request on another CPU. We avoid this improbably race by locking with the cfg80211_mutex as we should have done in the first place. While at it add the assert on locking on reg_is_valid_request(). Cc: stable@kernel.org Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 5 ++++- net/wireless/reg.c | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'net/wireless') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 66e0fb6a6420..632504060789 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2570,6 +2570,8 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } + mutex_lock(&cfg80211_mutex); + if (!reg_is_valid_request(alpha2)) { r = -EINVAL; goto bad_reg; @@ -2607,13 +2609,14 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) BUG_ON(rule_idx != num_rules); - mutex_lock(&cfg80211_mutex); r = set_regdom(rd); + mutex_unlock(&cfg80211_mutex); return r; bad_reg: + mutex_unlock(&cfg80211_mutex); kfree(rd); return r; } diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 48db569d4c6b..8d176a8010ba 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -382,6 +382,8 @@ static int call_crda(const char *alpha2) /* Used by nl80211 before kmalloc'ing our regulatory domain */ bool reg_is_valid_request(const char *alpha2) { + assert_cfg80211_lock(); + if (!last_request) return false; -- cgit v1.2.3 From e43e820c9cc5cc37ca8fc07d1f18cb29f13a2ce8 Mon Sep 17 00:00:00 2001 From: Pavel Roskin Date: Mon, 18 May 2009 21:43:52 -0400 Subject: cfg80211: fix compile error with CONFIG_CFG80211_DEBUGFS If CONFIG_CFG80211_DEBUGFS is enabled and CONFIG_MAC80211_DEBUGFS is not, compilation fails in net/wireless/debugfs.c: net/wireless/debugfs.c: In function 'cfg80211_debugfs_drv_add': net/wireless/debugfs.c:117: error: 'struct cfg80211_registered_device' has no member named 'debugfs' The debugfs filed is needed if and only if CONFIG_CFG80211_DEBUGFS is enabled, so use that instead of CONFIG_MAC80211_DEBUGFS. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville --- net/wireless/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/wireless') diff --git a/net/wireless/core.h b/net/wireless/core.h index 9511c2be83d8..ab512bcd8153 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -51,7 +51,7 @@ struct cfg80211_registered_device { struct cfg80211_scan_request *scan_req; /* protected by RTNL */ unsigned long suspend_at; -#ifdef CONFIG_MAC80211_DEBUGFS +#ifdef CONFIG_CFG80211_DEBUGFS /* Debugfs entries */ struct wiphy_debugfsdentries { struct dentry *rts_threshold; -- cgit v1.2.3 From bbcf3f02771e069d0e113fe9bb62c27b671abf97 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 19 May 2009 17:49:47 -0400 Subject: cfg80211: warn when wiphy_apply_custom_regulatory() does nothing Device drivers using wiphy_apply_custom_regulatory() want some regulatory settings applied to their wiphy, if no bands were configured on the wiphy then something went wrong. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/reg.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 8d176a8010ba..df0ced9405d3 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1344,13 +1344,22 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, const struct ieee80211_regdomain *regd) { enum ieee80211_band band; + unsigned int bands_set = 0; mutex_lock(&cfg80211_mutex); for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (wiphy->bands[band]) - handle_band_custom(wiphy, band, regd); + if (!wiphy->bands[band]) + continue; + handle_band_custom(wiphy, band, regd); + bands_set++; } mutex_unlock(&cfg80211_mutex); + + /* + * no point in calling this if it won't have any effect + * on your device's supportd bands. + */ + WARN_ON(!bands_set); } EXPORT_SYMBOL(wiphy_apply_custom_regulatory); -- cgit v1.2.3 From 87057825824973f29cf2f37cff1e549170b2d7e6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 19 May 2009 17:19:36 +0200 Subject: wext: remove atomic requirement for wireless stats The requirement for wireless stats to be atomic is now mostly artificial since we hold the rtnl _and_ the dev_base_lock for iterating the device list. Doing that is not required, just the rtnl is sufficient (and the rtnl is required for other reasons outlined in commit "wext: fix get_wireless_stats locking"). This will fix http://bugzilla.kernel.org/show_bug.cgi?id=13344 and make things easier for drivers. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/wext.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/wext.c b/net/wireless/wext.c index d3bbef70cc7c..22378daceb94 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c @@ -636,8 +636,10 @@ static void wireless_seq_printf_stats(struct seq_file *seq, /* * Print info for /proc/net/wireless (print all entries) */ -static int wireless_seq_show(struct seq_file *seq, void *v) +static int wireless_dev_seq_show(struct seq_file *seq, void *v) { + might_sleep(); + if (v == SEQ_START_TOKEN) seq_printf(seq, "Inter-| sta-| Quality | Discarded " "packets | Missed | WE\n" @@ -651,21 +653,41 @@ static int wireless_seq_show(struct seq_file *seq, void *v) static void *wireless_dev_seq_start(struct seq_file *seq, loff_t *pos) { + struct net *net = seq_file_net(seq); + loff_t off; + struct net_device *dev; + rtnl_lock(); - return dev_seq_start(seq, pos); + if (!*pos) + return SEQ_START_TOKEN; + + off = 1; + for_each_netdev(net, dev) + if (off++ == *pos) + return dev; + return NULL; +} + +static void *wireless_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct net *net = seq_file_net(seq); + + ++*pos; + + return v == SEQ_START_TOKEN ? + first_net_device(net) : next_net_device(v); } static void wireless_dev_seq_stop(struct seq_file *seq, void *v) { - dev_seq_stop(seq, v); rtnl_unlock(); } static const struct seq_operations wireless_seq_ops = { .start = wireless_dev_seq_start, - .next = dev_seq_next, + .next = wireless_dev_seq_next, .stop = wireless_dev_seq_stop, - .show = wireless_seq_show, + .show = wireless_dev_seq_show, }; static int seq_open_wireless(struct inode *inode, struct file *file) -- cgit v1.2.3 From e31a16d6f64ef0e324c6f54d5112703c3f13a9c4 Mon Sep 17 00:00:00 2001 From: Zhu Yi Date: Thu, 21 May 2009 21:47:03 +0800 Subject: wireless: move some utility functions from mac80211 to cfg80211 The patch moves some utility functions from mac80211 to cfg80211. Because these functions are doing generic 802.11 operations so they are not mac80211 specific. The moving allows some fullmac drivers to be also benefit from these utility functions. Signed-off-by: Zhu Yi Signed-off-by: Samuel Ortiz Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ar9170/main.c | 2 +- drivers/net/wireless/ath/ath5k/pcu.c | 4 +- drivers/net/wireless/ath/ath9k/hw.c | 8 +- drivers/net/wireless/b43/main.c | 2 +- drivers/net/wireless/rt2x00/rt2x00crypto.c | 2 +- include/linux/ieee80211.h | 9 + include/net/cfg80211.h | 47 +++++ include/net/mac80211.h | 28 --- net/mac80211/ieee80211_i.h | 2 - net/mac80211/mesh.h | 4 - net/mac80211/rx.c | 89 +-------- net/mac80211/util.c | 73 ------- net/mac80211/wme.c | 30 +-- net/wireless/util.c | 305 +++++++++++++++++++++++++++++ 14 files changed, 375 insertions(+), 230 deletions(-) (limited to 'net/wireless') diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c index 4ef1d2fc859c..99df9ddae9cb 100644 --- a/drivers/net/wireless/ath/ar9170/main.c +++ b/drivers/net/wireless/ath/ar9170/main.c @@ -1555,7 +1555,7 @@ static int ar9170_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, switch (key->alg) { case ALG_WEP: - if (key->keylen == LEN_WEP40) + if (key->keylen == WLAN_KEY_LEN_WEP40) ktype = AR9170_ENC_ALG_WEP64; else ktype = AR9170_ENC_ALG_WEP128; diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c index 579aa0a96ab8..ec35503f6a40 100644 --- a/drivers/net/wireless/ath/ath5k/pcu.c +++ b/drivers/net/wireless/ath/ath5k/pcu.c @@ -1038,9 +1038,9 @@ int ath5k_keycache_type(const struct ieee80211_key_conf *key) case ALG_CCMP: return AR5K_KEYTABLE_TYPE_CCM; case ALG_WEP: - if (key->keylen == LEN_WEP40) + if (key->keylen == WLAN_KEY_LEN_WEP40) return AR5K_KEYTABLE_TYPE_40; - else if (key->keylen == LEN_WEP104) + else if (key->keylen == WLAN_KEY_LEN_WEP104) return AR5K_KEYTABLE_TYPE_104; return -EINVAL; default: diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 4acfab514916..1579c9407ed5 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2472,14 +2472,14 @@ bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry, } break; case ATH9K_CIPHER_WEP: - if (k->kv_len < LEN_WEP40) { + if (k->kv_len < WLAN_KEY_LEN_WEP40) { DPRINTF(ah->ah_sc, ATH_DBG_ANY, "WEP key length %u too small\n", k->kv_len); return false; } - if (k->kv_len <= LEN_WEP40) + if (k->kv_len <= WLAN_KEY_LEN_WEP40) keyType = AR_KEYTABLE_TYPE_40; - else if (k->kv_len <= LEN_WEP104) + else if (k->kv_len <= WLAN_KEY_LEN_WEP104) keyType = AR_KEYTABLE_TYPE_104; else keyType = AR_KEYTABLE_TYPE_128; @@ -2498,7 +2498,7 @@ bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry, key2 = get_unaligned_le32(k->kv_val + 6); key3 = get_unaligned_le16(k->kv_val + 10); key4 = get_unaligned_le32(k->kv_val + 12); - if (k->kv_len <= LEN_WEP104) + if (k->kv_len <= WLAN_KEY_LEN_WEP104) key4 &= 0xff; /* diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index ec8516eadc4f..cb4a8712946a 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3637,7 +3637,7 @@ static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, err = -EINVAL; switch (key->alg) { case ALG_WEP: - if (key->keylen == LEN_WEP40) + if (key->keylen == WLAN_KEY_LEN_WEP40) algorithm = B43_SEC_ALGO_WEP40; else algorithm = B43_SEC_ALGO_WEP104; diff --git a/drivers/net/wireless/rt2x00/rt2x00crypto.c b/drivers/net/wireless/rt2x00/rt2x00crypto.c index 57ab42cfed34..bc4e81e21841 100644 --- a/drivers/net/wireless/rt2x00/rt2x00crypto.c +++ b/drivers/net/wireless/rt2x00/rt2x00crypto.c @@ -33,7 +33,7 @@ enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key) { switch (key->alg) { case ALG_WEP: - if (key->keylen == LEN_WEP40) + if (key->keylen == WLAN_KEY_LEN_WEP40) return CIPHER_WEP64; else return CIPHER_WEP128; diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 05c29c01174c..34de8b21f6d4 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -493,6 +493,7 @@ struct ieee80211s_hdr { /* Mesh flags */ #define MESH_FLAGS_AE_A4 0x1 #define MESH_FLAGS_AE_A5_A6 0x2 +#define MESH_FLAGS_AE 0x3 #define MESH_FLAGS_PS_DEEP 0x4 /** @@ -1085,6 +1086,14 @@ enum ieee80211_spectrum_mgmt_actioncode { WLAN_ACTION_SPCT_CHL_SWITCH = 4, }; +/* Security key length */ +enum ieee80211_key_len { + WLAN_KEY_LEN_WEP40 = 5, + WLAN_KEY_LEN_WEP104 = 13, + WLAN_KEY_LEN_CCMP = 16, + WLAN_KEY_LEN_TKIP = 32, +}; + /* * IEEE 802.11-2007 7.3.2.9 Country information element * diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 389f1d20adf4..f20da7d63b1e 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1244,6 +1244,53 @@ extern int ieee80211_radiotap_iterator_init( extern int ieee80211_radiotap_iterator_next( struct ieee80211_radiotap_iterator *iterator); +extern const unsigned char rfc1042_header[6]; +extern const unsigned char bridge_tunnel_header[6]; + +/** + * ieee80211_get_hdrlen_from_skb - get header length from data + * + * Given an skb with a raw 802.11 header at the data pointer this function + * returns the 802.11 header length in bytes (not including encryption + * headers). If the data in the sk_buff is too short to contain a valid 802.11 + * header the function returns 0. + * + * @skb: the frame + */ +unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb); + +/** + * ieee80211_hdrlen - get header length in bytes from frame control + * @fc: frame control field in little-endian format + */ +unsigned int ieee80211_hdrlen(__le16 fc); + +/** + * ieee80211_data_to_8023 - convert an 802.11 data frame to 802.3 + * @skb: the 802.11 data frame + * @addr: the device MAC address + * @iftype: the virtual interface type + */ +int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr, + enum nl80211_iftype iftype); + +/** + * ieee80211_data_from_8023 - convert an 802.3 frame to 802.11 + * @skb: the 802.3 frame + * @addr: the device MAC address + * @iftype: the virtual interface type + * @bssid: the network bssid (used only for iftype STATION and ADHOC) + * @qos: build 802.11 QoS data frame + */ +int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr, + enum nl80211_iftype iftype, u8 *bssid, bool qos); + +/** + * cfg80211_classify8021d - determine the 802.1p/1d tag for a data frame + * @skb: the data frame + */ +unsigned int cfg80211_classify8021d(struct sk_buff *skb); + /* * Regulatory helper functions for wiphys */ diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 2d0610581efc..d72346ff3247 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -671,16 +671,6 @@ enum ieee80211_key_alg { ALG_AES_CMAC, }; -/** - * enum ieee80211_key_len - key length - * @LEN_WEP40: WEP 5-byte long key - * @LEN_WEP104: WEP 13-byte long key - */ -enum ieee80211_key_len { - LEN_WEP40 = 5, - LEN_WEP104 = 13, -}; - /** * enum ieee80211_key_flags - key flags * @@ -1811,24 +1801,6 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw, struct sk_buff * ieee80211_get_buffered_bc(struct ieee80211_hw *hw, struct ieee80211_vif *vif); -/** - * ieee80211_get_hdrlen_from_skb - get header length from data - * - * Given an skb with a raw 802.11 header at the data pointer this function - * returns the 802.11 header length in bytes (not including encryption - * headers). If the data in the sk_buff is too short to contain a valid 802.11 - * header the function returns 0. - * - * @skb: the frame - */ -unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb); - -/** - * ieee80211_hdrlen - get header length in bytes from frame control - * @fc: frame control field in little-endian format - */ -unsigned int ieee80211_hdrlen(__le16 fc); - /** * ieee80211_get_tkip_key - get a TKIP rc4 for skb * diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8db8d16d206c..c088c46704a3 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1085,8 +1085,6 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw) /* utility functions/constants */ extern void *mac80211_wiphy_privid; /* for wiphy privid */ -extern const unsigned char rfc1042_header[6]; -extern const unsigned char bridge_tunnel_header[6]; u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, enum nl80211_iftype type); int ieee80211_frame_duration(struct ieee80211_local *local, size_t len, diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 832bb503ca9b..c7d72819cdd2 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -191,12 +191,8 @@ struct mesh_rmc { #define PLINK_CATEGORY 30 #define MESH_PATH_SEL_CATEGORY 32 -/* Mesh Header Flags */ -#define IEEE80211S_FLAGS_AE 0x3 - /* Public interfaces */ /* Various */ -int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr); int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr, struct ieee80211_sub_if_data *sdata); int mesh_rmc_check(u8 *addr, struct ieee80211s_hdr *mesh_hdr, diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index f3a041cc5dcf..6a9b8e63a6bf 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1247,93 +1247,12 @@ ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc) } static int -ieee80211_data_to_8023(struct ieee80211_rx_data *rx) +__ieee80211_data_to_8023(struct ieee80211_rx_data *rx) { struct net_device *dev = rx->dev; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data; - u16 hdrlen, ethertype; - u8 *payload; - u8 dst[ETH_ALEN]; - u8 src[ETH_ALEN] __aligned(2); - struct sk_buff *skb = rx->skb; struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) - return -1; - - hdrlen = ieee80211_hdrlen(hdr->frame_control); - - /* convert IEEE 802.11 header + possible LLC headers into Ethernet - * header - * IEEE 802.11 address fields: - * ToDS FromDS Addr1 Addr2 Addr3 Addr4 - * 0 0 DA SA BSSID n/a - * 0 1 DA BSSID SA n/a - * 1 0 BSSID SA DA n/a - * 1 1 RA TA DA SA - */ - memcpy(dst, ieee80211_get_DA(hdr), ETH_ALEN); - memcpy(src, ieee80211_get_SA(hdr), ETH_ALEN); - - switch (hdr->frame_control & - cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) { - case cpu_to_le16(IEEE80211_FCTL_TODS): - if (unlikely(sdata->vif.type != NL80211_IFTYPE_AP && - sdata->vif.type != NL80211_IFTYPE_AP_VLAN)) - return -1; - break; - case cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS): - if (unlikely(sdata->vif.type != NL80211_IFTYPE_WDS && - sdata->vif.type != NL80211_IFTYPE_MESH_POINT)) - return -1; - if (ieee80211_vif_is_mesh(&sdata->vif)) { - struct ieee80211s_hdr *meshdr = (struct ieee80211s_hdr *) - (skb->data + hdrlen); - hdrlen += ieee80211_get_mesh_hdrlen(meshdr); - if (meshdr->flags & MESH_FLAGS_AE_A5_A6) { - memcpy(dst, meshdr->eaddr1, ETH_ALEN); - memcpy(src, meshdr->eaddr2, ETH_ALEN); - } - } - break; - case cpu_to_le16(IEEE80211_FCTL_FROMDS): - if (sdata->vif.type != NL80211_IFTYPE_STATION || - (is_multicast_ether_addr(dst) && - !compare_ether_addr(src, dev->dev_addr))) - return -1; - break; - case cpu_to_le16(0): - if (sdata->vif.type != NL80211_IFTYPE_ADHOC) - return -1; - break; - } - - if (unlikely(skb->len - hdrlen < 8)) - return -1; - - payload = skb->data + hdrlen; - ethertype = (payload[6] << 8) | payload[7]; - - if (likely((compare_ether_addr(payload, rfc1042_header) == 0 && - ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || - compare_ether_addr(payload, bridge_tunnel_header) == 0)) { - /* remove RFC1042 or Bridge-Tunnel encapsulation and - * replace EtherType */ - skb_pull(skb, hdrlen + 6); - memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); - memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); - } else { - struct ethhdr *ehdr; - __be16 len; - - skb_pull(skb, hdrlen); - len = htons(skb->len); - ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr)); - memcpy(ehdr->h_dest, dst, ETH_ALEN); - memcpy(ehdr->h_source, src, ETH_ALEN); - ehdr->h_proto = len; - } - return 0; + return ieee80211_data_to_8023(rx->skb, dev->dev_addr, sdata->vif.type); } /* @@ -1472,7 +1391,7 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx) if (!(rx->flags & IEEE80211_RX_AMSDU)) return RX_CONTINUE; - err = ieee80211_data_to_8023(rx); + err = __ieee80211_data_to_8023(rx); if (unlikely(err)) return RX_DROP_UNUSABLE; @@ -1658,7 +1577,7 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx) if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) return RX_DROP_MONITOR; - err = ieee80211_data_to_8023(rx); + err = __ieee80211_data_to_8023(rx); if (unlikely(err)) return RX_DROP_UNUSABLE; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index ffb6e88f2ecd..949d857debd8 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -35,15 +35,6 @@ /* privid for wiphys to determine whether they belong to us or not */ void *mac80211_wiphy_privid = &mac80211_wiphy_privid; -/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ -/* Ethernet-II snap header (RFC1042 for most EtherTypes) */ -const unsigned char rfc1042_header[] __aligned(2) = - { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; - -/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ -const unsigned char bridge_tunnel_header[] __aligned(2) = - { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; - struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy) { struct ieee80211_local *local; @@ -103,70 +94,6 @@ u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, return NULL; } -unsigned int ieee80211_hdrlen(__le16 fc) -{ - unsigned int hdrlen = 24; - - if (ieee80211_is_data(fc)) { - if (ieee80211_has_a4(fc)) - hdrlen = 30; - if (ieee80211_is_data_qos(fc)) - hdrlen += IEEE80211_QOS_CTL_LEN; - goto out; - } - - if (ieee80211_is_ctl(fc)) { - /* - * ACK and CTS are 10 bytes, all others 16. To see how - * to get this condition consider - * subtype mask: 0b0000000011110000 (0x00F0) - * ACK subtype: 0b0000000011010000 (0x00D0) - * CTS subtype: 0b0000000011000000 (0x00C0) - * bits that matter: ^^^ (0x00E0) - * value of those: 0b0000000011000000 (0x00C0) - */ - if ((fc & cpu_to_le16(0x00E0)) == cpu_to_le16(0x00C0)) - hdrlen = 10; - else - hdrlen = 16; - } -out: - return hdrlen; -} -EXPORT_SYMBOL(ieee80211_hdrlen); - -unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb) -{ - const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *)skb->data; - unsigned int hdrlen; - - if (unlikely(skb->len < 10)) - return 0; - hdrlen = ieee80211_hdrlen(hdr->frame_control); - if (unlikely(hdrlen > skb->len)) - return 0; - return hdrlen; -} -EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb); - -int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) -{ - int ae = meshhdr->flags & IEEE80211S_FLAGS_AE; - /* 7.1.3.5a.2 */ - switch (ae) { - case 0: - return 6; - case 1: - return 12; - case 2: - return 18; - case 3: - return 24; - default: - return 6; - } -} - void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx) { struct sk_buff *skb = tx->skb; diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 45b74f38b867..694343b9102b 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -23,34 +23,6 @@ */ const int ieee802_1d_to_ac[8] = { 2, 3, 3, 2, 1, 1, 0, 0 }; -static const char llc_ip_hdr[8] = {0xAA, 0xAA, 0x3, 0, 0, 0, 0x08, 0}; - -/* Given a data frame determine the 802.1p/1d tag to use. */ -static unsigned int classify_1d(struct sk_buff *skb) -{ - unsigned int dscp; - - /* skb->priority values from 256->263 are magic values to - * directly indicate a specific 802.1d priority. This is used - * to allow 802.1d priority to be passed directly in from VLAN - * tags, etc. - */ - if (skb->priority >= 256 && skb->priority <= 263) - return skb->priority - 256; - - switch (skb->protocol) { - case htons(ETH_P_IP): - dscp = ip_hdr(skb)->tos & 0xfc; - break; - - default: - return 0; - } - - return dscp >> 5; -} - - static int wme_downgrade_ac(struct sk_buff *skb) { switch (skb->priority) { @@ -94,7 +66,7 @@ static u16 classify80211(struct ieee80211_local *local, struct sk_buff *skb) /* use the data classifier to determine what 802.1d tag the * data frame has */ - skb->priority = classify_1d(skb); + skb->priority = cfg80211_classify8021d(skb); /* in case we are a client verify acm is not set for this ac */ while (unlikely(local->wmm_acm & BIT(skb->priority))) { diff --git a/net/wireless/util.c b/net/wireless/util.c index b94c8604ad7c..d072bff463aa 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -4,7 +4,9 @@ * Copyright 2007-2009 Johannes Berg */ #include +#include #include +#include #include "core.h" struct ieee80211_rate * @@ -198,3 +200,306 @@ int cfg80211_validate_key_settings(struct key_params *params, int key_idx, return 0; } + +/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ +/* Ethernet-II snap header (RFC1042 for most EtherTypes) */ +const unsigned char rfc1042_header[] __aligned(2) = + { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; +EXPORT_SYMBOL(rfc1042_header); + +/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ +const unsigned char bridge_tunnel_header[] __aligned(2) = + { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; +EXPORT_SYMBOL(bridge_tunnel_header); + +unsigned int ieee80211_hdrlen(__le16 fc) +{ + unsigned int hdrlen = 24; + + if (ieee80211_is_data(fc)) { + if (ieee80211_has_a4(fc)) + hdrlen = 30; + if (ieee80211_is_data_qos(fc)) + hdrlen += IEEE80211_QOS_CTL_LEN; + goto out; + } + + if (ieee80211_is_ctl(fc)) { + /* + * ACK and CTS are 10 bytes, all others 16. To see how + * to get this condition consider + * subtype mask: 0b0000000011110000 (0x00F0) + * ACK subtype: 0b0000000011010000 (0x00D0) + * CTS subtype: 0b0000000011000000 (0x00C0) + * bits that matter: ^^^ (0x00E0) + * value of those: 0b0000000011000000 (0x00C0) + */ + if ((fc & cpu_to_le16(0x00E0)) == cpu_to_le16(0x00C0)) + hdrlen = 10; + else + hdrlen = 16; + } +out: + return hdrlen; +} +EXPORT_SYMBOL(ieee80211_hdrlen); + +unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb) +{ + const struct ieee80211_hdr *hdr = + (const struct ieee80211_hdr *)skb->data; + unsigned int hdrlen; + + if (unlikely(skb->len < 10)) + return 0; + hdrlen = ieee80211_hdrlen(hdr->frame_control); + if (unlikely(hdrlen > skb->len)) + return 0; + return hdrlen; +} +EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb); + +int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) +{ + int ae = meshhdr->flags & MESH_FLAGS_AE; + /* 7.1.3.5a.2 */ + switch (ae) { + case 0: + return 6; + case 1: + return 12; + case 2: + return 18; + case 3: + return 24; + default: + return 6; + } +} + +int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr, + enum nl80211_iftype iftype) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + u16 hdrlen, ethertype; + u8 *payload; + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN] __aligned(2); + + if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) + return -1; + + hdrlen = ieee80211_hdrlen(hdr->frame_control); + + /* convert IEEE 802.11 header + possible LLC headers into Ethernet + * header + * IEEE 802.11 address fields: + * ToDS FromDS Addr1 Addr2 Addr3 Addr4 + * 0 0 DA SA BSSID n/a + * 0 1 DA BSSID SA n/a + * 1 0 BSSID SA DA n/a + * 1 1 RA TA DA SA + */ + memcpy(dst, ieee80211_get_DA(hdr), ETH_ALEN); + memcpy(src, ieee80211_get_SA(hdr), ETH_ALEN); + + switch (hdr->frame_control & + cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) { + case cpu_to_le16(IEEE80211_FCTL_TODS): + if (unlikely(iftype != NL80211_IFTYPE_AP && + iftype != NL80211_IFTYPE_AP_VLAN)) + return -1; + break; + case cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS): + if (unlikely(iftype != NL80211_IFTYPE_WDS && + iftype != NL80211_IFTYPE_MESH_POINT)) + return -1; + if (iftype == NL80211_IFTYPE_MESH_POINT) { + struct ieee80211s_hdr *meshdr = + (struct ieee80211s_hdr *) (skb->data + hdrlen); + hdrlen += ieee80211_get_mesh_hdrlen(meshdr); + if (meshdr->flags & MESH_FLAGS_AE_A5_A6) { + memcpy(dst, meshdr->eaddr1, ETH_ALEN); + memcpy(src, meshdr->eaddr2, ETH_ALEN); + } + } + break; + case cpu_to_le16(IEEE80211_FCTL_FROMDS): + if (iftype != NL80211_IFTYPE_STATION || + (is_multicast_ether_addr(dst) && + !compare_ether_addr(src, addr))) + return -1; + break; + case cpu_to_le16(0): + if (iftype != NL80211_IFTYPE_ADHOC) + return -1; + break; + } + + if (unlikely(skb->len - hdrlen < 8)) + return -1; + + payload = skb->data + hdrlen; + ethertype = (payload[6] << 8) | payload[7]; + + if (likely((compare_ether_addr(payload, rfc1042_header) == 0 && + ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || + compare_ether_addr(payload, bridge_tunnel_header) == 0)) { + /* remove RFC1042 or Bridge-Tunnel encapsulation and + * replace EtherType */ + skb_pull(skb, hdrlen + 6); + memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); + memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); + } else { + struct ethhdr *ehdr; + __be16 len; + + skb_pull(skb, hdrlen); + len = htons(skb->len); + ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr)); + memcpy(ehdr->h_dest, dst, ETH_ALEN); + memcpy(ehdr->h_source, src, ETH_ALEN); + ehdr->h_proto = len; + } + return 0; +} +EXPORT_SYMBOL(ieee80211_data_to_8023); + +int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr, + enum nl80211_iftype iftype, u8 *bssid, bool qos) +{ + struct ieee80211_hdr hdr; + u16 hdrlen, ethertype; + __le16 fc; + const u8 *encaps_data; + int encaps_len, skip_header_bytes; + int nh_pos, h_pos; + int head_need; + + if (unlikely(skb->len < ETH_HLEN)) + return -EINVAL; + + nh_pos = skb_network_header(skb) - skb->data; + h_pos = skb_transport_header(skb) - skb->data; + + /* convert Ethernet header to proper 802.11 header (based on + * operation mode) */ + ethertype = (skb->data[12] << 8) | skb->data[13]; + fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA); + + switch (iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); + /* DA BSSID SA */ + memcpy(hdr.addr1, skb->data, ETH_ALEN); + memcpy(hdr.addr2, addr, ETH_ALEN); + memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN); + hdrlen = 24; + break; + case NL80211_IFTYPE_STATION: + fc |= cpu_to_le16(IEEE80211_FCTL_TODS); + /* BSSID SA DA */ + memcpy(hdr.addr1, bssid, ETH_ALEN); + memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); + memcpy(hdr.addr3, skb->data, ETH_ALEN); + hdrlen = 24; + break; + case NL80211_IFTYPE_ADHOC: + /* DA SA BSSID */ + memcpy(hdr.addr1, skb->data, ETH_ALEN); + memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); + memcpy(hdr.addr3, bssid, ETH_ALEN); + hdrlen = 24; + break; + default: + return -EOPNOTSUPP; + } + + if (qos) { + fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA); + hdrlen += 2; + } + + hdr.frame_control = fc; + hdr.duration_id = 0; + hdr.seq_ctrl = 0; + + skip_header_bytes = ETH_HLEN; + if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) { + encaps_data = bridge_tunnel_header; + encaps_len = sizeof(bridge_tunnel_header); + skip_header_bytes -= 2; + } else if (ethertype > 0x600) { + encaps_data = rfc1042_header; + encaps_len = sizeof(rfc1042_header); + skip_header_bytes -= 2; + } else { + encaps_data = NULL; + encaps_len = 0; + } + + skb_pull(skb, skip_header_bytes); + nh_pos -= skip_header_bytes; + h_pos -= skip_header_bytes; + + head_need = hdrlen + encaps_len - skb_headroom(skb); + + if (head_need > 0 || skb_cloned(skb)) { + head_need = max(head_need, 0); + if (head_need) + skb_orphan(skb); + + if (pskb_expand_head(skb, head_need, 0, GFP_ATOMIC)) { + printk(KERN_ERR "failed to reallocate Tx buffer\n"); + return -ENOMEM; + } + skb->truesize += head_need; + } + + if (encaps_data) { + memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len); + nh_pos += encaps_len; + h_pos += encaps_len; + } + + memcpy(skb_push(skb, hdrlen), &hdr, hdrlen); + + nh_pos += hdrlen; + h_pos += hdrlen; + + /* Update skb pointers to various headers since this modified frame + * is going to go through Linux networking code that may potentially + * need things like pointer to IP header. */ + skb_set_mac_header(skb, 0); + skb_set_network_header(skb, nh_pos); + skb_set_transport_header(skb, h_pos); + + return 0; +} +EXPORT_SYMBOL(ieee80211_data_from_8023); + +/* Given a data frame determine the 802.1p/1d tag to use. */ +unsigned int cfg80211_classify8021d(struct sk_buff *skb) +{ + unsigned int dscp; + + /* skb->priority values from 256->263 are magic values to + * directly indicate a specific 802.1d priority. This is used + * to allow 802.1d priority to be passed directly in from VLAN + * tags, etc. + */ + if (skb->priority >= 256 && skb->priority <= 263) + return skb->priority - 256; + + switch (skb->protocol) { + case htons(ETH_P_IP): + dscp = ip_hdr(skb)->tos & 0xfc; + break; + default: + return 0; + } + + return dscp >> 5; +} +EXPORT_SYMBOL(cfg80211_classify8021d); -- cgit v1.2.3