iwd configure location and files
by Jupiter
Hi,
I was running connman with wpa_supplicant on an embedded IoT device
with 2 wireless network interfaces, a home WiFi network and a 4G LTE
network interface managed by ofono, it was working well. Given the
connman is moving to using iwd, I am now building iwd 1.19 on Yocto
Honister, while the iwd was running well, it could not connect to the
home WiFi network.
# systemctl status iwd -l
* iwd.service - Wireless service
Loaded: loaded
(8;;file://solar/lib/systemd/system/iwd.service/lib/systemd/system/iwd.service8;;;
enabled; vendor preset: enabled)
Active: active (running) since Wed 2021-08-04 15:11:22 UTC; 1min 34s ago
Main PID: 203 (iwd)
CGroup: /system.slice/iwd.service
`-203 /usr/libexec/iwd
Aug 04 15:11:23 solar iwd[203]: 48.0 Mbps
Aug 04 15:11:23 solar iwd[203]: 54.0 Mbps
Aug 04 15:11:23 solar iwd[203]: HT Capabilities:
Aug 04 15:11:23 solar iwd[203]: HT20
Aug 04 15:11:23 solar iwd[203]: Short GI for 20Mhz
Aug 04 15:11:23 solar iwd[203]: HT RX MCS indexes:
Aug 04 15:11:23 solar iwd[203]: 0-7
Aug 04 15:11:23 solar iwd[203]: 32
Aug 04 15:11:23 solar iwd[203]: Ciphers: CCMP TKIP BIP
Aug 04 15:11:23 solar iwd[203]: Supported iftypes: station ap
p2p-client p2p-go
I could see the WiFi network, but could not scan, nor could connect to
the network:
[iwd]# station mlan0 scan
[iwd]# station wlan0 get-networks
Available networks
--------------------------------------------------------------------------------
Network name Security Signal
--------------------------------------------------------------------------------
JupiterIoT psk ****
# station wlan0 connect JupiterIoT
Type the network passphrase for JupiterIoT psk.
Passphrase: superpassphrase
Operation aborted
# iwctl station wlan0 show
Station: wlan0
--------------------------------------------------------------------------------
Settable Property Value
--------------------------------------------------------------------------------
Scanning no
State disconnected
I believe it is not configured properly. According to the the document
https://iwd.wiki.kernel.org/networkconfigurationsettings, the network
configure files are stored in /var/lib/iwd by default, but I can only
see an empty folder /var/lib/iwd/hotspot, there are several files in
/lib/systed/network:
# ls /lib/systemd/network
80-container-host0.network 80-wifi-ap.network.example
80-container-ve.network 80-wifi-station.network
80-container-vz.network 80-wifi-station.network.example
80-iwd.link 80-wired.network
80-vm-vt.network 99-default.link
80-wifi-adhoc.network
Did you mean I need to copy network files from /lib/systemd/network to
/var/lib/iwd? For example, to run iwd for a home WiFi network, should
I copy the 80-wifi-station.network to /var/lib/iwd?
Thank you and appreciate your kind advice.
Kind regards,
- jh
6 months, 2 weeks
[PATCH v2] offchannel: introduce new offchannel module
by James Prestwood
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
6 months, 3 weeks
[PATCH] autotests: p2p: Make waiting for dhclient more reliable
by Andrew Zaborowski
Make sure we wipe the leases file both for server and client, so that
dhclient doesn't try to re-use leases from previous tests (should really
happen) and waste time waiting for a reply. Extend the timeout from 1s
to 5s, sometimes it takes dhclient 1s just to start. Disable verbose
mode if not needed to avoid dhclient stalling if the pipe is not being
read.
---
autotests/testP2P/connection_test.py | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/autotests/testP2P/connection_test.py b/autotests/testP2P/connection_test.py
index ddebc70a..d1681164 100644
--- a/autotests/testP2P/connection_test.py
+++ b/autotests/testP2P/connection_test.py
@@ -12,6 +12,8 @@ from config import ctx
from wpas import Wpas
class Test(unittest.TestCase):
+ leases_path = '/tmp/dhcp.lease'
+
def test_1_client_go_neg_responder(self):
self.p2p_connect_test(preauthorize=False, go=False)
@@ -92,10 +94,11 @@ class Test(unittest.TestCase):
peer_ifname = wpas.p2p_group['ifname']
self.assertEqual(wpas.p2p_group['role'], 'GO' if not go else 'client')
+ os.system('> ' + self.leases_path)
+
if not go:
ctx.start_process(['ip', 'addr', 'add','dev', peer_ifname, '192.168.1.20/255.255.255.0']).wait()
- os.system('> /tmp/dhcp.leases')
- dhcp = ctx.start_process(['dhcpd', '-f', '-cf', '/tmp/dhcpd.conf', '-lf', '/tmp/dhcp.leases', peer_ifname])
+ dhcp = ctx.start_process(['dhcpd', '-f', '-cf', '/tmp/dhcpd.conf', '-lf', self.leases_path, peer_ifname])
self.dhcp = dhcp
wd.wait_for_object_condition(wpas, 'len(obj.p2p_clients) == 1', max_wait=3)
@@ -105,12 +108,17 @@ class Test(unittest.TestCase):
self.assertEqual(wpas.p2p_group['ip_addr'], '192.168.1.2')
self.assertEqual(wpas.p2p_group['ip_mask'], '255.255.255.240')
self.assertEqual(wpas.p2p_group['go_ip_addr'], '192.168.1.1')
- dhcp = ctx.start_process(['dhclient', '-v', '-d', '--no-pid', '-cf', '/dev/null', '-lf', '/tmp/dhcp.leases',
- '-sf', '/tmp/dhclient-script', peer_ifname])
+
+ extra_params = []
+ if ctx.is_verbose('dhclient'):
+ extra_params.append('-v')
+
+ dhcp = ctx.start_process(['dhclient', '-d', '--no-pid', '-cf', '/dev/null', '-lf', self.leases_path,
+ '-sf', '/tmp/dhclient-script'] + extra_params + [peer_ifname])
self.dhcp = dhcp
wd.wait_for_object_condition(peer, 'obj.connected', max_wait=15)
- time.sleep(1) # Give the client time to set the IP
+ time.sleep(5) # Give the client time to set the IP
testutil.test_ip_address_match(peer_ifname, peer.connected_ip)
if not go:
@@ -146,7 +154,7 @@ class Test(unittest.TestCase):
self.wpas = None
if self.dhcp is not None:
ctx.stop_process(self.dhcp)
- for path in ['/tmp/dhcp.leases']:
+ for path in [self.leases_path, self.leases_path + '~']:
if os.path.exists(path):
os.remove(path)
--
2.32.0
7 months
[PATCH v3 1/3] scan: move scan_freq_set* into util
by James Prestwood
This will allow scan_freq_set utilities to be used in any
modules requiring unit testing
---
src/scan.c | 160 ----------------------------------------------------
src/scan.h | 14 -----
src/util.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++++
src/util.h | 15 +++++
4 files changed, 176 insertions(+), 174 deletions(-)
diff --git a/src/scan.c b/src/scan.c
index 4a0e274e..97b0a933 100644
--- a/src/scan.c
+++ b/src/scan.c
@@ -2017,166 +2017,6 @@ bool scan_get_firmware_scan(uint64_t wdev_id, scan_notify_func_t notify,
return true;
}
-struct scan_freq_set {
- uint16_t channels_2ghz;
- struct l_uintset *channels_5ghz;
-};
-
-struct scan_freq_set *scan_freq_set_new(void)
-{
- struct scan_freq_set *ret = l_new(struct scan_freq_set, 1);
-
- /* 802.11-2012, 8.4.2.10 hints that 200 is the largest channel number */
- ret->channels_5ghz = l_uintset_new_from_range(1, 200);
-
- return ret;
-}
-
-void scan_freq_set_free(struct scan_freq_set *freqs)
-{
- l_uintset_free(freqs->channels_5ghz);
- l_free(freqs);
-}
-
-bool scan_freq_set_add(struct scan_freq_set *freqs, uint32_t freq)
-{
- enum band_freq band;
- uint8_t channel;
-
- channel = band_freq_to_channel(freq, &band);
- if (!channel)
- return false;
-
- switch (band) {
- case BAND_FREQ_2_4_GHZ:
- freqs->channels_2ghz |= 1 << (channel - 1);
- return true;
- case BAND_FREQ_5_GHZ:
- return l_uintset_put(freqs->channels_5ghz, channel);
- }
-
- return false;
-}
-
-bool scan_freq_set_contains(const struct scan_freq_set *freqs, uint32_t freq)
-{
- enum band_freq band;
- uint8_t channel;
-
- channel = band_freq_to_channel(freq, &band);
- if (!channel)
- return false;
-
- switch (band) {
- case BAND_FREQ_2_4_GHZ:
- return freqs->channels_2ghz & (1 << (channel - 1));
- case BAND_FREQ_5_GHZ:
- return l_uintset_contains(freqs->channels_5ghz, channel);
- }
-
- return false;
-}
-
-uint32_t scan_freq_set_get_bands(struct scan_freq_set *freqs)
-{
- uint32_t bands = 0;
- uint32_t max;
-
- if (freqs->channels_2ghz)
- bands |= BAND_FREQ_2_4_GHZ;
-
- max = l_uintset_get_max(freqs->channels_5ghz);
-
- if (l_uintset_find_min(freqs->channels_5ghz) <= max)
- bands |= BAND_FREQ_5_GHZ;
-
- return bands;
-}
-
-static void scan_channels_5ghz_add(uint32_t channel, void *user_data)
-{
- struct l_uintset *to = user_data;
-
- l_uintset_put(to, channel);
-}
-
-void scan_freq_set_merge(struct scan_freq_set *to,
- const struct scan_freq_set *from)
-{
- to->channels_2ghz |= from->channels_2ghz;
-
- l_uintset_foreach(from->channels_5ghz, scan_channels_5ghz_add,
- to->channels_5ghz);
-}
-
-bool scan_freq_set_isempty(const struct scan_freq_set *set)
-{
- if (set->channels_2ghz == 0 && l_uintset_isempty(set->channels_5ghz))
- return true;
-
- return false;
-}
-
-struct channels_5ghz_foreach_data {
- scan_freq_set_func_t func;
- void *user_data;
-};
-
-static void scan_channels_5ghz_frequency(uint32_t channel, void *user_data)
-{
- const struct channels_5ghz_foreach_data *channels_5ghz_data = user_data;
- uint32_t freq;
-
- freq = band_channel_to_freq(channel, BAND_FREQ_5_GHZ);
-
- channels_5ghz_data->func(freq, channels_5ghz_data->user_data);
-}
-
-void scan_freq_set_foreach(const struct scan_freq_set *freqs,
- scan_freq_set_func_t func, void *user_data)
-{
- struct channels_5ghz_foreach_data data = { };
- uint8_t channel;
- uint32_t freq;
-
- if (unlikely(!freqs || !func))
- return;
-
- data.func = func;
- data.user_data = user_data;
-
- l_uintset_foreach(freqs->channels_5ghz, scan_channels_5ghz_frequency,
- &data);
-
- if (!freqs->channels_2ghz)
- return;
-
- for (channel = 1; channel <= 14; channel++) {
- if (freqs->channels_2ghz & (1 << (channel - 1))) {
- freq = band_channel_to_freq(channel, BAND_FREQ_2_4_GHZ);
-
- func(freq, user_data);
- }
- }
-}
-
-void scan_freq_set_constrain(struct scan_freq_set *set,
- const struct scan_freq_set *constraint)
-{
- struct l_uintset *intersection;
-
- intersection = l_uintset_intersect(constraint->channels_5ghz,
- set->channels_5ghz);
- if (!intersection)
- /* This shouldn't ever be the case. */
- return;
-
- l_uintset_free(set->channels_5ghz);
- set->channels_5ghz = intersection;
-
- set->channels_2ghz &= constraint->channels_2ghz;
-}
-
bool scan_wdev_add(uint64_t wdev_id)
{
struct scan_context *sc;
diff --git a/src/scan.h b/src/scan.h
index 1623468e..81cb9e46 100644
--- a/src/scan.h
+++ b/src/scan.h
@@ -108,7 +108,6 @@ typedef bool (*scan_notify_func_t)(int err, struct l_queue *bss_list,
const struct scan_freq_set *freqs,
void *userdata);
typedef void (*scan_destroy_func_t)(void *userdata);
-typedef void (*scan_freq_set_func_t)(uint32_t freq, void *userdata);
static inline int scan_bss_addr_cmp(const struct scan_bss *a1,
const struct scan_bss *a2)
@@ -168,18 +167,5 @@ struct scan_bss *scan_bss_new_from_probe_req(const struct mmpdu_header *mpdu,
size_t body_len,
uint32_t frequency, int rssi);
-struct scan_freq_set *scan_freq_set_new(void);
-void scan_freq_set_free(struct scan_freq_set *freqs);
-bool scan_freq_set_add(struct scan_freq_set *freqs, uint32_t freq);
-bool scan_freq_set_contains(const struct scan_freq_set *freqs, uint32_t freq);
-uint32_t scan_freq_set_get_bands(struct scan_freq_set *freqs);
-void scan_freq_set_foreach(const struct scan_freq_set *freqs,
- scan_freq_set_func_t func, void *user_data);
-void scan_freq_set_merge(struct scan_freq_set *to,
- const struct scan_freq_set *from);
-void scan_freq_set_constrain(struct scan_freq_set *set,
- const struct scan_freq_set *constraint);
-bool scan_freq_set_isempty(const struct scan_freq_set *set);
-
bool scan_wdev_add(uint64_t wdev_id);
bool scan_wdev_remove(uint64_t wdev_id);
diff --git a/src/util.c b/src/util.c
index ca56316d..381894f4 100644
--- a/src/util.c
+++ b/src/util.c
@@ -35,6 +35,7 @@
#include "ell/useful.h"
#include "src/util.h"
+#include "src/band.h"
const char *util_ssid_to_utf8(size_t len, const uint8_t *ssid)
{
@@ -309,3 +310,163 @@ bool util_ip_prefix_tohl(const char *ip, uint8_t *prefix_out,
return true;
}
+
+struct scan_freq_set {
+ uint16_t channels_2ghz;
+ struct l_uintset *channels_5ghz;
+};
+
+struct scan_freq_set *scan_freq_set_new(void)
+{
+ struct scan_freq_set *ret = l_new(struct scan_freq_set, 1);
+
+ /* 802.11-2012, 8.4.2.10 hints that 200 is the largest channel number */
+ ret->channels_5ghz = l_uintset_new_from_range(1, 200);
+
+ return ret;
+}
+
+void scan_freq_set_free(struct scan_freq_set *freqs)
+{
+ l_uintset_free(freqs->channels_5ghz);
+ l_free(freqs);
+}
+
+bool scan_freq_set_add(struct scan_freq_set *freqs, uint32_t freq)
+{
+ enum band_freq band;
+ uint8_t channel;
+
+ channel = band_freq_to_channel(freq, &band);
+ if (!channel)
+ return false;
+
+ switch (band) {
+ case BAND_FREQ_2_4_GHZ:
+ freqs->channels_2ghz |= 1 << (channel - 1);
+ return true;
+ case BAND_FREQ_5_GHZ:
+ return l_uintset_put(freqs->channels_5ghz, channel);
+ }
+
+ return false;
+}
+
+bool scan_freq_set_contains(const struct scan_freq_set *freqs, uint32_t freq)
+{
+ enum band_freq band;
+ uint8_t channel;
+
+ channel = band_freq_to_channel(freq, &band);
+ if (!channel)
+ return false;
+
+ switch (band) {
+ case BAND_FREQ_2_4_GHZ:
+ return freqs->channels_2ghz & (1 << (channel - 1));
+ case BAND_FREQ_5_GHZ:
+ return l_uintset_contains(freqs->channels_5ghz, channel);
+ }
+
+ return false;
+}
+
+uint32_t scan_freq_set_get_bands(struct scan_freq_set *freqs)
+{
+ uint32_t bands = 0;
+ uint32_t max;
+
+ if (freqs->channels_2ghz)
+ bands |= BAND_FREQ_2_4_GHZ;
+
+ max = l_uintset_get_max(freqs->channels_5ghz);
+
+ if (l_uintset_find_min(freqs->channels_5ghz) <= max)
+ bands |= BAND_FREQ_5_GHZ;
+
+ return bands;
+}
+
+static void scan_channels_5ghz_add(uint32_t channel, void *user_data)
+{
+ struct l_uintset *to = user_data;
+
+ l_uintset_put(to, channel);
+}
+
+void scan_freq_set_merge(struct scan_freq_set *to,
+ const struct scan_freq_set *from)
+{
+ to->channels_2ghz |= from->channels_2ghz;
+
+ l_uintset_foreach(from->channels_5ghz, scan_channels_5ghz_add,
+ to->channels_5ghz);
+}
+
+bool scan_freq_set_isempty(const struct scan_freq_set *set)
+{
+ if (set->channels_2ghz == 0 && l_uintset_isempty(set->channels_5ghz))
+ return true;
+
+ return false;
+}
+
+struct channels_5ghz_foreach_data {
+ scan_freq_set_func_t func;
+ void *user_data;
+};
+
+static void scan_channels_5ghz_frequency(uint32_t channel, void *user_data)
+{
+ const struct channels_5ghz_foreach_data *channels_5ghz_data = user_data;
+ uint32_t freq;
+
+ freq = band_channel_to_freq(channel, BAND_FREQ_5_GHZ);
+
+ channels_5ghz_data->func(freq, channels_5ghz_data->user_data);
+}
+
+void scan_freq_set_foreach(const struct scan_freq_set *freqs,
+ scan_freq_set_func_t func, void *user_data)
+{
+ struct channels_5ghz_foreach_data data = { };
+ uint8_t channel;
+ uint32_t freq;
+
+ if (unlikely(!freqs || !func))
+ return;
+
+ data.func = func;
+ data.user_data = user_data;
+
+ l_uintset_foreach(freqs->channels_5ghz, scan_channels_5ghz_frequency,
+ &data);
+
+ if (!freqs->channels_2ghz)
+ return;
+
+ for (channel = 1; channel <= 14; channel++) {
+ if (freqs->channels_2ghz & (1 << (channel - 1))) {
+ freq = band_channel_to_freq(channel, BAND_FREQ_2_4_GHZ);
+
+ func(freq, user_data);
+ }
+ }
+}
+
+void scan_freq_set_constrain(struct scan_freq_set *set,
+ const struct scan_freq_set *constraint)
+{
+ struct l_uintset *intersection;
+
+ intersection = l_uintset_intersect(constraint->channels_5ghz,
+ set->channels_5ghz);
+ if (!intersection)
+ /* This shouldn't ever be the case. */
+ return;
+
+ l_uintset_free(set->channels_5ghz);
+ set->channels_5ghz = intersection;
+
+ set->channels_2ghz &= constraint->channels_2ghz;
+}
diff --git a/src/util.h b/src/util.h
index 23353ddb..8cced9cd 100644
--- a/src/util.h
+++ b/src/util.h
@@ -101,4 +101,19 @@ static inline bool util_ip_subnet_match(uint8_t prefix_len,
~((1u << (8 - (prefix_len % 8))) - 1));
}
+typedef void (*scan_freq_set_func_t)(uint32_t freq, void *userdata);
+
+struct scan_freq_set *scan_freq_set_new(void);
+void scan_freq_set_free(struct scan_freq_set *freqs);
+bool scan_freq_set_add(struct scan_freq_set *freqs, uint32_t freq);
+bool scan_freq_set_contains(const struct scan_freq_set *freqs, uint32_t freq);
+uint32_t scan_freq_set_get_bands(struct scan_freq_set *freqs);
+void scan_freq_set_foreach(const struct scan_freq_set *freqs,
+ scan_freq_set_func_t func, void *user_data);
+void scan_freq_set_merge(struct scan_freq_set *to,
+ const struct scan_freq_set *from);
+void scan_freq_set_constrain(struct scan_freq_set *set,
+ const struct scan_freq_set *constraint);
+bool scan_freq_set_isempty(const struct scan_freq_set *set);
+
#endif /* __UTIL_H */
--
2.31.1
7 months
[PATCH v2 1/3] scan: move scan_freq_set* into util
by James Prestwood
This will allow scan_freq_set utilities to be used in any
modules requiring unit testing
---
src/scan.c | 160 ----------------------------------------------------
src/scan.h | 14 -----
src/util.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++++
src/util.h | 15 +++++
4 files changed, 176 insertions(+), 174 deletions(-)
diff --git a/src/scan.c b/src/scan.c
index 4a0e274e..97b0a933 100644
--- a/src/scan.c
+++ b/src/scan.c
@@ -2017,166 +2017,6 @@ bool scan_get_firmware_scan(uint64_t wdev_id, scan_notify_func_t notify,
return true;
}
-struct scan_freq_set {
- uint16_t channels_2ghz;
- struct l_uintset *channels_5ghz;
-};
-
-struct scan_freq_set *scan_freq_set_new(void)
-{
- struct scan_freq_set *ret = l_new(struct scan_freq_set, 1);
-
- /* 802.11-2012, 8.4.2.10 hints that 200 is the largest channel number */
- ret->channels_5ghz = l_uintset_new_from_range(1, 200);
-
- return ret;
-}
-
-void scan_freq_set_free(struct scan_freq_set *freqs)
-{
- l_uintset_free(freqs->channels_5ghz);
- l_free(freqs);
-}
-
-bool scan_freq_set_add(struct scan_freq_set *freqs, uint32_t freq)
-{
- enum band_freq band;
- uint8_t channel;
-
- channel = band_freq_to_channel(freq, &band);
- if (!channel)
- return false;
-
- switch (band) {
- case BAND_FREQ_2_4_GHZ:
- freqs->channels_2ghz |= 1 << (channel - 1);
- return true;
- case BAND_FREQ_5_GHZ:
- return l_uintset_put(freqs->channels_5ghz, channel);
- }
-
- return false;
-}
-
-bool scan_freq_set_contains(const struct scan_freq_set *freqs, uint32_t freq)
-{
- enum band_freq band;
- uint8_t channel;
-
- channel = band_freq_to_channel(freq, &band);
- if (!channel)
- return false;
-
- switch (band) {
- case BAND_FREQ_2_4_GHZ:
- return freqs->channels_2ghz & (1 << (channel - 1));
- case BAND_FREQ_5_GHZ:
- return l_uintset_contains(freqs->channels_5ghz, channel);
- }
-
- return false;
-}
-
-uint32_t scan_freq_set_get_bands(struct scan_freq_set *freqs)
-{
- uint32_t bands = 0;
- uint32_t max;
-
- if (freqs->channels_2ghz)
- bands |= BAND_FREQ_2_4_GHZ;
-
- max = l_uintset_get_max(freqs->channels_5ghz);
-
- if (l_uintset_find_min(freqs->channels_5ghz) <= max)
- bands |= BAND_FREQ_5_GHZ;
-
- return bands;
-}
-
-static void scan_channels_5ghz_add(uint32_t channel, void *user_data)
-{
- struct l_uintset *to = user_data;
-
- l_uintset_put(to, channel);
-}
-
-void scan_freq_set_merge(struct scan_freq_set *to,
- const struct scan_freq_set *from)
-{
- to->channels_2ghz |= from->channels_2ghz;
-
- l_uintset_foreach(from->channels_5ghz, scan_channels_5ghz_add,
- to->channels_5ghz);
-}
-
-bool scan_freq_set_isempty(const struct scan_freq_set *set)
-{
- if (set->channels_2ghz == 0 && l_uintset_isempty(set->channels_5ghz))
- return true;
-
- return false;
-}
-
-struct channels_5ghz_foreach_data {
- scan_freq_set_func_t func;
- void *user_data;
-};
-
-static void scan_channels_5ghz_frequency(uint32_t channel, void *user_data)
-{
- const struct channels_5ghz_foreach_data *channels_5ghz_data = user_data;
- uint32_t freq;
-
- freq = band_channel_to_freq(channel, BAND_FREQ_5_GHZ);
-
- channels_5ghz_data->func(freq, channels_5ghz_data->user_data);
-}
-
-void scan_freq_set_foreach(const struct scan_freq_set *freqs,
- scan_freq_set_func_t func, void *user_data)
-{
- struct channels_5ghz_foreach_data data = { };
- uint8_t channel;
- uint32_t freq;
-
- if (unlikely(!freqs || !func))
- return;
-
- data.func = func;
- data.user_data = user_data;
-
- l_uintset_foreach(freqs->channels_5ghz, scan_channels_5ghz_frequency,
- &data);
-
- if (!freqs->channels_2ghz)
- return;
-
- for (channel = 1; channel <= 14; channel++) {
- if (freqs->channels_2ghz & (1 << (channel - 1))) {
- freq = band_channel_to_freq(channel, BAND_FREQ_2_4_GHZ);
-
- func(freq, user_data);
- }
- }
-}
-
-void scan_freq_set_constrain(struct scan_freq_set *set,
- const struct scan_freq_set *constraint)
-{
- struct l_uintset *intersection;
-
- intersection = l_uintset_intersect(constraint->channels_5ghz,
- set->channels_5ghz);
- if (!intersection)
- /* This shouldn't ever be the case. */
- return;
-
- l_uintset_free(set->channels_5ghz);
- set->channels_5ghz = intersection;
-
- set->channels_2ghz &= constraint->channels_2ghz;
-}
-
bool scan_wdev_add(uint64_t wdev_id)
{
struct scan_context *sc;
diff --git a/src/scan.h b/src/scan.h
index 1623468e..81cb9e46 100644
--- a/src/scan.h
+++ b/src/scan.h
@@ -108,7 +108,6 @@ typedef bool (*scan_notify_func_t)(int err, struct l_queue *bss_list,
const struct scan_freq_set *freqs,
void *userdata);
typedef void (*scan_destroy_func_t)(void *userdata);
-typedef void (*scan_freq_set_func_t)(uint32_t freq, void *userdata);
static inline int scan_bss_addr_cmp(const struct scan_bss *a1,
const struct scan_bss *a2)
@@ -168,18 +167,5 @@ struct scan_bss *scan_bss_new_from_probe_req(const struct mmpdu_header *mpdu,
size_t body_len,
uint32_t frequency, int rssi);
-struct scan_freq_set *scan_freq_set_new(void);
-void scan_freq_set_free(struct scan_freq_set *freqs);
-bool scan_freq_set_add(struct scan_freq_set *freqs, uint32_t freq);
-bool scan_freq_set_contains(const struct scan_freq_set *freqs, uint32_t freq);
-uint32_t scan_freq_set_get_bands(struct scan_freq_set *freqs);
-void scan_freq_set_foreach(const struct scan_freq_set *freqs,
- scan_freq_set_func_t func, void *user_data);
-void scan_freq_set_merge(struct scan_freq_set *to,
- const struct scan_freq_set *from);
-void scan_freq_set_constrain(struct scan_freq_set *set,
- const struct scan_freq_set *constraint);
-bool scan_freq_set_isempty(const struct scan_freq_set *set);
-
bool scan_wdev_add(uint64_t wdev_id);
bool scan_wdev_remove(uint64_t wdev_id);
diff --git a/src/util.c b/src/util.c
index ca56316d..381894f4 100644
--- a/src/util.c
+++ b/src/util.c
@@ -35,6 +35,7 @@
#include "ell/useful.h"
#include "src/util.h"
+#include "src/band.h"
const char *util_ssid_to_utf8(size_t len, const uint8_t *ssid)
{
@@ -309,3 +310,163 @@ bool util_ip_prefix_tohl(const char *ip, uint8_t *prefix_out,
return true;
}
+
+struct scan_freq_set {
+ uint16_t channels_2ghz;
+ struct l_uintset *channels_5ghz;
+};
+
+struct scan_freq_set *scan_freq_set_new(void)
+{
+ struct scan_freq_set *ret = l_new(struct scan_freq_set, 1);
+
+ /* 802.11-2012, 8.4.2.10 hints that 200 is the largest channel number */
+ ret->channels_5ghz = l_uintset_new_from_range(1, 200);
+
+ return ret;
+}
+
+void scan_freq_set_free(struct scan_freq_set *freqs)
+{
+ l_uintset_free(freqs->channels_5ghz);
+ l_free(freqs);
+}
+
+bool scan_freq_set_add(struct scan_freq_set *freqs, uint32_t freq)
+{
+ enum band_freq band;
+ uint8_t channel;
+
+ channel = band_freq_to_channel(freq, &band);
+ if (!channel)
+ return false;
+
+ switch (band) {
+ case BAND_FREQ_2_4_GHZ:
+ freqs->channels_2ghz |= 1 << (channel - 1);
+ return true;
+ case BAND_FREQ_5_GHZ:
+ return l_uintset_put(freqs->channels_5ghz, channel);
+ }
+
+ return false;
+}
+
+bool scan_freq_set_contains(const struct scan_freq_set *freqs, uint32_t freq)
+{
+ enum band_freq band;
+ uint8_t channel;
+
+ channel = band_freq_to_channel(freq, &band);
+ if (!channel)
+ return false;
+
+ switch (band) {
+ case BAND_FREQ_2_4_GHZ:
+ return freqs->channels_2ghz & (1 << (channel - 1));
+ case BAND_FREQ_5_GHZ:
+ return l_uintset_contains(freqs->channels_5ghz, channel);
+ }
+
+ return false;
+}
+
+uint32_t scan_freq_set_get_bands(struct scan_freq_set *freqs)
+{
+ uint32_t bands = 0;
+ uint32_t max;
+
+ if (freqs->channels_2ghz)
+ bands |= BAND_FREQ_2_4_GHZ;
+
+ max = l_uintset_get_max(freqs->channels_5ghz);
+
+ if (l_uintset_find_min(freqs->channels_5ghz) <= max)
+ bands |= BAND_FREQ_5_GHZ;
+
+ return bands;
+}
+
+static void scan_channels_5ghz_add(uint32_t channel, void *user_data)
+{
+ struct l_uintset *to = user_data;
+
+ l_uintset_put(to, channel);
+}
+
+void scan_freq_set_merge(struct scan_freq_set *to,
+ const struct scan_freq_set *from)
+{
+ to->channels_2ghz |= from->channels_2ghz;
+
+ l_uintset_foreach(from->channels_5ghz, scan_channels_5ghz_add,
+ to->channels_5ghz);
+}
+
+bool scan_freq_set_isempty(const struct scan_freq_set *set)
+{
+ if (set->channels_2ghz == 0 && l_uintset_isempty(set->channels_5ghz))
+ return true;
+
+ return false;
+}
+
+struct channels_5ghz_foreach_data {
+ scan_freq_set_func_t func;
+ void *user_data;
+};
+
+static void scan_channels_5ghz_frequency(uint32_t channel, void *user_data)
+{
+ const struct channels_5ghz_foreach_data *channels_5ghz_data = user_data;
+ uint32_t freq;
+
+ freq = band_channel_to_freq(channel, BAND_FREQ_5_GHZ);
+
+ channels_5ghz_data->func(freq, channels_5ghz_data->user_data);
+}
+
+void scan_freq_set_foreach(const struct scan_freq_set *freqs,
+ scan_freq_set_func_t func, void *user_data)
+{
+ struct channels_5ghz_foreach_data data = { };
+ uint8_t channel;
+ uint32_t freq;
+
+ if (unlikely(!freqs || !func))
+ return;
+
+ data.func = func;
+ data.user_data = user_data;
+
+ l_uintset_foreach(freqs->channels_5ghz, scan_channels_5ghz_frequency,
+ &data);
+
+ if (!freqs->channels_2ghz)
+ return;
+
+ for (channel = 1; channel <= 14; channel++) {
+ if (freqs->channels_2ghz & (1 << (channel - 1))) {
+ freq = band_channel_to_freq(channel, BAND_FREQ_2_4_GHZ);
+
+ func(freq, user_data);
+ }
+ }
+}
+
+void scan_freq_set_constrain(struct scan_freq_set *set,
+ const struct scan_freq_set *constraint)
+{
+ struct l_uintset *intersection;
+
+ intersection = l_uintset_intersect(constraint->channels_5ghz,
+ set->channels_5ghz);
+ if (!intersection)
+ /* This shouldn't ever be the case. */
+ return;
+
+ l_uintset_free(set->channels_5ghz);
+ set->channels_5ghz = intersection;
+
+ set->channels_2ghz &= constraint->channels_2ghz;
+}
diff --git a/src/util.h b/src/util.h
index 23353ddb..8cced9cd 100644
--- a/src/util.h
+++ b/src/util.h
@@ -101,4 +101,19 @@ static inline bool util_ip_subnet_match(uint8_t prefix_len,
~((1u << (8 - (prefix_len % 8))) - 1));
}
+typedef void (*scan_freq_set_func_t)(uint32_t freq, void *userdata);
+
+struct scan_freq_set *scan_freq_set_new(void);
+void scan_freq_set_free(struct scan_freq_set *freqs);
+bool scan_freq_set_add(struct scan_freq_set *freqs, uint32_t freq);
+bool scan_freq_set_contains(const struct scan_freq_set *freqs, uint32_t freq);
+uint32_t scan_freq_set_get_bands(struct scan_freq_set *freqs);
+void scan_freq_set_foreach(const struct scan_freq_set *freqs,
+ scan_freq_set_func_t func, void *user_data);
+void scan_freq_set_merge(struct scan_freq_set *to,
+ const struct scan_freq_set *from);
+void scan_freq_set_constrain(struct scan_freq_set *set,
+ const struct scan_freq_set *constraint);
+bool scan_freq_set_isempty(const struct scan_freq_set *set);
+
#endif /* __UTIL_H */
--
2.31.1
7 months
[PATCH] offchannel: introduce new offchannel module
by James Prestwood
---
Makefile.am | 1 +
src/offchannel.c | 338 +++++++++++++++++++++++++++++++++++++++++++++++
src/offchannel.h | 29 ++++
3 files changed, 368 insertions(+)
create mode 100644 src/offchannel.c
create mode 100644 src/offchannel.h
diff --git a/Makefile.am b/Makefile.am
index 275dd1b9..5c5db879 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..0ec067cd
--- /dev/null
+++ b/src/offchannel.c
@@ -0,0 +1,338 @@
+/*
+ *
+ * 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;
+ struct l_timeout *timeout;
+ int error;
+
+ struct wiphy_radio_work_item work;
+
+ bool roc_started : 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 struct offchannel_info *find_offchannel(uint64_t wdev_id)
+{
+ return l_queue_find(offchannel_list, match_wdev, &wdev_id);
+}
+
+static void offchannel_free(void *user_data)
+{
+ struct offchannel_info *info = user_data;
+
+ if (info->timeout)
+ l_timeout_remove(info->timeout);
+
+ l_free(info);
+}
+
+static void offchannel_destroy(void *user_data)
+{
+ struct offchannel_info *info = user_data;
+
+ if (info->destroy)
+ info->destroy(info->error, info->user_data);
+
+ l_queue_remove(offchannel_list, info);
+
+ offchannel_free(info);
+}
+
+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 error;
+ }
+
+ 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 error;
+ }
+
+ return;
+
+error:
+ offchannel_cancel(info->wdev_id, info->work.id);
+}
+
+static void offchannel_timeout(struct l_timeout *timeout, void *user_data)
+{
+ struct offchannel_info *info = user_data;
+
+ info->error = -EBUSY;
+
+ offchannel_cancel(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;
+ }
+
+ /*
+ * Don't rely on kernel timers for cleanup. This timeout is modified
+ * when the ROC is started and will time the actual duration at that
+ * point. If this timer expires prior to ROC starting the caller will
+ * be notified with -EBUSY in the destroy callback. In addition the
+ * offchannel_info is tracked in a queue only at this point so the
+ * queue will only have one outstanding item per wdev.
+ */
+ info->timeout = l_timeout_create_ms(info->duration, offchannel_timeout,
+ info, NULL);
+ l_queue_push_head(offchannel_list, info);
+
+ return false;
+}
+
+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_work_destroy(struct wiphy_radio_work_item *item)
+{
+ struct offchannel_info *info = l_container_of(item,
+ struct offchannel_info, work);
+
+ /*
+ * Cancelled even before the kernel replied to the command. This
+ * could happen due to an IO error, parsing error, or if a very short
+ * duration is used. But if so there is no need to cancel ROC since the
+ * driver never went off channel in the first place.
+ */
+ if (info->roc_cmd_id) {
+ info->error = -ECANCELED;
+ l_genl_family_cancel(nl80211, info->roc_cmd_id);
+ goto destroy;
+ }
+
+ /*
+ * If info->roc_cmd_id is zero but we still have no cookie, something
+ * happened in offchannel_roc_cb. Basically, the command failed. The ROC
+ * does not need to be cancelled and destroy can be called immediately.
+ * An appropriate error will have been set by offchannel_roc_cb.
+ */
+ if (!info->roc_cookie)
+ goto destroy;
+
+ /*
+ * Cancelled prior to ROC completing, cancel ROC, and set -ECANCELLED
+ */
+ if (info->roc_started && info->timeout) {
+ offchannel_cancel_roc(info);
+
+ l_timeout_remove(info->timeout);
+ info->timeout = NULL;
+
+ info->error = -ECANCELED;
+ }
+
+ /* Otherwise the ROC finished normally */
+
+destroy:
+ offchannel_destroy(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;
+
+ 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)
+{
+ wiphy_radio_work_done(wiphy_find_by_wdev(wdev_id), 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 = find_offchannel(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:
+ l_timeout_modify_ms(info->timeout, info->duration);
+
+ info->roc_started = true;
+
+ if (info->started)
+ info->started(info->user_data);
+
+ break;
+ case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL:
+ /* No matching ROC call? This shouldn't happen... */
+ if (L_WARN_ON(!info->roc_started))
+ return;
+
+ l_timeout_remove(info->timeout);
+ info->timeout = NULL;
+
+ info->error = 0;
+ offchannel_cancel(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, offchannel_free);
+}
+
+IWD_MODULE(offchannel, offchannel_init, offchannel_exit);
+IWD_MODULE_DEPENDS(offchannel, wiphy);
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
7 months
[PATCH 1/6] ie: add DPP configurator connectivity element
by James Prestwood
---
src/ie.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/ie.h b/src/ie.h
index 685350d6..d460a7cc 100644
--- a/src/ie.h
+++ b/src/ie.h
@@ -292,6 +292,7 @@ enum ie_vendor_wfa_oi_type {
IE_WFA_OI_OSEN = 0x12,
IE_WFA_OI_OWE_TRANSITION = 0x1c,
IE_WFA_OI_ROAMING_SELECTION = 0x1d,
+ IE_WFA_OI_CONFIGURATOR_CONNECTIVITY = 0x1e,
};
enum ie_advertisement_id {
--
2.31.1
7 months
[PATCH v2 1/2] netdev: always honor disconnect events if issued by AP
by James Prestwood
The disconnect event handler was mistakenly bailing out if FT or
reassociation was going on. This was done because a disconnect
event is sent by the kernel when CMD_AUTH/CMD_ASSOC is used.
The problem is an AP could also disconnect IWD which should never
be ignored.
To fix this always parse the disconnect event and, if issued by
the AP, always notify watchers of the disconnect.
---
src/netdev.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/src/netdev.c b/src/netdev.c
index 96e473a9..e35a8b82 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -1165,8 +1165,7 @@ static void netdev_disconnect_event(struct l_genl_msg *msg,
l_debug("");
- if (!netdev->connected || netdev->disconnect_cmd_id > 0 ||
- netdev->in_ft || netdev->in_reassoc)
+ if (!netdev->connected || netdev->disconnect_cmd_id > 0)
return;
if (!l_genl_attr_init(&attr, msg)) {
@@ -1190,6 +1189,13 @@ static void netdev_disconnect_event(struct l_genl_msg *msg,
}
}
+ /*
+ * Only ignore this event if issued by the kernel since this is
+ * normal when using CMD_AUTH/ASSOC.
+ */
+ if (!disconnect_by_ap && (netdev->in_ft || netdev->in_reassoc))
+ return;
+
l_info("Received Deauthentication event, reason: %hu, from_ap: %s",
reason_code, disconnect_by_ap ? "true" : "false");
@@ -3171,6 +3177,7 @@ static void netdev_associate_event(struct l_genl_msg *msg,
if (!netdev->ap) {
netdev->associated = true;
+ netdev->in_reassoc = false;
return;
}
--
2.31.1
7 months, 1 week
[PATCH 1/2] netdev: only set in_reassoc if using an auth-proto
by James Prestwood
The kernel strangely sends a disconnect event when using auth/assoc
commands, but does not for CMD_CONNECT (only deauth). This wasn't
observed and IWD assumed a disconnect event comes unconditionally.
Because of this in_reassoc is checked in netdev_disconnect_event
and the event is ignored if set.
When CMD_CONNECT is used to reassociate this would cause disconnect
events to be ignored when they should not be, preventing IWD from
going into a disconnected state post-roam.
Now, in_reassoc is only set if an auth-proto is used.
---
src/netdev.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/netdev.c b/src/netdev.c
index 96e473a9..34ace286 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -4144,13 +4144,14 @@ int netdev_reassociate(struct netdev *netdev, struct scan_bss *target_bss,
netdev->associated = false;
netdev->operational = false;
netdev->connected = false;
- netdev->in_reassoc = true;
netdev_connect_common(netdev, target_bss, orig_bss, hs, NULL, 0,
event_filter, cb, user_data);
- if (netdev->ap)
+ if (netdev->ap) {
+ netdev->in_reassoc = true;
memcpy(netdev->ap->prev_bssid, orig_bss->addr, ETH_ALEN);
+ }
netdev_rssi_polling_update(netdev);
--
2.31.1
7 months, 1 week