diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7a29b709077f..bb307a11ee63 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5854,6 +5854,7 @@ struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy, struct wireless_dev *wdev, enum nl80211_commands cmd, enum nl80211_attrs attr, + unsigned int portid, int vendor_event_idx, int approxlen, gfp_t gfp); @@ -5903,6 +5904,15 @@ cfg80211_vendor_cmd_alloc_reply_skb(struct wiphy *wiphy, int approxlen) */ int cfg80211_vendor_cmd_reply(struct sk_buff *skb); +/** + * cfg80211_vendor_cmd_get_sender + * @wiphy: the wiphy + * + * Return the current netlink port ID in a vendor command handler. + * Valid to call only there. + */ +unsigned int cfg80211_vendor_cmd_get_sender(struct wiphy *wiphy); + /** * cfg80211_vendor_event_alloc - allocate vendor-specific event skb * @wiphy: the wiphy @@ -5930,7 +5940,42 @@ cfg80211_vendor_event_alloc(struct wiphy *wiphy, struct wireless_dev *wdev, { return __cfg80211_alloc_event_skb(wiphy, wdev, NL80211_CMD_VENDOR, NL80211_ATTR_VENDOR_DATA, - event_idx, approxlen, gfp); + 0, event_idx, approxlen, gfp); +} + +/** + * cfg80211_vendor_event_alloc_ucast - alloc unicast vendor-specific event skb + * @wiphy: the wiphy + * @wdev: the wireless device + * @event_idx: index of the vendor event in the wiphy's vendor_events + * @portid: port ID of the receiver + * @approxlen: an upper bound of the length of the data that will + * be put into the skb + * @gfp: allocation flags + * + * This function allocates and pre-fills an skb for an event to send to + * a specific (userland) socket. This socket would previously have been + * obtained by cfg80211_vendor_cmd_get_sender(), and the caller MUST take + * care to register a netlink notifier to see when the socket closes. + * + * If wdev != NULL, both the ifindex and identifier of the specified + * wireless device are added to the event message before the vendor data + * attribute. + * + * When done filling the skb, call cfg80211_vendor_event() with the + * skb to send the event. + * + * Return: An allocated and pre-filled skb. %NULL if any errors happen. + */ +static inline struct sk_buff * +cfg80211_vendor_event_alloc_ucast(struct wiphy *wiphy, + struct wireless_dev *wdev, + unsigned int portid, int approxlen, + int event_idx, gfp_t gfp) +{ + return __cfg80211_alloc_event_skb(wiphy, wdev, NL80211_CMD_VENDOR, + NL80211_ATTR_VENDOR_DATA, + portid, event_idx, approxlen, gfp); } /** @@ -6030,7 +6075,7 @@ static inline struct sk_buff * cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, int approxlen, gfp_t gfp) { return __cfg80211_alloc_event_skb(wiphy, NULL, NL80211_CMD_TESTMODE, - NL80211_ATTR_TESTDATA, -1, + NL80211_ATTR_TESTDATA, 0, -1, approxlen, gfp); } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d5badbbb28a3..c5df5211d29a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4,7 +4,7 @@ * Copyright 2006-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018 Intel Corporation + * Copyright (C) 2018-2019 Intel Corporation */ #include @@ -9306,6 +9306,7 @@ struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy, struct wireless_dev *wdev, enum nl80211_commands cmd, enum nl80211_attrs attr, + unsigned int portid, int vendor_event_idx, int approxlen, gfp_t gfp) { @@ -9329,7 +9330,7 @@ struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy, return NULL; } - return __cfg80211_alloc_vendor_skb(rdev, wdev, approxlen, 0, 0, + return __cfg80211_alloc_vendor_skb(rdev, wdev, approxlen, portid, 0, cmd, attr, info, gfp); } EXPORT_SYMBOL(__cfg80211_alloc_event_skb); @@ -9338,6 +9339,7 @@ void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp) { struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; void *hdr = ((void **)skb->cb)[1]; + struct nlmsghdr *nlhdr = nlmsg_hdr(skb); struct nlattr *data = ((void **)skb->cb)[2]; enum nl80211_multicast_groups mcgrp = NL80211_MCGRP_TESTMODE; @@ -9347,11 +9349,16 @@ void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp) nla_nest_end(skb, data); genlmsg_end(skb, hdr); - if (data->nla_type == NL80211_ATTR_VENDOR_DATA) - mcgrp = NL80211_MCGRP_VENDOR; + if (nlhdr->nlmsg_pid) { + genlmsg_unicast(wiphy_net(&rdev->wiphy), skb, + nlhdr->nlmsg_pid); + } else { + if (data->nla_type == NL80211_ATTR_VENDOR_DATA) + mcgrp = NL80211_MCGRP_VENDOR; - genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), skb, 0, - mcgrp, gfp); + genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), + skb, 0, mcgrp, gfp); + } } EXPORT_SYMBOL(__cfg80211_send_event_skb); @@ -12736,6 +12743,17 @@ int cfg80211_vendor_cmd_reply(struct sk_buff *skb) } EXPORT_SYMBOL_GPL(cfg80211_vendor_cmd_reply); +unsigned int cfg80211_vendor_cmd_get_sender(struct wiphy *wiphy) +{ + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + + if (WARN_ON(!rdev->cur_cmd_info)) + return 0; + + return rdev->cur_cmd_info->snd_portid; +} +EXPORT_SYMBOL_GPL(cfg80211_vendor_cmd_get_sender); + static int nl80211_set_qos_map(struct sk_buff *skb, struct genl_info *info) {