diff options
author | Jouni Malinen <jouni.malinen@atheros.com> | 2010-02-15 05:53:10 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-02-15 16:14:15 -0500 |
commit | 026331c4d9b526561ea96f95fac4bfc52b69e316 (patch) | |
tree | a82b0a92a7f03a1d151a9db123320689c73d98c7 /net/wireless/nl80211.c | |
parent | 8404080568613d93ad7cf0a16dfb68459b42a264 (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.c | 260 |
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 | ||
4533 | static 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 | |||
4573 | static 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); | ||
4661 | unlock_rtnl: | ||
4662 | rtnl_unlock(); | ||
4663 | return err; | ||
4664 | } | ||
4665 | |||
4529 | static struct genl_ops nl80211_ops[] = { | 4666 | static 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 | ||
4811 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 4960 | static 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 | ||
5630 | int 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 | |||
5670 | void 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 | |||
5708 | static 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 | |||
5730 | static struct notifier_block nl80211_netlink_notifier = { | ||
5731 | .notifier_call = nl80211_netlink_notify, | ||
5732 | }; | ||
5733 | |||
5481 | /* initialisation/exit functions */ | 5734 | /* initialisation/exit functions */ |
5482 | 5735 | ||
5483 | int nl80211_init(void) | 5736 | int 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 | ||
5520 | void nl80211_exit(void) | 5777 | void 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 | } |