aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2015-08-15 15:39:49 -0400
committerJohannes Berg <johannes.berg@intel.com>2015-09-22 09:21:22 -0400
commit7bdbe400d1b2aac116513f90b75969ad2365fba6 (patch)
treeeccc79938135d9f5eb7fdad2ca3f20c3c62679ec /net/wireless
parentdd55ab59b6234c73522dc533757e89e6a77c2c38 (diff)
nl80211: support vendor dumpit commands
In order to transfer many items in vendor commands, support the dumpit netlink method for them. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/nl80211.c192
1 files changed, 192 insertions, 0 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 5d8748b4c8a2..a4e6c951950f 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -3,6 +3,7 @@
3 * 3 *
4 * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> 4 * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
5 * Copyright 2013-2014 Intel Mobile Communications GmbH 5 * Copyright 2013-2014 Intel Mobile Communications GmbH
6 * Copyright 2015 Intel Deutschland GmbH
6 */ 7 */
7 8
8#include <linux/if.h> 9#include <linux/if.h>
@@ -9938,6 +9939,9 @@ static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info)
9938 if (!wdev->netdev && !wdev->p2p_started) 9939 if (!wdev->netdev && !wdev->p2p_started)
9939 return -ENETDOWN; 9940 return -ENETDOWN;
9940 } 9941 }
9942
9943 if (!vcmd->doit)
9944 return -EOPNOTSUPP;
9941 } else { 9945 } else {
9942 wdev = NULL; 9946 wdev = NULL;
9943 } 9947 }
@@ -9957,6 +9961,193 @@ static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info)
9957 return -EOPNOTSUPP; 9961 return -EOPNOTSUPP;
9958} 9962}
9959 9963
9964static int nl80211_prepare_vendor_dump(struct sk_buff *skb,
9965 struct netlink_callback *cb,
9966 struct cfg80211_registered_device **rdev,
9967 struct wireless_dev **wdev)
9968{
9969 u32 vid, subcmd;
9970 unsigned int i;
9971 int vcmd_idx = -1;
9972 int err;
9973 void *data = NULL;
9974 unsigned int data_len = 0;
9975
9976 rtnl_lock();
9977
9978 if (cb->args[0]) {
9979 /* subtract the 1 again here */
9980 struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1);
9981 struct wireless_dev *tmp;
9982
9983 if (!wiphy) {
9984 err = -ENODEV;
9985 goto out_unlock;
9986 }
9987 *rdev = wiphy_to_rdev(wiphy);
9988 *wdev = NULL;
9989
9990 if (cb->args[1]) {
9991 list_for_each_entry(tmp, &(*rdev)->wdev_list, list) {
9992 if (tmp->identifier == cb->args[1] - 1) {
9993 *wdev = tmp;
9994 break;
9995 }
9996 }
9997 }
9998
9999 /* keep rtnl locked in successful case */
10000 return 0;
10001 }
10002
10003 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
10004 nl80211_fam.attrbuf, nl80211_fam.maxattr,
10005 nl80211_policy);
10006 if (err)
10007 goto out_unlock;
10008
10009 if (!nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_ID] ||
10010 !nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD]) {
10011 err = -EINVAL;
10012 goto out_unlock;
10013 }
10014
10015 *wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk),
10016 nl80211_fam.attrbuf);
10017 if (IS_ERR(*wdev))
10018 *wdev = NULL;
10019
10020 *rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk),
10021 nl80211_fam.attrbuf);
10022 if (IS_ERR(*rdev)) {
10023 err = PTR_ERR(*rdev);
10024 goto out_unlock;
10025 }
10026
10027 vid = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_ID]);
10028 subcmd = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD]);
10029
10030 for (i = 0; i < (*rdev)->wiphy.n_vendor_commands; i++) {
10031 const struct wiphy_vendor_command *vcmd;
10032
10033 vcmd = &(*rdev)->wiphy.vendor_commands[i];
10034
10035 if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd)
10036 continue;
10037
10038 if (!vcmd->dumpit) {
10039 err = -EOPNOTSUPP;
10040 goto out_unlock;
10041 }
10042
10043 vcmd_idx = i;
10044 break;
10045 }
10046
10047 if (vcmd_idx < 0) {
10048 err = -EOPNOTSUPP;
10049 goto out_unlock;
10050 }
10051
10052 if (nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_DATA]) {
10053 data = nla_data(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_DATA]);
10054 data_len = nla_len(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_DATA]);
10055 }
10056
10057 /* 0 is the first index - add 1 to parse only once */
10058 cb->args[0] = (*rdev)->wiphy_idx + 1;
10059 /* add 1 to know if it was NULL */
10060 cb->args[1] = *wdev ? (*wdev)->identifier + 1 : 0;
10061 cb->args[2] = vcmd_idx;
10062 cb->args[3] = (unsigned long)data;
10063 cb->args[4] = data_len;
10064
10065 /* keep rtnl locked in successful case */
10066 return 0;
10067 out_unlock:
10068 rtnl_unlock();
10069 return err;
10070}
10071
10072static int nl80211_vendor_cmd_dump(struct sk_buff *skb,
10073 struct netlink_callback *cb)
10074{
10075 struct cfg80211_registered_device *rdev;
10076 struct wireless_dev *wdev;
10077 unsigned int vcmd_idx;
10078 const struct wiphy_vendor_command *vcmd;
10079 void *data;
10080 int data_len;
10081 int err;
10082 struct nlattr *vendor_data;
10083
10084 err = nl80211_prepare_vendor_dump(skb, cb, &rdev, &wdev);
10085 if (err)
10086 return err;
10087
10088 vcmd_idx = cb->args[2];
10089 data = (void *)cb->args[3];
10090 data_len = cb->args[4];
10091 vcmd = &rdev->wiphy.vendor_commands[vcmd_idx];
10092
10093 if (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV |
10094 WIPHY_VENDOR_CMD_NEED_NETDEV)) {
10095 if (!wdev)
10096 return -EINVAL;
10097 if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_NETDEV &&
10098 !wdev->netdev)
10099 return -EINVAL;
10100
10101 if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) {
10102 if (wdev->netdev &&
10103 !netif_running(wdev->netdev))
10104 return -ENETDOWN;
10105 if (!wdev->netdev && !wdev->p2p_started)
10106 return -ENETDOWN;
10107 }
10108 }
10109
10110 while (1) {
10111 void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).portid,
10112 cb->nlh->nlmsg_seq, NLM_F_MULTI,
10113 NL80211_CMD_VENDOR);
10114 if (!hdr)
10115 break;
10116
10117 if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
10118 (wdev && nla_put_u64(skb, NL80211_ATTR_WDEV,
10119 wdev_id(wdev)))) {
10120 genlmsg_cancel(skb, hdr);
10121 break;
10122 }
10123
10124 vendor_data = nla_nest_start(skb, NL80211_ATTR_VENDOR_DATA);
10125 if (!vendor_data) {
10126 genlmsg_cancel(skb, hdr);
10127 break;
10128 }
10129
10130 err = vcmd->dumpit(&rdev->wiphy, wdev, skb, data, data_len,
10131 (unsigned long *)&cb->args[5]);
10132 nla_nest_end(skb, vendor_data);
10133
10134 if (err == -ENOBUFS || err == -ENOENT) {
10135 genlmsg_cancel(skb, hdr);
10136 break;
10137 } else if (err) {
10138 genlmsg_cancel(skb, hdr);
10139 goto out;
10140 }
10141
10142 genlmsg_end(skb, hdr);
10143 }
10144
10145 err = skb->len;
10146 out:
10147 rtnl_unlock();
10148 return err;
10149}
10150
9960struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy, 10151struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy,
9961 enum nl80211_commands cmd, 10152 enum nl80211_commands cmd,
9962 enum nl80211_attrs attr, 10153 enum nl80211_attrs attr,
@@ -10994,6 +11185,7 @@ static const struct genl_ops nl80211_ops[] = {
10994 { 11185 {
10995 .cmd = NL80211_CMD_VENDOR, 11186 .cmd = NL80211_CMD_VENDOR,
10996 .doit = nl80211_vendor_cmd, 11187 .doit = nl80211_vendor_cmd,
11188 .dumpit = nl80211_vendor_cmd_dump,
10997 .policy = nl80211_policy, 11189 .policy = nl80211_policy,
10998 .flags = GENL_ADMIN_PERM, 11190 .flags = GENL_ADMIN_PERM,
10999 .internal_flags = NL80211_FLAG_NEED_WIPHY | 11191 .internal_flags = NL80211_FLAG_NEED_WIPHY |