mac80211: verify virtual interfaces in driver API
authorJohannes Berg <johannes.berg@intel.com>
Thu, 3 Nov 2011 13:41:13 +0000 (14:41 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 9 Nov 2011 21:01:02 +0000 (16:01 -0500)
The driver is never informed about monitor or
AP_VLAN interfaces, so whenever we pass those
to it later this is a bug. Verify we don't as
there are some cases where this could happen.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
net/mac80211/driver-ops.h
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/pm.c
net/mac80211/util.c

index 5f165d7eb2db4c22c3411129c4f93b72216a2794..b12ed52732c814b976bfc0084e680782faf5442d 100644 (file)
@@ -5,6 +5,11 @@
 #include "ieee80211_i.h"
 #include "driver-trace.h"
 
+static inline void check_sdata_in_driver(struct ieee80211_sub_if_data *sdata)
+{
+       WARN_ON(!(sdata->flags & IEEE80211_SDATA_IN_DRIVER));
+}
+
 static inline void drv_tx(struct ieee80211_local *local, struct sk_buff *skb)
 {
        local->ops->tx(&local->hw, skb);
@@ -69,15 +74,23 @@ static inline int drv_resume(struct ieee80211_local *local)
 #endif
 
 static inline int drv_add_interface(struct ieee80211_local *local,
-                                   struct ieee80211_vif *vif)
+                                   struct ieee80211_sub_if_data *sdata)
 {
        int ret;
 
        might_sleep();
 
-       trace_drv_add_interface(local, vif_to_sdata(vif));
-       ret = local->ops->add_interface(&local->hw, vif);
+       if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
+                   sdata->vif.type == NL80211_IFTYPE_MONITOR))
+               return -EINVAL;
+
+       trace_drv_add_interface(local, sdata);
+       ret = local->ops->add_interface(&local->hw, &sdata->vif);
        trace_drv_return_int(local, ret);
+
+       if (ret == 0)
+               sdata->flags |= IEEE80211_SDATA_IN_DRIVER;
+
        return ret;
 }
 
@@ -89,6 +102,8 @@ static inline int drv_change_interface(struct ieee80211_local *local,
 
        might_sleep();
 
+       check_sdata_in_driver(sdata);
+
        trace_drv_change_interface(local, sdata, type, p2p);
        ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p);
        trace_drv_return_int(local, ret);
@@ -96,12 +111,15 @@ static inline int drv_change_interface(struct ieee80211_local *local,
 }
 
 static inline void drv_remove_interface(struct ieee80211_local *local,
-                                       struct ieee80211_vif *vif)
+                                       struct ieee80211_sub_if_data *sdata)
 {
        might_sleep();
 
-       trace_drv_remove_interface(local, vif_to_sdata(vif));
-       local->ops->remove_interface(&local->hw, vif);
+       check_sdata_in_driver(sdata);
+
+       trace_drv_remove_interface(local, sdata);
+       local->ops->remove_interface(&local->hw, &sdata->vif);
+       sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER;
        trace_drv_return_void(local);
 }
 
