aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/nl80211.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r--net/wireless/nl80211.c136
1 files changed, 136 insertions, 0 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 01523ba81baf..bb8de268a6bf 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -3416,6 +3416,128 @@ unlock_rtnl:
3416 return err; 3416 return err;
3417} 3417}
3418 3418
3419#ifdef CONFIG_NL80211_TESTMODE
3420static struct genl_multicast_group nl80211_testmode_mcgrp = {
3421 .name = "testmode",
3422};
3423
3424static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
3425{
3426 struct cfg80211_registered_device *rdev;
3427 int err;
3428
3429 if (!info->attrs[NL80211_ATTR_TESTDATA])
3430 return -EINVAL;
3431
3432 rtnl_lock();
3433
3434 rdev = cfg80211_get_dev_from_info(info);
3435 if (IS_ERR(rdev)) {
3436 err = PTR_ERR(rdev);
3437 goto unlock_rtnl;
3438 }
3439
3440 err = -EOPNOTSUPP;
3441 if (rdev->ops->testmode_cmd) {
3442 rdev->testmode_info = info;
3443 err = rdev->ops->testmode_cmd(&rdev->wiphy,
3444 nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
3445 nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
3446 rdev->testmode_info = NULL;
3447 }
3448
3449 cfg80211_put_dev(rdev);
3450
3451 unlock_rtnl:
3452 rtnl_unlock();
3453 return err;
3454}
3455
3456static struct sk_buff *
3457__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
3458 int approxlen, u32 pid, u32 seq, gfp_t gfp)
3459{
3460 struct sk_buff *skb;
3461 void *hdr;
3462 struct nlattr *data;
3463
3464 skb = nlmsg_new(approxlen + 100, gfp);
3465 if (!skb)
3466 return NULL;
3467
3468 hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE);
3469 if (!hdr) {
3470 kfree_skb(skb);
3471 return NULL;
3472 }
3473
3474 NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3475 data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
3476
3477 ((void **)skb->cb)[0] = rdev;
3478 ((void **)skb->cb)[1] = hdr;
3479 ((void **)skb->cb)[2] = data;
3480
3481 return skb;
3482
3483 nla_put_failure:
3484 kfree_skb(skb);
3485 return NULL;
3486}
3487
3488struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
3489 int approxlen)
3490{
3491 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
3492
3493 if (WARN_ON(!rdev->testmode_info))
3494 return NULL;
3495
3496 return __cfg80211_testmode_alloc_skb(rdev, approxlen,
3497 rdev->testmode_info->snd_pid,
3498 rdev->testmode_info->snd_seq,
3499 GFP_KERNEL);
3500}
3501EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
3502
3503int cfg80211_testmode_reply(struct sk_buff *skb)
3504{
3505 struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
3506 void *hdr = ((void **)skb->cb)[1];
3507 struct nlattr *data = ((void **)skb->cb)[2];
3508
3509 if (WARN_ON(!rdev->testmode_info)) {
3510 kfree_skb(skb);
3511 return -EINVAL;
3512 }
3513
3514 nla_nest_end(skb, data);
3515 genlmsg_end(skb, hdr);
3516 return genlmsg_reply(skb, rdev->testmode_info);
3517}
3518EXPORT_SYMBOL(cfg80211_testmode_reply);
3519
3520struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
3521 int approxlen, gfp_t gfp)
3522{
3523 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
3524
3525 return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
3526}
3527EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
3528
3529void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
3530{
3531 void *hdr = ((void **)skb->cb)[1];
3532 struct nlattr *data = ((void **)skb->cb)[2];
3533
3534 nla_nest_end(skb, data);
3535 genlmsg_end(skb, hdr);
3536 genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp);
3537}
3538EXPORT_SYMBOL(cfg80211_testmode_event);
3539#endif
3540
3419static struct genl_ops nl80211_ops[] = { 3541static struct genl_ops nl80211_ops[] = {
3420 { 3542 {
3421 .cmd = NL80211_CMD_GET_WIPHY, 3543 .cmd = NL80211_CMD_GET_WIPHY,
@@ -3629,6 +3751,14 @@ static struct genl_ops nl80211_ops[] = {
3629 .policy = nl80211_policy, 3751 .policy = nl80211_policy,
3630 .flags = GENL_ADMIN_PERM, 3752 .flags = GENL_ADMIN_PERM,
3631 }, 3753 },
3754#ifdef CONFIG_NL80211_TESTMODE
3755 {
3756 .cmd = NL80211_CMD_TESTMODE,
3757 .doit = nl80211_testmode_do,
3758 .policy = nl80211_policy,
3759 .flags = GENL_ADMIN_PERM,
3760 },
3761#endif
3632}; 3762};
3633static struct genl_multicast_group nl80211_mlme_mcgrp = { 3763static struct genl_multicast_group nl80211_mlme_mcgrp = {
3634 .name = "mlme", 3764 .name = "mlme",
@@ -4102,6 +4232,12 @@ int nl80211_init(void)
4102 if (err) 4232 if (err)
4103 goto err_out; 4233 goto err_out;
4104 4234
4235#ifdef CONFIG_NL80211_TESTMODE
4236 err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
4237 if (err)
4238 goto err_out;
4239#endif
4240
4105 return 0; 4241 return 0;
4106 err_out: 4242 err_out:
4107 genl_unregister_family(&nl80211_fam); 4243 genl_unregister_family(&nl80211_fam);