diff options
author | Johannes Berg <johannes.berg@intel.com> | 2015-08-15 15:39:49 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2015-09-22 09:21:22 -0400 |
commit | 7bdbe400d1b2aac116513f90b75969ad2365fba6 (patch) | |
tree | eccc79938135d9f5eb7fdad2ca3f20c3c62679ec /net/wireless | |
parent | dd55ab59b6234c73522dc533757e89e6a77c2c38 (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.c | 192 |
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 | ||
9964 | static 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 | |||
10072 | static 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 | |||
9960 | struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy, | 10151 | struct 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 | |