diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 136 |
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 | ||
3420 | static struct genl_multicast_group nl80211_testmode_mcgrp = { | ||
3421 | .name = "testmode", | ||
3422 | }; | ||
3423 | |||
3424 | static 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 | |||
3456 | static 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 | |||
3488 | struct 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 | } | ||
3501 | EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb); | ||
3502 | |||
3503 | int 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 | } | ||
3518 | EXPORT_SYMBOL(cfg80211_testmode_reply); | ||
3519 | |||
3520 | struct 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 | } | ||
3527 | EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb); | ||
3528 | |||
3529 | void 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 | } | ||
3538 | EXPORT_SYMBOL(cfg80211_testmode_event); | ||
3539 | #endif | ||
3540 | |||
3419 | static struct genl_ops nl80211_ops[] = { | 3541 | static 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 | }; |
3633 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 3763 | static 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); |