ap mode problem
by KeithG
Group,
Trying to use ap mode on a USB dongle and having difficulty making a connection.
Kernel 5.10.27, iwd 1.13
This is the dongle:
[ 29.109521] usb 1-1.5: New USB device found, idVendor=7392,
idProduct=7811, bcdDevice= 2.00
[ 29.109575] usb 1-1.5: New USB device strings: Mfr=1, Product=2,
SerialNumber=3
[ 29.109594] usb 1-1.5: Product: 802.11n WLAN Adapter
[ 29.109608] usb 1-1.5: Manufacturer: Realtek
[ 29.109622] usb 1-1.5: SerialNumber: 00e04c000001
...
[ 53.336830] rtl8192cu: Chip version 0x10
[ 53.878190] rtl8192cu: Board Type 0
[ 53.878822] rtl_usb: rx_max_size 15360, rx_urb_num 8, in_ep 1
[ 53.879097] rtl8192cu: Loading firmware rtlwifi/rtl8192cufw_TMSC.bin
[ 53.879320] ieee80211 phy0: Selected rate control algorithm 'rtl_rc'
[ 54.017808] usbcore: registered new interface driver rtl8192cu
I enabled AP mode from iwctl:
first setting the mode to AP then
"ap wlan0 start SSID password"
This is what is in the journal:
Apr 21 11:47:28 deskrune systemd[1]: Started Wireless service.
Apr 21 11:47:28 deskrune iwd[2298]: Wiphy: 0, Name: phy0
Apr 21 11:47:28 deskrune iwd[2298]: Permanent Address: 80:1f:02:a2:d4:4c
Apr 21 11:47:28 deskrune iwd[2298]: Bands: 2.4 GHz
Apr 21 11:47:28 deskrune iwd[2298]: Ciphers: CCMP TKIP BIP
Apr 21 11:47:28 deskrune iwd[2298]: Supported iftypes: ad-hoc
station ap p2p-client p2p-go
...
Apr 21 11:47:28 deskrune iwd[2298]: Wiphy phy0 will only use the
default interface
Apr 21 11:47:51 deskrune iwd[2298]: AP Probe Request from ae:53:10:32:ee:8a
Apr 21 11:47:51 deskrune iwd[2298]: AP Probe Response delivered OK
Apr 21 11:47:51 deskrune iwd[2298]: AP Authentication from ae:53:10:32:ee:8a
Apr 21 11:47:51 deskrune iwd[2298]: AP Authentication frame 2 ACKed by STA
Apr 21 11:47:51 deskrune iwd[2298]: AP Association Request from
ae:53:10:32:ee:8a
Apr 21 11:47:51 deskrune iwd[2298]: AP (Re)Association Response ACK received
Apr 21 11:47:52 deskrune iwd[2298]: 4-Way handshake failed for
ifindex: 3, reason: 15
Apr 21 11:47:54 deskrune iwd[2298]: AP Probe Request from b0:02:47:cc:83:f9
Apr 21 11:47:54 deskrune iwd[2298]: AP Probe Response delivered OK
Apr 21 11:47:54 deskrune iwd[2298]: AP Probe Request from b0:02:47:cc:83:f9
Apr 21 11:47:57 deskrune iwd[2298]: AP Probe Request from ee:b9:80:a7:13:81
Apr 21 11:47:57 deskrune iwd[2298]: AP Probe Response delivered OK
Apr 21 11:47:58 deskrune iwd[2298]: AP Probe Request from b8:27:eb:18:74:9c
Apr 21 11:47:58 deskrune iwd[2298]: AP Probe Response delivered OK
Apr 21 11:47:58 deskrune iwd[2298]: AP Probe Request from b8:27:eb:18:74:9c
Apr 21 11:47:58 deskrune iwd[2298]: AP Probe Request from b8:27:eb:18:74:9c
Apr 21 11:48:01 deskrune iwd[2298]: AP Deauthentication from
ae:53:10:32:ee:8a, reason 3
Is this incompatible? It works with hostapd/dnsmasq.
Keith
1 year
[PATCH] main: Add D-Bus Daemon.GetInfo method
by Andrew Zaborowski
Expose the state directory/storage directory path on D-Bus because it
can't be known to clients until IWD runs, and client might need to
occasionally fiddle with the network config files. While there also
expose the IWD version string, similar to how some other D-Bus services
do.
---
I'm not happy with the name of the interface and the name of the method
but couldn't find better names that would work well and satisfy
everybody. "net.connman.IWD" or "net.connman.iwd.Control" are other
names I considered, and as for GetInfo the alternative could be using
properties similar to how org.freedesktop.NetworkManager exposes
Version, org.freedesktop.ColorManager exposes DaemonVersion,
org.gnome.DisplayManager exposes Version{Major,Minor,Micro}, etc.
---
src/dbus.h | 1 +
src/main.c | 42 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 43 insertions(+)
diff --git a/src/dbus.h b/src/dbus.h
index 4936dc6c..b3896108 100644
--- a/src/dbus.h
+++ b/src/dbus.h
@@ -24,6 +24,7 @@
#define IWD_SERVICE "net.connman.iwd"
+#define IWD_DAEMON_INTERFACE "net.connman.iwd.Daemon"
#define IWD_AGENT_MANAGER_INTERFACE "net.connman.iwd.AgentManager"
#define IWD_WIPHY_INTERFACE "net.connman.iwd.Adapter"
#define IWD_DEVICE_INTERFACE "net.connman.iwd.Device"
diff --git a/src/main.c b/src/main.c
index 2ee6188c..f65fa7f4 100644
--- a/src/main.c
+++ b/src/main.c
@@ -185,6 +185,16 @@ static void request_name_callback(struct l_dbus *dbus, bool success,
if (!l_dbus_object_manager_enable(dbus, "/"))
l_warn("Unable to register the ObjectManager");
+ if (!l_dbus_object_add_interface(dbus, IWD_BASE_PATH,
+ IWD_DAEMON_INTERFACE,
+ NULL) ||
+ !l_dbus_object_add_interface(dbus, IWD_BASE_PATH,
+ L_DBUS_INTERFACE_PROPERTIES,
+ NULL))
+ l_info("Unable to add %s and/or %s at %s",
+ IWD_DAEMON_INTERFACE, L_DBUS_INTERFACE_PROPERTIES,
+ IWD_BASE_PATH);
+
/* TODO: Always request nl80211 for now, ignoring auto-loading */
l_genl_request_family(genl, NL80211_GENL_NAME, nl80211_appeared,
NULL, NULL);
@@ -194,12 +204,44 @@ fail_exit:
l_main_quit();
}
+static struct l_dbus_message *iwd_dbus_get_info(struct l_dbus *dbus,
+ struct l_dbus_message *message,
+ void *user_data)
+{
+ struct l_dbus_message *reply;
+ struct l_dbus_message_builder *builder;
+ L_AUTO_FREE_VAR(char *, storage_dir) = storage_get_path(NULL);
+
+ reply = l_dbus_message_new_method_return(message);
+ builder = l_dbus_message_builder_new(reply);
+ l_dbus_message_builder_enter_array(builder, "{sv}");
+
+ dbus_append_dict_basic(builder, "StateDirectory", 's', storage_dir);
+ dbus_append_dict_basic(builder, "Version", 's', VERSION);
+
+ l_dbus_message_builder_leave_array(builder);
+ l_dbus_message_builder_finalize(builder);
+ l_dbus_message_builder_destroy(builder);
+
+ return reply;
+}
+
+static void iwd_setup_deamon_interface(struct l_dbus_interface *interface)
+{
+ l_dbus_interface_method(interface, "GetInfo", 0, iwd_dbus_get_info,
+ "a{sv}", "", "info");
+}
+
static void dbus_ready(void *user_data)
{
struct l_dbus *dbus = user_data;
l_dbus_name_acquire(dbus, "net.connman.iwd", false, false, false,
request_name_callback, NULL);
+
+ l_dbus_register_interface(dbus, IWD_DAEMON_INTERFACE,
+ iwd_setup_deamon_interface,
+ NULL, false);
}
static void dbus_disconnected(void *user_data)
--
2.27.0
1 year
[PATCH] build: fix ell/shared build failure
by Alvin Šipraga
Fix the following build error:
$ /path/to/iwd/configure --disable-dependency-tracking
$ make
GEN ell/shared
/bin/sh: line 5: ell/shared: No such file or directory
make: *** [Makefile:3656: ell/shared] Error 1
The error can also arise if building with --enable-external-ell.
Fixes: ed05585063f2 ("build: Always link in the ell/useful.h header file")
---
Makefile.am | 1 +
1 file changed, 1 insertion(+)
diff --git a/Makefile.am b/Makefile.am
index 644f3556..68035e46 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -625,6 +625,7 @@ unit/tls-settings.8021x: unit/cert-ca.pem unit/cert-client.pem unit/cert-client-
BUILT_SOURCES = $(ell_built_sources) src/builtin.h
ell/shared: Makefile
+ $(AM_V_at)$(MKDIR_P) ell
$(AM_V_GEN)for f in $(ell_shared) ; do \
if [ ! -f $$f ] ; then \
$(LN_S) -t ell -f $(abs_srcdir)/../ell/$$f ; \
--
2.30.2
1 year
Use of kernel crypto lib
by Alexander Monakov
Hi,
I raised the topic of inappropriate use of kernel crypto lib for
certchain validation in IWD and was told it was discussed previously. I
do not see such threads in the archives (but at the moment archives
before September 2019 are not available).
To me this design looks poorly justified. IWD seems to basically use the
kernel crypto API as a normal crypto library, just running with kernel
privileges. The kernel code is evidently imperfect, and IWD is basically
short-cutting any defense-in-depth measures that could have been in
place by feeding certificates presented by random APs to the kernel.
I don't want to get a kernel oops (or worse) by simply walking past by
an "evil" access point.
If history tells us that ASN.1 parsing code usually has mistakes, then
applying also the principle of least privilege I expect certificate
validation to run in a seccomp'ed child subprocess of IWD, not the
kernel.
I don't buy the comparison against signed modules offered on IRC. A random
webpage I visit cannot trivially elevate itself to root to attempt to
load a module with crafted signature. IWD's use strikes me as reckless
to put it mildly.
(there's also the "patch-the-kernel-to-use-IWD" angle, which is less
important, but still: whenever kernel crypto lib doesn't cut it, the
user has to upgrade the kernel or ditch IWD; in my adjacent thread it
happened due to leaf cert not following RFC, but it could plausibly
happen with completely valid certificates as well)
Alexander
1 year
[PATCH] client: Show WEP networks as unsupported
by Sean Anderson
WEP networks are not supported by iwd. However, the only indication is the
message "Operation not supported" while trying to connect. It is not clear
enough that this is due to intentional lack of support (as opposed to some
kind of misconfiguration). This patch explicitly lists WEP networks shown
with get-networks as unsupported. Hopefully this will make it clearer for
those of us not as familiar with iwd.
CC: James Prestwood <prestwoj(a)gmail.com>
CC: Marcel Holtmann <marcel(a)holtmann.org>
CC: Denis Kenzior <denkenz(a)gmail.com>
---
client/station.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/client/station.c b/client/station.c
index de25d12b..425b10a1 100644
--- a/client/station.c
+++ b/client/station.c
@@ -352,7 +352,7 @@ static void ordered_networks_display(struct l_queue *ordered_networks)
display_table_header("Available networks", "%s%-*s%-*s%-*s%*s",
MARGIN, 2, "", 32, "Network name",
- 10, "Security", 6, "Signal");
+ 18, "Security", 6, "Signal");
if (!l_queue_length(ordered_networks)) {
display("No networks available\n");
@@ -369,13 +369,16 @@ static void ordered_networks_display(struct l_queue *ordered_networks)
const char *network_name = network_get_name(network_i);
const char *network_type = network_get_type(network_i);
+ if (!strcmp(network_type, "wep"))
+ network_type = "wep (unsupported)";
+
if (display_signal_as_dbms)
dbms = l_strdup_printf("%d", network->signal_strength);
display("%s%-*s%-*s%-*s%-*s\n", MARGIN, 2,
network_is_connected(network_i) ?
COLOR_BOLDGRAY "> " COLOR_OFF : "",
- 32, network_name, 10, network_type,
+ 32, network_name, 18, network_type,
6, display_signal_as_dbms ? dbms :
dbms_tostars(network->signal_strength));
--
2.25.1
1 year
[PATCH] adhoc: set operstate on Start/Stop
by James Prestwood
Similar to 06aa84cca set the operstate when AdHoc is started and
stopped as it is no longer always set by netdev (only for station/p2p
interface types)
---
src/adhoc.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/src/adhoc.c b/src/adhoc.c
index 506e596a..7db405c5 100644
--- a/src/adhoc.c
+++ b/src/adhoc.c
@@ -494,6 +494,11 @@ static void adhoc_join_cb(struct netdev *netdev, int result, void *user_data)
return;
}
+ l_rtnl_set_linkmode_and_operstate(iwd_get_rtnl(),
+ netdev_get_ifindex(adhoc->netdev),
+ IF_LINK_MODE_DEFAULT, IF_OPER_UP,
+ NULL, NULL, NULL);
+
adhoc->sta_watch_id = netdev_station_watch_add(netdev,
adhoc_station_changed_cb, adhoc);
@@ -649,6 +654,11 @@ static struct l_dbus_message *adhoc_dbus_stop(struct l_dbus *dbus,
if (netdev_leave_adhoc(adhoc->netdev, adhoc_leave_cb, adhoc))
return dbus_error_failed(message);
+ l_rtnl_set_linkmode_and_operstate(iwd_get_rtnl(),
+ netdev_get_ifindex(adhoc->netdev),
+ IF_LINK_MODE_DORMANT, IF_OPER_DOWN,
+ NULL, NULL, NULL);
+
adhoc->pending = l_dbus_message_ref(message);
return NULL;
--
2.26.2
1 year
Thoughts from recent troubleshooting session
by Alexander Monakov
Hi!
Three days ago I popped in the IRC while investigating why IWD wouldn't
connect to my org's WiFi with settings I ported from wpa_supplicant's
config. We managed to narrow down the issue to kernel crypto lib failing
to validate the leaf certificate given by the AP against the CA cert
(the leaf is missing the AKID).
Troubleshooting experience seems to be very hit and miss for new users
as I'll attempt to explain below.
dmesg was telling me that iwd was associating to the AP, and then one
second later disassociating with reason 23=IEEE8021X_FAILED.
I tried running 'iwd -d' and 'IWD_TLS_DEBUG=1 iwd -d'. The first
unwelcome surprise was that there was almost zero difference in the
logs, so I had to spend a few cycles checking if I typo'ed IWD_TLS_DEBUG
vs. IWD_DEBUG_TLS or whatever.
From perusing mailing list archives I got the idea to try filling
EAP-Identity. This indeed got me to the TLS phase and I got dumped
certificates in /tmp.
Apparently, all other tools implicitly pick up configured inner identity
as the outer identity if the latter is not specified (and indeed I did
not need to specify it in wpa_supplicant Linux config, or on my phone).
The documentation is not doing a good job of explaining the situation:
it says EAP-Identity might be needed depending on RADIUS server, but it
doesn't help me realize that *even if* I did not have to specify it for
other tools, I may need to copy-paste it explicitly for IWD.
Another thing I want to highlight here is that debug spew from 'iwd -d'
seems really unstructured and it's difficult to see which phases were
completed successfully, and where things have gone off the rails.
I see a recent commit improved the situation a bit by hinting about
EAP-Identity specifically.
With dumped certificates in /tmp I could see that the AP is presenting
the expected root certificate, and 'openssl verify' successfully
validates the leaf. I spent a bit more time looking up the
certchain_verify tool in libell and figuring how to feed it PEM
certificates (this also seems improved with a recent commit, thanks!),
which narrowed down the problem to the kernel failing to validate our
certificate chain.
Thanks to another recent commit I can successfully connect with IWD if I
do not provide the CA cert in the config, but that is an undesirable
workaround.
Happy hacking.
Alexander
1 year
[PATCH] netdev: set connected to false in netdev_reassociate
by James Prestwood
Commit 1fe5070 added a workaround for drivers which may send the
connect event prior to the connect callback/ack. This caused IWD
to fail to start eapol if reassociation was used due to
netdev_reassociate never setting netdev->connected = false.
netdev_reassociate uses the same code path as normal connections,
but when the connect callback came in connected was already set
to true which then prevents eapol from being registered. Then,
once the connect event comes in, there is no frame watch for
eapol and IWD doesn't respond to any handshake frames.
---
src/netdev.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/netdev.c b/src/netdev.c
index 7a3f15e3..4fbe813a 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -3449,6 +3449,7 @@ int netdev_reassociate(struct netdev *netdev, struct scan_bss *target_bss,
netdev->associated = false;
netdev->operational = false;
+ netdev->connected = false;
netdev_rssi_polling_update(netdev);
--
2.26.2
1 year
[PATCH] auto-t: add FT-back to original AP on FT-PSK-roam
by James Prestwood
---
autotests/testFT-PSK-roam/connection_test.py | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/autotests/testFT-PSK-roam/connection_test.py b/autotests/testFT-PSK-roam/connection_test.py
index 37c6da18..7399e5e1 100644
--- a/autotests/testFT-PSK-roam/connection_test.py
+++ b/autotests/testFT-PSK-roam/connection_test.py
@@ -98,6 +98,25 @@ class Test(unittest.TestCase):
self.assertRaises(Exception, testutil.test_ifaces_connected,
(self.bss_hostapd[0].ifname, device.name))
+ rule0.signal = -2000
+ rule1.signal = -8000
+
+ condition = 'obj.state == DeviceState.roaming'
+ wd.wait_for_object_condition(device, condition)
+
+ # Check that iwd is on BSS 0 once out of roaming state and doesn't
+ # go through 'disconnected', 'autoconnect', 'connecting' in between
+ from_condition = 'obj.state == DeviceState.roaming'
+ to_condition = 'obj.state == DeviceState.connected'
+ wd.wait_for_object_change(device, from_condition, to_condition)
+
+ self.assertTrue(self.bss_hostapd[0].list_sta())
+
+ testutil.test_iface_operstate(device.name)
+ testutil.test_ifaces_connected(self.bss_hostapd[0].ifname, device.name)
+ self.assertRaises(Exception, testutil.test_ifaces_connected,
+ (self.bss_hostapd[1].ifname, device.name))
+
def tearDown(self):
os.system('ifconfig "' + self.bss_hostapd[0].ifname + '" down')
os.system('ifconfig "' + self.bss_hostapd[1].ifname + '" down')
--
2.26.2
1 year
[PATCH v6 1/3] ft: netdev: refactor FT-over-DS into two stages
by James Prestwood
FT-over-DS followed the same pattern as FT-over-Air which worked,
but really limited how the protocol could be used. FT-over-DS is
unique in that we can authenticate to many APs by sending out
FT action frames and parsing the results. Once parsed IWD can
immediately Reassociate, or do so at a later time.
To take advantage of this IWD need to separate FT-over-DS into
two stages: action frame and reassociation.
The initial action frame stage is started by netdev. The target
BSS is sent an FT action frame and a new cache entry is created
in ft.c. Once the response is received the entry is updated
with all the needed data to Reassociate. To limit the record
keeping on netdev each FT-over-DS entry holds a userdata pointer
so netdev doesn't need to maintain its own list of data for
callbacks.
Once the action response is parsed netdev will call back signalling
the action frame sequence was completed (either successfully or not).
At this point the 'normal' FT procedure can start using the
FT-over-DS auth-proto.
---
src/ft.c | 74 ++----------------
src/ft.h | 1 -
src/netdev.c | 211 ++++++++++++++++++++++++++++++++++++++++-----------
src/netdev.h | 8 ++
4 files changed, 184 insertions(+), 110 deletions(-)
diff --git a/src/ft.c b/src/ft.c
index d5f1db5b..76285d13 100644
--- a/src/ft.c
+++ b/src/ft.c
@@ -156,40 +156,6 @@ static bool ft_parse_authentication_resp_frame(const uint8_t *data, size_t len,
return true;
}
-static bool ft_parse_action_resp_frame(const uint8_t *frame, size_t frame_len,
- const uint8_t *spa, const uint8_t *aa,
- uint16_t *out_status,
- const uint8_t **out_ies,
- size_t *out_ies_len)
-{
- uint16_t status = 0;
-
- /* Category FT */
- if (frame[0] != 6)
- return false;
-
- /* FT Action */
- if (frame[1] != 2)
- return false;
-
- if (memcmp(frame + 2, spa, 6))
- return false;
- if (memcmp(frame + 8, aa, 6))
- return false;
-
- status = l_get_le16(frame + 14);
-
- if (out_status)
- *out_status = status;
-
- if (status == 0 && out_ies) {
- *out_ies = frame + 16;
- *out_ies_len = frame_len - 16;
- }
-
- return true;
-}
-
static bool ft_parse_associate_resp_frame(const uint8_t *frame, size_t frame_len,
uint16_t *out_status, const uint8_t **rsne,
const uint8_t **mde, const uint8_t **fte)
@@ -612,34 +578,6 @@ bool ft_over_ds_prepare_handshake(struct ft_ds_info *info,
return true;
}
-static int ft_rx_action(struct auth_proto *ap, const uint8_t *frame,
- size_t frame_len)
-{
- struct ft_sm *ft = l_container_of(ap, struct ft_sm, ap);
- uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
- const uint8_t *ies = NULL;
- size_t ies_len;
- int ret;
-
- if (!ft_parse_action_resp_frame(frame, frame_len, ft->hs->spa,
- ft->hs->aa, &status_code,
- &ies, &ies_len))
- return -EBADMSG;
-
- /* AP Rejected the authenticate / associate */
- if (status_code != 0)
- goto auth_error;
-
- ret = ft_process_ies(ft->hs, ies, ies_len);
- if (ret < 0)
- goto auth_error;
-
- return ft_tx_reassociate(ft);
-
-auth_error:
- return (int)status_code;
-}
-
void ft_ds_info_free(struct ft_ds_info *info)
{
__typeof__(info->free) destroy = info->free;
@@ -827,6 +765,13 @@ static void ft_sm_free(struct auth_proto *ap)
l_free(ft);
}
+static bool ft_over_ds_start(struct auth_proto *ap)
+{
+ struct ft_sm *ft = l_container_of(ap, struct ft_sm, ap);
+
+ return ft_tx_reassociate(ft) == 0;
+}
+
bool ft_build_authenticate_ies(struct handshake_state *hs,
const uint8_t *new_snonce, uint8_t *buf,
size_t *len)
@@ -938,20 +883,17 @@ struct auth_proto *ft_over_air_sm_new(struct handshake_state *hs,
}
struct auth_proto *ft_over_ds_sm_new(struct handshake_state *hs,
- ft_tx_authenticate_func_t tx_auth,
ft_tx_associate_func_t tx_assoc,
void *user_data)
{
struct ft_sm *ft = l_new(struct ft_sm, 1);
ft->tx_assoc = tx_assoc;
- ft->tx_auth = tx_auth;
ft->hs = hs;
ft->user_data = user_data;
- ft->ap.rx_authenticate = ft_rx_action;
ft->ap.rx_associate = ft_rx_associate;
- ft->ap.start = ft_start;
+ ft->ap.start = ft_over_ds_start;
ft->ap.free = ft_sm_free;
return &ft->ap;
diff --git a/src/ft.h b/src/ft.h
index 907c3d5d..7c009f16 100644
--- a/src/ft.h
+++ b/src/ft.h
@@ -57,7 +57,6 @@ struct auth_proto *ft_over_air_sm_new(struct handshake_state *hs,
void *user_data);
struct auth_proto *ft_over_ds_sm_new(struct handshake_state *hs,
- ft_tx_authenticate_func_t tx_auth,
ft_tx_associate_func_t tx_assoc,
void *user_data);
diff --git a/src/netdev.c b/src/netdev.c
index 232d098d..426d2930 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -92,6 +92,16 @@ struct netdev_handshake_state {
enum connection_type type;
};
+struct netdev_ft_over_ds_info {
+ struct ft_ds_info super;
+ struct netdev *netdev;
+ struct l_timeout *timeout;
+ netdev_ft_over_ds_cb_t cb;
+ void *user_data;
+
+ bool parsed : 1;
+};
+
struct netdev {
uint32_t index;
uint64_t wdev_id;
@@ -159,6 +169,8 @@ struct netdev {
struct l_genl_msg *auth_cmd;
struct wiphy_radio_work_item work;
+ struct netdev_ft_over_ds_info *ft_ds_info;
+
bool connected : 1;
bool associated : 1;
bool operational : 1;
@@ -741,6 +753,9 @@ static void netdev_connect_free(struct netdev *netdev)
l_genl_family_cancel(nl80211, netdev->disconnect_cmd_id);
netdev->disconnect_cmd_id = 0;
}
+
+ if (netdev->ft_ds_info)
+ ft_ds_info_free(&netdev->ft_ds_info->super);
}
static void netdev_connect_failed(struct netdev *netdev,
@@ -3424,6 +3439,12 @@ int netdev_reassociate(struct netdev *netdev, struct scan_bss *target_bss,
if (err < 0)
return err;
+ /* In case of a previous failed over-DS attempt */
+ if (netdev->ft_ds_info) {
+ ft_ds_info_free(&netdev->ft_ds_info->super);
+ netdev->ft_ds_info = NULL;
+ }
+
memcpy(netdev->prev_bssid, orig_bss->addr, ETH_ALEN);
netdev->associated = false;
@@ -3652,6 +3673,12 @@ static void netdev_ft_tx_associate(struct iovec *ie_iov, size_t iov_len,
MMPDU_STATUS_CODE_UNSPECIFIED);
return;
}
+
+ /* No need to keep this around at this point */
+ if (netdev->ft_ds_info) {
+ ft_ds_info_free(&netdev->ft_ds_info->super);
+ netdev->ft_ds_info = NULL;
+ }
}
static void prepare_ft(struct netdev *netdev, struct scan_bss *target_bss)
@@ -3719,44 +3746,54 @@ static void prepare_ft(struct netdev *netdev, struct scan_bss *target_bss)
}
}
-static void netdev_ft_request_cb(struct l_genl_msg *msg, void *user_data)
+static void netdev_ft_over_ds_auth_failed(struct netdev_ft_over_ds_info *info,
+ uint16_t status)
{
- struct netdev *netdev = user_data;
- int err = l_genl_msg_get_error(msg);
+ if (info->cb)
+ info->cb(info->netdev, status, info->super.aa, info->user_data);
- if (err < 0) {
- l_error("Could not send CMD_FRAME (%d)", err);
- netdev_connect_failed(netdev,
- NETDEV_RESULT_AUTHENTICATION_FAILED,
- MMPDU_STATUS_CODE_UNSPECIFIED);
- }
+ ft_ds_info_free(&info->super);
+
+ info->netdev->ft_ds_info = NULL;
}
static void netdev_ft_response_frame_event(const struct mmpdu_header *hdr,
const void *body, size_t body_len,
int rssi, void *user_data)
{
+ struct netdev *netdev = user_data;
+ struct netdev_ft_over_ds_info *info = netdev->ft_ds_info;
int ret;
uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
- struct netdev *netdev = user_data;
- if (!netdev->ap || !netdev->in_ft)
+ if (!info)
return;
- ret = auth_proto_rx_authenticate(netdev->ap, body, body_len);
+ ret = ft_over_ds_parse_action_response(&info->super, netdev->handshake,
+ body, body_len);
if (ret < 0)
- goto ft_error;
- else if (ret > 0) {
+ return;
+
+ l_timeout_remove(info->timeout);
+ info->timeout = NULL;
+
+ /* Now make sure the packet contained a success status code */
+ if (ret > 0) {
status_code = (uint16_t)ret;
goto ft_error;
}
+ info->parsed = true;
+
+ if (info->cb)
+ info->cb(netdev, 0, info->super.aa, info->user_data);
+
return;
ft_error:
- netdev_connect_failed(netdev, NETDEV_RESULT_AUTHENTICATION_FAILED,
- status_code);
- return;
+ l_debug("FT-over-DS to "MAC" failed", MAC_STR(info->super.aa));
+
+ netdev_ft_over_ds_auth_failed(info, status_code);
}
static void netdev_qos_map_frame_event(const struct mmpdu_header *hdr,
@@ -3781,29 +3818,6 @@ static void netdev_qos_map_frame_event(const struct mmpdu_header *hdr,
netdev_send_qos_map_set(netdev, body + 4, body_len - 4);
}
-static void netdev_ft_over_ds_tx_authenticate(struct iovec *iov,
- size_t iov_len, void *user_data)
-{
- struct netdev *netdev = user_data;
- uint8_t ft_req[14];
- struct handshake_state *hs = netdev->handshake;
- struct iovec iovs[iov_len + 1];
-
- ft_req[0] = 6; /* FT category */
- ft_req[1] = 1; /* FT Request action */
- memcpy(ft_req + 2, netdev->addr, 6);
- memcpy(ft_req + 8, hs->aa, 6);
-
- iovs[0].iov_base = ft_req;
- iovs[0].iov_len = sizeof(ft_req);
- memcpy(iovs + 1, iov, sizeof(*iov) * iov_len);
-
- netdev_send_action_framev(netdev, netdev->prev_bssid, iovs, iov_len + 1,
- netdev->prev_frequency,
- netdev_ft_request_cb,
- netdev);
-}
-
static bool netdev_ft_work_ready(struct wiphy_radio_work_item *item)
{
struct netdev *netdev = l_container_of(item, struct netdev, work);
@@ -3855,6 +3869,11 @@ int netdev_fast_transition_over_ds(struct netdev *netdev,
struct scan_bss *target_bss,
netdev_connect_cb_t cb)
{
+ struct netdev_ft_over_ds_info *info = netdev->ft_ds_info;
+
+ if (!info || !info->parsed)
+ return -ENOENT;
+
if (!netdev->operational)
return -ENOTCONN;
@@ -3865,13 +3884,13 @@ int netdev_fast_transition_over_ds(struct netdev *netdev,
prepare_ft(netdev, target_bss);
- handshake_state_new_snonce(netdev->handshake);
+ ft_over_ds_prepare_handshake(&info->super, netdev->handshake);
netdev->connect_cb = cb;
netdev->ap = ft_over_ds_sm_new(netdev->handshake,
- netdev_ft_over_ds_tx_authenticate,
- netdev_ft_tx_associate, netdev);
+ netdev_ft_tx_associate,
+ netdev);
wiphy_radio_work_insert(netdev->wiphy, &netdev->work, 1,
&ft_work_ops);
@@ -3879,6 +3898,112 @@ int netdev_fast_transition_over_ds(struct netdev *netdev,
return 0;
}
+static void netdev_ft_request_cb(struct l_genl_msg *msg, void *user_data)
+{
+ struct netdev_ft_over_ds_info *info = user_data;
+
+ if (l_genl_msg_get_error(msg) < 0) {
+ l_error("Could not send CMD_FRAME for FT-over-DS");
+ netdev_ft_over_ds_auth_failed(info,
+ MMPDU_STATUS_CODE_UNSPECIFIED);
+ }
+}
+
+static void netdev_ft_over_ds_timeout(struct l_timeout *timeout,
+ void *user_data)
+{
+ struct netdev_ft_over_ds_info *info = user_data;
+
+ l_timeout_remove(info->timeout);
+ info->timeout = NULL;
+
+ l_debug("");
+
+ netdev_ft_over_ds_auth_failed(info, MMPDU_STATUS_CODE_UNSPECIFIED);
+}
+
+static void netdev_ft_ds_info_free(struct ft_ds_info *ft)
+{
+ struct netdev_ft_over_ds_info *info = l_container_of(ft,
+ struct netdev_ft_over_ds_info, super);
+
+ if (info->timeout)
+ l_timeout_remove(info->timeout);
+
+ l_free(info);
+}
+
+int netdev_fast_transition_over_ds_action(struct netdev *netdev,
+ const struct scan_bss *target_bss,
+ netdev_ft_over_ds_cb_t cb,
+ void *user_data)
+{
+ struct netdev_ft_over_ds_info *info;
+ uint8_t ft_req[14];
+ struct handshake_state *hs = netdev->handshake;
+ struct iovec iovs[5];
+ uint8_t buf[512];
+ size_t len;
+
+ /* TODO: Just allow single outstanding action frame for now */
+ if (netdev->ft_ds_info)
+ return -EALREADY;
+
+ if (!netdev->operational)
+ return -ENOTCONN;
+
+ if (!netdev->handshake->mde || !target_bss->mde_present ||
+ l_get_le16(netdev->handshake->mde + 2) !=
+ l_get_le16(target_bss->mde))
+ return -EINVAL;
+
+ l_debug("");
+
+ info = l_new(struct netdev_ft_over_ds_info, 1);
+ info->netdev = netdev;
+
+ memcpy(info->super.spa, hs->spa, ETH_ALEN);
+ memcpy(info->super.aa, target_bss->addr, ETH_ALEN);
+ memcpy(info->super.mde, target_bss->mde, sizeof(info->super.mde));
+ l_getrandom(info->super.snonce, 32);
+ info->super.free = netdev_ft_ds_info_free;
+
+ info->cb = cb;
+ info->user_data = user_data;
+
+ ft_req[0] = 6; /* FT category */
+ ft_req[1] = 1; /* FT Request action */
+ memcpy(ft_req + 2, netdev->addr, 6);
+ memcpy(ft_req + 8, info->super.aa, 6);
+
+ iovs[0].iov_base = ft_req;
+ iovs[0].iov_len = sizeof(ft_req);
+
+ if (!ft_build_authenticate_ies(hs, info->super.snonce, buf, &len))
+ goto failed;
+
+ iovs[1].iov_base = buf;
+ iovs[1].iov_len = len;
+
+ iovs[2].iov_base = NULL;
+
+ netdev->ft_ds_info = info;
+
+ info->timeout = l_timeout_create_ms(300, netdev_ft_over_ds_timeout,
+ info, NULL);
+
+ netdev_send_action_framev(netdev, netdev->handshake->aa, iovs, 2,
+ netdev->frequency,
+ netdev_ft_request_cb,
+ info);
+
+ return 0;
+
+failed:
+ l_free(info);
+ return -EIO;
+}
+
static void netdev_preauth_cb(const uint8_t *pmk, void *user_data)
{
struct netdev_preauth_state *preauth = user_data;
diff --git a/src/netdev.h b/src/netdev.h
index 43ea3893..987504f6 100644
--- a/src/netdev.h
+++ b/src/netdev.h
@@ -125,6 +125,10 @@ typedef void (*netdev_get_station_cb_t)(
const char *netdev_iftype_to_string(uint32_t iftype);
+typedef void (*netdev_ft_over_ds_cb_t)(struct netdev *netdev,
+ uint16_t status, const uint8_t *bssid,
+ void *user_data);
+
struct wiphy *netdev_get_wiphy(struct netdev *netdev);
const uint8_t *netdev_get_address(struct netdev *netdev);
uint32_t netdev_get_ifindex(struct netdev *netdev);
@@ -158,6 +162,10 @@ int netdev_reassociate(struct netdev *netdev, struct scan_bss *target_bss,
netdev_connect_cb_t cb, void *user_data);
int netdev_fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
netdev_connect_cb_t cb);
+int netdev_fast_transition_over_ds_action(struct netdev *netdev,
+ const struct scan_bss *target_bss,
+ netdev_ft_over_ds_cb_t cb,
+ void *user_data);
int netdev_fast_transition_over_ds(struct netdev *netdev,
struct scan_bss *target_bss,
netdev_connect_cb_t cb);
--
2.26.2
1 year