|
@@ -4361,6 +4361,93 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static int nl80211_testmode_dump(struct sk_buff *skb,
|
|
|
+ struct netlink_callback *cb)
|
|
|
+{
|
|
|
+ struct cfg80211_registered_device *dev;
|
|
|
+ int err;
|
|
|
+ long phy_idx;
|
|
|
+ void *data = NULL;
|
|
|
+ int data_len = 0;
|
|
|
+
|
|
|
+ if (cb->args[0]) {
|
|
|
+ /*
|
|
|
+ * 0 is a valid index, but not valid for args[0],
|
|
|
+ * so we need to offset by 1.
|
|
|
+ */
|
|
|
+ phy_idx = cb->args[0] - 1;
|
|
|
+ } else {
|
|
|
+ err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
|
|
|
+ nl80211_fam.attrbuf, nl80211_fam.maxattr,
|
|
|
+ nl80211_policy);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ if (!nl80211_fam.attrbuf[NL80211_ATTR_WIPHY])
|
|
|
+ return -EINVAL;
|
|
|
+ phy_idx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]);
|
|
|
+ if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA])
|
|
|
+ cb->args[1] =
|
|
|
+ (long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA];
|
|
|
+ }
|
|
|
+
|
|
|
+ if (cb->args[1]) {
|
|
|
+ data = nla_data((void *)cb->args[1]);
|
|
|
+ data_len = nla_len((void *)cb->args[1]);
|
|
|
+ }
|
|
|
+
|
|
|
+ mutex_lock(&cfg80211_mutex);
|
|
|
+ dev = cfg80211_rdev_by_wiphy_idx(phy_idx);
|
|
|
+ if (!dev) {
|
|
|
+ mutex_unlock(&cfg80211_mutex);
|
|
|
+ return -ENOENT;
|
|
|
+ }
|
|
|
+ cfg80211_lock_rdev(dev);
|
|
|
+ mutex_unlock(&cfg80211_mutex);
|
|
|
+
|
|
|
+ if (!dev->ops->testmode_dump) {
|
|
|
+ err = -EOPNOTSUPP;
|
|
|
+ goto out_err;
|
|
|
+ }
|
|
|
+
|
|
|
+ while (1) {
|
|
|
+ void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).pid,
|
|
|
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
|
|
+ NL80211_CMD_TESTMODE);
|
|
|
+ struct nlattr *tmdata;
|
|
|
+
|
|
|
+ if (nla_put_u32(skb, NL80211_ATTR_WIPHY, dev->wiphy_idx) < 0) {
|
|
|
+ genlmsg_cancel(skb, hdr);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
|
|
|
+ if (!tmdata) {
|
|
|
+ genlmsg_cancel(skb, hdr);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ err = dev->ops->testmode_dump(&dev->wiphy, skb, cb,
|
|
|
+ data, data_len);
|
|
|
+ nla_nest_end(skb, tmdata);
|
|
|
+
|
|
|
+ if (err == -ENOBUFS || err == -ENOENT) {
|
|
|
+ genlmsg_cancel(skb, hdr);
|
|
|
+ break;
|
|
|
+ } else if (err) {
|
|
|
+ genlmsg_cancel(skb, hdr);
|
|
|
+ goto out_err;
|
|
|
+ }
|
|
|
+
|
|
|
+ genlmsg_end(skb, hdr);
|
|
|
+ }
|
|
|
+
|
|
|
+ err = skb->len;
|
|
|
+ /* see above */
|
|
|
+ cb->args[0] = phy_idx + 1;
|
|
|
+ out_err:
|
|
|
+ cfg80211_unlock_rdev(dev);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
static struct sk_buff *
|
|
|
__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
|
|
|
int approxlen, u32 pid, u32 seq, gfp_t gfp)
|
|
@@ -5658,6 +5745,7 @@ static struct genl_ops nl80211_ops[] = {
|
|
|
{
|
|
|
.cmd = NL80211_CMD_TESTMODE,
|
|
|
.doit = nl80211_testmode_do,
|
|
|
+ .dumpit = nl80211_testmode_dump,
|
|
|
.policy = nl80211_policy,
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
.internal_flags = NL80211_FLAG_NEED_WIPHY |
|