aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/nl80211.c
diff options
context:
space:
mode:
authorJouni Malinen <jouni.malinen@atheros.com>2010-02-15 05:53:10 -0500
committerJohn W. Linville <linville@tuxdriver.com>2010-02-15 16:14:15 -0500
commit026331c4d9b526561ea96f95fac4bfc52b69e316 (patch)
treea82b0a92a7f03a1d151a9db123320689c73d98c7 /net/wireless/nl80211.c
parent8404080568613d93ad7cf0a16dfb68459b42a264 (diff)
cfg80211/mac80211: allow registering for and sending action frames
This implements a new command to register for action frames that userspace wants to handle instead of the in-kernel rejection. It is then responsible for rejecting ones that it decided not to handle. There is no unregistration, but the socket can be closed for that. Frames that are not registered for will not be forwarded to userspace and will be rejected by the kernel, the cfg80211 API helps implementing that. Additionally, this patch adds a new command that allows doing action frame transmission from userspace. It can be used either to exchange action frames on the current operational channel (e.g., with the AP with which we are currently associated) or to exchange off-channel Public Action frames with the remain-on-channel command. Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com> Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r--net/wireless/nl80211.c260
1 files changed, 259 insertions, 1 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index a95ab9e4c19e..328112081358 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1,7 +1,7 @@
1/* 1/*
2 * This is the new netlink-based wireless configuration interface. 2 * This is the new netlink-based wireless configuration interface.
3 * 3 *
4 * Copyright 2006-2009 Johannes Berg <johannes@sipsolutions.net> 4 * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
5 */ 5 */
6 6
7#include <linux/if.h> 7#include <linux/if.h>
@@ -145,6 +145,9 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
145 [NL80211_ATTR_DURATION] = { .type = NLA_U32 }, 145 [NL80211_ATTR_DURATION] = { .type = NLA_U32 },
146 [NL80211_ATTR_COOKIE] = { .type = NLA_U64 }, 146 [NL80211_ATTR_COOKIE] = { .type = NLA_U64 },
147 [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED }, 147 [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED },
148 [NL80211_ATTR_FRAME] = { .type = NLA_BINARY,
149 .len = IEEE80211_MAX_DATA_LEN },
150 [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },
148}; 151};
149 152
150/* policy for the attributes */ 153/* policy for the attributes */
@@ -577,6 +580,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
577 CMD(flush_pmksa, FLUSH_PMKSA); 580 CMD(flush_pmksa, FLUSH_PMKSA);
578 CMD(remain_on_channel, REMAIN_ON_CHANNEL); 581 CMD(remain_on_channel, REMAIN_ON_CHANNEL);
579 CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); 582 CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
583 CMD(action, ACTION);
580 if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { 584 if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
581 i++; 585 i++;
582 NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); 586 NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
@@ -4526,6 +4530,139 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
4526 return err; 4530 return err;
4527} 4531}
4528 4532
4533static int nl80211_register_action(struct sk_buff *skb, struct genl_info *info)
4534{
4535 struct cfg80211_registered_device *rdev;
4536 struct net_device *dev;
4537 int err;
4538
4539 if (!info->attrs[NL80211_ATTR_FRAME_MATCH])
4540 return -EINVAL;
4541
4542 if (nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]) < 1)
4543 return -EINVAL;
4544
4545 rtnl_lock();
4546
4547 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4548 if (err)
4549 goto unlock_rtnl;
4550
4551 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
4552 err = -EOPNOTSUPP;
4553 goto out;
4554 }
4555
4556 /* not much point in registering if we can't reply */
4557 if (!rdev->ops->action) {
4558 err = -EOPNOTSUPP;
4559 goto out;
4560 }
4561
4562 err = cfg80211_mlme_register_action(dev->ieee80211_ptr, info->snd_pid,
4563 nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]),
4564 nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]));
4565 out:
4566 cfg80211_unlock_rdev(rdev);
4567 dev_put(dev);
4568 unlock_rtnl:
4569 rtnl_unlock();
4570 return err;
4571}
4572
4573static int nl80211_action(struct sk_buff *skb, struct genl_info *info)
4574{
4575 struct cfg80211_registered_device *rdev;
4576 struct net_device *dev;
4577 struct ieee80211_channel *chan;
4578 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
4579 u32 freq;
4580 int err;
4581 void *hdr;
4582 u64 cookie;
4583 struct sk_buff *msg;
4584
4585 if (!info->attrs[NL80211_ATTR_FRAME] ||
4586 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
4587 return -EINVAL;
4588
4589 rtnl_lock();
4590
4591 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4592 if (err)
4593 goto unlock_rtnl;
4594
4595 if (!rdev->ops->action) {
4596 err = -EOPNOTSUPP;
4597 goto out;
4598 }
4599
4600 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
4601 err = -EOPNOTSUPP;
4602 goto out;
4603 }
4604
4605 if (!netif_running(dev)) {
4606 err = -ENETDOWN;
4607 goto out;
4608 }
4609
4610 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
4611 channel_type = nla_get_u32(
4612 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
4613 if (channel_type != NL80211_CHAN_NO_HT &&
4614 channel_type != NL80211_CHAN_HT20 &&
4615 channel_type != NL80211_CHAN_HT40PLUS &&
4616 channel_type != NL80211_CHAN_HT40MINUS)
4617 err = -EINVAL;
4618 goto out;
4619 }
4620
4621 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
4622 chan = rdev_freq_to_chan(rdev, freq, channel_type);
4623 if (chan == NULL) {
4624 err = -EINVAL;
4625 goto out;
4626 }
4627
4628 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
4629 if (!msg) {
4630 err = -ENOMEM;
4631 goto out;
4632 }
4633
4634 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
4635 NL80211_CMD_ACTION);
4636
4637 if (IS_ERR(hdr)) {
4638 err = PTR_ERR(hdr);
4639 goto free_msg;
4640 }
4641 err = cfg80211_mlme_action(rdev, dev, chan, channel_type,
4642 nla_data(info->attrs[NL80211_ATTR_FRAME]),
4643 nla_len(info->attrs[NL80211_ATTR_FRAME]),
4644 &cookie);
4645 if (err)
4646 goto free_msg;
4647
4648 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
4649
4650 genlmsg_end(msg, hdr);
4651 err = genlmsg_reply(msg, info);
4652 goto out;
4653
4654 nla_put_failure:
4655 err = -ENOBUFS;
4656 free_msg:
4657 nlmsg_free(msg);
4658 out:
4659 cfg80211_unlock_rdev(rdev);
4660 dev_put(dev);
4661unlock_rtnl:
4662 rtnl_unlock();
4663 return err;
4664}
4665
4529static struct genl_ops nl80211_ops[] = { 4666static struct genl_ops nl80211_ops[] = {
4530 { 4667 {
4531 .cmd = NL80211_CMD_GET_WIPHY, 4668 .cmd = NL80211_CMD_GET_WIPHY,
@@ -4806,6 +4943,18 @@ static struct genl_ops nl80211_ops[] = {
4806 .policy = nl80211_policy, 4943 .policy = nl80211_policy,
4807 .flags = GENL_ADMIN_PERM, 4944 .flags = GENL_ADMIN_PERM,
4808 }, 4945 },
4946 {
4947 .cmd = NL80211_CMD_REGISTER_ACTION,
4948 .doit = nl80211_register_action,
4949 .policy = nl80211_policy,
4950 .flags = GENL_ADMIN_PERM,
4951 },
4952 {
4953 .cmd = NL80211_CMD_ACTION,
4954 .doit = nl80211_action,
4955 .policy = nl80211_policy,
4956 .flags = GENL_ADMIN_PERM,
4957 },
4809}; 4958};
4810 4959
4811static struct genl_multicast_group nl80211_mlme_mcgrp = { 4960static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -5478,6 +5627,110 @@ void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
5478 nl80211_mlme_mcgrp.id, gfp); 5627 nl80211_mlme_mcgrp.id, gfp);
5479} 5628}
5480 5629
5630int nl80211_send_action(struct cfg80211_registered_device *rdev,
5631 struct net_device *netdev, u32 nlpid,
5632 int freq, const u8 *buf, size_t len, gfp_t gfp)
5633{
5634 struct sk_buff *msg;
5635 void *hdr;
5636 int err;
5637
5638 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
5639 if (!msg)
5640 return -ENOMEM;
5641
5642 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ACTION);
5643 if (!hdr) {
5644 nlmsg_free(msg);
5645 return -ENOMEM;
5646 }
5647
5648 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5649 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5650 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
5651 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
5652
5653 err = genlmsg_end(msg, hdr);
5654 if (err < 0) {
5655 nlmsg_free(msg);
5656 return err;
5657 }
5658
5659 err = genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
5660 if (err < 0)
5661 return err;
5662 return 0;
5663
5664 nla_put_failure:
5665 genlmsg_cancel(msg, hdr);
5666 nlmsg_free(msg);
5667 return -ENOBUFS;
5668}
5669
5670void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev,
5671 struct net_device *netdev, u64 cookie,
5672 const u8 *buf, size_t len, bool ack,
5673 gfp_t gfp)
5674{
5675 struct sk_buff *msg;
5676 void *hdr;
5677
5678 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
5679 if (!msg)
5680 return;
5681
5682 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ACTION_TX_STATUS);
5683 if (!hdr) {
5684 nlmsg_free(msg);
5685 return;
5686 }
5687
5688 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5689 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5690 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
5691 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
5692 if (ack)
5693 NLA_PUT_FLAG(msg, NL80211_ATTR_ACK);
5694
5695 if (genlmsg_end(msg, hdr) < 0) {
5696 nlmsg_free(msg);
5697 return;
5698 }
5699
5700 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
5701 return;
5702
5703 nla_put_failure:
5704 genlmsg_cancel(msg, hdr);
5705 nlmsg_free(msg);
5706}
5707
5708static int nl80211_netlink_notify(struct notifier_block * nb,
5709 unsigned long state,
5710 void *_notify)
5711{
5712 struct netlink_notify *notify = _notify;
5713 struct cfg80211_registered_device *rdev;
5714 struct wireless_dev *wdev;
5715
5716 if (state != NETLINK_URELEASE)
5717 return NOTIFY_DONE;
5718
5719 rcu_read_lock();
5720
5721 list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list)
5722 list_for_each_entry_rcu(wdev, &rdev->netdev_list, list)
5723 cfg80211_mlme_unregister_actions(wdev, notify->pid);
5724
5725 rcu_read_unlock();
5726
5727 return NOTIFY_DONE;
5728}
5729
5730static struct notifier_block nl80211_netlink_notifier = {
5731 .notifier_call = nl80211_netlink_notify,
5732};
5733
5481/* initialisation/exit functions */ 5734/* initialisation/exit functions */
5482 5735
5483int nl80211_init(void) 5736int nl80211_init(void)
@@ -5511,6 +5764,10 @@ int nl80211_init(void)
5511 goto err_out; 5764 goto err_out;
5512#endif 5765#endif
5513 5766
5767 err = netlink_register_notifier(&nl80211_netlink_notifier);
5768 if (err)
5769 goto err_out;
5770
5514 return 0; 5771 return 0;
5515 err_out: 5772 err_out:
5516 genl_unregister_family(&nl80211_fam); 5773 genl_unregister_family(&nl80211_fam);
@@ -5519,5 +5776,6 @@ int nl80211_init(void)
5519 5776
5520void nl80211_exit(void) 5777void nl80211_exit(void)
5521{ 5778{
5779 netlink_unregister_notifier(&nl80211_netlink_notifier);
5522 genl_unregister_family(&nl80211_fam); 5780 genl_unregister_family(&nl80211_fam);
5523} 5781}