diff options
author | Johannes Berg <johannes.berg@intel.com> | 2013-11-13 07:37:47 -0500 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-12-03 10:27:17 -0500 |
commit | ad7e718c9b4f717823fd920a0103f7b0fb06183f (patch) | |
tree | cb87f5792da6330705586e232f8e39b46e188e26 /net/wireless/nl80211.c | |
parent | 7869303b17a3cc78c9e9f26544be98b5734ac97c (diff) |
nl80211: vendor command support
Add support for vendor-specific commands to nl80211. This is
intended to be used for really vendor-specific functionality
that can't be implemented in a generic fashion for any reason.
It's *NOT* intended to be used for any normal/generic feature
or any optimisations that could be implemented across drivers.
Currently, only vendor commands (with replies) are supported,
no dump operations or vendor-specific notifications.
Also add a function wdev_to_ieee80211_vif() to mac80211 which
is needed for mac80211-based drivers wanting to implement any
vendor commands.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 237 |
1 files changed, 169 insertions, 68 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index bdcf256e3628..6989989de092 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -358,6 +358,9 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { | |||
358 | [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY }, | 358 | [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY }, |
359 | [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG }, | 359 | [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG }, |
360 | [NL80211_ATTR_OPMODE_NOTIF] = { .type = NLA_U8 }, | 360 | [NL80211_ATTR_OPMODE_NOTIF] = { .type = NLA_U8 }, |
361 | [NL80211_ATTR_VENDOR_ID] = { .type = NLA_U32 }, | ||
362 | [NL80211_ATTR_VENDOR_SUBCMD] = { .type = NLA_U32 }, | ||
363 | [NL80211_ATTR_VENDOR_DATA] = { .type = NLA_BINARY }, | ||
361 | }; | 364 | }; |
362 | 365 | ||
363 | /* policy for the key attributes */ | 366 | /* policy for the key attributes */ |
@@ -1166,6 +1169,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, | |||
1166 | struct nlattr *nl_bands, *nl_band; | 1169 | struct nlattr *nl_bands, *nl_band; |
1167 | struct nlattr *nl_freqs, *nl_freq; | 1170 | struct nlattr *nl_freqs, *nl_freq; |
1168 | struct nlattr *nl_cmds; | 1171 | struct nlattr *nl_cmds; |
1172 | struct nlattr *nl_vendor_cmds; | ||
1169 | enum ieee80211_band band; | 1173 | enum ieee80211_band band; |
1170 | struct ieee80211_channel *chan; | 1174 | struct ieee80211_channel *chan; |
1171 | int i; | 1175 | int i; |
@@ -1561,6 +1565,19 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, | |||
1561 | (nla_put_flag(msg, NL80211_ATTR_SUPPORT_5_MHZ) || | 1565 | (nla_put_flag(msg, NL80211_ATTR_SUPPORT_5_MHZ) || |
1562 | nla_put_flag(msg, NL80211_ATTR_SUPPORT_10_MHZ))) | 1566 | nla_put_flag(msg, NL80211_ATTR_SUPPORT_10_MHZ))) |
1563 | goto nla_put_failure; | 1567 | goto nla_put_failure; |
1568 | state->split_start++; | ||
1569 | break; | ||
1570 | case 11: | ||
1571 | nl_vendor_cmds = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); | ||
1572 | if (!nl_vendor_cmds) | ||
1573 | goto nla_put_failure; | ||
1574 | |||
1575 | for (i = 0; i < dev->wiphy.n_vendor_commands; i++) | ||
1576 | if (nla_put(msg, i + 1, | ||
1577 | sizeof(struct nl80211_vendor_cmd_info), | ||
1578 | &dev->wiphy.vendor_commands[i].info)) | ||
1579 | goto nla_put_failure; | ||
1580 | nla_nest_end(msg, nl_vendor_cmds); | ||
1564 | 1581 | ||
1565 | /* done */ | 1582 | /* done */ |
1566 | state->split_start = 0; | 1583 | state->split_start = 0; |
@@ -6682,6 +6699,40 @@ static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info) | |||
6682 | return err; | 6699 | return err; |
6683 | } | 6700 | } |
6684 | 6701 | ||
6702 | static struct sk_buff * | ||
6703 | __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev, | ||
6704 | int approxlen, u32 portid, u32 seq, | ||
6705 | enum nl80211_commands cmd, | ||
6706 | enum nl80211_attrs attr, gfp_t gfp) | ||
6707 | { | ||
6708 | struct sk_buff *skb; | ||
6709 | void *hdr; | ||
6710 | struct nlattr *data; | ||
6711 | |||
6712 | skb = nlmsg_new(approxlen + 100, gfp); | ||
6713 | if (!skb) | ||
6714 | return NULL; | ||
6715 | |||
6716 | hdr = nl80211hdr_put(skb, portid, seq, 0, cmd); | ||
6717 | if (!hdr) { | ||
6718 | kfree_skb(skb); | ||
6719 | return NULL; | ||
6720 | } | ||
6721 | |||
6722 | if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx)) | ||
6723 | goto nla_put_failure; | ||
6724 | data = nla_nest_start(skb, attr); | ||
6725 | |||
6726 | ((void **)skb->cb)[0] = rdev; | ||
6727 | ((void **)skb->cb)[1] = hdr; | ||
6728 | ((void **)skb->cb)[2] = data; | ||
6729 | |||
6730 | return skb; | ||
6731 | |||
6732 | nla_put_failure: | ||
6733 | kfree_skb(skb); | ||
6734 | return NULL; | ||
6735 | } | ||
6685 | 6736 | ||
6686 | #ifdef CONFIG_NL80211_TESTMODE | 6737 | #ifdef CONFIG_NL80211_TESTMODE |
6687 | static struct genl_multicast_group nl80211_testmode_mcgrp = { | 6738 | static struct genl_multicast_group nl80211_testmode_mcgrp = { |
@@ -6710,11 +6761,11 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info) | |||
6710 | if (!info->attrs[NL80211_ATTR_TESTDATA]) | 6761 | if (!info->attrs[NL80211_ATTR_TESTDATA]) |
6711 | return -EINVAL; | 6762 | return -EINVAL; |
6712 | 6763 | ||
6713 | rdev->testmode_info = info; | 6764 | rdev->cur_cmd_info = info; |
6714 | err = rdev_testmode_cmd(rdev, wdev, | 6765 | err = rdev_testmode_cmd(rdev, wdev, |
6715 | nla_data(info->attrs[NL80211_ATTR_TESTDATA]), | 6766 | nla_data(info->attrs[NL80211_ATTR_TESTDATA]), |
6716 | nla_len(info->attrs[NL80211_ATTR_TESTDATA])); | 6767 | nla_len(info->attrs[NL80211_ATTR_TESTDATA])); |
6717 | rdev->testmode_info = NULL; | 6768 | rdev->cur_cmd_info = NULL; |
6718 | 6769 | ||
6719 | return err; | 6770 | return err; |
6720 | } | 6771 | } |
@@ -6814,77 +6865,14 @@ static int nl80211_testmode_dump(struct sk_buff *skb, | |||
6814 | return err; | 6865 | return err; |
6815 | } | 6866 | } |
6816 | 6867 | ||
6817 | static struct sk_buff * | ||
6818 | __cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev, | ||
6819 | int approxlen, u32 portid, u32 seq, gfp_t gfp) | ||
6820 | { | ||
6821 | struct sk_buff *skb; | ||
6822 | void *hdr; | ||
6823 | struct nlattr *data; | ||
6824 | |||
6825 | skb = nlmsg_new(approxlen + 100, gfp); | ||
6826 | if (!skb) | ||
6827 | return NULL; | ||
6828 | |||
6829 | hdr = nl80211hdr_put(skb, portid, seq, 0, NL80211_CMD_TESTMODE); | ||
6830 | if (!hdr) { | ||
6831 | kfree_skb(skb); | ||
6832 | return NULL; | ||
6833 | } | ||
6834 | |||
6835 | if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx)) | ||
6836 | goto nla_put_failure; | ||
6837 | data = nla_nest_start(skb, NL80211_ATTR_TESTDATA); | ||
6838 | |||
6839 | ((void **)skb->cb)[0] = rdev; | ||
6840 | ((void **)skb->cb)[1] = hdr; | ||
6841 | ((void **)skb->cb)[2] = data; | ||
6842 | |||
6843 | return skb; | ||
6844 | |||
6845 | nla_put_failure: | ||
6846 | kfree_skb(skb); | ||
6847 | return NULL; | ||
6848 | } | ||
6849 | |||
6850 | struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, | ||
6851 | int approxlen) | ||
6852 | { | ||
6853 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
6854 | |||
6855 | if (WARN_ON(!rdev->testmode_info)) | ||
6856 | return NULL; | ||
6857 | |||
6858 | return __cfg80211_testmode_alloc_skb(rdev, approxlen, | ||
6859 | rdev->testmode_info->snd_portid, | ||
6860 | rdev->testmode_info->snd_seq, | ||
6861 | GFP_KERNEL); | ||
6862 | } | ||
6863 | EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb); | ||
6864 | |||
6865 | int cfg80211_testmode_reply(struct sk_buff *skb) | ||
6866 | { | ||
6867 | struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; | ||
6868 | void *hdr = ((void **)skb->cb)[1]; | ||
6869 | struct nlattr *data = ((void **)skb->cb)[2]; | ||
6870 | |||
6871 | if (WARN_ON(!rdev->testmode_info)) { | ||
6872 | kfree_skb(skb); | ||
6873 | return -EINVAL; | ||
6874 | } | ||
6875 | |||
6876 | nla_nest_end(skb, data); | ||
6877 | genlmsg_end(skb, hdr); | ||
6878 | return genlmsg_reply(skb, rdev->testmode_info); | ||
6879 | } | ||
6880 | EXPORT_SYMBOL(cfg80211_testmode_reply); | ||
6881 | |||
6882 | struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, | 6868 | struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, |
6883 | int approxlen, gfp_t gfp) | 6869 | int approxlen, gfp_t gfp) |
6884 | { | 6870 | { |
6885 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | 6871 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
6886 | 6872 | ||
6887 | return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp); | 6873 | return __cfg80211_alloc_vendor_skb(rdev, approxlen, 0, 0, |
6874 | NL80211_CMD_TESTMODE, | ||
6875 | NL80211_ATTR_TESTDATA, gfp); | ||
6888 | } | 6876 | } |
6889 | EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb); | 6877 | EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb); |
6890 | 6878 | ||
@@ -8867,6 +8855,111 @@ static int nl80211_crit_protocol_stop(struct sk_buff *skb, | |||
8867 | return 0; | 8855 | return 0; |
8868 | } | 8856 | } |
8869 | 8857 | ||
8858 | static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info) | ||
8859 | { | ||
8860 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | ||
8861 | struct wireless_dev *wdev = | ||
8862 | __cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs); | ||
8863 | int i, err; | ||
8864 | u32 vid, subcmd; | ||
8865 | |||
8866 | if (!rdev->wiphy.vendor_commands) | ||
8867 | return -EOPNOTSUPP; | ||
8868 | |||
8869 | if (IS_ERR(wdev)) { | ||
8870 | err = PTR_ERR(wdev); | ||
8871 | if (err != -EINVAL) | ||
8872 | return err; | ||
8873 | wdev = NULL; | ||
8874 | } else if (wdev->wiphy != &rdev->wiphy) { | ||
8875 | return -EINVAL; | ||
8876 | } | ||
8877 | |||
8878 | if (!info->attrs[NL80211_ATTR_VENDOR_ID] || | ||
8879 | !info->attrs[NL80211_ATTR_VENDOR_SUBCMD]) | ||
8880 | return -EINVAL; | ||
8881 | |||
8882 | vid = nla_get_u32(info->attrs[NL80211_ATTR_VENDOR_ID]); | ||
8883 | subcmd = nla_get_u32(info->attrs[NL80211_ATTR_VENDOR_SUBCMD]); | ||
8884 | for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) { | ||
8885 | const struct wiphy_vendor_command *vcmd; | ||
8886 | void *data = NULL; | ||
8887 | int len = 0; | ||
8888 | |||
8889 | vcmd = &rdev->wiphy.vendor_commands[i]; | ||
8890 | |||
8891 | if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd) | ||
8892 | continue; | ||
8893 | |||
8894 | if (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV | | ||
8895 | WIPHY_VENDOR_CMD_NEED_NETDEV)) { | ||
8896 | if (!wdev) | ||
8897 | return -EINVAL; | ||
8898 | if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_NETDEV && | ||
8899 | !wdev->netdev) | ||
8900 | return -EINVAL; | ||
8901 | |||
8902 | if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) { | ||
8903 | if (wdev->netdev && | ||
8904 | !netif_running(wdev->netdev)) | ||
8905 | return -ENETDOWN; | ||
8906 | if (!wdev->netdev && !wdev->p2p_started) | ||
8907 | return -ENETDOWN; | ||
8908 | } | ||
8909 | } else { | ||
8910 | wdev = NULL; | ||
8911 | } | ||
8912 | |||
8913 | if (info->attrs[NL80211_ATTR_VENDOR_DATA]) { | ||
8914 | data = nla_data(info->attrs[NL80211_ATTR_VENDOR_DATA]); | ||
8915 | len = nla_len(info->attrs[NL80211_ATTR_VENDOR_DATA]); | ||
8916 | } | ||
8917 | |||
8918 | rdev->cur_cmd_info = info; | ||
8919 | err = rdev->wiphy.vendor_commands[i].doit(&rdev->wiphy, wdev, | ||
8920 | data, len); | ||
8921 | rdev->cur_cmd_info = NULL; | ||
8922 | return err; | ||
8923 | } | ||
8924 | |||
8925 | return -EOPNOTSUPP; | ||
8926 | } | ||
8927 | |||
8928 | struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy, | ||
8929 | enum nl80211_commands cmd, | ||
8930 | enum nl80211_attrs attr, | ||
8931 | int approxlen) | ||
8932 | { | ||
8933 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
8934 | |||
8935 | if (WARN_ON(!rdev->cur_cmd_info)) | ||
8936 | return NULL; | ||
8937 | |||
8938 | return __cfg80211_alloc_vendor_skb(rdev, approxlen, | ||
8939 | rdev->cur_cmd_info->snd_portid, | ||
8940 | rdev->cur_cmd_info->snd_seq, | ||
8941 | cmd, attr, GFP_KERNEL); | ||
8942 | } | ||
8943 | EXPORT_SYMBOL(__cfg80211_alloc_reply_skb); | ||
8944 | |||
8945 | int cfg80211_vendor_cmd_reply(struct sk_buff *skb) | ||
8946 | { | ||
8947 | struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; | ||
8948 | void *hdr = ((void **)skb->cb)[1]; | ||
8949 | struct nlattr *data = ((void **)skb->cb)[2]; | ||
8950 | |||
8951 | if (WARN_ON(!rdev->cur_cmd_info)) { | ||
8952 | kfree_skb(skb); | ||
8953 | return -EINVAL; | ||
8954 | } | ||
8955 | |||
8956 | nla_nest_end(skb, data); | ||
8957 | genlmsg_end(skb, hdr); | ||
8958 | return genlmsg_reply(skb, rdev->cur_cmd_info); | ||
8959 | } | ||
8960 | EXPORT_SYMBOL_GPL(cfg80211_vendor_cmd_reply); | ||
8961 | |||
8962 | |||
8870 | #define NL80211_FLAG_NEED_WIPHY 0x01 | 8963 | #define NL80211_FLAG_NEED_WIPHY 0x01 |
8871 | #define NL80211_FLAG_NEED_NETDEV 0x02 | 8964 | #define NL80211_FLAG_NEED_NETDEV 0x02 |
8872 | #define NL80211_FLAG_NEED_RTNL 0x04 | 8965 | #define NL80211_FLAG_NEED_RTNL 0x04 |
@@ -9591,6 +9684,14 @@ static struct genl_ops nl80211_ops[] = { | |||
9591 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | | 9684 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | |
9592 | NL80211_FLAG_NEED_RTNL, | 9685 | NL80211_FLAG_NEED_RTNL, |
9593 | }, | 9686 | }, |
9687 | { | ||
9688 | .cmd = NL80211_CMD_VENDOR, | ||
9689 | .doit = nl80211_vendor_cmd, | ||
9690 | .policy = nl80211_policy, | ||
9691 | .flags = GENL_ADMIN_PERM, | ||
9692 | .internal_flags = NL80211_FLAG_NEED_WIPHY | | ||
9693 | NL80211_FLAG_NEED_RTNL, | ||
9694 | }, | ||
9594 | }; | 9695 | }; |
9595 | 9696 | ||
9596 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 9697 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |