From: James Prestwood <prestwoj at gmail.com>
To: iwd at lists.01.org
Subject: [PATCH v2] offchannel: introduce new offchannel module
Date: Tue, 30 Nov 2021 13:49:37 -0800 [thread overview]
Message-ID: <20211130214937.470466-1-prestwoj@gmail.com> (raw)
[-- Attachment #1: Type: text/plain, Size: 11166 bytes --]
This module provides a convenient wrapper around both
CMD_[CANCEL_]_REMAIN_ON_CHANNEL APIs.
Certain protocols require going offchannel to send frames, and/or
wait for a response. The frame-xchg module somewhat does this but
has some limitations. For example you cannot just go offchannel;
an initial frame must be sent out to start the procedure. In addition
frame-xchg does not work for broadcasts since it expects an ACK.
This module is much simpler and only handles going offchannel for
a duration. During this time frames may be sent or received. After
the duration the caller will get a callback and any included error
if there was one. Any offchannel request can be cancelled prior to
the duration expriring if the offchannel work has finished early.
---
Makefile.am | 1 +
src/offchannel.c | 302 +++++++++++++++++++++++++++++++++++++++++++++++
src/offchannel.h | 29 +++++
3 files changed, 332 insertions(+)
create mode 100644 src/offchannel.c
create mode 100644 src/offchannel.h
v2:
* Removed internal timer and intead rely on the kernel to keep track
* Specially handle the cancel case where the command had been sent to
the kernel already. For this we need to wait until the genl callback
to cancel (since we then have the cookie).
diff --git a/Makefile.am b/Makefile.am
index 1e1c230f..d93dd2c9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -247,6 +247,7 @@ src_iwd_SOURCES = src/main.c linux/nl80211.h src/iwd.h src/missing.h \
src/ip-pool.h src/ip-pool.c \
src/band.h src/band.c \
src/sysfs.h src/sysfs.c \
+ src/offchannel.h src/offchannel.c \
$(eap_sources) \
$(builtin_sources)
diff --git a/src/offchannel.c b/src/offchannel.c
new file mode 100644
index 00000000..4388e398
--- /dev/null
+++ b/src/offchannel.c
@@ -0,0 +1,302 @@
+/*
+ *
+ * Wireless daemon for Linux
+ *
+ * Copyright (C) 2021 Intel Corporation. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <ell/ell.h>
+
+#include "linux/nl80211.h"
+
+#include "src/offchannel.h"
+#include "src/wiphy.h"
+#include "src/nl80211util.h"
+#include "src/iwd.h"
+#include "src/module.h"
+
+struct offchannel_info {
+ uint64_t wdev_id;
+ uint32_t freq;
+ uint32_t duration;
+
+ uint32_t roc_cmd_id;
+ uint64_t roc_cookie;
+
+ offchannel_started_cb_t started;
+ offchannel_destroy_cb_t destroy;
+ void *user_data;
+ int error;
+
+ struct wiphy_radio_work_item work;
+
+ bool needs_cancel : 1;
+};
+
+static struct l_genl_family *nl80211;
+static struct l_queue *offchannel_list;
+
+static bool match_wdev(const void *a, const void *user_data)
+{
+ const struct offchannel_info *info = a;
+ const uint64_t *wdev_id = user_data;
+
+ return info->wdev_id == *wdev_id;
+}
+
+static void offchannel_cancel_roc(struct offchannel_info *info)
+{
+ struct l_genl_msg *msg;
+
+ msg = l_genl_msg_new(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL);
+
+ l_genl_msg_append_attr(msg, NL80211_ATTR_WDEV, 8, &info->wdev_id);
+ l_genl_msg_append_attr(msg, NL80211_ATTR_COOKIE, 8, &info->roc_cookie);
+
+ /* Nothing much can be done if this fails */
+ if (!l_genl_family_send(nl80211, msg, NULL, NULL, NULL))
+ l_genl_msg_unref(msg);
+}
+
+static void offchannel_roc_cb(struct l_genl_msg *msg, void *user_data)
+{
+ struct offchannel_info *info = user_data;
+
+ info->error = l_genl_msg_get_error(msg);
+ info->roc_cmd_id = 0;
+
+ if (info->error < 0) {
+ l_debug("Error from CMD_REMAIN_ON_CHANNEL (%d)", info->error);
+ goto work_done;
+ }
+
+ info->error = nl80211_parse_attrs(msg, NL80211_ATTR_COOKIE,
+ &info->roc_cookie, NL80211_ATTR_UNSPEC);
+ if (info->error < 0) {
+ l_error("Could not parse ROC cookie");
+ goto work_done;
+ }
+
+ /* This request was cancelled, and ROC needs to be cancelled */
+ if (info->needs_cancel) {
+ offchannel_cancel_roc(info);
+ goto work_done;
+ }
+
+ return;
+
+work_done:
+ wiphy_radio_work_done(wiphy_find_by_wdev(info->wdev_id), info->work.id);
+}
+
+static bool offchannel_work_ready(struct wiphy_radio_work_item *item)
+{
+ struct l_genl_msg *msg;
+ struct offchannel_info *info = l_container_of(item,
+ struct offchannel_info, work);
+
+ msg = l_genl_msg_new(NL80211_CMD_REMAIN_ON_CHANNEL);
+
+ l_genl_msg_append_attr(msg, NL80211_ATTR_WDEV, 8, &info->wdev_id);
+ l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY_FREQ, 4, &info->freq);
+ l_genl_msg_append_attr(msg, NL80211_ATTR_DURATION, 4, &info->duration);
+
+ info->roc_cmd_id = l_genl_family_send(nl80211, msg, offchannel_roc_cb,
+ info, NULL);
+ if (!info->roc_cmd_id) {
+ info->error = -EIO;
+ l_genl_msg_unref(msg);
+ return true;
+ }
+
+ l_queue_push_head(offchannel_list, info);
+
+ return false;
+}
+
+static void offchannel_work_destroy(struct wiphy_radio_work_item *item)
+{
+ struct offchannel_info *info = l_container_of(item,
+ struct offchannel_info, work);
+
+ if (info->destroy)
+ info->destroy(info->error, info->user_data);
+
+ l_queue_remove(offchannel_list, info);
+ l_free(info);
+}
+
+static const struct wiphy_radio_work_item_ops offchannel_work_ops = {
+ .do_work = offchannel_work_ready,
+ .destroy = offchannel_work_destroy,
+};
+
+uint32_t offchannel_start(uint64_t wdev_id, uint32_t freq, uint32_t duration,
+ offchannel_started_cb_t started, void *user_data,
+ offchannel_destroy_cb_t destroy)
+{
+ struct offchannel_info *info = l_new(struct offchannel_info, 1);
+
+ info->wdev_id = wdev_id;
+ info->freq = freq;
+ info->duration = duration;
+ info->started = started;
+ info->destroy = destroy;
+ info->user_data = user_data;
+ /*
+ * Set error as cancelled in case this work gets cancelled prior to
+ * the wiphy work starting.
+ */
+ info->error = -ECANCELED;
+
+ return wiphy_radio_work_insert(wiphy_find_by_wdev(wdev_id),
+ &info->work, 1, &offchannel_work_ops);
+}
+
+void offchannel_cancel(uint64_t wdev_id, uint32_t id)
+{
+ struct wiphy *wiphy = wiphy_find_by_wdev(wdev_id);
+ struct offchannel_info *info;
+
+ /* Hasn't even started yet */
+ if (!wiphy_radio_work_is_running(wiphy, id))
+ goto work_done;
+
+ info = l_queue_find(offchannel_list, match_wdev, &wdev_id);
+ if (!info)
+ return;
+
+ if (info->roc_cmd_id) {
+ /*
+ * The command has been sent to the kernel. This means we must
+ * wait till ROC starts and cancel at that time. Cancelling just
+ * the ROC command now will have no effect.
+ */
+ if (l_genl_family_request_sent(nl80211, info->roc_cmd_id))
+ info->needs_cancel = true;
+
+ l_genl_family_cancel(nl80211, info->roc_cmd_id);
+ info->roc_cmd_id = 0;
+
+ if (!info->needs_cancel)
+ goto work_done;
+
+ return;
+ }
+
+ /*
+ * Something must have happened in the genl callback. Any errors there
+ * will be handled already
+ */
+ if (!info->roc_cookie)
+ return;
+
+ /*
+ * At this point we know ROC has at least been queued (potentially not
+ * started) and can be cancelled.
+ */
+ offchannel_cancel_roc(info);
+
+work_done:
+ wiphy_radio_work_done(wiphy, id);
+}
+
+static void offchannel_mlme_notify(struct l_genl_msg *msg, void *user_data)
+{
+ struct offchannel_info *info;
+ uint64_t wdev_id;
+ uint64_t cookie;
+ uint8_t cmd;
+
+ cmd = l_genl_msg_get_command(msg);
+
+ if (cmd != NL80211_CMD_REMAIN_ON_CHANNEL &&
+ cmd != NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL)
+ return;
+
+ if (nl80211_parse_attrs(msg, NL80211_ATTR_WDEV, &wdev_id,
+ NL80211_ATTR_COOKIE, &cookie,
+ NL80211_ATTR_UNSPEC) < 0)
+ return;
+
+ info = l_queue_find(offchannel_list, match_wdev, &wdev_id);
+ if (!info)
+ return;
+
+ /* ROC must have been started elsewhere, not by IWD */
+ if (info->roc_cookie != cookie)
+ return;
+
+ switch (cmd) {
+ case NL80211_CMD_REMAIN_ON_CHANNEL:
+ if (info->started)
+ info->started(info->user_data);
+
+ break;
+ case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL:
+ info->error = 0;
+
+ wiphy_radio_work_done(wiphy_find_by_wdev(info->wdev_id),
+ info->work.id);
+ break;
+ default:
+ return;
+ }
+}
+
+static int offchannel_init(void)
+{
+ struct l_genl *genl = iwd_get_genl();
+
+ nl80211 = l_genl_family_new(genl, NL80211_GENL_NAME);
+ if (!nl80211) {
+ l_error("Failed to obtain nl80211");
+ return -EIO;
+ }
+
+ if (!l_genl_family_register(nl80211, "mlme", offchannel_mlme_notify,
+ NULL, NULL)) {
+ l_error("Failed to register for MLME");
+ l_genl_family_free(nl80211);
+ nl80211 = NULL;
+
+ return -EIO;
+ }
+
+ offchannel_list = l_queue_new();
+
+ return 0;
+}
+
+static void offchannel_exit(void)
+{
+ l_debug("");
+
+ l_genl_family_free(nl80211);
+ nl80211 = NULL;
+
+ l_queue_destroy(offchannel_list, l_free);
+}
+
+IWD_MODULE(offchannel, offchannel_init, offchannel_exit);
diff --git a/src/offchannel.h b/src/offchannel.h
new file mode 100644
index 00000000..1ffa94f1
--- /dev/null
+++ b/src/offchannel.h
@@ -0,0 +1,29 @@
+/*
+ *
+ * Wireless daemon for Linux
+ *
+ * Copyright (C) 2021 Intel Corporation. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+typedef void (*offchannel_started_cb_t)(void *user_data);
+typedef void (*offchannel_destroy_cb_t)(int error, void *user_data);
+
+uint32_t offchannel_start(uint64_t wdev_id, uint32_t freq, uint32_t duration,
+ offchannel_started_cb_t started, void *user_data,
+ offchannel_destroy_cb_t destroy);
+void offchannel_cancel(uint64_t wdev_id, uint32_t id);
--
2.31.1
next reply other threads:[~2021-11-30 21:49 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-11-30 21:49 James Prestwood [this message]
-- strict thread matches above, loose matches on Subject: below --
2021-11-30 22:06 [PATCH v2] offchannel: introduce new offchannel module Paul Menzel
2021-12-01 18:06 Denis Kenzior
2021-12-06 18:03 James Prestwood
2021-12-06 18:48 Denis Kenzior
2021-12-06 19:01 James Prestwood
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20211130214937.470466-1-prestwoj@gmail.com \
--to=iwd@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox