summaryrefslogtreecommitdiff
path: root/net/wireless/nl80211.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-06-15 16:33:17 +0400
committerJohannes Berg <johannes.berg@intel.com>2012-07-09 16:51:46 +0400
commit89a54e48b9cbb44aed1bf6cd712e087b96b6ae65 (patch)
treef605a69704d49c5535adf2906b276ca2207a078f /net/wireless/nl80211.c
parentf72b85b8eb6657fae95ac8f5cb20954b4d87a520 (diff)
downloadlinux-89a54e48b9cbb44aed1bf6cd712e087b96b6ae65.tar.xz
nl80211: prepare for non-netdev wireless devs
In order to support a P2P device abstraction and Bluetooth high-speed AMPs, we need to have a way to identify virtual interfaces that don't have a netdev associated. Do this by adding a NL80211_ATTR_WDEV attribute to identify a wdev which may or may not also be a netdev. To simplify things, use a 64-bit value with the high 32 bits being the wiphy index for this new wdev identifier in the nl80211 API. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r--net/wireless/nl80211.c135
1 files changed, 108 insertions, 27 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 2a5cdb60bc6e..35a9b15289f1 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -46,28 +46,60 @@ static struct genl_family nl80211_fam = {
.post_doit = nl80211_post_doit,
};
-/* internal helper: get rdev and dev */
-static int get_rdev_dev_by_ifindex(struct net *netns, struct nlattr **attrs,
- struct cfg80211_registered_device **rdev,
- struct net_device **dev)
+/* returns ERR_PTR values */
+static struct wireless_dev *
+__cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
{
- int ifindex;
+ struct cfg80211_registered_device *rdev;
+ struct wireless_dev *result = NULL;
+ bool have_ifidx = attrs[NL80211_ATTR_IFINDEX];
+ bool have_wdev_id = attrs[NL80211_ATTR_WDEV];
+ u64 wdev_id;
+ int wiphy_idx = -1;
+ int ifidx = -1;
- if (!attrs[NL80211_ATTR_IFINDEX])
- return -EINVAL;
+ assert_cfg80211_lock();
- ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
- *dev = dev_get_by_index(netns, ifindex);
- if (!*dev)
- return -ENODEV;
+ if (!have_ifidx && !have_wdev_id)
+ return ERR_PTR(-EINVAL);
- *rdev = cfg80211_get_dev_from_ifindex(netns, ifindex);
- if (IS_ERR(*rdev)) {
- dev_put(*dev);
- return PTR_ERR(*rdev);
+ if (have_ifidx)
+ ifidx = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
+ if (have_wdev_id) {
+ wdev_id = nla_get_u64(attrs[NL80211_ATTR_WDEV]);
+ wiphy_idx = wdev_id >> 32;
}
- return 0;
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+ struct wireless_dev *wdev;
+
+ if (wiphy_net(&rdev->wiphy) != netns)
+ continue;
+
+ if (have_wdev_id && rdev->wiphy_idx != wiphy_idx)
+ continue;
+
+ mutex_lock(&rdev->devlist_mtx);
+ list_for_each_entry(wdev, &rdev->wdev_list, list) {
+ if (have_ifidx && wdev->netdev &&
+ wdev->netdev->ifindex == ifidx) {
+ result = wdev;
+ break;
+ }
+ if (have_wdev_id && wdev->identifier == (u32)wdev_id) {
+ result = wdev;
+ break;
+ }
+ }
+ mutex_unlock(&rdev->devlist_mtx);
+
+ if (result)
+ break;
+ }
+
+ if (result)
+ return result;
+ return ERR_PTR(-ENODEV);
}
static struct cfg80211_registered_device *
@@ -79,13 +111,40 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
assert_cfg80211_lock();
if (!attrs[NL80211_ATTR_WIPHY] &&
- !attrs[NL80211_ATTR_IFINDEX])
+ !attrs[NL80211_ATTR_IFINDEX] &&
+ !attrs[NL80211_ATTR_WDEV])
return ERR_PTR(-EINVAL);
if (attrs[NL80211_ATTR_WIPHY])
rdev = cfg80211_rdev_by_wiphy_idx(
nla_get_u32(attrs[NL80211_ATTR_WIPHY]));
+ if (attrs[NL80211_ATTR_WDEV]) {
+ u64 wdev_id = nla_get_u64(attrs[NL80211_ATTR_WDEV]);
+ struct wireless_dev *wdev;
+ bool found = false;
+
+ tmp = cfg80211_rdev_by_wiphy_idx(wdev_id >> 32);
+ if (tmp) {
+ /* make sure wdev exists */
+ mutex_lock(&tmp->devlist_mtx);
+ list_for_each_entry(wdev, &tmp->wdev_list, list) {
+ if (wdev->identifier != (u32)wdev_id)
+ continue;
+ found = true;
+ break;
+ }
+ mutex_unlock(&tmp->devlist_mtx);
+
+ if (!found)
+ tmp = NULL;
+
+ if (rdev && tmp != rdev)
+ return ERR_PTR(-EINVAL);
+ rdev = tmp;
+ }
+ }
+
if (attrs[NL80211_ATTR_IFINDEX]) {
int ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
netdev = dev_get_by_index(netns, ifindex);
@@ -294,6 +353,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 },
[NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 },
[NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 },
+ [NL80211_ATTR_WDEV] = { .type = NLA_U64 },
};
/* policy for the key attributes */
@@ -1674,6 +1734,8 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
struct net_device *dev)
{
void *hdr;
+ u64 wdev_id = (u64)dev->ieee80211_ptr->identifier |
+ ((u64)rdev->wiphy_idx << 32);
hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
if (!hdr)
@@ -1684,6 +1746,7 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
nla_put_string(msg, NL80211_ATTR_IFNAME, dev->name) ||
nla_put_u32(msg, NL80211_ATTR_IFTYPE,
dev->ieee80211_ptr->iftype) ||
+ nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id) ||
nla_put_u32(msg, NL80211_ATTR_GENERATION,
rdev->devlist_generation ^
(cfg80211_rdev_list_generation << 2)))
@@ -1724,7 +1787,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
if_idx = 0;
mutex_lock(&rdev->devlist_mtx);
- list_for_each_entry(wdev, &rdev->netdev_list, list) {
+ list_for_each_entry(wdev, &rdev->wdev_list, list) {
if (if_idx < if_start) {
if_idx++;
continue;
@@ -2350,7 +2413,7 @@ static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev,
mutex_lock(&rdev->devlist_mtx);
- list_for_each_entry(wdev, &rdev->netdev_list, list) {
+ list_for_each_entry(wdev, &rdev->wdev_list, list) {
if (wdev->iftype != NL80211_IFTYPE_AP &&
wdev->iftype != NL80211_IFTYPE_P2P_GO)
continue;
@@ -6660,8 +6723,8 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
struct genl_info *info)
{
struct cfg80211_registered_device *rdev;
+ struct wireless_dev *wdev;
struct net_device *dev;
- int err;
bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL;
if (rtnl)
@@ -6676,21 +6739,39 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
}
info->user_ptr[0] = rdev;
} else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
- err = get_rdev_dev_by_ifindex(genl_info_net(info), info->attrs,
- &rdev, &dev);
- if (err) {
+ mutex_lock(&cfg80211_mutex);
+ wdev = __cfg80211_wdev_from_attrs(genl_info_net(info),
+ info->attrs);
+ if (IS_ERR(wdev)) {
+ mutex_unlock(&cfg80211_mutex);
if (rtnl)
rtnl_unlock();
- return err;
+ return PTR_ERR(wdev);
}
+
+ if (!wdev->netdev) {
+ mutex_unlock(&cfg80211_mutex);
+ if (rtnl)
+ rtnl_unlock();
+ return -EINVAL;
+ }
+
+ dev = wdev->netdev;
+ rdev = wiphy_to_dev(wdev->wiphy);
+
if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
!netif_running(dev)) {
- cfg80211_unlock_rdev(rdev);
- dev_put(dev);
+ mutex_unlock(&cfg80211_mutex);
if (rtnl)
rtnl_unlock();
return -ENETDOWN;
}
+
+ dev_hold(dev);
+ cfg80211_lock_rdev(rdev);
+
+ mutex_unlock(&cfg80211_mutex);
+
info->user_ptr[0] = rdev;
info->user_ptr[1] = dev;
}
@@ -8483,7 +8564,7 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
rcu_read_lock();
list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
- list_for_each_entry_rcu(wdev, &rdev->netdev_list, list)
+ list_for_each_entry_rcu(wdev, &rdev->wdev_list, list)
cfg80211_mlme_unregister_socket(wdev, notify->pid);
if (rdev->ap_beacons_nlpid == notify->pid)
rdev->ap_beacons_nlpid = 0;