@@ -124,6 +142,8 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,
 {
        might_sleep();
 
+       check_sdata_in_driver(sdata);
+
        trace_drv_bss_info_changed(local, sdata, info, changed);
        if (local->ops->bss_info_changed)
                local->ops->bss_info_changed(&local->hw, &sdata->vif, info, changed);
@@ -139,6 +159,8 @@ static inline int drv_tx_sync(struct ieee80211_local *local,
 
        might_sleep();
 
+       check_sdata_in_driver(sdata);
+
        trace_drv_tx_sync(local, sdata, bssid, type);
        if (local->ops->tx_sync)
                ret = local->ops->tx_sync(&local->hw, &sdata->vif,
@@ -154,6 +176,8 @@ static inline void drv_finish_tx_sync(struct ieee80211_local *local,
 {
        might_sleep();
 
+       check_sdata_in_driver(sdata);
+
        trace_drv_finish_tx_sync(local, sdata, bssid, type);
        if (local->ops->finish_tx_sync)
                local->ops->finish_tx_sync(&local->hw, &sdata->vif,
@@ -211,6 +235,8 @@ static inline int drv_set_key(struct ieee80211_local *local,
 
        might_sleep();
 
+       check_sdata_in_driver(sdata);
+
        trace_drv_set_key(local, cmd, sdata, sta, key);
        ret = local->ops->set_key(&local->hw, cmd, &sdata->vif, sta, key);
        trace_drv_return_int(local, ret);
@@ -228,6 +254,8 @@ static inline void drv_update_tkip_key(struct ieee80211_local *local,
        if (sta)
                ista = &sta->sta;
 
+       check_sdata_in_driver(sdata);
+
        trace_drv_update_tkip_key(local, sdata, conf, ista, iv32);
        if (local->ops->update_tkip_key)
                local->ops->update_tkip_key(&local->hw, &sdata->vif, conf,
@@ -243,6 +271,8 @@ static inline int drv_hw_scan(struct ieee80211_local *local,
 
        might_sleep();
 
+       check_sdata_in_driver(sdata);
+
        trace_drv_hw_scan(local, sdata);
        ret = local->ops->hw_scan(&local->hw, &sdata->vif, req);
        trace_drv_return_int(local, ret);
@@ -254,6 +284,8 @@ static inline void drv_cancel_hw_scan(struct ieee80211_local *local,
 {
        might_sleep();
 
+       check_sdata_in_driver(sdata);
+
        trace_drv_cancel_hw_scan(local, sdata);
        local->ops->cancel_hw_scan(&local->hw, &sdata->vif);
        trace_drv_return_void(local);
@@ -269,6 +301,8 @@ drv_sched_scan_start(struct ieee80211_local *local,
 
        might_sleep();
 
+       check_sdata_in_driver(sdata);
+
        trace_drv_sched_scan_start(local, sdata);
        ret = local->ops->sched_scan_start(&local->hw, &sdata->vif,
                                              req, ies);
@@ -281,6 +315,8 @@ static inline void drv_sched_scan_stop(struct ieee80211_local *local,
 {
        might_sleep();
 
+       check_sdata_in_driver(sdata);
+
        trace_drv_sched_scan_stop(local, sdata);
        local->ops->sched_scan_stop(&local->hw, &sdata->vif);
        trace_drv_return_void(local);
@@ -377,6 +413,8 @@ static inline void drv_sta_notify(struct ieee80211_local *local,
                                  enum sta_notify_cmd cmd,
                                  struct ieee80211_sta *sta)
 {
+       check_sdata_in_driver(sdata);
+
        trace_drv_sta_notify(local, sdata, cmd, sta);
        if (local->ops->sta_notify)
                local->ops->sta_notify(&local->hw, &sdata->vif, cmd, sta);
@@ -391,6 +429,8 @@ static inline int drv_sta_add(struct ieee80211_local *local,
 
        might_sleep();
 
+       check_sdata_in_driver(sdata);
+
        trace_drv_sta_add(local, sdata, sta);
        if (local->ops->sta_add)
                ret = local->ops->sta_add(&local->hw, &sdata->vif, sta);
@@ -406,6 +446,8 @@ static inline void drv_sta_remove(struct ieee80211_local *local,
 {
        might_sleep();
 
+       check_sdata_in_driver(sdata);
+
        trace_drv_sta_remove(local, sdata, sta);
        if (local->ops->sta_remove)
                local->ops->sta_remove(&local->hw, &sdata->vif, sta);
@@ -421,6 +463,8 @@ static inline int drv_conf_tx(struct ieee80211_local *local,
 
        might_sleep();
 
+       check_sdata_in_driver(sdata);
+
        trace_drv_conf_tx(local, sdata, queue, params);
        if (local->ops->conf_tx)
                ret = local->ops->conf_tx(&local->hw, &sdata->vif,
@@ -436,6 +480,8 @@ static inline u64 drv_get_tsf(struct ieee80211_local *local,
 
        might_sleep();
 
+       check_sdata_in_driver(sdata);
+
        trace_drv_get_tsf(local, sdata);
        if (local->ops->get_tsf)
                ret = local->ops->get_tsf(&local->hw, &sdata->vif);
@@ -449,6 +495,8 @@ static inline void drv_set_tsf(struct ieee80211_local *local,
 {
        might_sleep();
 
+       check_sdata_in_driver(sdata);
+
        trace_drv_set_tsf(local, sdata, tsf);
        if (local->ops->set_tsf)
                local->ops->set_tsf(&local->hw, &sdata->vif, tsf);
@@ -460,6 +508,8 @@ static inline void drv_reset_tsf(struct ieee80211_local *local,
 {
        might_sleep();
 
+       check_sdata_in_driver(sdata);
+
        trace_drv_reset_tsf(local, sdata);
        if (local->ops->reset_tsf)
                local->ops->reset_tsf(&local->hw, &sdata->vif);
@@ -489,6 +539,8 @@ static inline int drv_ampdu_action(struct ieee80211_local *local,
 
        might_sleep();
 
+       check_sdata_in_driver(sdata);
+
        trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn, buf_size);
 
        if (local->ops->ampdu_action)
@@ -644,6 +696,8 @@ static inline int drv_set_bitrate_mask(struct ieee80211_local *local,
 
        might_sleep();
 
+       check_sdata_in_driver(sdata);
+
        trace_drv_set_bitrate_mask(local, sdata, mask);
        if (local->ops->set_bitrate_mask)
                ret = local->ops->set_bitrate_mask(&local->hw,
@@ -657,6 +711,8 @@ static inline void drv_set_rekey_data(struct ieee80211_local *local,
                                      struct ieee80211_sub_if_data *sdata,
                                      struct cfg80211_gtk_rekey_data *data)
 {
+       check_sdata_in_driver(sdata);
+
        trace_drv_set_rekey_data(local, sdata, data);
        if (local->ops->set_rekey_data)
                local->ops->set_rekey_data(&local->hw, &sdata->vif, data);
index e2447096c94a9617b447a5c3fb7a268c37aac68d..386330c89bafe019f60914da460cb894ad96d75c 100644 (file)
@@ -543,6 +543,7 @@ struct ieee80211_if_mesh {
  *     associated stations and deliver multicast frames both
  *     back to wireless media and to the local net stack.
  * @IEEE80211_SDATA_DISCONNECT_RESUME: Disconnect after resume.
+ * @IEEE80211_SDATA_IN_DRIVER: indicates interface was added to driver
  */
 enum ieee80211_sub_if_data_flags {
        IEEE80211_SDATA_ALLMULTI                = BIT(0),
@@ -550,6 +551,7 @@ enum ieee80211_sub_if_data_flags {
        IEEE80211_SDATA_OPERATING_GMODE         = BIT(2),
        IEEE80211_SDATA_DONT_BRIDGE_PACKETS     = BIT(3),
        IEEE80211_SDATA_DISCONNECT_RESUME       = BIT(4),
+       IEEE80211_SDATA_IN_DRIVER               = BIT(5),
 };
 
 /**
index 33a974663f794601a1fe6d32c7cb4cd7d37c165b..4ee624c543cbb6dd8c44c2a4cb2eee8d2c7bc7a1 100644 (file)
@@ -265,7 +265,7 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
                break;
        default:
                if (coming_up) {
-                       res = drv_add_interface(local, &sdata->vif);
+                       res = drv_add_interface(local, sdata);
                        if (res)
                                goto err_stop;
                }
@@ -345,7 +345,7 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
 
        return 0;
  err_del_interface:
-       drv_remove_interface(local, &sdata->vif);
+       drv_remove_interface(local, sdata);
  err_stop:
        if (!local->open_count)
                drv_stop(local);
@@ -520,7 +520,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
                ieee80211_free_keys(sdata);
 
                if (going_down)
-                       drv_remove_interface(local, &sdata->vif);
+                       drv_remove_interface(local, sdata);
        }
 
        sdata->bss = NULL;
index 9ee7164b207c23017eba9b9fc064eb1ab0248537..596efaf50e096c354aaa3fb0c01abefe1afe87ab 100644 (file)
@@ -125,7 +125,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
                ieee80211_bss_info_change_notify(sdata,
                        BSS_CHANGED_BEACON_ENABLED);
 
-               drv_remove_interface(local, &sdata->vif);
+               drv_remove_interface(local, sdata);
        }
 
        /* stop hardware - this must stop RX */
index 83c482177ecbef1ed255343d2821d93c11d22828..98ca5479324b89b6c99678842b64af21ca69633d 100644 (file)
@@ -1006,7 +1006,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
                    sdata->vif.type != NL80211_IFTYPE_MONITOR &&
                    ieee80211_sdata_running(sdata))
-                       res = drv_add_interface(local, &sdata->vif);
+                       res = drv_add_interface(local, sdata);
        }
 
        /* add STAs back */