diff options
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/core.c | 4 | ||||
-rw-r--r-- | net/wireless/core.h | 9 | ||||
-rw-r--r-- | net/wireless/mlme.c | 166 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 260 | ||||
-rw-r--r-- | net/wireless/nl80211.h | 8 |
5 files changed, 446 insertions, 1 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c index 71b6b3a9cf1f..51908dc2ea00 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -677,6 +677,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
677 | INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work); | 677 | INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work); |
678 | INIT_LIST_HEAD(&wdev->event_list); | 678 | INIT_LIST_HEAD(&wdev->event_list); |
679 | spin_lock_init(&wdev->event_lock); | 679 | spin_lock_init(&wdev->event_lock); |
680 | INIT_LIST_HEAD(&wdev->action_registrations); | ||
681 | spin_lock_init(&wdev->action_registrations_lock); | ||
682 | |||
680 | mutex_lock(&rdev->devlist_mtx); | 683 | mutex_lock(&rdev->devlist_mtx); |
681 | list_add_rcu(&wdev->list, &rdev->netdev_list); | 684 | list_add_rcu(&wdev->list, &rdev->netdev_list); |
682 | rdev->devlist_generation++; | 685 | rdev->devlist_generation++; |
@@ -792,6 +795,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
792 | sysfs_remove_link(&dev->dev.kobj, "phy80211"); | 795 | sysfs_remove_link(&dev->dev.kobj, "phy80211"); |
793 | list_del_rcu(&wdev->list); | 796 | list_del_rcu(&wdev->list); |
794 | rdev->devlist_generation++; | 797 | rdev->devlist_generation++; |
798 | cfg80211_mlme_purge_actions(wdev); | ||
795 | #ifdef CONFIG_CFG80211_WEXT | 799 | #ifdef CONFIG_CFG80211_WEXT |
796 | kfree(wdev->wext.keys); | 800 | kfree(wdev->wext.keys); |
797 | #endif | 801 | #endif |
diff --git a/net/wireless/core.h b/net/wireless/core.h index c326a667022a..d52da913145a 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -329,6 +329,15 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
329 | const u8 *resp_ie, size_t resp_ie_len, | 329 | const u8 *resp_ie, size_t resp_ie_len, |
330 | u16 status, bool wextev, | 330 | u16 status, bool wextev, |
331 | struct cfg80211_bss *bss); | 331 | struct cfg80211_bss *bss); |
332 | int cfg80211_mlme_register_action(struct wireless_dev *wdev, u32 snd_pid, | ||
333 | const u8 *match_data, int match_len); | ||
334 | void cfg80211_mlme_unregister_actions(struct wireless_dev *wdev, u32 nlpid); | ||
335 | void cfg80211_mlme_purge_actions(struct wireless_dev *wdev); | ||
336 | int cfg80211_mlme_action(struct cfg80211_registered_device *rdev, | ||
337 | struct net_device *dev, | ||
338 | struct ieee80211_channel *chan, | ||
339 | enum nl80211_channel_type channel_type, | ||
340 | const u8 *buf, size_t len, u64 *cookie); | ||
332 | 341 | ||
333 | /* SME */ | 342 | /* SME */ |
334 | int __cfg80211_connect(struct cfg80211_registered_device *rdev, | 343 | int __cfg80211_connect(struct cfg80211_registered_device *rdev, |
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 94d151f6f73e..62bc8855e123 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c | |||
@@ -728,3 +728,169 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, | |||
728 | nl80211_send_sta_event(rdev, dev, mac_addr, sinfo, gfp); | 728 | nl80211_send_sta_event(rdev, dev, mac_addr, sinfo, gfp); |
729 | } | 729 | } |
730 | EXPORT_SYMBOL(cfg80211_new_sta); | 730 | EXPORT_SYMBOL(cfg80211_new_sta); |
731 | |||
732 | struct cfg80211_action_registration { | ||
733 | struct list_head list; | ||
734 | |||
735 | u32 nlpid; | ||
736 | |||
737 | int match_len; | ||
738 | |||
739 | u8 match[]; | ||
740 | }; | ||
741 | |||
742 | int cfg80211_mlme_register_action(struct wireless_dev *wdev, u32 snd_pid, | ||
743 | const u8 *match_data, int match_len) | ||
744 | { | ||
745 | struct cfg80211_action_registration *reg, *nreg; | ||
746 | int err = 0; | ||
747 | |||
748 | nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL); | ||
749 | if (!nreg) | ||
750 | return -ENOMEM; | ||
751 | |||
752 | spin_lock_bh(&wdev->action_registrations_lock); | ||
753 | |||
754 | list_for_each_entry(reg, &wdev->action_registrations, list) { | ||
755 | int mlen = min(match_len, reg->match_len); | ||
756 | |||
757 | if (memcmp(reg->match, match_data, mlen) == 0) { | ||
758 | err = -EALREADY; | ||
759 | break; | ||
760 | } | ||
761 | } | ||
762 | |||
763 | if (err) { | ||
764 | kfree(nreg); | ||
765 | goto out; | ||
766 | } | ||
767 | |||
768 | memcpy(nreg->match, match_data, match_len); | ||
769 | nreg->match_len = match_len; | ||
770 | nreg->nlpid = snd_pid; | ||
771 | list_add(&nreg->list, &wdev->action_registrations); | ||
772 | |||
773 | out: | ||
774 | spin_unlock_bh(&wdev->action_registrations_lock); | ||
775 | return err; | ||
776 | } | ||
777 | |||
778 | void cfg80211_mlme_unregister_actions(struct wireless_dev *wdev, u32 nlpid) | ||
779 | { | ||
780 | struct cfg80211_action_registration *reg, *tmp; | ||
781 | |||
782 | spin_lock_bh(&wdev->action_registrations_lock); | ||
783 | |||
784 | list_for_each_entry_safe(reg, tmp, &wdev->action_registrations, list) { | ||
785 | if (reg->nlpid == nlpid) { | ||
786 | list_del(®->list); | ||
787 | kfree(reg); | ||
788 | } | ||
789 | } | ||
790 | |||
791 | spin_unlock_bh(&wdev->action_registrations_lock); | ||
792 | } | ||
793 | |||
794 | void cfg80211_mlme_purge_actions(struct wireless_dev *wdev) | ||
795 | { | ||
796 | struct cfg80211_action_registration *reg, *tmp; | ||
797 | |||
798 | spin_lock_bh(&wdev->action_registrations_lock); | ||
799 | |||
800 | list_for_each_entry_safe(reg, tmp, &wdev->action_registrations, list) { | ||
801 | list_del(®->list); | ||
802 | kfree(reg); | ||
803 | } | ||
804 | |||
805 | spin_unlock_bh(&wdev->action_registrations_lock); | ||
806 | } | ||
807 | |||
808 | int cfg80211_mlme_action(struct cfg80211_registered_device *rdev, | ||
809 | struct net_device *dev, | ||
810 | struct ieee80211_channel *chan, | ||
811 | enum nl80211_channel_type channel_type, | ||
812 | const u8 *buf, size_t len, u64 *cookie) | ||
813 | { | ||
814 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
815 | const struct ieee80211_mgmt *mgmt; | ||
816 | |||
817 | if (rdev->ops->action == NULL) | ||
818 | return -EOPNOTSUPP; | ||
819 | if (len < 24 + 1) | ||
820 | return -EINVAL; | ||
821 | |||
822 | mgmt = (const struct ieee80211_mgmt *) buf; | ||
823 | if (!ieee80211_is_action(mgmt->frame_control)) | ||
824 | return -EINVAL; | ||
825 | if (mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) { | ||
826 | /* Verify that we are associated with the destination AP */ | ||
827 | if (!wdev->current_bss || | ||
828 | memcmp(wdev->current_bss->pub.bssid, mgmt->bssid, | ||
829 | ETH_ALEN) != 0 || | ||
830 | memcmp(wdev->current_bss->pub.bssid, mgmt->da, | ||
831 | ETH_ALEN) != 0) | ||
832 | return -ENOTCONN; | ||
833 | } | ||
834 | |||
835 | if (memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0) | ||
836 | return -EINVAL; | ||
837 | |||
838 | /* Transmit the Action frame as requested by user space */ | ||
839 | return rdev->ops->action(&rdev->wiphy, dev, chan, channel_type, | ||
840 | buf, len, cookie); | ||
841 | } | ||
842 | |||
843 | bool cfg80211_rx_action(struct net_device *dev, int freq, const u8 *buf, | ||
844 | size_t len, gfp_t gfp) | ||
845 | { | ||
846 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
847 | struct wiphy *wiphy = wdev->wiphy; | ||
848 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
849 | struct cfg80211_action_registration *reg; | ||
850 | const u8 *action_data; | ||
851 | int action_data_len; | ||
852 | bool result = false; | ||
853 | |||
854 | /* frame length - min size excluding category */ | ||
855 | action_data_len = len - (IEEE80211_MIN_ACTION_SIZE - 1); | ||
856 | |||
857 | /* action data starts with category */ | ||
858 | action_data = buf + IEEE80211_MIN_ACTION_SIZE - 1; | ||
859 | |||
860 | spin_lock_bh(&wdev->action_registrations_lock); | ||
861 | |||
862 | list_for_each_entry(reg, &wdev->action_registrations, list) { | ||
863 | if (reg->match_len > action_data_len) | ||
864 | continue; | ||
865 | |||
866 | if (memcmp(reg->match, action_data, reg->match_len)) | ||
867 | continue; | ||
868 | |||
869 | /* found match! */ | ||
870 | |||
871 | /* Indicate the received Action frame to user space */ | ||
872 | if (nl80211_send_action(rdev, dev, reg->nlpid, freq, | ||
873 | buf, len, gfp)) | ||
874 | continue; | ||
875 | |||
876 | result = true; | ||
877 | break; | ||
878 | } | ||
879 | |||
880 | spin_unlock_bh(&wdev->action_registrations_lock); | ||
881 | |||
882 | return result; | ||
883 | } | ||
884 | EXPORT_SYMBOL(cfg80211_rx_action); | ||
885 | |||
886 | void cfg80211_action_tx_status(struct net_device *dev, u64 cookie, | ||
887 | const u8 *buf, size_t len, bool ack, gfp_t gfp) | ||
888 | { | ||
889 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
890 | struct wiphy *wiphy = wdev->wiphy; | ||
891 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
892 | |||
893 | /* Indicate TX status of the Action frame to user space */ | ||
894 | nl80211_send_action_tx_status(rdev, dev, cookie, buf, len, ack, gfp); | ||
895 | } | ||
896 | EXPORT_SYMBOL(cfg80211_action_tx_status); | ||
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 | } |
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 14855b8fb430..4ca511102c6c 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h | |||
@@ -74,4 +74,12 @@ void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, | |||
74 | struct net_device *dev, const u8 *mac_addr, | 74 | struct net_device *dev, const u8 *mac_addr, |
75 | struct station_info *sinfo, gfp_t gfp); | 75 | struct station_info *sinfo, gfp_t gfp); |
76 | 76 | ||
77 | int nl80211_send_action(struct cfg80211_registered_device *rdev, | ||
78 | struct net_device *netdev, u32 nlpid, int freq, | ||
79 | const u8 *buf, size_t len, gfp_t gfp); | ||
80 | void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev, | ||
81 | struct net_device *netdev, u64 cookie, | ||
82 | const u8 *buf, size_t len, bool ack, | ||
83 | gfp_t gfp); | ||
84 | |||
77 | #endif /* __NET_WIRELESS_NL80211_H */ | 85 | #endif /* __NET_WIRELESS_NL80211_H */ |