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 | |
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>
-rw-r--r-- | include/net/cfg80211.h | 116 | ||||
-rw-r--r-- | include/net/mac80211.h | 13 | ||||
-rw-r--r-- | include/uapi/linux/nl80211.h | 41 | ||||
-rw-r--r-- | net/mac80211/util.c | 11 | ||||
-rw-r--r-- | net/wireless/core.h | 4 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 237 |
6 files changed, 348 insertions, 74 deletions
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 47290a4059ae..884ac69b5e55 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h | |||
@@ -2680,6 +2680,34 @@ struct wiphy_coalesce_support { | |||
2680 | }; | 2680 | }; |
2681 | 2681 | ||
2682 | /** | 2682 | /** |
2683 | * enum wiphy_vendor_command_flags - validation flags for vendor commands | ||
2684 | * @WIPHY_VENDOR_CMD_NEED_WDEV: vendor command requires wdev | ||
2685 | * @WIPHY_VENDOR_CMD_NEED_NETDEV: vendor command requires netdev | ||
2686 | * @WIPHY_VENDOR_CMD_NEED_RUNNING: interface/wdev must be up & running | ||
2687 | * (must be combined with %_WDEV or %_NETDEV) | ||
2688 | */ | ||
2689 | enum wiphy_vendor_command_flags { | ||
2690 | WIPHY_VENDOR_CMD_NEED_WDEV = BIT(0), | ||
2691 | WIPHY_VENDOR_CMD_NEED_NETDEV = BIT(1), | ||
2692 | WIPHY_VENDOR_CMD_NEED_RUNNING = BIT(2), | ||
2693 | }; | ||
2694 | |||
2695 | /** | ||
2696 | * struct wiphy_vendor_command - vendor command definition | ||
2697 | * @info: vendor command identifying information, as used in nl80211 | ||
2698 | * @flags: flags, see &enum wiphy_vendor_command_flags | ||
2699 | * @doit: callback for the operation, note that wdev is %NULL if the | ||
2700 | * flags didn't ask for a wdev and non-%NULL otherwise; the data | ||
2701 | * pointer may be %NULL if userspace provided no data at all | ||
2702 | */ | ||
2703 | struct wiphy_vendor_command { | ||
2704 | struct nl80211_vendor_cmd_info info; | ||
2705 | u32 flags; | ||
2706 | int (*doit)(struct wiphy *wiphy, struct wireless_dev *wdev, | ||
2707 | const void *data, int data_len); | ||
2708 | }; | ||
2709 | |||
2710 | /** | ||
2683 | * struct wiphy - wireless hardware description | 2711 | * struct wiphy - wireless hardware description |
2684 | * @reg_notifier: the driver's regulatory notification callback, | 2712 | * @reg_notifier: the driver's regulatory notification callback, |
2685 | * note that if your driver uses wiphy_apply_custom_regulatory() | 2713 | * note that if your driver uses wiphy_apply_custom_regulatory() |
@@ -2792,6 +2820,9 @@ struct wiphy_coalesce_support { | |||
2792 | * @extended_capabilities_mask: mask of the valid values | 2820 | * @extended_capabilities_mask: mask of the valid values |
2793 | * @extended_capabilities_len: length of the extended capabilities | 2821 | * @extended_capabilities_len: length of the extended capabilities |
2794 | * @coalesce: packet coalescing support information | 2822 | * @coalesce: packet coalescing support information |
2823 | * | ||
2824 | * @vendor_commands: array of vendor commands supported by the hardware | ||
2825 | * @n_vendor_commands: number of vendor commands | ||
2795 | */ | 2826 | */ |
2796 | struct wiphy { | 2827 | struct wiphy { |
2797 | /* assign these fields before you register the wiphy */ | 2828 | /* assign these fields before you register the wiphy */ |
@@ -2903,6 +2934,9 @@ struct wiphy { | |||
2903 | 2934 | ||
2904 | const struct wiphy_coalesce_support *coalesce; | 2935 | const struct wiphy_coalesce_support *coalesce; |
2905 | 2936 | ||
2937 | const struct wiphy_vendor_command *vendor_commands; | ||
2938 | int n_vendor_commands; | ||
2939 | |||
2906 | char priv[0] __aligned(NETDEV_ALIGN); | 2940 | char priv[0] __aligned(NETDEV_ALIGN); |
2907 | }; | 2941 | }; |
2908 | 2942 | ||
@@ -3847,6 +3881,75 @@ void wiphy_rfkill_start_polling(struct wiphy *wiphy); | |||
3847 | */ | 3881 | */ |
3848 | void wiphy_rfkill_stop_polling(struct wiphy *wiphy); | 3882 | void wiphy_rfkill_stop_polling(struct wiphy *wiphy); |
3849 | 3883 | ||
3884 | /** | ||
3885 | * DOC: Vendor commands | ||
3886 | * | ||
3887 | * Occasionally, there are special protocol or firmware features that | ||
3888 | * can't be implemented very openly. For this and similar cases, the | ||
3889 | * vendor command functionality allows implementing the features with | ||
3890 | * (typically closed-source) userspace and firmware, using nl80211 as | ||
3891 | * the configuration mechanism. | ||
3892 | * | ||
3893 | * A driver supporting vendor commands must register them as an array | ||
3894 | * in struct wiphy, with handlers for each one, each command has an | ||
3895 | * OUI and sub command ID to identify it. | ||
3896 | * | ||
3897 | * Note that this feature should not be (ab)used to implement protocol | ||
3898 | * features that could openly be shared across drivers. In particular, | ||
3899 | * it must never be required to use vendor commands to implement any | ||
3900 | * "normal" functionality that higher-level userspace like connection | ||
3901 | * managers etc. need. | ||
3902 | */ | ||
3903 | |||
3904 | struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy, | ||
3905 | enum nl80211_commands cmd, | ||
3906 | enum nl80211_attrs attr, | ||
3907 | int approxlen); | ||
3908 | |||
3909 | /** | ||
3910 | * cfg80211_vendor_cmd_alloc_reply_skb - allocate vendor command reply | ||
3911 | * @wiphy: the wiphy | ||
3912 | * @approxlen: an upper bound of the length of the data that will | ||
3913 | * be put into the skb | ||
3914 | * | ||
3915 | * This function allocates and pre-fills an skb for a reply to | ||
3916 | * a vendor command. Since it is intended for a reply, calling | ||
3917 | * it outside of a vendor command's doit() operation is invalid. | ||
3918 | * | ||
3919 | * The returned skb is pre-filled with some identifying data in | ||
3920 | * a way that any data that is put into the skb (with skb_put(), | ||
3921 | * nla_put() or similar) will end up being within the | ||
3922 | * %NL80211_ATTR_VENDOR_DATA attribute, so all that needs to be done | ||
3923 | * with the skb is adding data for the corresponding userspace tool | ||
3924 | * which can then read that data out of the vendor data attribute. | ||
3925 | * You must not modify the skb in any other way. | ||
3926 | * | ||
3927 | * When done, call cfg80211_vendor_cmd_reply() with the skb and return | ||
3928 | * its error code as the result of the doit() operation. | ||
3929 | * | ||
3930 | * Return: An allocated and pre-filled skb. %NULL if any errors happen. | ||
3931 | */ | ||
3932 | static inline struct sk_buff * | ||
3933 | cfg80211_vendor_cmd_alloc_reply_skb(struct wiphy *wiphy, int approxlen) | ||
3934 | { | ||
3935 | return __cfg80211_alloc_reply_skb(wiphy, NL80211_CMD_VENDOR, | ||
3936 | NL80211_ATTR_VENDOR_DATA, approxlen); | ||
3937 | } | ||
3938 | |||
3939 | /** | ||
3940 | * cfg80211_vendor_cmd_reply - send the reply skb | ||
3941 | * @skb: The skb, must have been allocated with | ||
3942 | * cfg80211_vendor_cmd_alloc_reply_skb() | ||
3943 | * | ||
3944 | * Since calling this function will usually be the last thing | ||
3945 | * before returning from the vendor command doit() you should | ||
3946 | * return the error code. Note that this function consumes the | ||
3947 | * skb regardless of the return value. | ||
3948 | * | ||
3949 | * Return: An error code or 0 on success. | ||
3950 | */ | ||
3951 | int cfg80211_vendor_cmd_reply(struct sk_buff *skb); | ||
3952 | |||
3850 | #ifdef CONFIG_NL80211_TESTMODE | 3953 | #ifdef CONFIG_NL80211_TESTMODE |
3851 | /** | 3954 | /** |
3852 | * DOC: Test mode | 3955 | * DOC: Test mode |
@@ -3882,8 +3985,12 @@ void wiphy_rfkill_stop_polling(struct wiphy *wiphy); | |||
3882 | * | 3985 | * |
3883 | * Return: An allocated and pre-filled skb. %NULL if any errors happen. | 3986 | * Return: An allocated and pre-filled skb. %NULL if any errors happen. |
3884 | */ | 3987 | */ |
3885 | struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, | 3988 | static inline struct sk_buff * |
3886 | int approxlen); | 3989 | cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, int approxlen) |
3990 | { | ||
3991 | return __cfg80211_alloc_reply_skb(wiphy, NL80211_CMD_TESTMODE, | ||
3992 | NL80211_ATTR_TESTDATA, approxlen); | ||
3993 | } | ||
3887 | 3994 | ||
3888 | /** | 3995 | /** |
3889 | * cfg80211_testmode_reply - send the reply skb | 3996 | * cfg80211_testmode_reply - send the reply skb |
@@ -3897,7 +4004,10 @@ struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, | |||
3897 | * | 4004 | * |
3898 | * Return: An error code or 0 on success. | 4005 | * Return: An error code or 0 on success. |
3899 | */ | 4006 | */ |
3900 | int cfg80211_testmode_reply(struct sk_buff *skb); | 4007 | static inline int cfg80211_testmode_reply(struct sk_buff *skb) |
4008 | { | ||
4009 | return cfg80211_vendor_cmd_reply(skb); | ||
4010 | } | ||
3901 | 4011 | ||
3902 | /** | 4012 | /** |
3903 | * cfg80211_testmode_alloc_event_skb - allocate testmode event | 4013 | * cfg80211_testmode_alloc_event_skb - allocate testmode event |
diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 73d99bc3e636..c014acc09ebc 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h | |||
@@ -1163,6 +1163,19 @@ static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif) | |||
1163 | } | 1163 | } |
1164 | 1164 | ||
1165 | /** | 1165 | /** |
1166 | * wdev_to_ieee80211_vif - return a vif struct from a wdev | ||
1167 | * @wdev: the wdev to get the vif for | ||
1168 | * | ||
1169 | * This can be used by mac80211 drivers with direct cfg80211 APIs | ||
1170 | * (like the vendor commands) that get a wdev. | ||
1171 | * | ||
1172 | * Note that this function may return %NULL if the given wdev isn't | ||
1173 | * associated with a vif that the driver knows about (e.g. monitor | ||
1174 | * or AP_VLAN interfaces.) | ||
1175 | */ | ||
1176 | struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev); | ||
1177 | |||
1178 | /** | ||
1166 | * enum ieee80211_key_flags - key flags | 1179 | * enum ieee80211_key_flags - key flags |
1167 | * | 1180 | * |
1168 | * These flags are used for communication about keys between the driver | 1181 | * These flags are used for communication about keys between the driver |
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 9a4d0e18251c..72ba3584c90d 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h | |||
@@ -693,6 +693,15 @@ | |||
693 | * other station that transmission must be blocked until the channel | 693 | * other station that transmission must be blocked until the channel |
694 | * switch is complete. | 694 | * switch is complete. |
695 | * | 695 | * |
696 | * @NL80211_CMD_VENDOR: Vendor-specified command/event. The command is specified | ||
697 | * by the %NL80211_ATTR_VENDOR_ID attribute and a sub-command in | ||
698 | * %NL80211_ATTR_VENDOR_SUBCMD. Parameter(s) can be transported in | ||
699 | * %NL80211_ATTR_VENDOR_DATA. | ||
700 | * For feature advertisement, the %NL80211_ATTR_VENDOR_DATA attribute is | ||
701 | * used in the wiphy data as a nested attribute containing descriptions | ||
702 | * (&struct nl80211_vendor_cmd_info) of the supported vendor commands. | ||
703 | * This may also be sent as an event with the same attributes. | ||
704 | * | ||
696 | * @NL80211_CMD_MAX: highest used command number | 705 | * @NL80211_CMD_MAX: highest used command number |
697 | * @__NL80211_CMD_AFTER_LAST: internal use | 706 | * @__NL80211_CMD_AFTER_LAST: internal use |
698 | */ | 707 | */ |
@@ -860,6 +869,8 @@ enum nl80211_commands { | |||
860 | 869 | ||
861 | NL80211_CMD_CHANNEL_SWITCH, | 870 | NL80211_CMD_CHANNEL_SWITCH, |
862 | 871 | ||
872 | NL80211_CMD_VENDOR, | ||
873 | |||
863 | /* add new commands above here */ | 874 | /* add new commands above here */ |
864 | 875 | ||
865 | /* used to define NL80211_CMD_MAX below */ | 876 | /* used to define NL80211_CMD_MAX below */ |
@@ -1524,6 +1535,12 @@ enum nl80211_commands { | |||
1524 | * Notification Element based on association request when used with | 1535 | * Notification Element based on association request when used with |
1525 | * %NL80211_CMD_NEW_STATION; u8 attribute. | 1536 | * %NL80211_CMD_NEW_STATION; u8 attribute. |
1526 | * | 1537 | * |
1538 | * @NL80211_ATTR_VENDOR_ID: The vendor ID, either a 24-bit OUI or, if | ||
1539 | * %NL80211_VENDOR_ID_IS_LINUX is set, a special Linux ID (not used yet) | ||
1540 | * @NL80211_ATTR_VENDOR_SUBCMD: vendor sub-command | ||
1541 | * @NL80211_ATTR_VENDOR_DATA: data for the vendor command, if any; this | ||
1542 | * attribute is also used for vendor command feature advertisement | ||
1543 | * | ||
1527 | * @NL80211_ATTR_MAX: highest attribute number currently defined | 1544 | * @NL80211_ATTR_MAX: highest attribute number currently defined |
1528 | * @__NL80211_ATTR_AFTER_LAST: internal use | 1545 | * @__NL80211_ATTR_AFTER_LAST: internal use |
1529 | */ | 1546 | */ |
@@ -1845,6 +1862,10 @@ enum nl80211_attrs { | |||
1845 | 1862 | ||
1846 | NL80211_ATTR_OPMODE_NOTIF, | 1863 | NL80211_ATTR_OPMODE_NOTIF, |
1847 | 1864 | ||
1865 | NL80211_ATTR_VENDOR_ID, | ||
1866 | NL80211_ATTR_VENDOR_SUBCMD, | ||
1867 | NL80211_ATTR_VENDOR_DATA, | ||
1868 | |||
1848 | /* add attributes here, update the policy in nl80211.c */ | 1869 | /* add attributes here, update the policy in nl80211.c */ |
1849 | 1870 | ||
1850 | __NL80211_ATTR_AFTER_LAST, | 1871 | __NL80211_ATTR_AFTER_LAST, |
@@ -3965,4 +3986,24 @@ enum nl80211_rxmgmt_flags { | |||
3965 | NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0, | 3986 | NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0, |
3966 | }; | 3987 | }; |
3967 | 3988 | ||
3989 | /* | ||
3990 | * If this flag is unset, the lower 24 bits are an OUI, if set | ||
3991 | * a Linux nl80211 vendor ID is used (no such IDs are allocated | ||
3992 | * yet, so that's not valid so far) | ||
3993 | */ | ||
3994 | #define NL80211_VENDOR_ID_IS_LINUX 0x80000000 | ||
3995 | |||
3996 | /** | ||
3997 | * struct nl80211_vendor_cmd_info - vendor command data | ||
3998 | * @vendor_id: If the %NL80211_VENDOR_ID_IS_LINUX flag is clear, then the | ||
3999 | * value is a 24-bit OUI; if it is set then a separately allocated ID | ||
4000 | * may be used, but no such IDs are allocated yet. New IDs should be | ||
4001 | * added to this file when needed. | ||
4002 | * @subcmd: sub-command ID for the command | ||
4003 | */ | ||
4004 | struct nl80211_vendor_cmd_info { | ||
4005 | __u32 vendor_id; | ||
4006 | __u32 subcmd; | ||
4007 | }; | ||
4008 | |||
3968 | #endif /* __LINUX_NL80211_H */ | 4009 | #endif /* __LINUX_NL80211_H */ |
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 06265d7f8cc3..4a376a724153 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
@@ -642,6 +642,17 @@ void ieee80211_iterate_active_interfaces_rtnl( | |||
642 | } | 642 | } |
643 | EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl); | 643 | EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl); |
644 | 644 | ||
645 | struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev) | ||
646 | { | ||
647 | struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); | ||
648 | |||
649 | if (!ieee80211_sdata_running(sdata) || | ||
650 | !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) | ||
651 | return NULL; | ||
652 | return &sdata->vif; | ||
653 | } | ||
654 | EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif); | ||
655 | |||
645 | /* | 656 | /* |
646 | * Nothing should have been stuffed into the workqueue during | 657 | * Nothing should have been stuffed into the workqueue during |
647 | * the suspend->resume cycle. If this WARN is seen then there | 658 | * the suspend->resume cycle. If this WARN is seen then there |
diff --git a/net/wireless/core.h b/net/wireless/core.h index 6716c5c3f748..ac77644cf894 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -67,9 +67,7 @@ struct cfg80211_registered_device { | |||
67 | struct work_struct scan_done_wk; | 67 | struct work_struct scan_done_wk; |
68 | struct work_struct sched_scan_results_wk; | 68 | struct work_struct sched_scan_results_wk; |
69 | 69 | ||
70 | #ifdef CONFIG_NL80211_TESTMODE | 70 | struct genl_info *cur_cmd_info; |
71 | struct genl_info *testmode_info; | ||
72 | #endif | ||
73 | 71 | ||
74 | struct work_struct conn_work; | 72 | struct work_struct conn_work; |
75 | struct work_struct event_work; | 73 | struct work_struct event_work; |
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 = { |