aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/nl80211.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-11-13 07:37:47 -0500
committerJohannes Berg <johannes.berg@intel.com>2013-12-03 10:27:17 -0500
commitad7e718c9b4f717823fd920a0103f7b0fb06183f (patch)
treecb87f5792da6330705586e232f8e39b46e188e26 /net/wireless/nl80211.c
parent7869303b17a3cc78c9e9f26544be98b5734ac97c (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.c237
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
6702static 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
6687static struct genl_multicast_group nl80211_testmode_mcgrp = { 6738static 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
6817static 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
6850struct 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}
6863EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
6864
6865int 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}
6880EXPORT_SYMBOL(cfg80211_testmode_reply);
6881
6882struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, 6868struct 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}
6889EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb); 6877EXPORT_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
8858static 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
8928struct 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}
8943EXPORT_SYMBOL(__cfg80211_alloc_reply_skb);
8944
8945int 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}
8960EXPORT_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
9596static struct genl_multicast_group nl80211_mlme_mcgrp = { 9697static struct genl_multicast_group nl80211_mlme_mcgrp = {