From patchwork Fri Dec 1 10:49:08 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Johannes Berg X-Patchwork-Id: 749471 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=sipsolutions.net header.i=@sipsolutions.net header.b="GDz7QVhZ" Received: from sipsolutions.net (s3.sipsolutions.net [IPv6:2a01:4f8:242:246e::2]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0F4B61994; Fri, 1 Dec 2023 02:49:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sipsolutions.net; s=mail; h=Content-Transfer-Encoding:MIME-Version: References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From:Content-Type:Sender :Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-To: Resent-Cc:Resent-Message-ID; bh=gCYgYqHnTsuL1EhCdX4RcAVkG0NIh0eg2vdMmu8RzJg=; t=1701427798; x=1702637398; b=GDz7QVhZ/FEw4V807vi0uoSEJxQi10jHgfWBrqKBvsYsl6k 6JoQcpR0vHAq/5zUd/JOlfLhUfmf5nRtiDrObBiTRTcNkhHMgWXtEM1bEEXQzHxrrFiGSNPF9tL6k TWeTeg+k1GjwPQ0Z400tPx8KnF2yLFZj8Tm4DA0LojpzHUo96+FrG0/SijMz1EZM34D9J9EpgKDuS Ia3ClOa0Cou1aj/CzH6IFaPSx7rl0SjtNnJoSt2wnTgNE2DDDI7IsWmfyNE2t9E8f4Pzi0+Q0ikMt BdUUbwMqgtR4n1ukAyoAtTWZCAVRFod+jHtV0oaE1TEwxdGBcKmVK0EyIqSnuY8Q==; Received: by sipsolutions.net with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.97) (envelope-from ) id 1r915n-0000000BBjV-15Ek; Fri, 01 Dec 2023 11:49:55 +0100 From: Johannes Berg To: linux-wireless@vger.kernel.org Cc: hostap@lists.infradead.org, netdev@vger.kernel.org, Johannes Berg Subject: [PATCH wpa_suppplicant 1/2] netlink: add netlink_process_one_event() Date: Fri, 1 Dec 2023 11:49:08 +0100 Message-ID: <20231201114952.c278cb7ac0c4.If32fddf88f23f3939bb73bc6926aad7f88804079@changeid> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20231201104952.26254-4-johannes@sipsolutions.net> References: <346b21d87c69f817ea3c37caceb34f1f56255884.camel@sipsolutions.net> <20231201104952.26254-4-johannes@sipsolutions.net> Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Johannes Berg Add a new function to read and process a single netlink event with a timeout, to be used in driver_nl80211. Signed-off-by: Johannes Berg --- src/drivers/netlink.c | 43 +++++++++++++++++++++++++++++++++++++++---- src/drivers/netlink.h | 2 ++ 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/drivers/netlink.c b/src/drivers/netlink.c index 7780479c3e91..bbfe86eee7a0 100644 --- a/src/drivers/netlink.c +++ b/src/drivers/netlink.c @@ -33,19 +33,20 @@ static void netlink_receive_link(struct netlink_data *netlink, } -static void netlink_receive(int sock, void *eloop_ctx, void *sock_ctx) +static void _netlink_process_one_event(struct netlink_data *netlink, + int wait_single) { - struct netlink_data *netlink = eloop_ctx; char buf[8192]; int left; struct sockaddr_nl from; socklen_t fromlen; struct nlmsghdr *h; - int max_events = 10; + int max_events = wait_single ? 1 : 10; try_again: fromlen = sizeof(from); - left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + left = recvfrom(netlink->sock, buf, sizeof(buf), + wait_single ? 0 : MSG_DONTWAIT, (struct sockaddr *) &from, &fromlen); if (left < 0) { if (errno != EINTR && errno != EAGAIN) @@ -88,6 +89,40 @@ try_again: } +void netlink_process_one_event(struct netlink_data *netlink, + unsigned int timeout_ms) +{ + if (timeout_ms) { + struct timeval timeout = { + .tv_sec = timeout_ms / 1000, + .tv_usec = 1000 * (timeout_ms % 1000), + }; + fd_set read_set; + int ret; + + FD_ZERO(&read_set); + FD_SET(netlink->sock, &read_set); + + ret = select(netlink->sock + 1, &read_set, NULL, NULL, + &timeout); + if (ret < 0) { + perror("select on netlink socket"); + return; + } + if (ret == 0) + return; + } + + _netlink_process_one_event(netlink, 1); +} + + +static void netlink_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + _netlink_process_one_event(eloop_ctx, 0); +} + + struct netlink_data * netlink_init(struct netlink_config *cfg) { struct netlink_data *netlink; diff --git a/src/drivers/netlink.h b/src/drivers/netlink.h index 3a7340e51534..faee28b722ea 100644 --- a/src/drivers/netlink.h +++ b/src/drivers/netlink.h @@ -21,6 +21,8 @@ struct netlink_config { }; struct netlink_data * netlink_init(struct netlink_config *cfg); +void netlink_process_one_event(struct netlink_data *netlink, + unsigned int timeout_ms); void netlink_deinit(struct netlink_data *netlink); int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex, int linkmode, int operstate); From patchwork Fri Dec 1 10:49:09 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Johannes Berg X-Patchwork-Id: 749792 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=sipsolutions.net header.i=@sipsolutions.net header.b="sx7I83Mw" Received: from sipsolutions.net (s3.sipsolutions.net [IPv6:2a01:4f8:242:246e::2]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BC80C1726; Fri, 1 Dec 2023 02:49:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sipsolutions.net; s=mail; h=Content-Transfer-Encoding:MIME-Version: References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From:Content-Type:Sender :Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-To: Resent-Cc:Resent-Message-ID; bh=rDc7S3TGwDRkuN19FCwRpWlUg/012zfS/c7UoSCfhOg=; t=1701427798; x=1702637398; b=sx7I83MwXTI5q1y3hsEdV1kcfKxOdHCD1XKDrnSv3DIMDNU 8COzLqD8xB+QrS+dvmNK6rDwPexvgf/y74xWxx0IvW6y5yD9AUm7rmYc8ygxGiq7BqyQ1dUTUzila drXGLx3zmqD4vTq7FlhkdsZbC3tdxFi5f3ldoWBUW5mbT7pWXMwpDRbDeG7ykl1ieqR8C08Y7D4dZ nP4zx6iHfAFlz3mdxufCo0oEAu54QzMmrYSJdjc6XNeuhdcewG8RGecH+pImX/aaIv8v499cjiv4C UgcqX5iEj38X/M9R3plurEx1II/VAbk1/GYCdXqZ3aKDdYNnuKFdZnJD/MdNJJ5Q==; Received: by sipsolutions.net with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.97) (envelope-from ) id 1r915n-0000000BBjV-3rZk; Fri, 01 Dec 2023 11:49:56 +0100 From: Johannes Berg To: linux-wireless@vger.kernel.org Cc: hostap@lists.infradead.org, netdev@vger.kernel.org, Johannes Berg Subject: [PATCH wpa_suppplicant 2/2] driver_nl82011: wait for rtnetlink event with carrier_up_count Date: Fri, 1 Dec 2023 11:49:09 +0100 Message-ID: <20231201114952.420b40a5f188.I75677b755f36ca63f8289d84de29b212f4c37ec0@changeid> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20231201104952.26254-4-johannes@sipsolutions.net> References: <346b21d87c69f817ea3c37caceb34f1f56255884.camel@sipsolutions.net> <20231201104952.26254-4-johannes@sipsolutions.net> Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Johannes Berg There's a race (see the comment in the code) between the kernel and userspace that can lead to dropping TX packets after the associated event was already received. If the kernel indicates the carrier_up_count, wait for the corresponding rtnetlink event to reach that count. This fixes the race since the rtnetlink event is sent after the async processing that's needed in the kernel before a frame can be transmitted. Signed-off-by: Johannes Berg --- src/drivers/driver_nl80211.c | 7 ++++ src/drivers/driver_nl80211.h | 2 ++ src/drivers/driver_nl80211_event.c | 52 ++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 03d54222bb52..b442dee1e710 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -1365,6 +1365,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, char ifname[IFNAMSIZ + 1]; char extra[100], *pos, *end; int init_failed; + u32 carrier_up_count = 0; extra[0] = '\0'; pos = extra; @@ -1396,6 +1397,9 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, pos += os_snprintf(pos, end - pos, " linkmode=%u", nla_get_u32((struct nlattr *) attr)); break; + case IFLA_CARRIER_UP_COUNT: + carrier_up_count = nla_get_u32((struct nlattr *) attr); + break; } attr = RTA_NEXT(attr, attrlen); } @@ -1415,6 +1419,9 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, if (init_failed) return; /* do not update interface state */ + if (carrier_up_count) + drv->carrier_up_count = carrier_up_count; + if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) { namebuf[0] = '\0'; if (if_indextoname(ifi->ifi_index, namebuf) && diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h index f82f604e9017..874988715b21 100644 --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -201,6 +201,8 @@ struct wpa_driver_nl80211_data { unsigned int puncturing:1; unsigned int qca_ap_allowed_freqs:1; + u32 carrier_up_count; + u32 ignore_next_local_disconnect; u32 ignore_next_local_deauth; diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c index 60b4fb51fcd8..4ff25b57ceeb 100644 --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -19,6 +19,7 @@ #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "driver_nl80211.h" +#include "netlink.h" static void @@ -256,6 +257,49 @@ static void nl80211_parse_wmm_params(struct nlattr *wmm_attr, } +/* + * Wait for an RTM newlink event with corresponding carrier up count + * + * There's a race condition with mac80211 and the network stack, which + * mostly hits depending on scheduling in time-simulation (tests), + * which is that mac80211 will both indicate carrier on to the network + * stack and send the associated/... event to userspace. Now, the + * internal kernel function netif_carrier_ok() immediately returns + * true after this, however, the linkwatch work still needs to run + * and change the TX queue qdisc away from noop (which drops all TX + * packets). + * + * When the race happens, userspace (and in particular tests) can see + * the associated/... event and immediately try to send a frame, at a + * time that the linkwatch work hasn't run yet, causing the frame to + * be dropped. + * + * Thus, if the kernel indicated the current carrier_up_count in an + * event, wait here for an RTM newlink event for our interface, so in + * in addition to seeing the associated/... event, we also know the + * carrier state has actually changed sufficiently to send packets, + * if it was meant to change. + * + * This works because the event to userspace is also sent from the + * asynchronous linkwatch work. + */ +static void +nl80211_wait_for_carrier_up_count(struct wpa_driver_nl80211_data *drv, + u32 carrier_up_count) +{ +#define WRAPPED_U32_LESS(x, y) ((s32)(y) - (s32)(x) < 0) + + if (WRAPPED_U32_LESS(drv->carrier_up_count, carrier_up_count)) + netlink_process_one_event(drv->global->netlink, 100); + + if (WRAPPED_U32_LESS(drv->carrier_up_count, carrier_up_count)) + wpa_printf(MSG_ERROR, + "nl80211: %s: carrier up count %u not seen (got %u)\n", + drv->first_bss->ifname, carrier_up_count, + drv->carrier_up_count); +} + + static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, const u8 *frame, size_t len, struct nlattr *wmm, struct nlattr *req_ie) @@ -3845,6 +3889,14 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, cmd == NL80211_CMD_SCAN_ABORTED)) nl80211_restore_ap_mode(bss); + /* see comment above wpa_driver_nl80211_own_ifname() */ + if (tb[NL80211_ATTR_CARRIER_UP_COUNT]) { + u32 carrier_up_count = + nla_get_u32(tb[NL80211_ATTR_CARRIER_UP_COUNT]); + + nl80211_wait_for_carrier_up_count(drv, carrier_up_count); + } + switch (cmd) { case NL80211_CMD_TRIGGER_SCAN: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan trigger"); From patchwork Fri Dec 1 10:41:17 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Johannes Berg X-Patchwork-Id: 749472 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=sipsolutions.net header.i=@sipsolutions.net header.b="ohTJK1dp" Received: from sipsolutions.net (s3.sipsolutions.net [IPv6:2a01:4f8:242:246e::2]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6EDB4D40; Fri, 1 Dec 2023 02:43:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sipsolutions.net; s=mail; h=Content-Transfer-Encoding:MIME-Version: References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From:Content-Type:Sender :Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-To: Resent-Cc:Resent-Message-ID; bh=nLp68dePSfpizVBKx02HJlI+GJJe6c/8548Gc3WLetc=; t=1701427419; x=1702637019; b=ohTJK1dpBauqNZg9cqfX2JmHRIuDzwzTLU8xZhcyBnbrbS2 rbfFDxAFRPv5/i6gFqMirTfnxxEe1exQ1ND0SROLSLbQviSx74tXaANhbu7R6Ljh9WjZTITwNwp37 XIC8YO7HIJkTxXUAUGQN7EGudz/evPh8Y7+M0HJCNuvu5BvmLtHQynNRJIMNv13phyKkBg6tDHygE BRfnjJi0y/kuVc1o8XskU3Oetyn5zFHFP6bN7MBGVX6aDbr4l2k9GXONwoABeYNUZshuPRRgn96Q3 MuEETOqeKiMtsOmvBLnlDVcMGw/XTvmmgM0OK5bCBNoYDh6+LM85h1YdjIKpbAFA==; Received: by sipsolutions.net with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.97) (envelope-from ) id 1r90zh-0000000BBV7-0Oz0; Fri, 01 Dec 2023 11:43:37 +0100 From: Johannes Berg To: linux-wireless@vger.kernel.org Cc: netdev@vger.kernel.org, Johannes Berg Subject: [PATCH wireless-next 3/3] wifi: nl80211: report carrier up count to userspace Date: Fri, 1 Dec 2023 11:41:17 +0100 Message-ID: <20231201114329.c43ed5db7146.Idd29862133993877b9fdff962dca3649e842249a@changeid> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20231201104329.25898-5-johannes@sipsolutions.net> References: <346b21d87c69f817ea3c37caceb34f1f56255884.camel@sipsolutions.net> <20231201104329.25898-5-johannes@sipsolutions.net> Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Johannes Berg There's a race in the carrier change that happens if userspace sees the RX association event via nl80211, but the driver (or mac80211) just prior to that set the carrier on. The carrier on event is actually only processed by the link watch work, so userspace can (and we've seen this at least in simulation with ARCH=um and time-travel) attempt to send a frame before that has run, if it was just waiting for the association to finish (only on open connections, of course, for encryption it has to go through the 4-way-handshake first before sending data frames.) There's really no completely good way to address this, I've previously analyzed this here: https://lore.kernel.org/netdev/346b21d87c69f817ea3c37caceb34f1f56255884.camel@sipsolutions.net/ This new solution requires both kernel and userspace work, it basically builds on #3 outlined in the email linked above, but with the addition of letting userspace _know_ that it may need to wait for the rtnetlink event. So to solve it, with this change userspace can see the value of the carrier_up_count at the association event, and if it hasn't yet seen the same value via an rtnetlink event (it is imperative that it doesn't query, it must wait for events) then it can wait for that event before trying to send data frames. For now, implement this for association and IBSS joined events. Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 16 ++++++++++++ net/wireless/nl80211.c | 47 ++++++++++++++++-------------------- 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 0cd1da2c2902..120936f81a28 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2830,6 +2830,20 @@ enum nl80211_commands { * @NL80211_ATTR_MLO_LINK_DISABLED: Flag attribute indicating that the link is * disabled. * + * @NL80211_ATTR_CARRIER_UP_COUNT: This u32 attribute is included in some + * events that indicate a successful connection (notably association), + * indicating the value of the netdev's carrier_up_count at the time + * of sending this event. Userspace can use this to fix a race: when + * the carrier is turned on, the actual handling thereof is done in + * an asynchronous manner in the kernel. Thus, if userspace attempts + * to send a frame immediately after receiving the event indicating + * successful connection over nl80211, that may not go through if the + * asynchronous processing in the kernel hasn't yet happened. To fix + * it then userspace should be listening to rtnetlink events, and if + * it didn't see the value of the carrier_up_count yet, it can wait + * for a further rtnetlink event with a value equal to or bigger than + * the value reported here, and only then transmit data. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3368,6 +3382,8 @@ enum nl80211_attrs { NL80211_ATTR_MLO_LINK_DISABLED, + NL80211_ATTR_CARRIER_UP_COUNT, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 403a4a38966a..d91a99f90aaa 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -818,6 +818,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_HW_TIMESTAMP_ENABLED] = { .type = NLA_FLAG }, [NL80211_ATTR_EMA_RNR_ELEMS] = { .type = NLA_NESTED }, [NL80211_ATTR_MLO_LINK_DISABLED] = { .type = NLA_FLAG }, + [NL80211_ATTR_CARRIER_UP_COUNT] = { .type = NLA_REJECT }, }; /* policy for the key attributes */ @@ -17738,11 +17739,13 @@ void nl80211_common_reg_change_event(enum nl80211_commands cmd_id, struct nl80211_mlme_event { enum nl80211_commands cmd; + const u8 *mac_addr; const u8 *buf; size_t buf_len; int uapsd_queues; const u8 *req_ies; size_t req_ies_len; + u32 carrier_up_count; bool reconnect; }; @@ -17766,12 +17769,17 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || - nla_put(msg, NL80211_ATTR_FRAME, event->buf_len, event->buf) || + (event->buf && + nla_put(msg, NL80211_ATTR_FRAME, event->buf_len, event->buf)) || (event->req_ies && nla_put(msg, NL80211_ATTR_REQ_IE, event->req_ies_len, event->req_ies))) goto nla_put_failure; + if (event->mac_addr && + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, event->mac_addr)) + goto nla_put_failure; + if (event->reconnect && nla_put_flag(msg, NL80211_ATTR_RECONNECT_REQUESTED)) goto nla_put_failure; @@ -17789,6 +17797,11 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, nla_nest_end(msg, nla_wmm); } + if (event->carrier_up_count && + nla_put_u32(msg, NL80211_ATTR_CARRIER_UP_COUNT, + event->carrier_up_count)) + goto nla_put_failure; + genlmsg_end(msg, hdr); genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, @@ -17824,6 +17837,7 @@ void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, .uapsd_queues = data->uapsd_queues, .req_ies = data->req_ies, .req_ies_len = data->req_ies_len, + .carrier_up_count = atomic_read(&netdev->carrier_up_count), }; nl80211_send_mlme_event(rdev, netdev, &event, GFP_KERNEL); @@ -18307,32 +18321,13 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *bssid, gfp_t gfp) { - struct sk_buff *msg; - void *hdr; + struct nl80211_mlme_event event = { + .cmd = NL80211_CMD_JOIN_IBSS, + .mac_addr = bssid, + .carrier_up_count = atomic_read(&netdev->carrier_up_count), + }; - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS); - if (!hdr) { - nlmsg_free(msg); - return; - } - - if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || - nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || - nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) - goto nla_put_failure; - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, - NL80211_MCGRP_MLME, gfp); - return; - - nla_put_failure: - nlmsg_free(msg); + nl80211_send_mlme_event(rdev, netdev, &event, gfp); } void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,