Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ad7e718c authored by Johannes Berg's avatar Johannes Berg
Browse files

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: default avatarJohannes Berg <johannes.berg@intel.com>
parent 7869303b
Loading
Loading
Loading
Loading
+113 −3
Original line number Diff line number Diff line
@@ -2679,6 +2679,34 @@ struct wiphy_coalesce_support {
	int max_pkt_offset;
};

/**
 * enum wiphy_vendor_command_flags - validation flags for vendor commands
 * @WIPHY_VENDOR_CMD_NEED_WDEV: vendor command requires wdev
 * @WIPHY_VENDOR_CMD_NEED_NETDEV: vendor command requires netdev
 * @WIPHY_VENDOR_CMD_NEED_RUNNING: interface/wdev must be up & running
 *	(must be combined with %_WDEV or %_NETDEV)
 */
enum wiphy_vendor_command_flags {
	WIPHY_VENDOR_CMD_NEED_WDEV = BIT(0),
	WIPHY_VENDOR_CMD_NEED_NETDEV = BIT(1),
	WIPHY_VENDOR_CMD_NEED_RUNNING = BIT(2),
};

/**
 * struct wiphy_vendor_command - vendor command definition
 * @info: vendor command identifying information, as used in nl80211
 * @flags: flags, see &enum wiphy_vendor_command_flags
 * @doit: callback for the operation, note that wdev is %NULL if the
 *	flags didn't ask for a wdev and non-%NULL otherwise; the data
 *	pointer may be %NULL if userspace provided no data at all
 */
struct wiphy_vendor_command {
	struct nl80211_vendor_cmd_info info;
	u32 flags;
	int (*doit)(struct wiphy *wiphy, struct wireless_dev *wdev,
		    const void *data, int data_len);
};

/**
 * struct wiphy - wireless hardware description
 * @reg_notifier: the driver's regulatory notification callback,
@@ -2792,6 +2820,9 @@ struct wiphy_coalesce_support {
 * @extended_capabilities_mask: mask of the valid values
 * @extended_capabilities_len: length of the extended capabilities
 * @coalesce: packet coalescing support information
 *
 * @vendor_commands: array of vendor commands supported by the hardware
 * @n_vendor_commands: number of vendor commands
 */
struct wiphy {
	/* assign these fields before you register the wiphy */
@@ -2903,6 +2934,9 @@ struct wiphy {

	const struct wiphy_coalesce_support *coalesce;

	const struct wiphy_vendor_command *vendor_commands;
	int n_vendor_commands;

	char priv[0] __aligned(NETDEV_ALIGN);
};

@@ -3847,6 +3881,75 @@ void wiphy_rfkill_start_polling(struct wiphy *wiphy);
 */
void wiphy_rfkill_stop_polling(struct wiphy *wiphy);

/**
 * DOC: Vendor commands
 *
 * Occasionally, there are special protocol or firmware features that
 * can't be implemented very openly. For this and similar cases, the
 * vendor command functionality allows implementing the features with
 * (typically closed-source) userspace and firmware, using nl80211 as
 * the configuration mechanism.
 *
 * A driver supporting vendor commands must register them as an array
 * in struct wiphy, with handlers for each one, each command has an
 * OUI and sub command ID to identify it.
 *
 * Note that this feature should not be (ab)used to implement protocol
 * features that could openly be shared across drivers. In particular,
 * it must never be required to use vendor commands to implement any
 * "normal" functionality that higher-level userspace like connection
 * managers etc. need.
 */

struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy,
					   enum nl80211_commands cmd,
					   enum nl80211_attrs attr,
					   int approxlen);

/**
 * cfg80211_vendor_cmd_alloc_reply_skb - allocate vendor command reply
 * @wiphy: the wiphy
 * @approxlen: an upper bound of the length of the data that will
 *	be put into the skb
 *
 * This function allocates and pre-fills an skb for a reply to
 * a vendor command. Since it is intended for a reply, calling
 * it outside of a vendor command's doit() operation is invalid.
 *
 * The returned skb is pre-filled with some identifying data in
 * a way that any data that is put into the skb (with skb_put(),
 * nla_put() or similar) will end up being within the
 * %NL80211_ATTR_VENDOR_DATA attribute, so all that needs to be done
 * with the skb is adding data for the corresponding userspace tool
 * which can then read that data out of the vendor data attribute.
 * You must not modify the skb in any other way.
 *
 * When done, call cfg80211_vendor_cmd_reply() with the skb and return
 * its error code as the result of the doit() operation.
 *
 * Return: An allocated and pre-filled skb. %NULL if any errors happen.
 */
static inline struct sk_buff *
cfg80211_vendor_cmd_alloc_reply_skb(struct wiphy *wiphy, int approxlen)
{
	return __cfg80211_alloc_reply_skb(wiphy, NL80211_CMD_VENDOR,
					  NL80211_ATTR_VENDOR_DATA, approxlen);
}

/**
 * cfg80211_vendor_cmd_reply - send the reply skb
 * @skb: The skb, must have been allocated with
 *	cfg80211_vendor_cmd_alloc_reply_skb()
 *
 * Since calling this function will usually be the last thing
 * before returning from the vendor command doit() you should
 * return the error code.  Note that this function consumes the
 * skb regardless of the return value.
 *
 * Return: An error code or 0 on success.
 */
int cfg80211_vendor_cmd_reply(struct sk_buff *skb);

#ifdef CONFIG_NL80211_TESTMODE
/**
 * DOC: Test mode
@@ -3882,8 +3985,12 @@ void wiphy_rfkill_stop_polling(struct wiphy *wiphy);
 *
 * Return: An allocated and pre-filled skb. %NULL if any errors happen.
 */
struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
						  int approxlen);
static inline struct sk_buff *
cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, int approxlen)
{
	return __cfg80211_alloc_reply_skb(wiphy, NL80211_CMD_TESTMODE,
					  NL80211_ATTR_TESTDATA, approxlen);
}

/**
 * cfg80211_testmode_reply - send the reply skb
@@ -3897,7 +4004,10 @@ struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
 *
 * Return: An error code or 0 on success.
 */
int cfg80211_testmode_reply(struct sk_buff *skb);
static inline int cfg80211_testmode_reply(struct sk_buff *skb)
{
	return cfg80211_vendor_cmd_reply(skb);
}

/**
 * cfg80211_testmode_alloc_event_skb - allocate testmode event
+13 −0
Original line number Diff line number Diff line
@@ -1162,6 +1162,19 @@ static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif)
	return false;
}

/**
 * wdev_to_ieee80211_vif - return a vif struct from a wdev
 * @wdev: the wdev to get the vif for
 *
 * This can be used by mac80211 drivers with direct cfg80211 APIs
 * (like the vendor commands) that get a wdev.
 *
 * Note that this function may return %NULL if the given wdev isn't
 * associated with a vif that the driver knows about (e.g. monitor
 * or AP_VLAN interfaces.)
 */
struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev);

/**
 * enum ieee80211_key_flags - key flags
 *
+41 −0
Original line number Diff line number Diff line
@@ -693,6 +693,15 @@
 *	other station that transmission must be blocked until the channel
 *	switch is complete.
 *
 * @NL80211_CMD_VENDOR: Vendor-specified command/event. The command is specified
 *	by the %NL80211_ATTR_VENDOR_ID attribute and a sub-command in
 *	%NL80211_ATTR_VENDOR_SUBCMD. Parameter(s) can be transported in
 *	%NL80211_ATTR_VENDOR_DATA.
 *	For feature advertisement, the %NL80211_ATTR_VENDOR_DATA attribute is
 *	used in the wiphy data as a nested attribute containing descriptions
 *	(&struct nl80211_vendor_cmd_info) of the supported vendor commands.
 *	This may also be sent as an event with the same attributes.
 *
 * @NL80211_CMD_MAX: highest used command number
 * @__NL80211_CMD_AFTER_LAST: internal use
 */
@@ -860,6 +869,8 @@ enum nl80211_commands {

	NL80211_CMD_CHANNEL_SWITCH,

	NL80211_CMD_VENDOR,

	/* add new commands above here */

	/* used to define NL80211_CMD_MAX below */
@@ -1524,6 +1535,12 @@ enum nl80211_commands {
 *	Notification Element based on association request when used with
 *	%NL80211_CMD_NEW_STATION; u8 attribute.
 *
 * @NL80211_ATTR_VENDOR_ID: The vendor ID, either a 24-bit OUI or, if
 *	%NL80211_VENDOR_ID_IS_LINUX is set, a special Linux ID (not used yet)
 * @NL80211_ATTR_VENDOR_SUBCMD: vendor sub-command
 * @NL80211_ATTR_VENDOR_DATA: data for the vendor command, if any; this
 *	attribute is also used for vendor command feature advertisement
 *
 * @NL80211_ATTR_MAX: highest attribute number currently defined
 * @__NL80211_ATTR_AFTER_LAST: internal use
 */
@@ -1845,6 +1862,10 @@ enum nl80211_attrs {

	NL80211_ATTR_OPMODE_NOTIF,

	NL80211_ATTR_VENDOR_ID,
	NL80211_ATTR_VENDOR_SUBCMD,
	NL80211_ATTR_VENDOR_DATA,

	/* add attributes here, update the policy in nl80211.c */

	__NL80211_ATTR_AFTER_LAST,
@@ -3965,4 +3986,24 @@ enum nl80211_rxmgmt_flags {
	NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0,
};

/*
 * If this flag is unset, the lower 24 bits are an OUI, if set
 * a Linux nl80211 vendor ID is used (no such IDs are allocated
 * yet, so that's not valid so far)
 */
#define NL80211_VENDOR_ID_IS_LINUX	0x80000000

/**
 * struct nl80211_vendor_cmd_info - vendor command data
 * @vendor_id: If the %NL80211_VENDOR_ID_IS_LINUX flag is clear, then the
 *	value is a 24-bit OUI; if it is set then a separately allocated ID
 *	may be used, but no such IDs are allocated yet. New IDs should be
 *	added to this file when needed.
 * @subcmd: sub-command ID for the command
 */
struct nl80211_vendor_cmd_info {
	__u32 vendor_id;
	__u32 subcmd;
};

#endif /* __LINUX_NL80211_H */
+11 −0
Original line number Diff line number Diff line
@@ -642,6 +642,17 @@ void ieee80211_iterate_active_interfaces_rtnl(
}
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl);

struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);

	if (!ieee80211_sdata_running(sdata) ||
	    !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
		return NULL;
	return &sdata->vif;
}
EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif);

/*
 * Nothing should have been stuffed into the workqueue during
 * the suspend->resume cycle. If this WARN is seen then there
+1 −3
Original line number Diff line number Diff line
@@ -67,9 +67,7 @@ struct cfg80211_registered_device {
	struct work_struct scan_done_wk;
	struct work_struct sched_scan_results_wk;

#ifdef CONFIG_NL80211_TESTMODE
	struct genl_info *testmode_info;
#endif
	struct genl_info *cur_cmd_info;

	struct work_struct conn_work;
	struct work_struct event_work;
Loading