From 77dbbb138986b26cb99f868d4b6410577ef4c040 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 13 Jul 2011 10:48:55 +0200 Subject: nl80211: advertise GTK rekey support, new triggers Since we now have the necessary API in place to support GTK rekeying, applications will need to know whether it is supported by a device. Add a pseudo-trigger that is used only to advertise that capability. Also, add some new triggers that match what iwlagn devices can do. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 4 ++++ net/wireless/nl80211.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index 880dbe2..112959d 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -488,6 +488,10 @@ int wiphy_register(struct wiphy *wiphy) int i; u16 ifmodes = wiphy->interface_modes; + if (WARN_ON((wiphy->wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) && + !(wiphy->wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY))) + return -EINVAL; + if (WARN_ON(wiphy->addresses && !wiphy->n_addresses)) return -EINVAL; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 6a82c89..a2e1e49 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -205,6 +205,10 @@ nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = { [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG }, [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG }, [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED }, + [NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG }, + [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG }, + [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG }, + [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG }, }; /* policy for GTK rekey offload attributes */ @@ -929,6 +933,16 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED); + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE); + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST); + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE); + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE); if (dev->wiphy.wowlan.n_patterns) { struct nl80211_wowlan_pattern_support pat = { .max_patterns = dev->wiphy.wowlan.n_patterns, @@ -5272,6 +5286,14 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); if (rdev->wowlan->magic_pkt) NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); + if (rdev->wowlan->gtk_rekey_failure) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE); + if (rdev->wowlan->eap_identity_req) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST); + if (rdev->wowlan->four_way_handshake) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE); + if (rdev->wowlan->rfkill_release) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE); if (rdev->wowlan->n_patterns) { struct nlattr *nl_pats, *nl_pat; int i, pat_len; @@ -5348,6 +5370,33 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) new_triggers.magic_pkt = true; } + if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED]) + return -EINVAL; + + if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) { + if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE)) + return -EINVAL; + new_triggers.gtk_rekey_failure = true; + } + + if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) { + if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ)) + return -EINVAL; + new_triggers.eap_identity_req = true; + } + + if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) { + if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE)) + return -EINVAL; + new_triggers.four_way_handshake = true; + } + + if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) { + if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE)) + return -EINVAL; + new_triggers.rfkill_release = true; + } + if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) { struct nlattr *pat; int n_patterns = 0; -- cgit v1.1 From 93b6aa693a355932b69c96956596b03f91baec35 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Wed, 13 Jul 2011 14:57:28 +0300 Subject: nl80211/cfg80211: add max_sched_scan_ssids in the hw description Some chips can scan more SSIDs with a single scheduled scan command than with a single normal scan command (eg. wl12xx chips). To support this, this patch creates a separate hardware description element that describes the amount of SSIDs supported in scheduled scans. Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a2e1e49..935b58b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -696,6 +696,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, dev->wiphy.coverage_class); NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, dev->wiphy.max_scan_ssids); + NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, + dev->wiphy.max_sched_scan_ssids); NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN, dev->wiphy.max_scan_ie_len); @@ -3511,7 +3513,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, tmp) n_ssids++; - if (n_ssids > wiphy->max_scan_ssids) + if (n_ssids > wiphy->max_sched_scan_ssids) return -EINVAL; if (info->attrs[NL80211_ATTR_IE]) -- cgit v1.1 From 5a865bad44984de245d20bfb7cff99993e8e8294 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Wed, 13 Jul 2011 14:57:29 +0300 Subject: nl80211/cfg80211: add max_sched_scan_ie_len in the hw description Some chips may support different lengths of user-supplied IEs with a single scheduled scan command than with a single normal scan command. To support this, this patch creates a separate hardware description element that describes the maximum size of user-supplied information element data supported in scheduled scans. Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 935b58b..44a3fc2 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -700,6 +700,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, dev->wiphy.max_sched_scan_ssids); NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN, dev->wiphy.max_scan_ie_len); + NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, + dev->wiphy.max_sched_scan_ie_len); if (dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_IBSS_RSN); @@ -3521,7 +3523,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, else ie_len = 0; - if (ie_len > wiphy->max_scan_ie_len) + if (ie_len > wiphy->max_sched_scan_ie_len) return -EINVAL; mutex_lock(&rdev->sched_scan_mtx); -- cgit v1.1 From 8bca5d815311f53ada13b62f5e590df40fef317e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 13 Jul 2011 19:50:34 +0200 Subject: mac80211: allow driver access to TKIP RX P1K When the driver wants to pre-program the TKIP RX phase 1 key, it needs to be able to obtain it for the peer's TA. Add API to allow it to generate it. The generation uses a dummy on-stack context since it doesn't know the RX queue. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/tkip.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'net') diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c index cc79e69..f49d00a 100644 --- a/net/mac80211/tkip.c +++ b/net/mac80211/tkip.c @@ -185,6 +185,17 @@ void ieee80211_get_tkip_p1k_iv(struct ieee80211_key_conf *keyconf, } EXPORT_SYMBOL(ieee80211_get_tkip_p1k_iv); +void ieee80211_get_tkip_rx_p1k(struct ieee80211_key_conf *keyconf, + const u8 *ta, u32 iv32, u16 *p1k) +{ + const u8 *tk = &keyconf->key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; + struct tkip_ctx ctx; + + tkip_mixing_phase1(tk, &ctx, ta, iv32); + memcpy(p1k, ctx.p1k, sizeof(ctx.p1k)); +} +EXPORT_SYMBOL(ieee80211_get_tkip_rx_p1k); + void ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf, struct sk_buff *skb, u8 *p2k) { -- cgit v1.1 From f850e00fcd7767d01101e2f0a6d464bee8e48b47 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 13 Jul 2011 19:50:53 +0200 Subject: mac80211: let key iteration get keys in install order ieee80211_iter_keys() currently returns keys in the backward order they were installed in, which is a bit confusing. Add them to the tail of the key list to make sure iterations go in the same order that keys were originally installed in. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/key.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 739bee1..5150c6d 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -278,7 +278,7 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, bool defunikey, defmultikey, defmgmtkey; if (new) - list_add(&new->list, &sdata->key_list); + list_add_tail(&new->list, &sdata->key_list); if (sta && pairwise) { rcu_assign_pointer(sta->ptk, new); -- cgit v1.1 From 94f9b97be5b3bf67392e43fb7f567721b09142c2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 14 Jul 2011 16:48:54 +0200 Subject: mac80211: be more careful in suspend/resume When suspending with all netdevs down, the device is stopped but we still call a number of driver callbacks that the driver might not expect. The same happens during resume, we might call a few callbacks without starting the driver. Fix this by checking open_count around more things and exiting quickly if it is 0. Also, while at this I noticed that the coverage class isn't reprogrammed after resume, so add that. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/pm.c | 3 +++ net/mac80211/util.c | 54 +++++++++++++++++++++++++++-------------------------- 2 files changed, 31 insertions(+), 26 deletions(-) (limited to 'net') diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index f87e993..6326d34 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -34,6 +34,9 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) struct ieee80211_sub_if_data *sdata; struct sta_info *sta; + if (!local->open_count) + goto suspend; + ieee80211_scan_cancel(local); if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 5bfb80c..8c2df33 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1157,27 +1157,37 @@ int ieee80211_reconfig(struct ieee80211_local *local) } #endif - /* restart hardware */ - if (local->open_count) { - /* - * Upon resume hardware can sometimes be goofy due to - * various platform / driver / bus issues, so restarting - * the device may at times not work immediately. Propagate - * the error. - */ - res = drv_start(local); - if (res) { - WARN(local->suspended, "Hardware became unavailable " - "upon resume. This could be a software issue " - "prior to suspend or a hardware issue.\n"); - return res; - } + /* setup fragmentation threshold */ + drv_set_frag_threshold(local, hw->wiphy->frag_threshold); + + /* setup RTS threshold */ + drv_set_rts_threshold(local, hw->wiphy->rts_threshold); + + /* reset coverage class */ + drv_set_coverage_class(local, hw->wiphy->coverage_class); + + /* everything else happens only if HW was up & running */ + if (!local->open_count) + goto wake_up; - ieee80211_led_radio(local, true); - ieee80211_mod_tpt_led_trig(local, - IEEE80211_TPT_LEDTRIG_FL_RADIO, 0); + /* + * Upon resume hardware can sometimes be goofy due to + * various platform / driver / bus issues, so restarting + * the device may at times not work immediately. Propagate + * the error. + */ + res = drv_start(local); + if (res) { + WARN(local->suspended, "Hardware became unavailable " + "upon resume. This could be a software issue " + "prior to suspend or a hardware issue.\n"); + return res; } + ieee80211_led_radio(local, true); + ieee80211_mod_tpt_led_trig(local, + IEEE80211_TPT_LEDTRIG_FL_RADIO, 0); + /* add interfaces */ list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && @@ -1201,12 +1211,6 @@ int ieee80211_reconfig(struct ieee80211_local *local) } mutex_unlock(&local->sta_mtx); - /* setup fragmentation threshold */ - drv_set_frag_threshold(local, hw->wiphy->frag_threshold); - - /* setup RTS threshold */ - drv_set_rts_threshold(local, hw->wiphy->rts_threshold); - /* reconfigure hardware */ ieee80211_hw_config(local, ~0); @@ -1287,9 +1291,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) if (ieee80211_sdata_running(sdata)) ieee80211_enable_keys(sdata); -#ifdef CONFIG_PM wake_up: -#endif ieee80211_wake_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_SUSPEND); -- cgit v1.1 From 2683d65bb016a0533c25ead3025cbd24886cf35f Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 14 Jul 2011 20:29:42 +0300 Subject: mac80211: reconfigure tx on device reconfiguration Add tx_conf array to save the current tx queues configuration, and reconfig it on resume (ieee80211_reconfig). On resume, the driver is being reconfigured. Without reconfiguring the tx queues as well, the driver might configure the device to use wrong ac params (e.g. ps-poll instead of uapsd). Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 4 ++++ net/mac80211/ieee80211_i.h | 1 + net/mac80211/mlme.c | 1 + net/mac80211/util.c | 7 ++++++- 4 files changed, 12 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index bfc36e9..3d1b091 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1255,6 +1255,10 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy, */ p.uapsd = false; + if (params->queue >= local->hw.queues) + return -EINVAL; + + local->tx_conf[params->queue] = p; if (drv_conf_tx(local, params->queue, &p)) { wiphy_debug(local->hw.wiphy, "failed to set TX queue parameters for queue %d\n", diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index dda0d1a..1b845bb 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -746,6 +746,7 @@ struct ieee80211_local { struct workqueue_struct *workqueue; unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES]; + struct ieee80211_tx_queue_params tx_conf[IEEE80211_MAX_QUEUES]; /* also used to protect ampdu_ac_queue and amdpu_ac_stop_refcnt */ spinlock_t queue_stop_reason_lock; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index c99237c..71e20a7 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -917,6 +917,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, params.aifs, params.cw_min, params.cw_max, params.txop, params.uapsd); #endif + local->tx_conf[queue] = params; if (drv_conf_tx(local, queue, ¶ms)) wiphy_debug(local->hw.wiphy, "failed to set TX queue parameters for queue %d\n", diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 8c2df33..ca440bc 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -799,6 +799,7 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata) qparam.uapsd = false; + local->tx_conf[queue] = qparam; drv_conf_tx(local, queue, &qparam); } @@ -1134,7 +1135,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) struct ieee80211_hw *hw = &local->hw; struct ieee80211_sub_if_data *sdata; struct sta_info *sta; - int res; + int res, i; #ifdef CONFIG_PM if (local->suspended) @@ -1211,6 +1212,10 @@ int ieee80211_reconfig(struct ieee80211_local *local) } mutex_unlock(&local->sta_mtx); + /* reconfigure tx conf */ + for (i = 0; i < hw->queues; i++) + drv_conf_tx(local, i, &local->tx_conf[i]); + /* reconfigure hardware */ ieee80211_hw_config(local, ~0); -- cgit v1.1 From 34850ab25d74ab4eead62c3b4a9e8036a25cc669 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 18 Jul 2011 18:08:35 +0200 Subject: cfg80211: allow userspace to control supported rates in scan Some P2P scans are not allowed to advertise 11b rates, but that is a rather special case so instead of having that, allow userspace to request the rate sets (per band) that are advertised in scan probe request frames. Since it's needed in two places now, factor out some common code parsing a rate array. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.h | 4 ++++ net/wireless/nl80211.c | 50 +++++++++++++++++++++++++++++++------------------- net/wireless/scan.c | 3 +++ net/wireless/util.c | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 19 deletions(-) (limited to 'net') diff --git a/net/wireless/core.h b/net/wireless/core.h index a570ff9..8672e02 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -447,6 +447,10 @@ int cfg80211_set_freq(struct cfg80211_registered_device *rdev, u16 cfg80211_calculate_bitrate(struct rate_info *rate); +int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, + const u8 *rates, unsigned int n_rates, + u32 *mask); + int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, u32 beacon_int); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 44a3fc2..20aa390 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -177,6 +177,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 }, [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 }, [NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED }, + [NL80211_ATTR_SCAN_SUPP_RATES] = { .type = NLA_NESTED }, }; /* policy for the key attributes */ @@ -3324,7 +3325,6 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) struct nlattr *attr; struct wiphy *wiphy; int err, tmp, n_ssids = 0, n_channels, i; - enum ieee80211_band band; size_t ie_len; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) @@ -3344,6 +3344,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) if (!n_channels) return -EINVAL; } else { + enum ieee80211_band band; n_channels = 0; for (band = 0; band < IEEE80211_NUM_BANDS; band++) @@ -3404,6 +3405,8 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) i++; } } else { + enum ieee80211_band band; + /* all channels */ for (band = 0; band < IEEE80211_NUM_BANDS; band++) { int j; @@ -3450,6 +3453,28 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) request->ie_len); } + for (i = 0; i < IEEE80211_NUM_BANDS; i++) + request->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1; + + if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) { + nla_for_each_nested(attr, + info->attrs[NL80211_ATTR_SCAN_SUPP_RATES], + tmp) { + enum ieee80211_band band = nla_type(attr); + + if (band < 0 || band > IEEE80211_NUM_BANDS) { + err = -EINVAL; + goto out_free; + } + err = ieee80211_get_ratemask(wiphy->bands[band], + nla_data(attr), + nla_len(attr), + &request->rates[band]); + if (err) + goto out_free; + } + } + request->dev = dev; request->wiphy = &rdev->wiphy; @@ -4336,25 +4361,12 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); struct ieee80211_supported_band *sband = wiphy->bands[ibss.channel->band]; - int i, j; - - if (n_rates == 0) - return -EINVAL; - - for (i = 0; i < n_rates; i++) { - int rate = (rates[i] & 0x7f) * 5; - bool found = false; + int err; - for (j = 0; j < sband->n_bitrates; j++) { - if (sband->bitrates[j].bitrate == rate) { - found = true; - ibss.basic_rates |= BIT(j); - break; - } - } - if (!found) - return -EINVAL; - } + err = ieee80211_get_ratemask(sband, rates, n_rates, + &ibss.basic_rates); + if (err) + return err; } if (info->attrs[NL80211_ATTR_MCAST_RATE] && diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 1c4672e..1e7ff94 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -862,6 +862,9 @@ int cfg80211_wext_siwscan(struct net_device *dev, creq->n_ssids = 0; } + for (i = 0; i < IEEE80211_NUM_BANDS; i++) + creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1; + rdev->scan_req = creq; err = rdev->ops->scan(wiphy, dev, creq); if (err) { diff --git a/net/wireless/util.c b/net/wireless/util.c index 4d7b83f..a329429 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1006,3 +1006,38 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, return -EBUSY; } + +int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, + const u8 *rates, unsigned int n_rates, + u32 *mask) +{ + int i, j; + + if (n_rates == 0 || n_rates > NL80211_MAX_SUPP_RATES) + return -EINVAL; + + *mask = 0; + + for (i = 0; i < n_rates; i++) { + int rate = (rates[i] & 0x7f) * 5; + bool found = false; + + for (j = 0; j < sband->n_bitrates; j++) { + if (sband->bitrates[j].bitrate == rate) { + found = true; + *mask |= BIT(j); + break; + } + } + if (!found) + return -EINVAL; + } + + /* + * mask must have at least one bit set here since we + * didn't accept a 0-length rates array nor allowed + * entries in the array that didn't exist + */ + + return 0; +} -- cgit v1.1 From 85a237fe308405ee38eb9c6733571c49902ec229 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 18 Jul 2011 18:08:36 +0200 Subject: mac80211: implement scan supported rates Scanning currently uses the TX rate mask to restrict the rate set, which is bogus. Make it use the new set of rates from userspace. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 4 ++-- net/mac80211/mlme.c | 5 +++-- net/mac80211/scan.c | 6 +++--- net/mac80211/util.c | 12 +++++------- net/mac80211/work.c | 3 ++- 5 files changed, 15 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 1b845bb..eb9d482 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1377,14 +1377,14 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, enum ieee80211_band band, u32 rate_mask, u8 channel); struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, - u8 *dst, + u8 *dst, u32 ratemask, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, bool directed); void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, - bool directed); + u32 ratemask, bool directed); void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, const size_t supp_rates_len, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 71e20a7..fee706d 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1220,7 +1220,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) } else { ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID); ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid[1], NULL, 0, - true); + (u32) -1, true); } ifmgd->probe_send_count++; @@ -1305,7 +1305,8 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID); skb = ieee80211_build_probe_req(sdata, ifmgd->associated->bssid, - ssid + 2, ssid[1], NULL, 0, true); + (u32) -1, ssid + 2, ssid[1], + NULL, 0, true); return skb; } diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 08a45ac..6f09eca 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -228,7 +228,6 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) { struct cfg80211_scan_request *req = local->scan_req; - struct ieee80211_sub_if_data *sdata = local->scan_sdata; enum ieee80211_band band; int i, ielen, n_chans; @@ -253,7 +252,7 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie, req->ie, req->ie_len, band, - sdata->rc_rateidx_mask[band], 0); + req->rates[band], 0); local->hw_scan_req->ie_len = ielen; return true; @@ -653,6 +652,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, { int i; struct ieee80211_sub_if_data *sdata = local->scan_sdata; + enum ieee80211_band band = local->hw.conf.channel->band; for (i = 0; i < local->scan_req->n_ssids; i++) ieee80211_send_probe_req( @@ -660,7 +660,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, local->scan_req->ssids[i].ssid, local->scan_req->ssids[i].ssid_len, local->scan_req->ie, local->scan_req->ie_len, - false); + local->scan_req->rates[band], false); /* * After sending probe requests, wait for probe responses diff --git a/net/mac80211/util.c b/net/mac80211/util.c index ca440bc..ddeb1b9 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1017,7 +1017,7 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, } struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, - u8 *dst, + u8 *dst, u32 ratemask, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, bool directed) @@ -1050,9 +1050,7 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, buf_len = ieee80211_build_preq_ies(local, buf, ie, ie_len, local->hw.conf.channel->band, - sdata->rc_rateidx_mask - [local->hw.conf.channel->band], - chan); + ratemask, chan); skb = ieee80211_probereq_get(&local->hw, &sdata->vif, ssid, ssid_len, @@ -1073,12 +1071,12 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, - bool directed) + u32 ratemask, bool directed) { struct sk_buff *skb; - skb = ieee80211_build_probe_req(sdata, dst, ssid, ssid_len, ie, ie_len, - directed); + skb = ieee80211_build_probe_req(sdata, dst, ratemask, ssid, ssid_len, + ie, ie_len, directed); if (skb) ieee80211_tx_skb(sdata, skb); } diff --git a/net/mac80211/work.c b/net/mac80211/work.c index edf8583..f0c74a1 100644 --- a/net/mac80211/work.c +++ b/net/mac80211/work.c @@ -450,7 +450,8 @@ ieee80211_direct_probe(struct ieee80211_work *wk) * will not answer to direct packet in unassociated state. */ ieee80211_send_probe_req(sdata, NULL, wk->probe_auth.ssid, - wk->probe_auth.ssid_len, NULL, 0, true); + wk->probe_auth.ssid_len, NULL, 0, + (u32) -1, true); wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; run_again(local, wk->timeout); -- cgit v1.1 From b2abb6e2bcb91ae384c5857dffd0bb97b76c7a68 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 19 Jul 2011 10:39:53 +0200 Subject: mac80211: sync driver before TX In P2P client mode, the GO (AP) to connect to might have periods of time where it is not available due to powersave. To allow the driver to sync with it and send frames to the GO only when it is available add a new callback tx_sync (and the corresponding finish_tx_sync). These callbacks can sleep unlike the actual TX. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/driver-ops.h | 31 +++++++++++++++++++++++++++++++ net/mac80211/driver-trace.h | 43 +++++++++++++++++++++++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/mlme.c | 24 +++++++++++++++++++++--- net/mac80211/work.c | 25 +++++++++++++++++++++++++ 5 files changed, 122 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index b2d6bba..1425380 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -130,6 +130,37 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local, trace_drv_return_void(local); } +static inline int drv_tx_sync(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + const u8 *bssid, + enum ieee80211_tx_sync_type type) +{ + int ret = 0; + + might_sleep(); + + trace_drv_tx_sync(local, sdata, bssid, type); + if (local->ops->tx_sync) + ret = local->ops->tx_sync(&local->hw, &sdata->vif, + bssid, type); + trace_drv_return_int(local, ret); + return ret; +} + +static inline void drv_finish_tx_sync(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + const u8 *bssid, + enum ieee80211_tx_sync_type type) +{ + might_sleep(); + + trace_drv_finish_tx_sync(local, sdata, bssid, type); + if (local->ops->finish_tx_sync) + local->ops->finish_tx_sync(&local->hw, &sdata->vif, + bssid, type); + trace_drv_return_void(local); +} + static inline u64 drv_prepare_multicast(struct ieee80211_local *local, struct netdev_hw_addr_list *mc_list) { diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 4470f6e8..f47b00dc7 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -319,6 +319,49 @@ TRACE_EVENT(drv_bss_info_changed, ) ); +DECLARE_EVENT_CLASS(tx_sync_evt, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + const u8 *bssid, + enum ieee80211_tx_sync_type type), + TP_ARGS(local, sdata, bssid, type), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + __array(char, bssid, ETH_ALEN) + __field(u32, sync_type) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + memcpy(__entry->bssid, bssid, ETH_ALEN); + __entry->sync_type = type; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT " bssid:%pM type:%d", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->bssid, __entry->sync_type + ) +); + +DEFINE_EVENT(tx_sync_evt, drv_tx_sync, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + const u8 *bssid, + enum ieee80211_tx_sync_type type), + TP_ARGS(local, sdata, bssid, type) +); + +DEFINE_EVENT(tx_sync_evt, drv_finish_tx_sync, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + const u8 *bssid, + enum ieee80211_tx_sync_type type), + TP_ARGS(local, sdata, bssid, type) +); + TRACE_EVENT(drv_prepare_multicast, TP_PROTO(struct ieee80211_local *local, int mc_count), diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index eb9d482..400c09b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -323,6 +323,7 @@ struct ieee80211_work { u8 key[WLAN_KEY_LEN_WEP104]; u8 key_len, key_idx; bool privacy; + bool synced; } probe_auth; struct { struct cfg80211_bss *bss; @@ -336,6 +337,7 @@ struct ieee80211_work { u8 ssid_len; u8 supp_rates_len; bool wmm_used, use_11n, uapsd_used; + bool synced; } assoc; struct { u32 duration; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index fee706d..d6470c7 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2335,14 +2335,16 @@ static enum work_done_result ieee80211_probe_auth_done(struct ieee80211_work *wk, struct sk_buff *skb) { + struct ieee80211_local *local = wk->sdata->local; + if (!skb) { cfg80211_send_auth_timeout(wk->sdata->dev, wk->filter_ta); - return WORK_DONE_DESTROY; + goto destroy; } if (wk->type == IEEE80211_WORK_AUTH) { cfg80211_send_rx_auth(wk->sdata->dev, skb->data, skb->len); - return WORK_DONE_DESTROY; + goto destroy; } mutex_lock(&wk->sdata->u.mgd.mtx); @@ -2352,6 +2354,12 @@ ieee80211_probe_auth_done(struct ieee80211_work *wk, wk->type = IEEE80211_WORK_AUTH; wk->probe_auth.tries = 0; return WORK_DONE_REQUEUE; + destroy: + if (wk->probe_auth.synced) + drv_finish_tx_sync(local, wk->sdata, wk->filter_ta, + IEEE80211_TX_SYNC_AUTH); + + return WORK_DONE_DESTROY; } int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, @@ -2424,6 +2432,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk, struct sk_buff *skb) { + struct ieee80211_local *local = wk->sdata->local; struct ieee80211_mgmt *mgmt; struct ieee80211_rx_status *rx_status; struct ieee802_11_elems elems; @@ -2431,7 +2440,7 @@ static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk, if (!skb) { cfg80211_send_assoc_timeout(wk->sdata->dev, wk->filter_ta); - return WORK_DONE_DESTROY; + goto destroy; } if (wk->type == IEEE80211_WORK_ASSOC_BEACON_WAIT) { @@ -2451,6 +2460,10 @@ static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk, status = le16_to_cpu(mgmt->u.assoc_resp.status_code); if (status == WLAN_STATUS_SUCCESS) { + if (wk->assoc.synced) + drv_finish_tx_sync(local, wk->sdata, wk->filter_ta, + IEEE80211_TX_SYNC_ASSOC); + mutex_lock(&wk->sdata->u.mgd.mtx); if (!ieee80211_assoc_success(wk, mgmt, skb->len)) { mutex_unlock(&wk->sdata->u.mgd.mtx); @@ -2464,6 +2477,11 @@ static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk, } cfg80211_send_rx_assoc(wk->sdata->dev, skb->data, skb->len); + destroy: + if (wk->assoc.synced) + drv_finish_tx_sync(local, wk->sdata, wk->filter_ta, + IEEE80211_TX_SYNC_ASSOC); + return WORK_DONE_DESTROY; } diff --git a/net/mac80211/work.c b/net/mac80211/work.c index f0c74a1..380b9a7 100644 --- a/net/mac80211/work.c +++ b/net/mac80211/work.c @@ -25,6 +25,7 @@ #include "ieee80211_i.h" #include "rate.h" +#include "driver-ops.h" #define IEEE80211_AUTH_TIMEOUT (HZ / 5) #define IEEE80211_AUTH_MAX_TRIES 3 @@ -427,6 +428,14 @@ ieee80211_direct_probe(struct ieee80211_work *wk) struct ieee80211_sub_if_data *sdata = wk->sdata; struct ieee80211_local *local = sdata->local; + if (!wk->probe_auth.synced) { + int ret = drv_tx_sync(local, sdata, wk->filter_ta, + IEEE80211_TX_SYNC_AUTH); + if (ret) + return WORK_ACT_TIMEOUT; + } + wk->probe_auth.synced = true; + wk->probe_auth.tries++; if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) { printk(KERN_DEBUG "%s: direct probe to %pM timed out\n", @@ -466,6 +475,14 @@ ieee80211_authenticate(struct ieee80211_work *wk) struct ieee80211_sub_if_data *sdata = wk->sdata; struct ieee80211_local *local = sdata->local; + if (!wk->probe_auth.synced) { + int ret = drv_tx_sync(local, sdata, wk->filter_ta, + IEEE80211_TX_SYNC_AUTH); + if (ret) + return WORK_ACT_TIMEOUT; + } + wk->probe_auth.synced = true; + wk->probe_auth.tries++; if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) { printk(KERN_DEBUG "%s: authentication with %pM" @@ -499,6 +516,14 @@ ieee80211_associate(struct ieee80211_work *wk) struct ieee80211_sub_if_data *sdata = wk->sdata; struct ieee80211_local *local = sdata->local; + if (!wk->assoc.synced) { + int ret = drv_tx_sync(local, sdata, wk->filter_ta, + IEEE80211_TX_SYNC_ASSOC); + if (ret) + return WORK_ACT_TIMEOUT; + } + wk->assoc.synced = true; + wk->assoc.tries++; if (wk->assoc.tries > IEEE80211_ASSOC_MAX_TRIES) { printk(KERN_DEBUG "%s: association with %pM" -- cgit v1.1 From 5966f2ddcda452455f8ab518b7cba221b5234a94 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 19 Jul 2011 12:57:13 +0300 Subject: cfg80211: enter psm when working as p2p_cli cfg80211_netdev_notifier_call() is configuring psm in case of NL80211_IFTYPE_STATION interface type (on NETDEV_UP). do the same for NL80211_IFTYPE_P2P_CLIENT interface type. Signed-off-by: Eliad Peller Reviewed-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index 112959d..645437c 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -922,7 +922,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, * Configure power management to the driver here so that its * correctly set also after interface type changes etc. */ - if (wdev->iftype == NL80211_IFTYPE_STATION && + if ((wdev->iftype == NL80211_IFTYPE_STATION || + wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) && rdev->ops->set_power_mgmt) if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, wdev->ps, -- cgit v1.1 From 0a557ed3a0a4f7e36d76da137b781344f548c464 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 19 Jul 2011 13:14:42 +0300 Subject: mac80211: check sta_info_get() return value ieee80211_stop_rx_ba_session() was calling sta_info_get() without rcu locking, and the return value was not checked. This resulted in the following panic: [] (ieee80211_stop_rx_ba_session+0x0/0x60 [mac80211]) [] (wl1271_event_handle+0x0/0xdc8 [wl12xx]) [] (wl1271_irq+0x0/0x4a0 [wl12xx]) [] (irq_thread+0x0/0x254) [] (kthread+0x0/0x8c) Signed-off-by: Eliad Peller Reviewed-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/agg-rx.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index ebadb9a..fd1aaf2 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -104,14 +104,22 @@ void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap, const u8 *addr) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); - struct sta_info *sta = sta_info_get(sdata, addr); + struct sta_info *sta; int i; + rcu_read_lock(); + sta = sta_info_get(sdata, addr); + if (!sta) { + rcu_read_unlock(); + return; + } + for (i = 0; i < STA_TID_NUM; i++) if (ba_rx_bitmap & BIT(i)) set_bit(i, sta->ampdu_mlme.tid_rx_stop_requested); ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work); + rcu_read_unlock(); } EXPORT_SYMBOL(ieee80211_stop_rx_ba_session); -- cgit v1.1 From a401d2bb363d942245acdd81c5b5a754011696ee Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 20 Jul 2011 00:52:16 +0200 Subject: cfg80211: fix scan crash on single-band cards commit 58389c69150e6032504dfcd3edca6b1975c8b5bc Author: Johannes Berg Date: Mon Jul 18 18:08:35 2011 +0200 cfg80211: allow userspace to control supported rates in scan made single-band cards crash since it would always access all wiphy->bands[]. Fix this and reject any attempts in the new helper ieee80211_get_ratemask() to do the same, rejecting rates configuration for unsupported bands. Reported-by: Pavel Roskin Tested-by: Pavel Roskin Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 4 +++- net/wireless/scan.c | 3 ++- net/wireless/util.c | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 20aa390..28d2aa1 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3454,7 +3454,9 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) } for (i = 0; i < IEEE80211_NUM_BANDS; i++) - request->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1; + if (wiphy->bands[i]) + request->rates[i] = + (1 << wiphy->bands[i]->n_bitrates) - 1; if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) { nla_for_each_nested(attr, diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 1e7ff94..2936cb8 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -863,7 +863,8 @@ int cfg80211_wext_siwscan(struct net_device *dev, } for (i = 0; i < IEEE80211_NUM_BANDS; i++) - creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1; + if (wiphy->bands[i]) + creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1; rdev->scan_req = creq; err = rdev->ops->scan(wiphy, dev, creq); diff --git a/net/wireless/util.c b/net/wireless/util.c index a329429..be75a3a 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1013,6 +1013,9 @@ int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, { int i, j; + if (!sband) + return -EINVAL; + if (n_rates == 0 || n_rates > NL80211_MAX_SUPP_RATES) return -EINVAL; -- cgit v1.1 From 0c03150e7ea8f7fcd03cfef29385e0010b22ee92 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 22 Jul 2011 07:47:06 +0000 Subject: bridge: send proper message_age in config BPDU A bridge topology with three systems: +------+ +------+ | A(2) |--| B(1) | +------+ +------+ \ / +------+ | C(3) | +------+ What is supposed to happen: * bridge with the lowest ID is elected root (for example: B) * C detects that A->C is higher cost path and puts in blocking state What happens. Bridge with lowest id (B) is elected correctly as root and things start out fine initially. But then config BPDU doesn't get transmitted from A -> C. Because of that the link from A-C is transistioned to the forwarding state. The root cause of this is that the configuration messages is generated with bogus message age, and dropped before sending. In the standardmessage_age is supposed to be: the time since the generation of the Configuration BPDU by the Root that instigated the generation of this Configuration BPDU. Reimplement this by recording the timestamp (age + jiffies) when recording config information. The old code incorrectly used the time elapsed on the ageing timer which was incorrect. See also: https://bugzilla.vyatta.com/show_bug.cgi?id=7164 Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/bridge/br_private.h | 1 + net/bridge/br_stp.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 54578f2..78cc364 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -124,6 +124,7 @@ struct net_bridge_port bridge_id designated_bridge; u32 path_cost; u32 designated_cost; + unsigned long designated_age; struct timer_list forward_delay_timer; struct timer_list hold_timer; diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index bb4383e..fcff622 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -164,8 +164,7 @@ void br_transmit_config(struct net_bridge_port *p) else { struct net_bridge_port *root = br_get_port(br, br->root_port); - bpdu.message_age = br->max_age - - (root->message_age_timer.expires - jiffies) + bpdu.message_age = (jiffies - root->designated_age) + MESSAGE_AGE_INCR; } bpdu.max_age = br->max_age; @@ -189,6 +188,7 @@ static inline void br_record_config_information(struct net_bridge_port *p, p->designated_cost = bpdu->root_path_cost; p->designated_bridge = bpdu->bridge_id; p->designated_port = bpdu->port_id; + p->designated_age = jiffies + bpdu->message_age; mod_timer(&p->message_age_timer, jiffies + (p->br->max_age - bpdu->message_age)); -- cgit v1.1 From 0652cac22ce3fa0c90e35d0a2862969fc394cb02 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 22 Jul 2011 07:47:07 +0000 Subject: bridge: ignore bogus STP config packets If the message_age is already greater than the max_age, then the BPDU is bogus. Linux won't generate BPDU, but conformance tester or buggy implementation might. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/bridge/br_stp_bpdu.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'net') diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c index 289646e..b69232b 100644 --- a/net/bridge/br_stp_bpdu.c +++ b/net/bridge/br_stp_bpdu.c @@ -210,6 +210,17 @@ void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb, bpdu.hello_time = br_get_ticks(buf+28); bpdu.forward_delay = br_get_ticks(buf+30); + if (bpdu.message_age > bpdu.max_age) { + if (net_ratelimit()) + br_notice(p->br, + "port %u config from %pM" + " (message_age %ul > max_age %ul)\n", + p->port_no, + eth_hdr(skb)->h_source, + bpdu.message_age, bpdu.max_age); + goto out; + } + br_received_config_bpdu(p, &bpdu); } -- cgit v1.1 From 56139fc5bd6e740706993ab3ad0d0aeff69a8479 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 22 Jul 2011 07:47:08 +0000 Subject: bridge: notifier called with the wrong device If a new device is added to a bridge, the ethernet address of the bridge network device may change. When the address changes, the appropriate callback is called, but with the wrong device argument. The address of the bridge device (ie br0) changes not the address of the device being passed to add_if (ie eth0). Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/bridge/br_if.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 1bacca4..3176e2e 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -388,7 +388,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) br_ifinfo_notify(RTM_NEWLINK, p); if (changed_addr) - call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); + call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev); dev_set_mtu(br->dev, br_min_mtu(br)); -- cgit v1.1 From 4ecb961c8b474ebef5aff55f715c7875e69dd57b Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 22 Jul 2011 07:47:09 +0000 Subject: bridge: add notification over netlink when STP changes state When STP changes state of interface need to send a new link message to reflect that change. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/bridge/br_netlink.c | 2 ++ net/bridge/br_stp.c | 4 +++- net/bridge/br_stp_if.c | 3 +++ net/bridge/br_stp_timer.c | 1 + 4 files changed, 9 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 6814083..5b1ed1b 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -188,6 +188,8 @@ static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) p->state = new_state; br_log_state(p); + br_ifinfo_notify(RTM_NEWLINK, p); + return 0; } diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index fcff622..4d651af 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -363,6 +363,8 @@ static void br_make_blocking(struct net_bridge_port *p) p->state = BR_STATE_BLOCKING; br_log_state(p); + br_ifinfo_notify(RTM_NEWLINK, p); + del_timer(&p->forward_delay_timer); } } @@ -386,8 +388,8 @@ static void br_make_forwarding(struct net_bridge_port *p) p->state = BR_STATE_LEARNING; br_multicast_enable_port(p); - br_log_state(p); + br_ifinfo_notify(RTM_NEWLINK, p); if (br->forward_delay != 0) mod_timer(&p->forward_delay_timer, jiffies + br->forward_delay); diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c index 6f615b8..10eda3c 100644 --- a/net/bridge/br_stp_if.c +++ b/net/bridge/br_stp_if.c @@ -88,6 +88,7 @@ void br_stp_enable_port(struct net_bridge_port *p) br_init_port(p); br_port_state_selection(p->br); br_log_state(p); + br_ifinfo_notify(RTM_NEWLINK, p); } /* called under bridge lock */ @@ -104,6 +105,8 @@ void br_stp_disable_port(struct net_bridge_port *p) p->topology_change_ack = 0; p->config_pending = 0; + br_ifinfo_notify(RTM_NEWLINK, p); + del_timer(&p->message_age_timer); del_timer(&p->forward_delay_timer); del_timer(&p->hold_timer); diff --git a/net/bridge/br_stp_timer.c b/net/bridge/br_stp_timer.c index 3e96514..58de2a0 100644 --- a/net/bridge/br_stp_timer.c +++ b/net/bridge/br_stp_timer.c @@ -97,6 +97,7 @@ static void br_forward_delay_timer_expired(unsigned long arg) netif_carrier_on(br->dev); } br_log_state(p); + br_ifinfo_notify(RTM_NEWLINK, p); spin_unlock(&br->lock); } -- cgit v1.1 From 160d73b8455edb1a12967e207790b1a93a4cb0e1 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 22 Jul 2011 07:47:10 +0000 Subject: bridge: minor cleanups Some minor cleanups that won't impact code: 1. Remove inline from non-critical functions; compiler will most likely inline them anyway. 2. Make function args const where possible. 3. Whitespace cleanup Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/bridge/br_private_stp.h | 3 ++- net/bridge/br_stp.c | 23 +++++++++++------------ net/bridge/br_stp_bpdu.c | 4 +--- 3 files changed, 14 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/net/bridge/br_private_stp.h b/net/bridge/br_private_stp.h index 642ef47..05ed9bc 100644 --- a/net/bridge/br_private_stp.h +++ b/net/bridge/br_private_stp.h @@ -56,7 +56,8 @@ extern void br_become_root_bridge(struct net_bridge *br); extern void br_config_bpdu_generation(struct net_bridge *); extern void br_configuration_update(struct net_bridge *); extern void br_port_state_selection(struct net_bridge *); -extern void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu); +extern void br_received_config_bpdu(struct net_bridge_port *p, + const struct br_config_bpdu *bpdu); extern void br_received_tcn_bpdu(struct net_bridge_port *p); extern void br_transmit_config(struct net_bridge_port *p); extern void br_transmit_tcn(struct net_bridge *br); diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index 4d651af..ad0a3f7 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -109,7 +109,6 @@ static void br_root_selection(struct net_bridge *br) list_for_each_entry(p, &br->port_list, list) { if (br_should_become_root_port(p, root_port)) root_port = p->port_no; - } br->root_port = root_port; @@ -145,7 +144,6 @@ void br_transmit_config(struct net_bridge_port *p) struct br_config_bpdu bpdu; struct net_bridge *br; - if (timer_pending(&p->hold_timer)) { p->config_pending = 1; return; @@ -181,8 +179,8 @@ void br_transmit_config(struct net_bridge_port *p) } /* called under bridge lock */ -static inline void br_record_config_information(struct net_bridge_port *p, - const struct br_config_bpdu *bpdu) +static void br_record_config_information(struct net_bridge_port *p, + const struct br_config_bpdu *bpdu) { p->designated_root = bpdu->root; p->designated_cost = bpdu->root_path_cost; @@ -195,7 +193,7 @@ static inline void br_record_config_information(struct net_bridge_port *p, } /* called under bridge lock */ -static inline void br_record_config_timeout_values(struct net_bridge *br, +static void br_record_config_timeout_values(struct net_bridge *br, const struct br_config_bpdu *bpdu) { br->max_age = bpdu->max_age; @@ -254,7 +252,8 @@ static void br_designated_port_selection(struct net_bridge *br) } /* called under bridge lock */ -static int br_supersedes_port_info(struct net_bridge_port *p, struct br_config_bpdu *bpdu) +static int br_supersedes_port_info(const struct net_bridge_port *p, + const struct br_config_bpdu *bpdu) { int t; @@ -285,7 +284,7 @@ static int br_supersedes_port_info(struct net_bridge_port *p, struct br_config_b } /* called under bridge lock */ -static inline void br_topology_change_acknowledged(struct net_bridge *br) +static void br_topology_change_acknowledged(struct net_bridge *br) { br->topology_change_detected = 0; del_timer(&br->tcn_timer); @@ -327,7 +326,7 @@ void br_config_bpdu_generation(struct net_bridge *br) } /* called under bridge lock */ -static inline void br_reply(struct net_bridge_port *p) +static void br_reply(struct net_bridge_port *p) { br_transmit_config(p); } @@ -381,8 +380,7 @@ static void br_make_forwarding(struct net_bridge_port *p) p->state = BR_STATE_FORWARDING; br_topology_change_detection(br); del_timer(&p->forward_delay_timer); - } - else if (br->stp_enabled == BR_KERNEL_STP) + } else if (br->stp_enabled == BR_KERNEL_STP) p->state = BR_STATE_LISTENING; else p->state = BR_STATE_LEARNING; @@ -433,14 +431,15 @@ void br_port_state_selection(struct net_bridge *br) } /* called under bridge lock */ -static inline void br_topology_change_acknowledge(struct net_bridge_port *p) +static void br_topology_change_acknowledge(struct net_bridge_port *p) { p->topology_change_ack = 1; br_transmit_config(p); } /* called under bridge lock */ -void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu) +void br_received_config_bpdu(struct net_bridge_port *p, + const struct br_config_bpdu *bpdu) { struct net_bridge *br; int was_root; diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c index b69232b..e16aade 100644 --- a/net/bridge/br_stp_bpdu.c +++ b/net/bridge/br_stp_bpdu.c @@ -222,9 +222,7 @@ void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb, } br_received_config_bpdu(p, &bpdu); - } - - else if (buf[0] == BPDU_TYPE_TCN) { + } else if (buf[0] == BPDU_TYPE_TCN) { br_received_tcn_bpdu(p); } out: -- cgit v1.1 From 1821f7cd65ad9ea56580b830ac79bf4c4fef59cb Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 22 Jul 2011 12:53:56 +0000 Subject: net: allow netif_carrier to be called safely from IRQ As reported by Ben Greer and Froncois Romieu. The code path in the netif_carrier code leads it to try and disable a late workqueue to reenable it immediately netif_carrier_on -> linkwatch_fire_event -> linkwatch_schedule_work -> cancel_delayed_work -> del_timer_sync If __cancel_delayed_work is used instead then there is no problem of waiting for running linkwatch_event. There is a race between linkwatch_event running re-scheduling but it is harmless to schedule an extra scan of the linkwatch queue. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/link_watch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/core/link_watch.c b/net/core/link_watch.c index a7b3421..357bd4e 100644 --- a/net/core/link_watch.c +++ b/net/core/link_watch.c @@ -126,7 +126,7 @@ static void linkwatch_schedule_work(int urgent) return; /* It's already running which is good enough. */ - if (!cancel_delayed_work(&linkwatch_work)) + if (!__cancel_delayed_work(&linkwatch_work)) return; /* Otherwise we reschedule it again for immediate execution. */ -- cgit v1.1 From b0fe4a31849063fcac0bdc93716ca92615e93f57 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Sat, 23 Jul 2011 02:00:41 +0000 Subject: ipv4: use RT_TOS after some rt_tos conversions rt_tos was changed to iph->tos but it must be filtered by RT_TOS Signed-off-by: Julian Anastasov Signed-off-by: David S. Miller --- net/ipv4/ipmr.c | 2 +- net/ipv4/route.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index aae2bd8..58e8791 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1796,7 +1796,7 @@ static struct mr_table *ipmr_rt_fib_lookup(struct net *net, struct sk_buff *skb) struct flowi4 fl4 = { .daddr = iph->daddr, .saddr = iph->saddr, - .flowi4_tos = iph->tos, + .flowi4_tos = RT_TOS(iph->tos), .flowi4_oif = rt->rt_oif, .flowi4_iif = rt->rt_iif, .flowi4_mark = rt->rt_mark, diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 3313730..1730689 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1740,7 +1740,7 @@ void ip_rt_get_source(u8 *addr, struct sk_buff *skb, struct rtable *rt) memset(&fl4, 0, sizeof(fl4)); fl4.daddr = iph->daddr; fl4.saddr = iph->saddr; - fl4.flowi4_tos = iph->tos; + fl4.flowi4_tos = RT_TOS(iph->tos); fl4.flowi4_oif = rt->dst.dev->ifindex; fl4.flowi4_iif = skb->dev->ifindex; fl4.flowi4_mark = skb->mark; -- cgit v1.1 From 559fafb94ad9e4cd8774f39241917c57396f9fc5 Mon Sep 17 00:00:00 2001 From: "xeb@mail.ru" Date: Fri, 22 Jul 2011 20:49:40 +0000 Subject: gre: fix improper error handling Fix improper protocol err_handler, current implementation is fully unapplicable and may cause kernel crash due to double kfree_skb. Signed-off-by: Dmitry Kozlov Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/gre.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/net/ipv4/gre.c b/net/ipv4/gre.c index 9dbe108..dbfc21d 100644 --- a/net/ipv4/gre.c +++ b/net/ipv4/gre.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -96,27 +97,17 @@ drop: static void gre_err(struct sk_buff *skb, u32 info) { const struct gre_protocol *proto; - u8 ver; - - if (!pskb_may_pull(skb, 12)) - goto drop; + const struct iphdr *iph = (const struct iphdr *)skb->data; + u8 ver = skb->data[(iph->ihl<<2) + 1]&0x7f; - ver = skb->data[1]&0x7f; if (ver >= GREPROTO_MAX) - goto drop; + return; rcu_read_lock(); proto = rcu_dereference(gre_proto[ver]); - if (!proto || !proto->err_handler) - goto drop_unlock; - proto->err_handler(skb, info); - rcu_read_unlock(); - return; - -drop_unlock: + if (proto && proto->err_handler) + proto->err_handler(skb, info); rcu_read_unlock(); -drop: - kfree_skb(skb); } static const struct net_protocol net_gre_protocol = { -- cgit v1.1