2014-06-21 13:41:40 +02:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Wireless daemon for Linux
|
|
|
|
*
|
2019-10-25 00:43:08 +02:00
|
|
|
* Copyright (C) 2013-2019 Intel Corporation. All rights reserved.
|
2014-06-21 13:41:40 +02:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
|
2014-08-07 05:28:58 +02:00
|
|
|
#include <stdlib.h>
|
2018-11-01 22:37:11 +01:00
|
|
|
#include <alloca.h>
|
2018-08-20 06:12:14 +02:00
|
|
|
#include <stdio.h>
|
2014-06-21 13:41:40 +02:00
|
|
|
#include <linux/rtnetlink.h>
|
|
|
|
#include <net/if_arp.h>
|
2016-06-16 17:54:20 +02:00
|
|
|
#include <linux/if.h>
|
2016-06-16 22:23:45 +02:00
|
|
|
#include <linux/if_packet.h>
|
2016-05-31 19:57:24 +02:00
|
|
|
#include <linux/if_ether.h>
|
2018-05-01 18:08:19 +02:00
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <linux/filter.h>
|
2016-06-16 22:23:45 +02:00
|
|
|
#include <sys/socket.h>
|
2016-06-14 18:17:43 +02:00
|
|
|
#include <errno.h>
|
|
|
|
|
2014-06-21 13:41:40 +02:00
|
|
|
#include <ell/ell.h>
|
|
|
|
|
2016-06-01 22:07:16 +02:00
|
|
|
#include "linux/nl80211.h"
|
2016-06-15 17:54:13 +02:00
|
|
|
|
|
|
|
#include "src/iwd.h"
|
2019-11-07 23:33:51 +01:00
|
|
|
#include "src/module.h"
|
2014-08-07 08:52:42 +02:00
|
|
|
#include "src/wiphy.h"
|
2016-06-15 17:54:13 +02:00
|
|
|
#include "src/ie.h"
|
2016-06-16 17:54:20 +02:00
|
|
|
#include "src/mpdu.h"
|
2016-06-15 17:54:13 +02:00
|
|
|
#include "src/eapol.h"
|
2016-11-02 23:46:19 +01:00
|
|
|
#include "src/handshake.h"
|
2016-06-15 17:54:13 +02:00
|
|
|
#include "src/crypto.h"
|
2016-06-14 18:17:43 +02:00
|
|
|
#include "src/scan.h"
|
2014-06-21 20:51:26 +02:00
|
|
|
#include "src/netdev.h"
|
2019-05-07 20:53:41 +02:00
|
|
|
#include "src/ft.h"
|
2017-04-29 03:53:27 +02:00
|
|
|
#include "src/util.h"
|
2017-09-01 01:18:41 +02:00
|
|
|
#include "src/watchlist.h"
|
2018-08-14 01:25:48 +02:00
|
|
|
#include "src/sae.h"
|
2018-10-14 05:41:06 +02:00
|
|
|
#include "src/nl80211util.h"
|
2019-07-15 21:04:43 +02:00
|
|
|
#include "src/nl80211cmd.h"
|
2018-11-16 23:22:55 +01:00
|
|
|
#include "src/owe.h"
|
2019-04-22 19:11:45 +02:00
|
|
|
#include "src/fils.h"
|
2019-05-03 20:59:51 +02:00
|
|
|
#include "src/auth-proto.h"
|
2020-01-16 03:40:59 +01:00
|
|
|
#include "src/frame-xchg.h"
|
2014-06-21 20:51:26 +02:00
|
|
|
|
2017-05-30 23:55:56 +02:00
|
|
|
#ifndef ENOTSUPP
|
|
|
|
#define ENOTSUPP 524
|
|
|
|
#endif
|
|
|
|
|
2019-05-18 00:05:52 +02:00
|
|
|
static uint32_t unicast_watch;
|
|
|
|
|
2018-06-22 02:32:55 +02:00
|
|
|
struct netdev_handshake_state {
|
|
|
|
struct handshake_state super;
|
|
|
|
uint32_t pairwise_new_key_cmd_id;
|
|
|
|
uint32_t group_new_key_cmd_id;
|
|
|
|
uint32_t group_management_new_key_cmd_id;
|
|
|
|
uint32_t set_station_cmd_id;
|
netdev: signal handshake complete after setting all keys
Currently, netdev triggers the HANDSHAKE_COMPLETE event after completing
the SET_STATION (after setting the pairwise key). Depending on the timing
this may happen before the GTK/IGTK are set which will result in group
traffic not working initially (the GTK/IGTK would still get set, but group
traffic would not work immediately after DBus said you were connected, this
mainly poses a problem with autotests).
In order to fix this, several flags were added in netdev_handshake_state:
ptk_installed, gtk_installed, igtk_installed, and completed. Each of these
flags are set true when their respective keys are set, and in each key
callback we try to trigger the handshake complete event (assuming all the
flags are true). Initially the gtk/igtk flags are set to true, for reasons
explained below.
In the WPA2 case, all the key setter functions are called sequentially from
eapol. With this change, the PTK is now set AFTER the gtk/igtk. This is
because the gtk/igtk are optional and only set if group traffic is allowed.
If the gtk/igtk are not used, we set the PTK and can immediately trigger the
handshake complete event (since gtk_installed/igtk_installed are initialized
as true). When the gtk/igtk are being set, we immediately set their flags to
false and wait for their callbacks in addition to the PTK callback. Doing it
this way handles both group traffic and non group traffic paths.
WPA1 throws a wrench into this since the group keys are obtained in a
separate handshake. For this case a new flag was added to the handshake_state,
'wait_for_gtk'. This allows netdev to set the PTK after the initial 4-way,
but still wait for the gtk/igtk setters to get called before triggering the
handshake complete event. As a precaution, netdev sets a timeout that will
trigger if the gtk/igtk setters are never called. In this case we can still
complete the connection, but print a warning that group traffic will not be
allowed.
2018-10-26 18:44:58 +02:00
|
|
|
bool ptk_installed;
|
|
|
|
bool gtk_installed;
|
|
|
|
bool igtk_installed;
|
|
|
|
bool complete;
|
2018-06-22 02:32:55 +02:00
|
|
|
struct netdev *netdev;
|
|
|
|
};
|
|
|
|
|
2016-06-01 22:26:02 +02:00
|
|
|
struct netdev {
|
|
|
|
uint32_t index;
|
2019-07-08 16:02:58 +02:00
|
|
|
uint64_t wdev_id;
|
2016-06-01 22:26:02 +02:00
|
|
|
char name[IFNAMSIZ];
|
|
|
|
uint32_t type;
|
|
|
|
uint8_t addr[ETH_ALEN];
|
2016-07-13 04:29:52 +02:00
|
|
|
struct wiphy *wiphy;
|
2016-07-13 04:26:27 +02:00
|
|
|
unsigned int ifi_flags;
|
2016-12-23 11:38:01 +01:00
|
|
|
uint32_t frequency;
|
2019-05-09 20:16:28 +02:00
|
|
|
uint32_t prev_frequency;
|
2016-06-16 22:23:45 +02:00
|
|
|
|
2016-06-14 18:17:43 +02:00
|
|
|
netdev_event_func_t event_filter;
|
|
|
|
netdev_connect_cb_t connect_cb;
|
2016-06-16 23:05:34 +02:00
|
|
|
netdev_disconnect_cb_t disconnect_cb;
|
2016-12-23 11:38:01 +01:00
|
|
|
netdev_neighbor_report_cb_t neighbor_report_cb;
|
2018-08-20 01:25:23 +02:00
|
|
|
netdev_command_cb_t adhoc_cb;
|
2016-06-14 18:17:43 +02:00
|
|
|
void *user_data;
|
2016-06-15 17:54:13 +02:00
|
|
|
struct eapol_sm *sm;
|
2018-08-14 01:25:48 +02:00
|
|
|
struct sae_sm *sae_sm;
|
2019-05-03 20:59:51 +02:00
|
|
|
struct auth_proto *ap;
|
2016-11-02 23:46:19 +01:00
|
|
|
struct handshake_state *handshake;
|
2016-08-05 14:25:34 +02:00
|
|
|
uint32_t connect_cmd_id;
|
2016-09-21 22:20:51 +02:00
|
|
|
uint32_t disconnect_cmd_id;
|
2018-07-17 23:15:59 +02:00
|
|
|
uint32_t join_adhoc_cmd_id;
|
|
|
|
uint32_t leave_adhoc_cmd_id;
|
2018-08-20 05:07:15 +02:00
|
|
|
uint32_t set_interface_cmd_id;
|
2018-10-19 23:19:26 +02:00
|
|
|
uint32_t rekey_offload_cmd_id;
|
2019-09-14 00:26:31 +02:00
|
|
|
uint32_t qos_map_cmd_id;
|
2020-03-18 19:54:19 +01:00
|
|
|
uint32_t mac_change_cmd_id;
|
2016-09-21 23:19:47 +02:00
|
|
|
enum netdev_result result;
|
2019-02-28 18:32:40 +01:00
|
|
|
uint16_t last_code; /* reason or status, depending on result */
|
2016-12-23 11:38:01 +01:00
|
|
|
struct l_timeout *neighbor_report_timeout;
|
2018-02-01 18:22:39 +01:00
|
|
|
struct l_timeout *sa_query_timeout;
|
netdev: signal handshake complete after setting all keys
Currently, netdev triggers the HANDSHAKE_COMPLETE event after completing
the SET_STATION (after setting the pairwise key). Depending on the timing
this may happen before the GTK/IGTK are set which will result in group
traffic not working initially (the GTK/IGTK would still get set, but group
traffic would not work immediately after DBus said you were connected, this
mainly poses a problem with autotests).
In order to fix this, several flags were added in netdev_handshake_state:
ptk_installed, gtk_installed, igtk_installed, and completed. Each of these
flags are set true when their respective keys are set, and in each key
callback we try to trigger the handshake complete event (assuming all the
flags are true). Initially the gtk/igtk flags are set to true, for reasons
explained below.
In the WPA2 case, all the key setter functions are called sequentially from
eapol. With this change, the PTK is now set AFTER the gtk/igtk. This is
because the gtk/igtk are optional and only set if group traffic is allowed.
If the gtk/igtk are not used, we set the PTK and can immediately trigger the
handshake complete event (since gtk_installed/igtk_installed are initialized
as true). When the gtk/igtk are being set, we immediately set their flags to
false and wait for their callbacks in addition to the PTK callback. Doing it
this way handles both group traffic and non group traffic paths.
WPA1 throws a wrench into this since the group keys are obtained in a
separate handshake. For this case a new flag was added to the handshake_state,
'wait_for_gtk'. This allows netdev to set the PTK after the initial 4-way,
but still wait for the gtk/igtk setters to get called before triggering the
handshake complete event. As a precaution, netdev sets a timeout that will
trigger if the gtk/igtk setters are never called. In this case we can still
complete the connection, but print a warning that group traffic will not be
allowed.
2018-10-26 18:44:58 +02:00
|
|
|
struct l_timeout *group_handshake_timeout;
|
2019-06-14 23:56:13 +02:00
|
|
|
struct l_timeout *gas_timeout;
|
2018-02-01 18:22:39 +01:00
|
|
|
uint16_t sa_query_id;
|
2017-01-12 09:36:00 +01:00
|
|
|
uint8_t prev_bssid[ETH_ALEN];
|
2019-05-07 20:53:42 +02:00
|
|
|
uint8_t prev_snonce[32];
|
2017-05-20 16:33:13 +02:00
|
|
|
int8_t rssi_levels[16];
|
2017-05-30 18:31:14 +02:00
|
|
|
uint8_t rssi_levels_num;
|
2017-05-20 16:33:13 +02:00
|
|
|
uint8_t cur_rssi_level_idx;
|
|
|
|
int8_t cur_rssi;
|
2017-08-18 14:22:58 +02:00
|
|
|
struct l_timeout *rssi_poll_timeout;
|
|
|
|
uint32_t rssi_poll_cmd_id;
|
2019-12-19 04:50:56 +01:00
|
|
|
uint8_t set_mac_once[6];
|
2016-06-20 12:42:04 +02:00
|
|
|
|
2018-08-17 19:37:47 +02:00
|
|
|
uint32_t set_powered_cmd_id;
|
2018-08-20 01:25:23 +02:00
|
|
|
netdev_command_cb_t set_powered_cb;
|
2018-08-17 19:37:47 +02:00
|
|
|
void *set_powered_user_data;
|
|
|
|
netdev_destroy_func_t set_powered_destroy;
|
|
|
|
|
2018-07-17 00:29:15 +02:00
|
|
|
struct watchlist station_watches;
|
|
|
|
|
2018-05-01 18:08:19 +02:00
|
|
|
struct l_io *pae_io; /* for drivers without EAPoL over NL80211 */
|
|
|
|
|
2020-07-15 23:29:17 +02:00
|
|
|
struct l_genl_msg *connect_cmd;
|
|
|
|
struct wiphy_radio_work_item work;
|
|
|
|
|
2016-07-20 00:45:48 +02:00
|
|
|
bool connected : 1;
|
2016-09-22 22:55:07 +02:00
|
|
|
bool operational : 1;
|
2016-06-23 00:49:16 +02:00
|
|
|
bool rekey_offload_support : 1;
|
2018-07-02 03:35:22 +02:00
|
|
|
bool pae_over_nl80211 : 1;
|
2017-01-12 09:36:00 +01:00
|
|
|
bool in_ft : 1;
|
2017-05-20 16:33:13 +02:00
|
|
|
bool cur_rssi_low : 1;
|
2018-06-14 04:11:23 +02:00
|
|
|
bool use_4addr : 1;
|
netdev: process association in netdev_associate_event
Apart from OWE, the association event was disregarded and all association
processing was done in netdev_connect_event. This led to
netdev_connect_event having to handle all the logic of both success and
failure, as well as parsing the association for FT and OWE. Also, without
checking the status code in the associate frame there is the potential
for the kernel to think we are connected even if association failed
(e.g. rogue AP).
This change introduces two flags into netdev, expect_connect_failure and
ignore_connect_event. All the FT processing that was once in
netdev_connect_event has now been moved into netdev_associate_event, as
well as non-FT associate frame processing. The connect event now only
handles failure cases for soft/half MAC cards.
Note: Since fullmac cards rely on the connect event, the eapol_start
and netdev_connect_ok were left in netdev_connect_event. Since neither
auth/assoc events come in on fullmac we shouldn't have any conflict with
the new flags.
Once a connection has completed association, EAPoL is started from
netdev_associate_event (if required) and the ignore_connect_event flag can
be set. This will bypass the connect event.
If a connection has failed during association for whatever reason, we can
set expect_connect_failure, the netdev reason, and the MPDU status code.
This allows netdev_connect_event to both handle the error, and, if required,
send a deauth telling the kernel that we have failed (protecting against the
rogue AP situation).
2019-03-05 22:42:33 +01:00
|
|
|
bool ignore_connect_event : 1;
|
|
|
|
bool expect_connect_failure : 1;
|
2019-03-08 23:09:42 +01:00
|
|
|
bool aborting : 1;
|
2019-11-20 23:23:42 +01:00
|
|
|
bool events_ready : 1;
|
2016-06-20 12:42:04 +02:00
|
|
|
};
|
|
|
|
|
2017-04-29 03:53:27 +02:00
|
|
|
struct netdev_preauth_state {
|
|
|
|
netdev_preauthenticate_cb_t cb;
|
|
|
|
void *user_data;
|
|
|
|
struct netdev *netdev;
|
|
|
|
};
|
|
|
|
|
2016-06-20 12:42:04 +02:00
|
|
|
struct netdev_watch {
|
|
|
|
uint32_t id;
|
|
|
|
netdev_watch_func_t callback;
|
|
|
|
void *user_data;
|
2016-06-01 22:26:02 +02:00
|
|
|
};
|
|
|
|
|
2014-06-21 13:41:40 +02:00
|
|
|
static struct l_netlink *rtnl = NULL;
|
2016-06-01 21:58:51 +02:00
|
|
|
static struct l_genl_family *nl80211;
|
2016-06-01 22:35:26 +02:00
|
|
|
static struct l_queue *netdev_list;
|
2018-08-18 06:40:49 +02:00
|
|
|
static struct watchlist netdev_watches;
|
2019-10-25 04:36:03 +02:00
|
|
|
static bool pae_over_nl80211;
|
2020-03-18 19:54:19 +01:00
|
|
|
static bool mac_per_ssid;
|
2014-06-21 13:41:40 +02:00
|
|
|
|
2018-10-20 17:35:28 +02:00
|
|
|
/* Cancels ongoing GTK/IGTK related commands (if any) */
|
|
|
|
static void netdev_handshake_state_cancel_rekey(
|
|
|
|
struct netdev_handshake_state *nhs)
|
2018-06-22 02:32:55 +02:00
|
|
|
{
|
|
|
|
if (nhs->group_new_key_cmd_id) {
|
|
|
|
l_genl_family_cancel(nl80211, nhs->group_new_key_cmd_id);
|
|
|
|
nhs->group_new_key_cmd_id = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nhs->group_management_new_key_cmd_id) {
|
|
|
|
l_genl_family_cancel(nl80211,
|
|
|
|
nhs->group_management_new_key_cmd_id);
|
|
|
|
nhs->group_management_new_key_cmd_id = 0;
|
|
|
|
}
|
2018-10-20 17:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_handshake_state_cancel_all(
|
|
|
|
struct netdev_handshake_state *nhs)
|
|
|
|
{
|
|
|
|
if (nhs->pairwise_new_key_cmd_id) {
|
|
|
|
l_genl_family_cancel(nl80211, nhs->pairwise_new_key_cmd_id);
|
|
|
|
nhs->pairwise_new_key_cmd_id = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
netdev_handshake_state_cancel_rekey(nhs);
|
2018-06-22 02:32:55 +02:00
|
|
|
|
|
|
|
if (nhs->set_station_cmd_id) {
|
|
|
|
l_genl_family_cancel(nl80211, nhs->set_station_cmd_id);
|
|
|
|
nhs->set_station_cmd_id = 0;
|
|
|
|
}
|
2018-10-20 17:35:28 +02:00
|
|
|
}
|
2018-06-22 02:32:55 +02:00
|
|
|
|
2018-10-20 17:35:28 +02:00
|
|
|
static void netdev_handshake_state_free(struct handshake_state *hs)
|
|
|
|
{
|
|
|
|
struct netdev_handshake_state *nhs =
|
2019-04-03 18:47:09 +02:00
|
|
|
l_container_of(hs, struct netdev_handshake_state, super);
|
2018-10-20 17:35:28 +02:00
|
|
|
|
|
|
|
netdev_handshake_state_cancel_all(nhs);
|
2018-06-22 02:32:55 +02:00
|
|
|
l_free(nhs);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct handshake_state *netdev_handshake_state_new(struct netdev *netdev)
|
|
|
|
{
|
|
|
|
struct netdev_handshake_state *nhs;
|
|
|
|
|
|
|
|
nhs = l_new(struct netdev_handshake_state, 1);
|
|
|
|
|
|
|
|
nhs->super.ifindex = netdev->index;
|
|
|
|
nhs->super.free = netdev_handshake_state_free;
|
|
|
|
|
|
|
|
nhs->netdev = netdev;
|
netdev: signal handshake complete after setting all keys
Currently, netdev triggers the HANDSHAKE_COMPLETE event after completing
the SET_STATION (after setting the pairwise key). Depending on the timing
this may happen before the GTK/IGTK are set which will result in group
traffic not working initially (the GTK/IGTK would still get set, but group
traffic would not work immediately after DBus said you were connected, this
mainly poses a problem with autotests).
In order to fix this, several flags were added in netdev_handshake_state:
ptk_installed, gtk_installed, igtk_installed, and completed. Each of these
flags are set true when their respective keys are set, and in each key
callback we try to trigger the handshake complete event (assuming all the
flags are true). Initially the gtk/igtk flags are set to true, for reasons
explained below.
In the WPA2 case, all the key setter functions are called sequentially from
eapol. With this change, the PTK is now set AFTER the gtk/igtk. This is
because the gtk/igtk are optional and only set if group traffic is allowed.
If the gtk/igtk are not used, we set the PTK and can immediately trigger the
handshake complete event (since gtk_installed/igtk_installed are initialized
as true). When the gtk/igtk are being set, we immediately set their flags to
false and wait for their callbacks in addition to the PTK callback. Doing it
this way handles both group traffic and non group traffic paths.
WPA1 throws a wrench into this since the group keys are obtained in a
separate handshake. For this case a new flag was added to the handshake_state,
'wait_for_gtk'. This allows netdev to set the PTK after the initial 4-way,
but still wait for the gtk/igtk setters to get called before triggering the
handshake complete event. As a precaution, netdev sets a timeout that will
trigger if the gtk/igtk setters are never called. In this case we can still
complete the connection, but print a warning that group traffic will not be
allowed.
2018-10-26 18:44:58 +02:00
|
|
|
/*
|
|
|
|
* Since GTK/IGTK are optional (NO_GROUP_TRAFFIC), we set them as
|
2020-01-21 07:21:38 +01:00
|
|
|
* 'installed' upon initialization. If/When the gtk/igtk callback is
|
netdev: signal handshake complete after setting all keys
Currently, netdev triggers the HANDSHAKE_COMPLETE event after completing
the SET_STATION (after setting the pairwise key). Depending on the timing
this may happen before the GTK/IGTK are set which will result in group
traffic not working initially (the GTK/IGTK would still get set, but group
traffic would not work immediately after DBus said you were connected, this
mainly poses a problem with autotests).
In order to fix this, several flags were added in netdev_handshake_state:
ptk_installed, gtk_installed, igtk_installed, and completed. Each of these
flags are set true when their respective keys are set, and in each key
callback we try to trigger the handshake complete event (assuming all the
flags are true). Initially the gtk/igtk flags are set to true, for reasons
explained below.
In the WPA2 case, all the key setter functions are called sequentially from
eapol. With this change, the PTK is now set AFTER the gtk/igtk. This is
because the gtk/igtk are optional and only set if group traffic is allowed.
If the gtk/igtk are not used, we set the PTK and can immediately trigger the
handshake complete event (since gtk_installed/igtk_installed are initialized
as true). When the gtk/igtk are being set, we immediately set their flags to
false and wait for their callbacks in addition to the PTK callback. Doing it
this way handles both group traffic and non group traffic paths.
WPA1 throws a wrench into this since the group keys are obtained in a
separate handshake. For this case a new flag was added to the handshake_state,
'wait_for_gtk'. This allows netdev to set the PTK after the initial 4-way,
but still wait for the gtk/igtk setters to get called before triggering the
handshake complete event. As a precaution, netdev sets a timeout that will
trigger if the gtk/igtk setters are never called. In this case we can still
complete the connection, but print a warning that group traffic will not be
allowed.
2018-10-26 18:44:58 +02:00
|
|
|
* called they will get set to false until we have received a successful
|
|
|
|
* callback from nl80211. From these callbacks we can check that all
|
|
|
|
* the keys have been installed, and only then trigger the handshake
|
|
|
|
* complete callback.
|
|
|
|
*/
|
|
|
|
nhs->gtk_installed = true;
|
|
|
|
nhs->igtk_installed = true;
|
2018-06-22 02:32:55 +02:00
|
|
|
|
|
|
|
return &nhs->super;
|
|
|
|
}
|
|
|
|
|
2018-05-24 19:12:09 +02:00
|
|
|
struct wiphy *netdev_get_wiphy(struct netdev *netdev)
|
|
|
|
{
|
|
|
|
return netdev->wiphy;
|
|
|
|
}
|
|
|
|
|
2016-06-01 22:27:39 +02:00
|
|
|
const uint8_t *netdev_get_address(struct netdev *netdev)
|
|
|
|
{
|
|
|
|
return netdev->addr;
|
|
|
|
}
|
|
|
|
|
2016-06-01 22:27:05 +02:00
|
|
|
uint32_t netdev_get_ifindex(struct netdev *netdev)
|
|
|
|
{
|
|
|
|
return netdev->index;
|
|
|
|
}
|
|
|
|
|
2019-07-08 16:02:58 +02:00
|
|
|
uint64_t netdev_get_wdev_id(struct netdev *netdev)
|
|
|
|
{
|
|
|
|
return netdev->wdev_id;
|
|
|
|
}
|
|
|
|
|
2017-08-14 14:49:16 +02:00
|
|
|
enum netdev_iftype netdev_get_iftype(struct netdev *netdev)
|
2016-06-02 00:04:10 +02:00
|
|
|
{
|
2018-07-17 00:29:14 +02:00
|
|
|
switch (netdev->type) {
|
|
|
|
case NL80211_IFTYPE_STATION:
|
|
|
|
return NETDEV_IFTYPE_STATION;
|
|
|
|
case NL80211_IFTYPE_AP:
|
|
|
|
return NETDEV_IFTYPE_AP;
|
|
|
|
case NL80211_IFTYPE_ADHOC:
|
|
|
|
return NETDEV_IFTYPE_ADHOC;
|
2019-03-11 15:43:21 +01:00
|
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
|
|
|
return NETDEV_IFTYPE_P2P_CLIENT;
|
|
|
|
case NL80211_IFTYPE_P2P_GO:
|
|
|
|
return NETDEV_IFTYPE_P2P_GO;
|
2018-07-17 00:29:14 +02:00
|
|
|
default:
|
2019-03-11 15:43:21 +01:00
|
|
|
/* can't really do much here */
|
|
|
|
l_error("unknown iftype %u", netdev->type);
|
2018-07-17 00:29:14 +02:00
|
|
|
return NETDEV_IFTYPE_STATION;
|
|
|
|
}
|
2016-06-02 00:04:10 +02:00
|
|
|
}
|
|
|
|
|
2016-06-02 00:05:56 +02:00
|
|
|
const char *netdev_get_name(struct netdev *netdev)
|
|
|
|
{
|
|
|
|
return netdev->name;
|
|
|
|
}
|
|
|
|
|
2016-06-20 12:42:03 +02:00
|
|
|
bool netdev_get_is_up(struct netdev *netdev)
|
|
|
|
{
|
2020-03-18 19:54:19 +01:00
|
|
|
bool powered = (netdev->ifi_flags & IFF_UP) != 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we are in the middle of changing the MAC we are in somewhat of a
|
|
|
|
* no mans land. Technically the iface may be down, but since we are
|
|
|
|
* not emitting any netdev DOWN events we want netdev_get_is_up to
|
|
|
|
* reflect the same state. Once MAC changing finishes any pending
|
|
|
|
* DOWN events will be emitted.
|
|
|
|
*/
|
|
|
|
if (netdev->mac_change_cmd_id && !powered)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return powered;
|
2016-07-13 04:26:27 +02:00
|
|
|
}
|
|
|
|
|
2016-12-23 11:38:07 +01:00
|
|
|
struct handshake_state *netdev_get_handshake(struct netdev *netdev)
|
|
|
|
{
|
|
|
|
return netdev->handshake;
|
|
|
|
}
|
|
|
|
|
2018-08-20 06:12:14 +02:00
|
|
|
const char *netdev_get_path(struct netdev *netdev)
|
|
|
|
{
|
2019-10-28 17:32:57 +01:00
|
|
|
static char path[256];
|
|
|
|
|
|
|
|
L_WARN_ON(snprintf(path, sizeof(path), "%s/%u",
|
|
|
|
wiphy_get_path(netdev->wiphy),
|
|
|
|
netdev->index) >= (int) sizeof(path));
|
|
|
|
path[sizeof(path) - 1] = '\0';
|
2018-08-20 06:12:14 +02:00
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2016-07-13 04:26:27 +02:00
|
|
|
static void netdev_set_powered_result(int error, uint16_t type,
|
|
|
|
const void *data,
|
|
|
|
uint32_t len, void *user_data)
|
|
|
|
{
|
2018-08-17 19:37:47 +02:00
|
|
|
struct netdev *netdev = user_data;
|
2016-07-13 04:26:27 +02:00
|
|
|
|
2018-08-17 19:37:47 +02:00
|
|
|
if (netdev->set_powered_cb)
|
|
|
|
netdev->set_powered_cb(netdev, error,
|
|
|
|
netdev->set_powered_user_data);
|
2016-07-13 04:26:27 +02:00
|
|
|
|
2018-08-17 19:37:47 +02:00
|
|
|
netdev->set_powered_cb = NULL;
|
2016-07-13 04:26:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_set_powered_destroy(void *user_data)
|
|
|
|
{
|
2018-08-17 19:37:47 +02:00
|
|
|
struct netdev *netdev = user_data;
|
2016-07-13 04:26:27 +02:00
|
|
|
|
2018-08-17 19:37:47 +02:00
|
|
|
netdev->set_powered_cmd_id = 0;
|
2016-07-13 04:26:27 +02:00
|
|
|
|
2018-08-17 19:37:47 +02:00
|
|
|
if (netdev->set_powered_destroy)
|
|
|
|
netdev->set_powered_destroy(netdev->set_powered_user_data);
|
2016-07-13 04:26:27 +02:00
|
|
|
|
2018-08-17 19:37:47 +02:00
|
|
|
netdev->set_powered_destroy = NULL;
|
|
|
|
netdev->set_powered_user_data = NULL;
|
2016-07-13 04:26:27 +02:00
|
|
|
}
|
|
|
|
|
2018-08-17 19:37:47 +02:00
|
|
|
int netdev_set_powered(struct netdev *netdev, bool powered,
|
2018-08-20 01:25:23 +02:00
|
|
|
netdev_command_cb_t callback, void *user_data,
|
2018-08-17 19:37:47 +02:00
|
|
|
netdev_destroy_func_t destroy)
|
|
|
|
{
|
2018-09-25 17:43:47 +02:00
|
|
|
if (netdev->set_powered_cmd_id ||
|
|
|
|
netdev->set_interface_cmd_id)
|
2018-08-17 19:37:47 +02:00
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
netdev->set_powered_cmd_id =
|
2020-02-15 10:48:39 +01:00
|
|
|
l_rtnl_set_powered(rtnl, netdev->index, powered,
|
2018-08-17 19:37:47 +02:00
|
|
|
netdev_set_powered_result, netdev,
|
|
|
|
netdev_set_powered_destroy);
|
|
|
|
if (!netdev->set_powered_cmd_id)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
netdev->set_powered_cb = callback;
|
|
|
|
netdev->set_powered_user_data = user_data;
|
|
|
|
netdev->set_powered_destroy = destroy;
|
2016-07-13 04:26:27 +02:00
|
|
|
|
|
|
|
return 0;
|
2016-06-20 12:42:03 +02:00
|
|
|
}
|
|
|
|
|
2017-08-18 14:22:58 +02:00
|
|
|
static void netdev_set_rssi_level_idx(struct netdev *netdev)
|
|
|
|
{
|
|
|
|
uint8_t new_level;
|
|
|
|
|
|
|
|
for (new_level = 0; new_level < netdev->rssi_levels_num; new_level++)
|
|
|
|
if (netdev->cur_rssi >= netdev->rssi_levels[new_level])
|
|
|
|
break;
|
|
|
|
|
|
|
|
netdev->cur_rssi_level_idx = new_level;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_rssi_poll_cb(struct l_genl_msg *msg, void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
struct l_genl_attr attr, nested;
|
|
|
|
uint16_t type, len;
|
|
|
|
const void *data;
|
|
|
|
bool found;
|
|
|
|
uint8_t prev_rssi_level_idx = netdev->cur_rssi_level_idx;
|
|
|
|
|
|
|
|
netdev->rssi_poll_cmd_id = 0;
|
|
|
|
|
|
|
|
if (!l_genl_attr_init(&attr, msg))
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
found = false;
|
|
|
|
while (l_genl_attr_next(&attr, &type, &len, &data)) {
|
|
|
|
if (type != NL80211_ATTR_STA_INFO)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found || !l_genl_attr_recurse(&attr, &nested))
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
found = false;
|
|
|
|
while (l_genl_attr_next(&nested, &type, &len, &data)) {
|
|
|
|
if (type != NL80211_STA_INFO_SIGNAL_AVG)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (len != 1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
found = true;
|
|
|
|
netdev->cur_rssi = *(const int8_t *) data;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note we don't have to handle LOW_SIGNAL_THRESHOLD here. The
|
|
|
|
* CQM single threshold RSSI monitoring should work even if the
|
|
|
|
* kernel driver doesn't support multiple thresholds. So the
|
|
|
|
* polling only handles the client-supplied threshold list.
|
|
|
|
*/
|
|
|
|
netdev_set_rssi_level_idx(netdev);
|
|
|
|
if (netdev->cur_rssi_level_idx != prev_rssi_level_idx)
|
|
|
|
netdev->event_filter(netdev, NETDEV_EVENT_RSSI_LEVEL_NOTIFY,
|
2019-03-01 00:41:51 +01:00
|
|
|
&netdev->cur_rssi_level_idx,
|
2017-08-18 14:22:58 +02:00
|
|
|
netdev->user_data);
|
|
|
|
|
|
|
|
done:
|
|
|
|
/* Rearm timer */
|
|
|
|
l_timeout_modify(netdev->rssi_poll_timeout, 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_rssi_poll(struct l_timeout *timeout, void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
|
|
|
|
msg = l_genl_msg_new_sized(NL80211_CMD_GET_STATION, 64);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN,
|
|
|
|
netdev->handshake->aa);
|
|
|
|
|
|
|
|
netdev->rssi_poll_cmd_id = l_genl_family_send(nl80211, msg,
|
|
|
|
netdev_rssi_poll_cb,
|
|
|
|
netdev, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* To be called whenever operational or rssi_levels_num are updated */
|
|
|
|
static void netdev_rssi_polling_update(struct netdev *netdev)
|
|
|
|
{
|
2018-05-24 20:12:36 +02:00
|
|
|
if (wiphy_has_ext_feature(netdev->wiphy,
|
2017-08-18 14:22:58 +02:00
|
|
|
NL80211_EXT_FEATURE_CQM_RSSI_LIST))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (netdev->operational && netdev->rssi_levels_num > 0) {
|
|
|
|
if (netdev->rssi_poll_timeout)
|
|
|
|
return;
|
|
|
|
|
|
|
|
netdev->rssi_poll_timeout =
|
|
|
|
l_timeout_create(1, netdev_rssi_poll, netdev, NULL);
|
|
|
|
} else {
|
|
|
|
if (!netdev->rssi_poll_timeout)
|
|
|
|
return;
|
|
|
|
|
|
|
|
l_timeout_remove(netdev->rssi_poll_timeout);
|
|
|
|
netdev->rssi_poll_timeout = NULL;
|
|
|
|
|
|
|
|
if (netdev->rssi_poll_cmd_id) {
|
|
|
|
l_genl_family_cancel(nl80211, netdev->rssi_poll_cmd_id);
|
|
|
|
netdev->rssi_poll_cmd_id = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-29 03:53:27 +02:00
|
|
|
static void netdev_preauth_destroy(void *data)
|
|
|
|
{
|
|
|
|
struct netdev_preauth_state *state = data;
|
|
|
|
|
|
|
|
if (state->cb)
|
|
|
|
state->cb(state->netdev, NETDEV_RESULT_ABORTED, NULL,
|
|
|
|
state->user_data);
|
|
|
|
|
|
|
|
l_free(state);
|
|
|
|
}
|
|
|
|
|
2016-08-02 22:48:11 +02:00
|
|
|
static void netdev_connect_free(struct netdev *netdev)
|
|
|
|
{
|
2020-07-15 23:29:17 +02:00
|
|
|
if (netdev->work.id)
|
|
|
|
wiphy_radio_work_done(netdev->wiphy, netdev->work.id);
|
|
|
|
|
2016-08-02 22:48:11 +02:00
|
|
|
if (netdev->sm) {
|
|
|
|
eapol_sm_free(netdev->sm);
|
|
|
|
netdev->sm = NULL;
|
|
|
|
}
|
|
|
|
|
2019-05-03 20:59:51 +02:00
|
|
|
if (netdev->ap) {
|
|
|
|
auth_proto_free(netdev->ap);
|
|
|
|
netdev->ap = NULL;
|
|
|
|
}
|
|
|
|
|
2017-04-29 03:53:27 +02:00
|
|
|
eapol_preauth_cancel(netdev->index);
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
if (netdev->handshake) {
|
|
|
|
handshake_state_free(netdev->handshake);
|
|
|
|
netdev->handshake = NULL;
|
2016-08-23 20:15:00 +02:00
|
|
|
}
|
2016-08-02 22:48:11 +02:00
|
|
|
|
2017-01-23 12:38:40 +01:00
|
|
|
if (netdev->neighbor_report_cb) {
|
|
|
|
netdev->neighbor_report_cb(netdev, -ENOTCONN, NULL, 0,
|
|
|
|
netdev->user_data);
|
|
|
|
netdev->neighbor_report_cb = NULL;
|
|
|
|
l_timeout_remove(netdev->neighbor_report_timeout);
|
|
|
|
}
|
|
|
|
|
2018-02-01 18:22:39 +01:00
|
|
|
if (netdev->sa_query_timeout) {
|
|
|
|
l_timeout_remove(netdev->sa_query_timeout);
|
|
|
|
netdev->sa_query_timeout = NULL;
|
|
|
|
}
|
|
|
|
|
netdev: signal handshake complete after setting all keys
Currently, netdev triggers the HANDSHAKE_COMPLETE event after completing
the SET_STATION (after setting the pairwise key). Depending on the timing
this may happen before the GTK/IGTK are set which will result in group
traffic not working initially (the GTK/IGTK would still get set, but group
traffic would not work immediately after DBus said you were connected, this
mainly poses a problem with autotests).
In order to fix this, several flags were added in netdev_handshake_state:
ptk_installed, gtk_installed, igtk_installed, and completed. Each of these
flags are set true when their respective keys are set, and in each key
callback we try to trigger the handshake complete event (assuming all the
flags are true). Initially the gtk/igtk flags are set to true, for reasons
explained below.
In the WPA2 case, all the key setter functions are called sequentially from
eapol. With this change, the PTK is now set AFTER the gtk/igtk. This is
because the gtk/igtk are optional and only set if group traffic is allowed.
If the gtk/igtk are not used, we set the PTK and can immediately trigger the
handshake complete event (since gtk_installed/igtk_installed are initialized
as true). When the gtk/igtk are being set, we immediately set their flags to
false and wait for their callbacks in addition to the PTK callback. Doing it
this way handles both group traffic and non group traffic paths.
WPA1 throws a wrench into this since the group keys are obtained in a
separate handshake. For this case a new flag was added to the handshake_state,
'wait_for_gtk'. This allows netdev to set the PTK after the initial 4-way,
but still wait for the gtk/igtk setters to get called before triggering the
handshake complete event. As a precaution, netdev sets a timeout that will
trigger if the gtk/igtk setters are never called. In this case we can still
complete the connection, but print a warning that group traffic will not be
allowed.
2018-10-26 18:44:58 +02:00
|
|
|
if (netdev->group_handshake_timeout) {
|
|
|
|
l_timeout_remove(netdev->group_handshake_timeout);
|
|
|
|
netdev->group_handshake_timeout = NULL;
|
|
|
|
}
|
|
|
|
|
2016-09-22 22:55:07 +02:00
|
|
|
netdev->operational = false;
|
2016-08-02 22:48:11 +02:00
|
|
|
netdev->connected = false;
|
|
|
|
netdev->connect_cb = NULL;
|
|
|
|
netdev->event_filter = NULL;
|
2016-09-21 23:19:47 +02:00
|
|
|
netdev->user_data = NULL;
|
|
|
|
netdev->result = NETDEV_RESULT_OK;
|
2019-02-28 18:32:40 +01:00
|
|
|
netdev->last_code = 0;
|
2017-01-12 09:36:00 +01:00
|
|
|
netdev->in_ft = false;
|
netdev: process association in netdev_associate_event
Apart from OWE, the association event was disregarded and all association
processing was done in netdev_connect_event. This led to
netdev_connect_event having to handle all the logic of both success and
failure, as well as parsing the association for FT and OWE. Also, without
checking the status code in the associate frame there is the potential
for the kernel to think we are connected even if association failed
(e.g. rogue AP).
This change introduces two flags into netdev, expect_connect_failure and
ignore_connect_event. All the FT processing that was once in
netdev_connect_event has now been moved into netdev_associate_event, as
well as non-FT associate frame processing. The connect event now only
handles failure cases for soft/half MAC cards.
Note: Since fullmac cards rely on the connect event, the eapol_start
and netdev_connect_ok were left in netdev_connect_event. Since neither
auth/assoc events come in on fullmac we shouldn't have any conflict with
the new flags.
Once a connection has completed association, EAPoL is started from
netdev_associate_event (if required) and the ignore_connect_event flag can
be set. This will bypass the connect event.
If a connection has failed during association for whatever reason, we can
set expect_connect_failure, the netdev reason, and the MPDU status code.
This allows netdev_connect_event to both handle the error, and, if required,
send a deauth telling the kernel that we have failed (protecting against the
rogue AP situation).
2019-03-05 22:42:33 +01:00
|
|
|
netdev->ignore_connect_event = false;
|
|
|
|
netdev->expect_connect_failure = false;
|
2016-08-02 22:48:11 +02:00
|
|
|
|
2020-07-15 23:29:17 +02:00
|
|
|
if (netdev->connect_cmd) {
|
|
|
|
l_genl_msg_unref(netdev->connect_cmd);
|
|
|
|
netdev->connect_cmd = NULL;
|
|
|
|
}
|
|
|
|
|
2017-08-18 14:22:58 +02:00
|
|
|
netdev_rssi_polling_update(netdev);
|
|
|
|
|
2016-08-05 14:25:34 +02:00
|
|
|
if (netdev->connect_cmd_id) {
|
|
|
|
l_genl_family_cancel(nl80211, netdev->connect_cmd_id);
|
|
|
|
netdev->connect_cmd_id = 0;
|
2016-09-21 23:19:47 +02:00
|
|
|
} else if (netdev->disconnect_cmd_id) {
|
|
|
|
l_genl_family_cancel(nl80211, netdev->disconnect_cmd_id);
|
|
|
|
netdev->disconnect_cmd_id = 0;
|
2016-08-05 14:25:34 +02:00
|
|
|
}
|
2016-08-02 22:48:11 +02:00
|
|
|
}
|
|
|
|
|
2019-01-24 21:56:23 +01:00
|
|
|
static void netdev_connect_failed(struct netdev *netdev,
|
|
|
|
enum netdev_result result,
|
2019-02-28 18:32:40 +01:00
|
|
|
uint16_t status_or_reason)
|
2016-09-21 23:19:47 +02:00
|
|
|
{
|
|
|
|
netdev_connect_cb_t connect_cb = netdev->connect_cb;
|
2016-12-12 18:34:28 +01:00
|
|
|
netdev_event_func_t event_filter = netdev->event_filter;
|
2016-09-21 23:19:47 +02:00
|
|
|
void *connect_data = netdev->user_data;
|
2019-01-24 21:56:23 +01:00
|
|
|
|
2016-09-23 04:03:00 +02:00
|
|
|
netdev->disconnect_cmd_id = 0;
|
|
|
|
|
2016-12-20 17:31:33 +01:00
|
|
|
/* Done this way to allow re-entrant netdev_connect calls */
|
2016-09-21 23:19:47 +02:00
|
|
|
netdev_connect_free(netdev);
|
|
|
|
|
|
|
|
if (connect_cb)
|
2019-02-28 18:32:40 +01:00
|
|
|
connect_cb(netdev, result, &status_or_reason, connect_data);
|
2016-12-12 18:34:28 +01:00
|
|
|
else if (event_filter)
|
|
|
|
event_filter(netdev, NETDEV_EVENT_DISCONNECT_BY_SME,
|
2019-03-01 00:41:51 +01:00
|
|
|
&status_or_reason,
|
2016-12-12 18:34:28 +01:00
|
|
|
connect_data);
|
2016-09-21 23:19:47 +02:00
|
|
|
}
|
|
|
|
|
2019-01-24 21:56:23 +01:00
|
|
|
static void netdev_disconnect_cb(struct l_genl_msg *msg, void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
|
2019-02-28 18:32:40 +01:00
|
|
|
netdev_connect_failed(netdev, netdev->result, netdev->last_code);
|
2019-01-24 21:56:23 +01:00
|
|
|
}
|
|
|
|
|
2016-06-01 22:35:26 +02:00
|
|
|
static void netdev_free(void *data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = data;
|
|
|
|
|
|
|
|
l_debug("Freeing netdev %s[%d]", netdev->name, netdev->index);
|
2016-06-15 17:54:13 +02:00
|
|
|
|
2017-01-23 12:38:40 +01:00
|
|
|
if (netdev->neighbor_report_cb) {
|
|
|
|
netdev->neighbor_report_cb(netdev, -ENODEV, NULL, 0,
|
|
|
|
netdev->user_data);
|
|
|
|
netdev->neighbor_report_cb = NULL;
|
|
|
|
l_timeout_remove(netdev->neighbor_report_timeout);
|
|
|
|
}
|
|
|
|
|
2017-02-10 03:23:15 +01:00
|
|
|
if (netdev->connected)
|
|
|
|
netdev_connect_free(netdev);
|
|
|
|
else if (netdev->disconnect_cmd_id) {
|
2016-09-21 22:20:51 +02:00
|
|
|
l_genl_family_cancel(nl80211, netdev->disconnect_cmd_id);
|
|
|
|
netdev->disconnect_cmd_id = 0;
|
|
|
|
|
|
|
|
if (netdev->disconnect_cb)
|
|
|
|
netdev->disconnect_cb(netdev, true, netdev->user_data);
|
|
|
|
|
|
|
|
netdev->disconnect_cb = NULL;
|
|
|
|
netdev->user_data = NULL;
|
2016-08-02 22:48:11 +02:00
|
|
|
}
|
2016-06-28 23:58:17 +02:00
|
|
|
|
2018-07-17 23:15:59 +02:00
|
|
|
if (netdev->join_adhoc_cmd_id) {
|
|
|
|
l_genl_family_cancel(nl80211, netdev->join_adhoc_cmd_id);
|
|
|
|
netdev->join_adhoc_cmd_id = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (netdev->leave_adhoc_cmd_id) {
|
|
|
|
l_genl_family_cancel(nl80211, netdev->leave_adhoc_cmd_id);
|
|
|
|
netdev->leave_adhoc_cmd_id = 0;
|
|
|
|
}
|
|
|
|
|
2018-08-17 19:37:47 +02:00
|
|
|
if (netdev->set_powered_cmd_id) {
|
|
|
|
l_netlink_cancel(rtnl, netdev->set_powered_cmd_id);
|
|
|
|
netdev->set_powered_cmd_id = 0;
|
|
|
|
}
|
|
|
|
|
2018-10-19 23:19:26 +02:00
|
|
|
if (netdev->rekey_offload_cmd_id) {
|
|
|
|
l_genl_family_cancel(nl80211, netdev->rekey_offload_cmd_id);
|
|
|
|
netdev->rekey_offload_cmd_id = 0;
|
|
|
|
}
|
|
|
|
|
2019-09-14 00:26:31 +02:00
|
|
|
if (netdev->qos_map_cmd_id) {
|
|
|
|
l_genl_family_cancel(nl80211, netdev->qos_map_cmd_id);
|
|
|
|
netdev->qos_map_cmd_id = 0;
|
|
|
|
}
|
|
|
|
|
2020-03-18 19:54:19 +01:00
|
|
|
if (netdev->mac_change_cmd_id) {
|
|
|
|
l_netlink_cancel(rtnl, netdev->mac_change_cmd_id);
|
|
|
|
netdev->mac_change_cmd_id = 0;
|
|
|
|
}
|
|
|
|
|
2019-11-20 23:23:42 +01:00
|
|
|
if (netdev->events_ready)
|
2018-10-05 03:53:14 +02:00
|
|
|
WATCHLIST_NOTIFY(&netdev_watches, netdev_watch_func_t,
|
|
|
|
netdev, NETDEV_WATCH_EVENT_DEL);
|
2018-08-18 06:40:49 +02:00
|
|
|
|
2019-12-06 17:11:51 +01:00
|
|
|
scan_wdev_remove(netdev->wdev_id);
|
|
|
|
|
2018-07-17 00:29:15 +02:00
|
|
|
watchlist_destroy(&netdev->station_watches);
|
2017-08-31 04:04:45 +02:00
|
|
|
|
2018-05-01 18:08:19 +02:00
|
|
|
l_io_destroy(netdev->pae_io);
|
|
|
|
|
2016-07-20 00:45:48 +02:00
|
|
|
l_free(netdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_shutdown_one(void *data, void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = data;
|
2016-06-15 17:54:13 +02:00
|
|
|
|
2016-07-13 04:29:52 +02:00
|
|
|
if (netdev_get_is_up(netdev))
|
2020-02-15 10:48:39 +01:00
|
|
|
l_rtnl_set_powered(rtnl, netdev->index, false,
|
|
|
|
NULL, NULL, NULL);
|
2016-06-01 22:35:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool netdev_match(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
const struct netdev *netdev = a;
|
|
|
|
uint32_t ifindex = L_PTR_TO_UINT(b);
|
|
|
|
|
|
|
|
return (netdev->index == ifindex);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct netdev *netdev_find(int ifindex)
|
|
|
|
{
|
|
|
|
return l_queue_find(netdev_list, netdev_match, L_UINT_TO_PTR(ifindex));
|
|
|
|
}
|
|
|
|
|
2016-06-16 18:06:17 +02:00
|
|
|
static void netdev_lost_beacon(struct netdev *netdev)
|
|
|
|
{
|
2016-08-02 22:48:11 +02:00
|
|
|
if (!netdev->connected)
|
2016-06-16 18:06:17 +02:00
|
|
|
return;
|
|
|
|
|
2016-08-02 22:48:11 +02:00
|
|
|
if (netdev->event_filter)
|
2019-03-01 00:41:51 +01:00
|
|
|
netdev->event_filter(netdev, NETDEV_EVENT_LOST_BEACON, NULL,
|
2016-06-16 18:06:17 +02:00
|
|
|
netdev->user_data);
|
|
|
|
}
|
|
|
|
|
2019-03-21 17:03:45 +01:00
|
|
|
/* Threshold RSSI for roaming to trigger, configurable in main.conf */
|
|
|
|
static int LOW_SIGNAL_THRESHOLD;
|
2017-05-20 16:33:13 +02:00
|
|
|
|
|
|
|
static void netdev_cqm_event_rssi_threshold(struct netdev *netdev,
|
|
|
|
uint32_t rssi_event)
|
2016-12-23 11:38:03 +01:00
|
|
|
{
|
2017-05-20 16:33:13 +02:00
|
|
|
int event;
|
|
|
|
|
2020-05-04 23:30:55 +02:00
|
|
|
if (!netdev->operational)
|
2016-12-23 11:38:03 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW &&
|
|
|
|
rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH)
|
|
|
|
return;
|
|
|
|
|
2017-05-20 16:33:13 +02:00
|
|
|
if (!netdev->event_filter)
|
|
|
|
return;
|
|
|
|
|
|
|
|
netdev->cur_rssi_low =
|
|
|
|
(rssi_event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW);
|
2017-08-18 14:22:58 +02:00
|
|
|
event = netdev->cur_rssi_low ? NETDEV_EVENT_RSSI_THRESHOLD_LOW :
|
2017-05-20 16:33:13 +02:00
|
|
|
NETDEV_EVENT_RSSI_THRESHOLD_HIGH;
|
|
|
|
|
2019-03-01 00:41:51 +01:00
|
|
|
netdev->event_filter(netdev, event, NULL, netdev->user_data);
|
2017-05-20 16:33:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_rssi_level_init(struct netdev *netdev)
|
|
|
|
{
|
|
|
|
if (netdev->connected && netdev->rssi_levels_num)
|
|
|
|
netdev_set_rssi_level_idx(netdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_cqm_event_rssi_value(struct netdev *netdev, int rssi_val)
|
|
|
|
{
|
|
|
|
bool new_rssi_low;
|
|
|
|
uint8_t prev_rssi_level_idx = netdev->cur_rssi_level_idx;
|
|
|
|
|
|
|
|
if (!netdev->connected)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (rssi_val > 127)
|
|
|
|
rssi_val = 127;
|
|
|
|
else if (rssi_val < -127)
|
|
|
|
rssi_val = -127;
|
|
|
|
|
|
|
|
netdev->cur_rssi = rssi_val;
|
|
|
|
|
|
|
|
if (!netdev->event_filter)
|
|
|
|
return;
|
2016-12-23 11:38:03 +01:00
|
|
|
|
2017-05-20 16:33:13 +02:00
|
|
|
new_rssi_low = rssi_val < LOW_SIGNAL_THRESHOLD;
|
|
|
|
if (netdev->cur_rssi_low != new_rssi_low) {
|
|
|
|
int event = new_rssi_low ?
|
2016-12-23 11:38:03 +01:00
|
|
|
NETDEV_EVENT_RSSI_THRESHOLD_LOW :
|
|
|
|
NETDEV_EVENT_RSSI_THRESHOLD_HIGH;
|
|
|
|
|
2017-05-20 16:33:13 +02:00
|
|
|
netdev->cur_rssi_low = new_rssi_low;
|
2019-03-01 00:41:51 +01:00
|
|
|
netdev->event_filter(netdev, event, NULL, netdev->user_data);
|
2016-12-23 11:38:03 +01:00
|
|
|
}
|
2017-05-20 16:33:13 +02:00
|
|
|
|
|
|
|
if (!netdev->rssi_levels_num)
|
|
|
|
return;
|
|
|
|
|
|
|
|
netdev_set_rssi_level_idx(netdev);
|
|
|
|
if (netdev->cur_rssi_level_idx != prev_rssi_level_idx)
|
|
|
|
netdev->event_filter(netdev, NETDEV_EVENT_RSSI_LEVEL_NOTIFY,
|
2019-03-01 00:41:51 +01:00
|
|
|
&netdev->cur_rssi_level_idx,
|
2017-05-20 16:33:13 +02:00
|
|
|
netdev->user_data);
|
2016-12-23 11:38:03 +01:00
|
|
|
}
|
|
|
|
|
2016-06-16 18:06:17 +02:00
|
|
|
static void netdev_cqm_event(struct l_genl_msg *msg, struct netdev *netdev)
|
|
|
|
{
|
|
|
|
struct l_genl_attr attr;
|
|
|
|
struct l_genl_attr nested;
|
|
|
|
uint16_t type, len;
|
|
|
|
const void *data;
|
2017-05-20 16:33:13 +02:00
|
|
|
uint32_t *rssi_event = NULL;
|
|
|
|
int32_t *rssi_val = NULL;
|
2016-06-16 18:06:17 +02:00
|
|
|
|
|
|
|
if (!l_genl_attr_init(&attr, msg))
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (l_genl_attr_next(&attr, &type, &len, &data)) {
|
|
|
|
switch (type) {
|
|
|
|
case NL80211_ATTR_CQM:
|
|
|
|
if (!l_genl_attr_recurse(&attr, &nested))
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (l_genl_attr_next(&nested, &type, &len, &data)) {
|
|
|
|
switch (type) {
|
|
|
|
case NL80211_ATTR_CQM_BEACON_LOSS_EVENT:
|
|
|
|
netdev_lost_beacon(netdev);
|
|
|
|
break;
|
2016-12-23 11:38:03 +01:00
|
|
|
|
|
|
|
case NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT:
|
|
|
|
if (len != 4)
|
|
|
|
continue;
|
|
|
|
|
2017-05-20 16:33:13 +02:00
|
|
|
rssi_event = (uint32_t *) data;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NL80211_ATTR_CQM_RSSI_LEVEL:
|
|
|
|
if (len != 4)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
rssi_val = (int32_t *) data;
|
2016-12-23 11:38:03 +01:00
|
|
|
break;
|
2016-06-16 18:06:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-05-20 16:33:13 +02:00
|
|
|
|
|
|
|
if (rssi_event) {
|
|
|
|
if (rssi_val)
|
|
|
|
netdev_cqm_event_rssi_value(netdev, *rssi_val);
|
|
|
|
else
|
|
|
|
netdev_cqm_event_rssi_threshold(netdev, *rssi_event);
|
|
|
|
}
|
2016-06-16 18:06:17 +02:00
|
|
|
}
|
|
|
|
|
2016-06-23 00:49:16 +02:00
|
|
|
static void netdev_rekey_offload_event(struct l_genl_msg *msg,
|
|
|
|
struct netdev *netdev)
|
|
|
|
{
|
|
|
|
struct l_genl_attr attr;
|
|
|
|
struct l_genl_attr nested;
|
|
|
|
uint16_t type, len;
|
|
|
|
const void *data;
|
|
|
|
uint64_t replay_ctr;
|
|
|
|
|
|
|
|
if (!l_genl_attr_init(&attr, msg))
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (l_genl_attr_next(&attr, &type, &len, &data)) {
|
2016-07-20 00:52:36 +02:00
|
|
|
if (type != NL80211_ATTR_REKEY_DATA)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!l_genl_attr_recurse(&attr, &nested))
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (l_genl_attr_next(&nested, &type, &len, &data)) {
|
|
|
|
if (type != NL80211_REKEY_DATA_REPLAY_CTR)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (len != sizeof(uint64_t)) {
|
|
|
|
l_warn("Invalid replay_ctr");
|
2016-06-23 00:49:16 +02:00
|
|
|
return;
|
2016-07-20 00:52:36 +02:00
|
|
|
}
|
2016-06-23 00:49:16 +02:00
|
|
|
|
2016-07-20 00:52:36 +02:00
|
|
|
replay_ctr = *((uint64_t *) data);
|
|
|
|
__eapol_update_replay_counter(netdev->index,
|
2016-06-23 00:49:16 +02:00
|
|
|
netdev->addr,
|
2016-12-12 18:34:21 +01:00
|
|
|
netdev->handshake->aa,
|
2016-06-23 00:49:16 +02:00
|
|
|
replay_ctr);
|
2016-07-20 00:52:36 +02:00
|
|
|
return;
|
2016-06-23 00:49:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-16 18:21:12 +02:00
|
|
|
static void netdev_disconnect_event(struct l_genl_msg *msg,
|
|
|
|
struct netdev *netdev)
|
|
|
|
{
|
|
|
|
struct l_genl_attr attr;
|
|
|
|
uint16_t type, len;
|
|
|
|
const void *data;
|
|
|
|
uint16_t reason_code = 0;
|
|
|
|
bool disconnect_by_ap = false;
|
2016-10-11 08:53:59 +02:00
|
|
|
netdev_event_func_t event_filter;
|
|
|
|
void *event_data;
|
2016-06-16 18:21:12 +02:00
|
|
|
|
|
|
|
l_debug("");
|
|
|
|
|
2017-01-12 09:36:00 +01:00
|
|
|
if (!netdev->connected || netdev->disconnect_cmd_id > 0 ||
|
|
|
|
netdev->in_ft)
|
2016-08-02 22:48:11 +02:00
|
|
|
return;
|
|
|
|
|
2016-06-16 18:21:12 +02:00
|
|
|
if (!l_genl_attr_init(&attr, msg)) {
|
|
|
|
l_error("attr init failed");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (l_genl_attr_next(&attr, &type, &len, &data)) {
|
|
|
|
switch (type) {
|
|
|
|
case NL80211_ATTR_REASON_CODE:
|
|
|
|
if (len != sizeof(uint16_t))
|
|
|
|
l_warn("Invalid reason code attribute");
|
|
|
|
else
|
|
|
|
reason_code = *((uint16_t *) data);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NL80211_ATTR_DISCONNECTED_BY_AP:
|
|
|
|
disconnect_by_ap = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
l_info("Received Deauthentication event, reason: %hu, from_ap: %s",
|
|
|
|
reason_code, disconnect_by_ap ? "true" : "false");
|
|
|
|
|
2016-10-11 08:53:59 +02:00
|
|
|
event_filter = netdev->event_filter;
|
|
|
|
event_data = netdev->user_data;
|
2016-08-02 22:48:11 +02:00
|
|
|
netdev_connect_free(netdev);
|
2016-10-11 08:53:59 +02:00
|
|
|
|
2017-08-14 14:31:17 +02:00
|
|
|
if (!event_filter)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (disconnect_by_ap)
|
2016-10-11 08:53:59 +02:00
|
|
|
event_filter(netdev, NETDEV_EVENT_DISCONNECT_BY_AP,
|
2019-03-01 00:41:51 +01:00
|
|
|
&reason_code, event_data);
|
2017-08-14 14:31:17 +02:00
|
|
|
else
|
|
|
|
event_filter(netdev, NETDEV_EVENT_DISCONNECT_BY_SME,
|
2019-03-01 00:41:51 +01:00
|
|
|
&reason_code, event_data);
|
2016-06-16 18:21:12 +02:00
|
|
|
}
|
|
|
|
|
2017-05-31 18:08:40 +02:00
|
|
|
static void netdev_cmd_disconnect_cb(struct l_genl_msg *msg, void *user_data)
|
2016-06-16 23:05:34 +02:00
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
2016-09-22 23:20:33 +02:00
|
|
|
void *disconnect_data;
|
|
|
|
netdev_disconnect_cb_t disconnect_cb;
|
2016-06-16 23:05:34 +02:00
|
|
|
bool r;
|
|
|
|
|
2016-09-23 04:03:00 +02:00
|
|
|
netdev->disconnect_cmd_id = 0;
|
2019-03-08 23:09:42 +01:00
|
|
|
netdev->aborting = false;
|
2016-09-22 23:20:33 +02:00
|
|
|
|
|
|
|
if (!netdev->disconnect_cb) {
|
|
|
|
netdev->user_data = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
disconnect_data = netdev->user_data;
|
|
|
|
disconnect_cb = netdev->disconnect_cb;
|
|
|
|
netdev->user_data = NULL;
|
|
|
|
netdev->disconnect_cb = NULL;
|
2016-08-02 22:48:11 +02:00
|
|
|
|
2016-06-16 23:05:34 +02:00
|
|
|
if (l_genl_msg_get_error(msg) < 0)
|
|
|
|
r = false;
|
|
|
|
else
|
|
|
|
r = true;
|
|
|
|
|
2016-09-22 23:20:33 +02:00
|
|
|
disconnect_cb(netdev, r, disconnect_data);
|
2016-06-16 23:05:34 +02:00
|
|
|
}
|
|
|
|
|
2017-05-31 18:08:40 +02:00
|
|
|
static struct l_genl_msg *netdev_build_cmd_disconnect(struct netdev *netdev,
|
|
|
|
uint16_t reason_code)
|
|
|
|
{
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
|
|
|
|
msg = l_genl_msg_new_sized(NL80211_CMD_DISCONNECT, 64);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_REASON_CODE, 2, &reason_code);
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_deauthenticate_event(struct l_genl_msg *msg,
|
|
|
|
struct netdev *netdev)
|
|
|
|
{
|
|
|
|
l_debug("");
|
|
|
|
}
|
|
|
|
|
2016-06-16 17:54:20 +02:00
|
|
|
static struct l_genl_msg *netdev_build_cmd_deauthenticate(struct netdev *netdev,
|
|
|
|
uint16_t reason_code)
|
|
|
|
{
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
|
|
|
|
msg = l_genl_msg_new_sized(NL80211_CMD_DEAUTHENTICATE, 128);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_REASON_CODE, 2, &reason_code);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN,
|
2016-12-12 18:34:21 +01:00
|
|
|
netdev->handshake->aa);
|
2016-06-16 17:54:20 +02:00
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2018-07-03 23:36:33 +02:00
|
|
|
static struct l_genl_msg *netdev_build_cmd_del_station(struct netdev *netdev,
|
|
|
|
const uint8_t *sta,
|
|
|
|
uint16_t reason_code,
|
|
|
|
bool disassociate)
|
|
|
|
{
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
uint8_t subtype = disassociate ?
|
|
|
|
MPDU_MANAGEMENT_SUBTYPE_DISASSOCIATION :
|
|
|
|
MPDU_MANAGEMENT_SUBTYPE_DEAUTHENTICATION;
|
|
|
|
|
|
|
|
msg = l_genl_msg_new_sized(NL80211_CMD_DEL_STATION, 64);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, 6, sta);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_MGMT_SUBTYPE, 1, &subtype);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_REASON_CODE, 2, &reason_code);
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2018-07-03 23:36:34 +02:00
|
|
|
static void netdev_del_sta_cb(struct l_genl_msg *msg, void *user_data)
|
|
|
|
{
|
2020-04-02 07:30:17 +02:00
|
|
|
int err = l_genl_msg_get_error(msg);
|
|
|
|
const char *ext_error;
|
|
|
|
|
|
|
|
if (err >= 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ext_error = l_genl_msg_get_extended_error(msg);
|
|
|
|
l_error("DEL_STATION failed: %s",
|
|
|
|
ext_error ? ext_error : strerror(-err));
|
2018-07-03 23:36:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int netdev_del_station(struct netdev *netdev, const uint8_t *sta,
|
|
|
|
uint16_t reason_code, bool disassociate)
|
|
|
|
{
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
|
|
|
|
msg = netdev_build_cmd_del_station(netdev, sta, reason_code,
|
|
|
|
disassociate);
|
|
|
|
|
|
|
|
if (!l_genl_family_send(nl80211, msg, netdev_del_sta_cb, NULL, NULL))
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-08-17 21:04:44 +02:00
|
|
|
static void netdev_operstate_cb(int error, uint16_t type,
|
|
|
|
const void *data,
|
|
|
|
uint32_t len, void *user_data)
|
2016-06-16 17:54:20 +02:00
|
|
|
{
|
2018-08-17 21:04:44 +02:00
|
|
|
if (!error)
|
|
|
|
return;
|
2016-08-02 22:48:11 +02:00
|
|
|
|
2018-08-17 21:04:44 +02:00
|
|
|
l_debug("netdev: %u, error: %s", L_PTR_TO_UINT(user_data),
|
|
|
|
strerror(-error));
|
2017-05-30 14:26:19 +02:00
|
|
|
}
|
2016-06-16 17:54:20 +02:00
|
|
|
|
2017-05-30 14:26:19 +02:00
|
|
|
static void netdev_connect_ok(struct netdev *netdev)
|
|
|
|
{
|
2020-02-15 10:48:39 +01:00
|
|
|
l_rtnl_set_linkmode_and_operstate(rtnl, netdev->index,
|
2019-05-06 20:17:53 +02:00
|
|
|
IF_LINK_MODE_DORMANT, IF_OPER_UP,
|
|
|
|
netdev_operstate_cb,
|
2018-08-17 21:04:44 +02:00
|
|
|
L_UINT_TO_PTR(netdev->index), NULL);
|
2016-06-16 17:54:20 +02:00
|
|
|
|
2016-09-22 22:55:07 +02:00
|
|
|
netdev->operational = true;
|
|
|
|
|
2016-08-04 17:46:41 +02:00
|
|
|
if (netdev->connect_cb) {
|
2019-02-28 18:32:40 +01:00
|
|
|
netdev->connect_cb(netdev, NETDEV_RESULT_OK, NULL,
|
|
|
|
netdev->user_data);
|
2016-08-04 17:46:41 +02:00
|
|
|
netdev->connect_cb = NULL;
|
|
|
|
}
|
2017-08-18 14:22:58 +02:00
|
|
|
|
|
|
|
netdev_rssi_polling_update(netdev);
|
2020-07-15 23:29:17 +02:00
|
|
|
|
|
|
|
if (netdev->work.id)
|
|
|
|
wiphy_radio_work_done(netdev->wiphy, netdev->work.id);
|
2016-06-16 17:54:20 +02:00
|
|
|
}
|
|
|
|
|
2018-06-22 02:32:55 +02:00
|
|
|
static void netdev_setting_keys_failed(struct netdev_handshake_state *nhs,
|
2019-02-27 21:22:42 +01:00
|
|
|
int err)
|
2016-06-16 21:45:56 +02:00
|
|
|
{
|
2018-06-22 02:32:55 +02:00
|
|
|
struct netdev *netdev = nhs->netdev;
|
2016-06-16 21:45:56 +02:00
|
|
|
struct l_genl_msg *msg;
|
|
|
|
|
|
|
|
/*
|
2018-06-22 02:32:54 +02:00
|
|
|
* Something went wrong with our sequence:
|
|
|
|
* 1. new_key(ptk)
|
|
|
|
* 2. new_key(gtk) [optional]
|
|
|
|
* 3. new_key(igtk) [optional]
|
2018-10-20 17:38:56 +02:00
|
|
|
* 4. rekey offload [optional]
|
|
|
|
* 5. set_station
|
2016-06-16 21:45:56 +02:00
|
|
|
*
|
|
|
|
* Cancel all pending commands, then de-authenticate
|
|
|
|
*/
|
2018-10-20 17:35:28 +02:00
|
|
|
netdev_handshake_state_cancel_all(nhs);
|
2018-10-20 17:38:56 +02:00
|
|
|
|
|
|
|
if (netdev->rekey_offload_cmd_id) {
|
|
|
|
l_genl_family_cancel(nl80211, netdev->rekey_offload_cmd_id);
|
|
|
|
netdev->rekey_offload_cmd_id = 0;
|
|
|
|
}
|
|
|
|
|
netdev: signal handshake complete after setting all keys
Currently, netdev triggers the HANDSHAKE_COMPLETE event after completing
the SET_STATION (after setting the pairwise key). Depending on the timing
this may happen before the GTK/IGTK are set which will result in group
traffic not working initially (the GTK/IGTK would still get set, but group
traffic would not work immediately after DBus said you were connected, this
mainly poses a problem with autotests).
In order to fix this, several flags were added in netdev_handshake_state:
ptk_installed, gtk_installed, igtk_installed, and completed. Each of these
flags are set true when their respective keys are set, and in each key
callback we try to trigger the handshake complete event (assuming all the
flags are true). Initially the gtk/igtk flags are set to true, for reasons
explained below.
In the WPA2 case, all the key setter functions are called sequentially from
eapol. With this change, the PTK is now set AFTER the gtk/igtk. This is
because the gtk/igtk are optional and only set if group traffic is allowed.
If the gtk/igtk are not used, we set the PTK and can immediately trigger the
handshake complete event (since gtk_installed/igtk_installed are initialized
as true). When the gtk/igtk are being set, we immediately set their flags to
false and wait for their callbacks in addition to the PTK callback. Doing it
this way handles both group traffic and non group traffic paths.
WPA1 throws a wrench into this since the group keys are obtained in a
separate handshake. For this case a new flag was added to the handshake_state,
'wait_for_gtk'. This allows netdev to set the PTK after the initial 4-way,
but still wait for the gtk/igtk setters to get called before triggering the
handshake complete event. As a precaution, netdev sets a timeout that will
trigger if the gtk/igtk setters are never called. In this case we can still
complete the connection, but print a warning that group traffic will not be
allowed.
2018-10-26 18:44:58 +02:00
|
|
|
if (netdev->group_handshake_timeout) {
|
|
|
|
l_timeout_remove(netdev->group_handshake_timeout);
|
|
|
|
netdev->group_handshake_timeout = NULL;
|
|
|
|
}
|
|
|
|
|
2016-09-21 23:19:47 +02:00
|
|
|
netdev->result = NETDEV_RESULT_KEY_SETTING_FAILED;
|
2019-02-27 21:22:42 +01:00
|
|
|
handshake_event(&nhs->super, HANDSHAKE_EVENT_SETTING_KEYS_FAILED, &err);
|
2018-06-22 20:13:28 +02:00
|
|
|
|
2018-07-03 23:36:33 +02:00
|
|
|
switch (netdev->type) {
|
|
|
|
case NL80211_IFTYPE_STATION:
|
2020-04-30 15:48:45 +02:00
|
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
2019-02-27 21:22:42 +01:00
|
|
|
msg = netdev_build_cmd_disconnect(netdev,
|
|
|
|
MMPDU_REASON_CODE_UNSPECIFIED);
|
2018-07-03 23:36:33 +02:00
|
|
|
netdev->disconnect_cmd_id = l_genl_family_send(nl80211, msg,
|
2019-01-24 21:56:23 +01:00
|
|
|
netdev_disconnect_cb,
|
2016-09-21 23:19:47 +02:00
|
|
|
netdev, NULL);
|
2018-07-03 23:36:33 +02:00
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_AP:
|
|
|
|
msg = netdev_build_cmd_del_station(netdev, nhs->super.spa,
|
2019-02-27 21:22:42 +01:00
|
|
|
MMPDU_REASON_CODE_UNSPECIFIED, false);
|
2018-07-03 23:36:33 +02:00
|
|
|
if (!l_genl_family_send(nl80211, msg, NULL, NULL, NULL))
|
|
|
|
l_error("error sending DEL_STATION");
|
|
|
|
}
|
2016-06-16 21:45:56 +02:00
|
|
|
}
|
|
|
|
|
netdev: signal handshake complete after setting all keys
Currently, netdev triggers the HANDSHAKE_COMPLETE event after completing
the SET_STATION (after setting the pairwise key). Depending on the timing
this may happen before the GTK/IGTK are set which will result in group
traffic not working initially (the GTK/IGTK would still get set, but group
traffic would not work immediately after DBus said you were connected, this
mainly poses a problem with autotests).
In order to fix this, several flags were added in netdev_handshake_state:
ptk_installed, gtk_installed, igtk_installed, and completed. Each of these
flags are set true when their respective keys are set, and in each key
callback we try to trigger the handshake complete event (assuming all the
flags are true). Initially the gtk/igtk flags are set to true, for reasons
explained below.
In the WPA2 case, all the key setter functions are called sequentially from
eapol. With this change, the PTK is now set AFTER the gtk/igtk. This is
because the gtk/igtk are optional and only set if group traffic is allowed.
If the gtk/igtk are not used, we set the PTK and can immediately trigger the
handshake complete event (since gtk_installed/igtk_installed are initialized
as true). When the gtk/igtk are being set, we immediately set their flags to
false and wait for their callbacks in addition to the PTK callback. Doing it
this way handles both group traffic and non group traffic paths.
WPA1 throws a wrench into this since the group keys are obtained in a
separate handshake. For this case a new flag was added to the handshake_state,
'wait_for_gtk'. This allows netdev to set the PTK after the initial 4-way,
but still wait for the gtk/igtk setters to get called before triggering the
handshake complete event. As a precaution, netdev sets a timeout that will
trigger if the gtk/igtk setters are never called. In this case we can still
complete the connection, but print a warning that group traffic will not be
allowed.
2018-10-26 18:44:58 +02:00
|
|
|
static void try_handshake_complete(struct netdev_handshake_state *nhs)
|
|
|
|
{
|
|
|
|
if (nhs->ptk_installed && nhs->gtk_installed && nhs->igtk_installed &&
|
|
|
|
!nhs->complete) {
|
|
|
|
nhs->complete = true;
|
2019-10-28 15:04:57 +01:00
|
|
|
handshake_event(&nhs->super, HANDSHAKE_EVENT_COMPLETE);
|
netdev: signal handshake complete after setting all keys
Currently, netdev triggers the HANDSHAKE_COMPLETE event after completing
the SET_STATION (after setting the pairwise key). Depending on the timing
this may happen before the GTK/IGTK are set which will result in group
traffic not working initially (the GTK/IGTK would still get set, but group
traffic would not work immediately after DBus said you were connected, this
mainly poses a problem with autotests).
In order to fix this, several flags were added in netdev_handshake_state:
ptk_installed, gtk_installed, igtk_installed, and completed. Each of these
flags are set true when their respective keys are set, and in each key
callback we try to trigger the handshake complete event (assuming all the
flags are true). Initially the gtk/igtk flags are set to true, for reasons
explained below.
In the WPA2 case, all the key setter functions are called sequentially from
eapol. With this change, the PTK is now set AFTER the gtk/igtk. This is
because the gtk/igtk are optional and only set if group traffic is allowed.
If the gtk/igtk are not used, we set the PTK and can immediately trigger the
handshake complete event (since gtk_installed/igtk_installed are initialized
as true). When the gtk/igtk are being set, we immediately set their flags to
false and wait for their callbacks in addition to the PTK callback. Doing it
this way handles both group traffic and non group traffic paths.
WPA1 throws a wrench into this since the group keys are obtained in a
separate handshake. For this case a new flag was added to the handshake_state,
'wait_for_gtk'. This allows netdev to set the PTK after the initial 4-way,
but still wait for the gtk/igtk setters to get called before triggering the
handshake complete event. As a precaution, netdev sets a timeout that will
trigger if the gtk/igtk setters are never called. In this case we can still
complete the connection, but print a warning that group traffic will not be
allowed.
2018-10-26 18:44:58 +02:00
|
|
|
|
|
|
|
netdev_connect_ok(nhs->netdev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-16 21:45:56 +02:00
|
|
|
static void netdev_set_station_cb(struct l_genl_msg *msg, void *user_data)
|
|
|
|
{
|
2018-06-22 02:32:55 +02:00
|
|
|
struct netdev_handshake_state *nhs = user_data;
|
|
|
|
struct netdev *netdev = nhs->netdev;
|
2017-05-30 23:55:56 +02:00
|
|
|
int err;
|
2016-06-16 21:45:56 +02:00
|
|
|
|
2018-06-22 02:32:55 +02:00
|
|
|
nhs->set_station_cmd_id = 0;
|
netdev: signal handshake complete after setting all keys
Currently, netdev triggers the HANDSHAKE_COMPLETE event after completing
the SET_STATION (after setting the pairwise key). Depending on the timing
this may happen before the GTK/IGTK are set which will result in group
traffic not working initially (the GTK/IGTK would still get set, but group
traffic would not work immediately after DBus said you were connected, this
mainly poses a problem with autotests).
In order to fix this, several flags were added in netdev_handshake_state:
ptk_installed, gtk_installed, igtk_installed, and completed. Each of these
flags are set true when their respective keys are set, and in each key
callback we try to trigger the handshake complete event (assuming all the
flags are true). Initially the gtk/igtk flags are set to true, for reasons
explained below.
In the WPA2 case, all the key setter functions are called sequentially from
eapol. With this change, the PTK is now set AFTER the gtk/igtk. This is
because the gtk/igtk are optional and only set if group traffic is allowed.
If the gtk/igtk are not used, we set the PTK and can immediately trigger the
handshake complete event (since gtk_installed/igtk_installed are initialized
as true). When the gtk/igtk are being set, we immediately set their flags to
false and wait for their callbacks in addition to the PTK callback. Doing it
this way handles both group traffic and non group traffic paths.
WPA1 throws a wrench into this since the group keys are obtained in a
separate handshake. For this case a new flag was added to the handshake_state,
'wait_for_gtk'. This allows netdev to set the PTK after the initial 4-way,
but still wait for the gtk/igtk setters to get called before triggering the
handshake complete event. As a precaution, netdev sets a timeout that will
trigger if the gtk/igtk setters are never called. In this case we can still
complete the connection, but print a warning that group traffic will not be
allowed.
2018-10-26 18:44:58 +02:00
|
|
|
nhs->ptk_installed = true;
|
2017-10-21 01:27:21 +02:00
|
|
|
|
2018-07-17 00:29:18 +02:00
|
|
|
if (netdev->type == NL80211_IFTYPE_STATION && !netdev->connected)
|
2016-08-02 22:48:11 +02:00
|
|
|
return;
|
|
|
|
|
2017-05-30 23:55:56 +02:00
|
|
|
err = l_genl_msg_get_error(msg);
|
2019-12-17 23:10:46 +01:00
|
|
|
if (err == -EOPNOTSUPP || err == -ENOTSUPP)
|
2017-05-30 23:55:56 +02:00
|
|
|
goto done;
|
|
|
|
|
|
|
|
if (err < 0) {
|
2020-04-02 07:30:17 +02:00
|
|
|
const char *ext_error = l_genl_msg_get_extended_error(msg);
|
|
|
|
|
|
|
|
l_error("Set Station failed for ifindex %d:%s", netdev->index,
|
|
|
|
ext_error ? ext_error : strerror(-err));
|
|
|
|
|
2019-02-27 21:22:42 +01:00
|
|
|
netdev_setting_keys_failed(nhs, err);
|
2016-06-16 21:45:56 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-05-30 23:55:56 +02:00
|
|
|
done:
|
netdev: signal handshake complete after setting all keys
Currently, netdev triggers the HANDSHAKE_COMPLETE event after completing
the SET_STATION (after setting the pairwise key). Depending on the timing
this may happen before the GTK/IGTK are set which will result in group
traffic not working initially (the GTK/IGTK would still get set, but group
traffic would not work immediately after DBus said you were connected, this
mainly poses a problem with autotests).
In order to fix this, several flags were added in netdev_handshake_state:
ptk_installed, gtk_installed, igtk_installed, and completed. Each of these
flags are set true when their respective keys are set, and in each key
callback we try to trigger the handshake complete event (assuming all the
flags are true). Initially the gtk/igtk flags are set to true, for reasons
explained below.
In the WPA2 case, all the key setter functions are called sequentially from
eapol. With this change, the PTK is now set AFTER the gtk/igtk. This is
because the gtk/igtk are optional and only set if group traffic is allowed.
If the gtk/igtk are not used, we set the PTK and can immediately trigger the
handshake complete event (since gtk_installed/igtk_installed are initialized
as true). When the gtk/igtk are being set, we immediately set their flags to
false and wait for their callbacks in addition to the PTK callback. Doing it
this way handles both group traffic and non group traffic paths.
WPA1 throws a wrench into this since the group keys are obtained in a
separate handshake. For this case a new flag was added to the handshake_state,
'wait_for_gtk'. This allows netdev to set the PTK after the initial 4-way,
but still wait for the gtk/igtk setters to get called before triggering the
handshake complete event. As a precaution, netdev sets a timeout that will
trigger if the gtk/igtk setters are never called. In this case we can still
complete the connection, but print a warning that group traffic will not be
allowed.
2018-10-26 18:44:58 +02:00
|
|
|
try_handshake_complete(nhs);
|
2016-06-16 21:45:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_new_group_key_cb(struct l_genl_msg *msg, void *data)
|
|
|
|
{
|
2018-06-22 02:32:55 +02:00
|
|
|
struct netdev_handshake_state *nhs = data;
|
|
|
|
struct netdev *netdev = nhs->netdev;
|
2019-02-27 21:22:42 +01:00
|
|
|
int err = l_genl_msg_get_error(msg);
|
2016-06-16 21:45:56 +02:00
|
|
|
|
2018-06-22 02:32:55 +02:00
|
|
|
nhs->group_new_key_cmd_id = 0;
|
2016-06-16 21:45:56 +02:00
|
|
|
|
2019-02-27 21:22:42 +01:00
|
|
|
if (err < 0) {
|
2020-04-02 07:30:17 +02:00
|
|
|
const char *ext_error = l_genl_msg_get_extended_error(msg);
|
|
|
|
|
|
|
|
l_error("New Key for Group Key failed for ifindex: %d:%s",
|
|
|
|
netdev->index,
|
|
|
|
ext_error ? ext_error : strerror(-err));
|
|
|
|
|
2019-02-27 21:22:42 +01:00
|
|
|
netdev_setting_keys_failed(nhs, err);
|
2016-06-16 21:45:56 +02:00
|
|
|
return;
|
netdev: signal handshake complete after setting all keys
Currently, netdev triggers the HANDSHAKE_COMPLETE event after completing
the SET_STATION (after setting the pairwise key). Depending on the timing
this may happen before the GTK/IGTK are set which will result in group
traffic not working initially (the GTK/IGTK would still get set, but group
traffic would not work immediately after DBus said you were connected, this
mainly poses a problem with autotests).
In order to fix this, several flags were added in netdev_handshake_state:
ptk_installed, gtk_installed, igtk_installed, and completed. Each of these
flags are set true when their respective keys are set, and in each key
callback we try to trigger the handshake complete event (assuming all the
flags are true). Initially the gtk/igtk flags are set to true, for reasons
explained below.
In the WPA2 case, all the key setter functions are called sequentially from
eapol. With this change, the PTK is now set AFTER the gtk/igtk. This is
because the gtk/igtk are optional and only set if group traffic is allowed.
If the gtk/igtk are not used, we set the PTK and can immediately trigger the
handshake complete event (since gtk_installed/igtk_installed are initialized
as true). When the gtk/igtk are being set, we immediately set their flags to
false and wait for their callbacks in addition to the PTK callback. Doing it
this way handles both group traffic and non group traffic paths.
WPA1 throws a wrench into this since the group keys are obtained in a
separate handshake. For this case a new flag was added to the handshake_state,
'wait_for_gtk'. This allows netdev to set the PTK after the initial 4-way,
but still wait for the gtk/igtk setters to get called before triggering the
handshake complete event. As a precaution, netdev sets a timeout that will
trigger if the gtk/igtk setters are never called. In this case we can still
complete the connection, but print a warning that group traffic will not be
allowed.
2018-10-26 18:44:58 +02:00
|
|
|
}
|
2016-06-16 21:45:56 +02:00
|
|
|
|
netdev: signal handshake complete after setting all keys
Currently, netdev triggers the HANDSHAKE_COMPLETE event after completing
the SET_STATION (after setting the pairwise key). Depending on the timing
this may happen before the GTK/IGTK are set which will result in group
traffic not working initially (the GTK/IGTK would still get set, but group
traffic would not work immediately after DBus said you were connected, this
mainly poses a problem with autotests).
In order to fix this, several flags were added in netdev_handshake_state:
ptk_installed, gtk_installed, igtk_installed, and completed. Each of these
flags are set true when their respective keys are set, and in each key
callback we try to trigger the handshake complete event (assuming all the
flags are true). Initially the gtk/igtk flags are set to true, for reasons
explained below.
In the WPA2 case, all the key setter functions are called sequentially from
eapol. With this change, the PTK is now set AFTER the gtk/igtk. This is
because the gtk/igtk are optional and only set if group traffic is allowed.
If the gtk/igtk are not used, we set the PTK and can immediately trigger the
handshake complete event (since gtk_installed/igtk_installed are initialized
as true). When the gtk/igtk are being set, we immediately set their flags to
false and wait for their callbacks in addition to the PTK callback. Doing it
this way handles both group traffic and non group traffic paths.
WPA1 throws a wrench into this since the group keys are obtained in a
separate handshake. For this case a new flag was added to the handshake_state,
'wait_for_gtk'. This allows netdev to set the PTK after the initial 4-way,
but still wait for the gtk/igtk setters to get called before triggering the
handshake complete event. As a precaution, netdev sets a timeout that will
trigger if the gtk/igtk setters are never called. In this case we can still
complete the connection, but print a warning that group traffic will not be
allowed.
2018-10-26 18:44:58 +02:00
|
|
|
nhs->gtk_installed = true;
|
|
|
|
try_handshake_complete(nhs);
|
2016-06-16 21:45:56 +02:00
|
|
|
}
|
|
|
|
|
2016-10-28 23:49:54 +02:00
|
|
|
static void netdev_new_group_management_key_cb(struct l_genl_msg *msg,
|
|
|
|
void *data)
|
|
|
|
{
|
2018-06-22 02:32:55 +02:00
|
|
|
struct netdev_handshake_state *nhs = data;
|
|
|
|
struct netdev *netdev = nhs->netdev;
|
2019-02-27 21:22:42 +01:00
|
|
|
int err = l_genl_msg_get_error(msg);
|
2016-10-28 23:49:54 +02:00
|
|
|
|
2018-06-22 02:32:55 +02:00
|
|
|
nhs->group_management_new_key_cmd_id = 0;
|
2016-10-28 23:49:54 +02:00
|
|
|
|
2019-02-27 21:22:42 +01:00
|
|
|
if (err < 0) {
|
2020-04-02 07:30:17 +02:00
|
|
|
const char *ext_error = l_genl_msg_get_extended_error(msg);
|
|
|
|
|
|
|
|
l_error("New Key for Group Mgmt failed for ifindex: %d:%s",
|
|
|
|
netdev->index,
|
|
|
|
ext_error ? ext_error : strerror(-err));
|
|
|
|
|
2019-02-27 21:22:42 +01:00
|
|
|
netdev_setting_keys_failed(nhs, err);
|
netdev: signal handshake complete after setting all keys
Currently, netdev triggers the HANDSHAKE_COMPLETE event after completing
the SET_STATION (after setting the pairwise key). Depending on the timing
this may happen before the GTK/IGTK are set which will result in group
traffic not working initially (the GTK/IGTK would still get set, but group
traffic would not work immediately after DBus said you were connected, this
mainly poses a problem with autotests).
In order to fix this, several flags were added in netdev_handshake_state:
ptk_installed, gtk_installed, igtk_installed, and completed. Each of these
flags are set true when their respective keys are set, and in each key
callback we try to trigger the handshake complete event (assuming all the
flags are true). Initially the gtk/igtk flags are set to true, for reasons
explained below.
In the WPA2 case, all the key setter functions are called sequentially from
eapol. With this change, the PTK is now set AFTER the gtk/igtk. This is
because the gtk/igtk are optional and only set if group traffic is allowed.
If the gtk/igtk are not used, we set the PTK and can immediately trigger the
handshake complete event (since gtk_installed/igtk_installed are initialized
as true). When the gtk/igtk are being set, we immediately set their flags to
false and wait for their callbacks in addition to the PTK callback. Doing it
this way handles both group traffic and non group traffic paths.
WPA1 throws a wrench into this since the group keys are obtained in a
separate handshake. For this case a new flag was added to the handshake_state,
'wait_for_gtk'. This allows netdev to set the PTK after the initial 4-way,
but still wait for the gtk/igtk setters to get called before triggering the
handshake complete event. As a precaution, netdev sets a timeout that will
trigger if the gtk/igtk setters are never called. In this case we can still
complete the connection, but print a warning that group traffic will not be
allowed.
2018-10-26 18:44:58 +02:00
|
|
|
return;
|
2016-10-28 23:49:54 +02:00
|
|
|
}
|
netdev: signal handshake complete after setting all keys
Currently, netdev triggers the HANDSHAKE_COMPLETE event after completing
the SET_STATION (after setting the pairwise key). Depending on the timing
this may happen before the GTK/IGTK are set which will result in group
traffic not working initially (the GTK/IGTK would still get set, but group
traffic would not work immediately after DBus said you were connected, this
mainly poses a problem with autotests).
In order to fix this, several flags were added in netdev_handshake_state:
ptk_installed, gtk_installed, igtk_installed, and completed. Each of these
flags are set true when their respective keys are set, and in each key
callback we try to trigger the handshake complete event (assuming all the
flags are true). Initially the gtk/igtk flags are set to true, for reasons
explained below.
In the WPA2 case, all the key setter functions are called sequentially from
eapol. With this change, the PTK is now set AFTER the gtk/igtk. This is
because the gtk/igtk are optional and only set if group traffic is allowed.
If the gtk/igtk are not used, we set the PTK and can immediately trigger the
handshake complete event (since gtk_installed/igtk_installed are initialized
as true). When the gtk/igtk are being set, we immediately set their flags to
false and wait for their callbacks in addition to the PTK callback. Doing it
this way handles both group traffic and non group traffic paths.
WPA1 throws a wrench into this since the group keys are obtained in a
separate handshake. For this case a new flag was added to the handshake_state,
'wait_for_gtk'. This allows netdev to set the PTK after the initial 4-way,
but still wait for the gtk/igtk setters to get called before triggering the
handshake complete event. As a precaution, netdev sets a timeout that will
trigger if the gtk/igtk setters are never called. In this case we can still
complete the connection, but print a warning that group traffic will not be
allowed.
2018-10-26 18:44:58 +02:00
|
|
|
|
|
|
|
nhs->igtk_installed = true;
|
|
|
|
try_handshake_complete(nhs);
|
2016-10-28 23:49:54 +02:00
|
|
|
}
|
|
|
|
|
2018-06-20 20:05:13 +02:00
|
|
|
static bool netdev_copy_tk(uint8_t *tk_buf, const uint8_t *tk,
|
|
|
|
uint32_t cipher, bool authenticator)
|
|
|
|
{
|
|
|
|
switch (cipher) {
|
|
|
|
case CRYPTO_CIPHER_CCMP:
|
|
|
|
/*
|
|
|
|
* 802.11-2016 12.8.3 Mapping PTK to CCMP keys:
|
|
|
|
* "A STA shall use the temporal key as the CCMP key
|
|
|
|
* for MPDUs between the two communicating STAs."
|
|
|
|
*/
|
|
|
|
memcpy(tk_buf, tk, 16);
|
|
|
|
break;
|
|
|
|
case CRYPTO_CIPHER_TKIP:
|
|
|
|
/*
|
|
|
|
* 802.11-2016 12.8.1 Mapping PTK to TKIP keys:
|
|
|
|
* "A STA shall use bits 0-127 of the temporal key as its
|
|
|
|
* input to the TKIP Phase 1 and Phase 2 mixing functions.
|
|
|
|
*
|
|
|
|
* A STA shall use bits 128-191 of the temporal key as
|
|
|
|
* the michael key for MSDUs from the Authenticator's STA
|
|
|
|
* to the Supplicant's STA.
|
|
|
|
*
|
|
|
|
* A STA shall use bits 192-255 of the temporal key as
|
|
|
|
* the michael key for MSDUs from the Supplicant's STA
|
|
|
|
* to the Authenticator's STA."
|
|
|
|
*/
|
|
|
|
if (authenticator) {
|
|
|
|
memcpy(tk_buf + NL80211_TKIP_DATA_OFFSET_ENCR_KEY,
|
|
|
|
tk, 16);
|
|
|
|
memcpy(tk_buf + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY,
|
|
|
|
tk + 16, 8);
|
|
|
|
memcpy(tk_buf + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
|
|
|
|
tk + 24, 8);
|
|
|
|
} else {
|
|
|
|
memcpy(tk_buf + NL80211_TKIP_DATA_OFFSET_ENCR_KEY,
|
|
|
|
tk, 16);
|
|
|
|
memcpy(tk_buf + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
|
|
|
|
tk + 16, 8);
|
|
|
|
memcpy(tk_buf + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY,
|
|
|
|
tk + 24, 8);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
l_error("Unexpected cipher: %x", cipher);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-10-06 01:03:12 +02:00
|
|
|
static const uint8_t *netdev_choose_key_address(
|
|
|
|
struct netdev_handshake_state *nhs)
|
|
|
|
{
|
|
|
|
return (nhs->super.authenticator) ? nhs->super.spa : nhs->super.aa;
|
|
|
|
}
|
|
|
|
|
2020-04-02 07:41:02 +02:00
|
|
|
static void netdev_set_gtk(struct handshake_state *hs, uint16_t key_index,
|
2016-06-16 21:45:56 +02:00
|
|
|
const uint8_t *gtk, uint8_t gtk_len,
|
|
|
|
const uint8_t *rsc, uint8_t rsc_len,
|
2018-06-22 02:32:55 +02:00
|
|
|
uint32_t cipher)
|
2016-06-16 21:45:56 +02:00
|
|
|
{
|
2018-06-22 02:32:55 +02:00
|
|
|
struct netdev_handshake_state *nhs =
|
2019-04-03 18:47:09 +02:00
|
|
|
l_container_of(hs, struct netdev_handshake_state, super);
|
2018-06-22 02:32:55 +02:00
|
|
|
struct netdev *netdev = nhs->netdev;
|
2016-06-16 21:45:56 +02:00
|
|
|
uint8_t gtk_buf[32];
|
|
|
|
struct l_genl_msg *msg;
|
2018-10-08 22:44:12 +02:00
|
|
|
const uint8_t *addr = (netdev->type == NL80211_IFTYPE_ADHOC) ?
|
|
|
|
nhs->super.aa : NULL;
|
2016-06-16 21:45:56 +02:00
|
|
|
|
netdev: signal handshake complete after setting all keys
Currently, netdev triggers the HANDSHAKE_COMPLETE event after completing
the SET_STATION (after setting the pairwise key). Depending on the timing
this may happen before the GTK/IGTK are set which will result in group
traffic not working initially (the GTK/IGTK would still get set, but group
traffic would not work immediately after DBus said you were connected, this
mainly poses a problem with autotests).
In order to fix this, several flags were added in netdev_handshake_state:
ptk_installed, gtk_installed, igtk_installed, and completed. Each of these
flags are set true when their respective keys are set, and in each key
callback we try to trigger the handshake complete event (assuming all the
flags are true). Initially the gtk/igtk flags are set to true, for reasons
explained below.
In the WPA2 case, all the key setter functions are called sequentially from
eapol. With this change, the PTK is now set AFTER the gtk/igtk. This is
because the gtk/igtk are optional and only set if group traffic is allowed.
If the gtk/igtk are not used, we set the PTK and can immediately trigger the
handshake complete event (since gtk_installed/igtk_installed are initialized
as true). When the gtk/igtk are being set, we immediately set their flags to
false and wait for their callbacks in addition to the PTK callback. Doing it
this way handles both group traffic and non group traffic paths.
WPA1 throws a wrench into this since the group keys are obtained in a
separate handshake. For this case a new flag was added to the handshake_state,
'wait_for_gtk'. This allows netdev to set the PTK after the initial 4-way,
but still wait for the gtk/igtk setters to get called before triggering the
handshake complete event. As a precaution, netdev sets a timeout that will
trigger if the gtk/igtk setters are never called. In this case we can still
complete the connection, but print a warning that group traffic will not be
allowed.
2018-10-26 18:44:58 +02:00
|
|
|
nhs->gtk_installed = false;
|
|
|
|
|
2016-06-16 21:45:56 +02:00
|
|
|
l_debug("%d", netdev->index);
|
|
|
|
|
2017-01-31 03:42:54 +01:00
|
|
|
if (crypto_cipher_key_len(cipher) != gtk_len) {
|
|
|
|
l_error("Unexpected key length: %d", gtk_len);
|
2019-02-27 21:22:42 +01:00
|
|
|
netdev_setting_keys_failed(nhs, -ERANGE);
|
2017-01-31 03:42:54 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-06-20 20:05:13 +02:00
|
|
|
if (!netdev_copy_tk(gtk_buf, gtk, cipher, false)) {
|
2019-02-27 21:22:42 +01:00
|
|
|
netdev_setting_keys_failed(nhs, -ENOENT);
|
2016-06-16 21:45:56 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
netdev: signal handshake complete after setting all keys
Currently, netdev triggers the HANDSHAKE_COMPLETE event after completing
the SET_STATION (after setting the pairwise key). Depending on the timing
this may happen before the GTK/IGTK are set which will result in group
traffic not working initially (the GTK/IGTK would still get set, but group
traffic would not work immediately after DBus said you were connected, this
mainly poses a problem with autotests).
In order to fix this, several flags were added in netdev_handshake_state:
ptk_installed, gtk_installed, igtk_installed, and completed. Each of these
flags are set true when their respective keys are set, and in each key
callback we try to trigger the handshake complete event (assuming all the
flags are true). Initially the gtk/igtk flags are set to true, for reasons
explained below.
In the WPA2 case, all the key setter functions are called sequentially from
eapol. With this change, the PTK is now set AFTER the gtk/igtk. This is
because the gtk/igtk are optional and only set if group traffic is allowed.
If the gtk/igtk are not used, we set the PTK and can immediately trigger the
handshake complete event (since gtk_installed/igtk_installed are initialized
as true). When the gtk/igtk are being set, we immediately set their flags to
false and wait for their callbacks in addition to the PTK callback. Doing it
this way handles both group traffic and non group traffic paths.
WPA1 throws a wrench into this since the group keys are obtained in a
separate handshake. For this case a new flag was added to the handshake_state,
'wait_for_gtk'. This allows netdev to set the PTK after the initial 4-way,
but still wait for the gtk/igtk setters to get called before triggering the
handshake complete event. As a precaution, netdev sets a timeout that will
trigger if the gtk/igtk setters are never called. In this case we can still
complete the connection, but print a warning that group traffic will not be
allowed.
2018-10-26 18:44:58 +02:00
|
|
|
if (hs->wait_for_gtk) {
|
|
|
|
l_timeout_remove(netdev->group_handshake_timeout);
|
|
|
|
netdev->group_handshake_timeout = NULL;
|
|
|
|
}
|
|
|
|
|
2018-10-08 22:44:09 +02:00
|
|
|
msg = nl80211_build_new_key_group(netdev->index, cipher, key_index,
|
2018-10-08 22:44:12 +02:00
|
|
|
gtk_buf, gtk_len, rsc, rsc_len, addr);
|
2018-10-08 22:44:09 +02:00
|
|
|
|
2018-06-22 02:32:55 +02:00
|
|
|
nhs->group_new_key_cmd_id =
|
2016-06-16 21:45:56 +02:00
|
|
|
l_genl_family_send(nl80211, msg, netdev_new_group_key_cb,
|
2018-06-22 02:32:55 +02:00
|
|
|
nhs, NULL);
|
2016-06-16 21:45:56 +02:00
|
|
|
|
2018-06-22 02:32:55 +02:00
|
|
|
if (nhs->group_new_key_cmd_id > 0)
|
2016-06-16 21:45:56 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
l_genl_msg_unref(msg);
|
2019-02-27 21:22:42 +01:00
|
|
|
netdev_setting_keys_failed(nhs, -EIO);
|
2016-06-16 21:45:56 +02:00
|
|
|
}
|
|
|
|
|
2020-04-02 07:41:02 +02:00
|
|
|
static void netdev_set_igtk(struct handshake_state *hs, uint16_t key_index,
|
2016-10-28 23:49:54 +02:00
|
|
|
const uint8_t *igtk, uint8_t igtk_len,
|
|
|
|
const uint8_t *ipn, uint8_t ipn_len,
|
2018-06-22 02:32:55 +02:00
|
|
|
uint32_t cipher)
|
2016-10-28 23:49:54 +02:00
|
|
|
{
|
2018-06-22 02:32:55 +02:00
|
|
|
struct netdev_handshake_state *nhs =
|
2019-04-03 18:47:09 +02:00
|
|
|
l_container_of(hs, struct netdev_handshake_state, super);
|
2016-10-28 23:49:54 +02:00
|
|
|
uint8_t igtk_buf[16];
|
2018-06-22 02:32:55 +02:00
|
|
|
struct netdev *netdev = nhs->netdev;
|
2016-10-28 23:49:54 +02:00
|
|
|
struct l_genl_msg *msg;
|
|
|
|
|
netdev: signal handshake complete after setting all keys
Currently, netdev triggers the HANDSHAKE_COMPLETE event after completing
the SET_STATION (after setting the pairwise key). Depending on the timing
this may happen before the GTK/IGTK are set which will result in group
traffic not working initially (the GTK/IGTK would still get set, but group
traffic would not work immediately after DBus said you were connected, this
mainly poses a problem with autotests).
In order to fix this, several flags were added in netdev_handshake_state:
ptk_installed, gtk_installed, igtk_installed, and completed. Each of these
flags are set true when their respective keys are set, and in each key
callback we try to trigger the handshake complete event (assuming all the
flags are true). Initially the gtk/igtk flags are set to true, for reasons
explained below.
In the WPA2 case, all the key setter functions are called sequentially from
eapol. With this change, the PTK is now set AFTER the gtk/igtk. This is
because the gtk/igtk are optional and only set if group traffic is allowed.
If the gtk/igtk are not used, we set the PTK and can immediately trigger the
handshake complete event (since gtk_installed/igtk_installed are initialized
as true). When the gtk/igtk are being set, we immediately set their flags to
false and wait for their callbacks in addition to the PTK callback. Doing it
this way handles both group traffic and non group traffic paths.
WPA1 throws a wrench into this since the group keys are obtained in a
separate handshake. For this case a new flag was added to the handshake_state,
'wait_for_gtk'. This allows netdev to set the PTK after the initial 4-way,
but still wait for the gtk/igtk setters to get called before triggering the
handshake complete event. As a precaution, netdev sets a timeout that will
trigger if the gtk/igtk setters are never called. In this case we can still
complete the connection, but print a warning that group traffic will not be
allowed.
2018-10-26 18:44:58 +02:00
|
|
|
nhs->igtk_installed = false;
|
|
|
|
|
2016-10-28 23:49:54 +02:00
|
|
|
l_debug("%d", netdev->index);
|
|
|
|
|
2017-01-31 03:42:54 +01:00
|
|
|
if (crypto_cipher_key_len(cipher) != igtk_len) {
|
|
|
|
l_error("Unexpected key length: %d", igtk_len);
|
2019-02-27 21:22:42 +01:00
|
|
|
netdev_setting_keys_failed(nhs, -ERANGE);
|
2017-01-31 03:42:54 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-10-28 23:49:54 +02:00
|
|
|
switch (cipher) {
|
|
|
|
case CRYPTO_CIPHER_BIP:
|
|
|
|
memcpy(igtk_buf, igtk, 16);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
l_error("Unexpected cipher: %x", cipher);
|
2019-02-27 21:22:42 +01:00
|
|
|
netdev_setting_keys_failed(nhs, -ENOENT);
|
2016-10-28 23:49:54 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-04-02 07:45:55 +02:00
|
|
|
if (key_index == 0x0400 || key_index == 0x0500) {
|
|
|
|
l_warn("Received an invalid IGTK key index (%04hx)"
|
|
|
|
" that is likely in"
|
|
|
|
" big endian format. Trying to fix and"
|
|
|
|
" proceed anyway", key_index);
|
|
|
|
key_index = bswap_16(key_index);
|
|
|
|
}
|
|
|
|
|
2018-10-08 22:44:09 +02:00
|
|
|
msg = nl80211_build_new_key_group(netdev->index, cipher, key_index,
|
2018-10-08 22:44:12 +02:00
|
|
|
igtk_buf, igtk_len, ipn, ipn_len, NULL);
|
2018-10-08 22:44:09 +02:00
|
|
|
|
2018-06-22 02:32:55 +02:00
|
|
|
nhs->group_management_new_key_cmd_id =
|
2016-10-28 23:49:54 +02:00
|
|
|
l_genl_family_send(nl80211, msg,
|
|
|
|
netdev_new_group_management_key_cb,
|
2018-06-22 02:32:55 +02:00
|
|
|
nhs, NULL);
|
2016-10-28 23:49:54 +02:00
|
|
|
|
2018-06-22 02:32:55 +02:00
|
|
|
if (nhs->group_management_new_key_cmd_id > 0)
|
2016-10-28 23:49:54 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
l_genl_msg_unref(msg);
|
2019-02-27 21:22:42 +01:00
|
|
|
netdev_setting_keys_failed(nhs, -EIO);
|
2016-10-28 23:49:54 +02:00
|
|
|
}
|
|
|
|
|
2018-06-22 02:32:54 +02:00
|
|
|
static void netdev_new_pairwise_key_cb(struct l_genl_msg *msg, void *data)
|
2016-06-16 21:45:56 +02:00
|
|
|
{
|
2018-06-22 02:32:55 +02:00
|
|
|
struct netdev_handshake_state *nhs = data;
|
|
|
|
struct netdev *netdev = nhs->netdev;
|
2018-07-17 00:29:18 +02:00
|
|
|
const uint8_t *addr = netdev_choose_key_address(nhs);
|
2019-02-27 21:22:42 +01:00
|
|
|
int err = l_genl_msg_get_error(msg);
|
2016-06-16 21:45:56 +02:00
|
|
|
|
2018-06-22 02:32:55 +02:00
|
|
|
nhs->pairwise_new_key_cmd_id = 0;
|
2016-06-16 21:45:56 +02:00
|
|
|
|
2019-02-27 21:22:42 +01:00
|
|
|
if (err < 0) {
|
2020-04-02 07:30:17 +02:00
|
|
|
const char *ext_error = l_genl_msg_get_extended_error(msg);
|
|
|
|
|
|
|
|
l_error("New Key for Pairwise Key failed for ifindex: %d:%s",
|
|
|
|
netdev->index,
|
|
|
|
ext_error ? ext_error : strerror(-err));
|
2017-10-21 01:27:20 +02:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2018-01-15 14:31:22 +01:00
|
|
|
/*
|
|
|
|
* Set the AUTHORIZED flag using a SET_STATION command even if
|
|
|
|
* we're already operational, it will not hurt during re-keying
|
|
|
|
* and is necessary after an FT.
|
|
|
|
*/
|
2018-10-08 22:44:09 +02:00
|
|
|
msg = nl80211_build_set_station_authorized(netdev->index, addr);
|
2017-10-21 01:27:20 +02:00
|
|
|
|
2018-06-22 02:32:55 +02:00
|
|
|
nhs->set_station_cmd_id =
|
2017-10-21 01:27:21 +02:00
|
|
|
l_genl_family_send(nl80211, msg, netdev_set_station_cb,
|
2018-06-22 02:32:55 +02:00
|
|
|
nhs, NULL);
|
|
|
|
if (nhs->set_station_cmd_id > 0)
|
2017-10-21 01:27:21 +02:00
|
|
|
return;
|
2017-10-21 01:27:20 +02:00
|
|
|
|
2017-10-21 01:27:21 +02:00
|
|
|
l_genl_msg_unref(msg);
|
2019-02-27 21:22:42 +01:00
|
|
|
err = -EIO;
|
2017-10-21 01:27:20 +02:00
|
|
|
error:
|
2019-02-27 21:22:42 +01:00
|
|
|
netdev_setting_keys_failed(nhs, err);
|
2016-06-16 21:45:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct l_genl_msg *netdev_build_cmd_new_key_pairwise(
|
|
|
|
struct netdev *netdev,
|
|
|
|
uint32_t cipher,
|
|
|
|
const uint8_t *aa,
|
|
|
|
const uint8_t *tk,
|
|
|
|
size_t tk_len)
|
|
|
|
{
|
|
|
|
uint8_t key_id = 0;
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
|
|
|
|
msg = l_genl_msg_new_sized(NL80211_CMD_NEW_KEY, 512);
|
|
|
|
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_KEY_DATA, tk_len, tk);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_KEY_CIPHER, 4, &cipher);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, aa);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_KEY_IDX, 1, &key_id);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
netdev: signal handshake complete after setting all keys
Currently, netdev triggers the HANDSHAKE_COMPLETE event after completing
the SET_STATION (after setting the pairwise key). Depending on the timing
this may happen before the GTK/IGTK are set which will result in group
traffic not working initially (the GTK/IGTK would still get set, but group
traffic would not work immediately after DBus said you were connected, this
mainly poses a problem with autotests).
In order to fix this, several flags were added in netdev_handshake_state:
ptk_installed, gtk_installed, igtk_installed, and completed. Each of these
flags are set true when their respective keys are set, and in each key
callback we try to trigger the handshake complete event (assuming all the
flags are true). Initially the gtk/igtk flags are set to true, for reasons
explained below.
In the WPA2 case, all the key setter functions are called sequentially from
eapol. With this change, the PTK is now set AFTER the gtk/igtk. This is
because the gtk/igtk are optional and only set if group traffic is allowed.
If the gtk/igtk are not used, we set the PTK and can immediately trigger the
handshake complete event (since gtk_installed/igtk_installed are initialized
as true). When the gtk/igtk are being set, we immediately set their flags to
false and wait for their callbacks in addition to the PTK callback. Doing it
this way handles both group traffic and non group traffic paths.
WPA1 throws a wrench into this since the group keys are obtained in a
separate handshake. For this case a new flag was added to the handshake_state,
'wait_for_gtk'. This allows netdev to set the PTK after the initial 4-way,
but still wait for the gtk/igtk setters to get called before triggering the
handshake complete event. As a precaution, netdev sets a timeout that will
trigger if the gtk/igtk setters are never called. In this case we can still
complete the connection, but print a warning that group traffic will not be
allowed.
2018-10-26 18:44:58 +02:00
|
|
|
static void netdev_group_timeout_cb(struct l_timeout *timeout, void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev_handshake_state *nhs = user_data;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There was a problem with the ptk, this should have triggered a key
|
|
|
|
* setting failure event already.
|
|
|
|
*/
|
|
|
|
if (!nhs->ptk_installed)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this happens, we never completed the group handshake. We can still
|
|
|
|
* complete the connection, but we will not have group traffic.
|
|
|
|
*/
|
|
|
|
l_warn("completing connection with no group traffic on ifindex %d",
|
|
|
|
nhs->netdev->index);
|
|
|
|
|
|
|
|
nhs->complete = true;
|
2019-10-28 15:04:57 +01:00
|
|
|
handshake_event(&nhs->super, HANDSHAKE_EVENT_COMPLETE);
|
netdev: signal handshake complete after setting all keys
Currently, netdev triggers the HANDSHAKE_COMPLETE event after completing
the SET_STATION (after setting the pairwise key). Depending on the timing
this may happen before the GTK/IGTK are set which will result in group
traffic not working initially (the GTK/IGTK would still get set, but group
traffic would not work immediately after DBus said you were connected, this
mainly poses a problem with autotests).
In order to fix this, several flags were added in netdev_handshake_state:
ptk_installed, gtk_installed, igtk_installed, and completed. Each of these
flags are set true when their respective keys are set, and in each key
callback we try to trigger the handshake complete event (assuming all the
flags are true). Initially the gtk/igtk flags are set to true, for reasons
explained below.
In the WPA2 case, all the key setter functions are called sequentially from
eapol. With this change, the PTK is now set AFTER the gtk/igtk. This is
because the gtk/igtk are optional and only set if group traffic is allowed.
If the gtk/igtk are not used, we set the PTK and can immediately trigger the
handshake complete event (since gtk_installed/igtk_installed are initialized
as true). When the gtk/igtk are being set, we immediately set their flags to
false and wait for their callbacks in addition to the PTK callback. Doing it
this way handles both group traffic and non group traffic paths.
WPA1 throws a wrench into this since the group keys are obtained in a
separate handshake. For this case a new flag was added to the handshake_state,
'wait_for_gtk'. This allows netdev to set the PTK after the initial 4-way,
but still wait for the gtk/igtk setters to get called before triggering the
handshake complete event. As a precaution, netdev sets a timeout that will
trigger if the gtk/igtk setters are never called. In this case we can still
complete the connection, but print a warning that group traffic will not be
allowed.
2018-10-26 18:44:58 +02:00
|
|
|
|
|
|
|
netdev_connect_ok(nhs->netdev);
|
|
|
|
}
|
|
|
|
|
2018-06-22 02:32:55 +02:00
|
|
|
static void netdev_set_tk(struct handshake_state *hs,
|
|
|
|
const uint8_t *tk, uint32_t cipher)
|
2016-06-16 21:45:56 +02:00
|
|
|
{
|
2018-06-22 02:32:55 +02:00
|
|
|
struct netdev_handshake_state *nhs =
|
2019-04-03 18:47:09 +02:00
|
|
|
l_container_of(hs, struct netdev_handshake_state, super);
|
2016-06-16 21:45:56 +02:00
|
|
|
uint8_t tk_buf[32];
|
2018-06-22 02:32:55 +02:00
|
|
|
struct netdev *netdev = nhs->netdev;
|
2016-06-16 21:45:56 +02:00
|
|
|
struct l_genl_msg *msg;
|
2018-07-17 00:29:18 +02:00
|
|
|
const uint8_t *addr = netdev_choose_key_address(nhs);
|
2019-02-27 21:22:42 +01:00
|
|
|
int err;
|
2018-07-17 00:29:18 +02:00
|
|
|
|
netdev: signal handshake complete after setting all keys
Currently, netdev triggers the HANDSHAKE_COMPLETE event after completing
the SET_STATION (after setting the pairwise key). Depending on the timing
this may happen before the GTK/IGTK are set which will result in group
traffic not working initially (the GTK/IGTK would still get set, but group
traffic would not work immediately after DBus said you were connected, this
mainly poses a problem with autotests).
In order to fix this, several flags were added in netdev_handshake_state:
ptk_installed, gtk_installed, igtk_installed, and completed. Each of these
flags are set true when their respective keys are set, and in each key
callback we try to trigger the handshake complete event (assuming all the
flags are true). Initially the gtk/igtk flags are set to true, for reasons
explained below.
In the WPA2 case, all the key setter functions are called sequentially from
eapol. With this change, the PTK is now set AFTER the gtk/igtk. This is
because the gtk/igtk are optional and only set if group traffic is allowed.
If the gtk/igtk are not used, we set the PTK and can immediately trigger the
handshake complete event (since gtk_installed/igtk_installed are initialized
as true). When the gtk/igtk are being set, we immediately set their flags to
false and wait for their callbacks in addition to the PTK callback. Doing it
this way handles both group traffic and non group traffic paths.
WPA1 throws a wrench into this since the group keys are obtained in a
separate handshake. For this case a new flag was added to the handshake_state,
'wait_for_gtk'. This allows netdev to set the PTK after the initial 4-way,
but still wait for the gtk/igtk setters to get called before triggering the
handshake complete event. As a precaution, netdev sets a timeout that will
trigger if the gtk/igtk setters are never called. In this case we can still
complete the connection, but print a warning that group traffic will not be
allowed.
2018-10-26 18:44:58 +02:00
|
|
|
/*
|
|
|
|
* WPA1 does the group handshake after the 4-way finishes so we can't
|
|
|
|
* rely on the gtk/igtk being set immediately after the ptk. Since
|
|
|
|
* 'gtk_installed' is initially set to true (to handle NO_GROUP_TRAFFIC)
|
|
|
|
* we must set it false so we don't notify that the connection was
|
|
|
|
* successful until we get the gtk/igtk callbacks. Note that we do not
|
|
|
|
* need to set igtk_installed false because the igtk could not happen at
|
|
|
|
* all.
|
|
|
|
*/
|
|
|
|
if (hs->wait_for_gtk) {
|
|
|
|
nhs->gtk_installed = false;
|
|
|
|
|
|
|
|
netdev->group_handshake_timeout = l_timeout_create(2,
|
|
|
|
netdev_group_timeout_cb, nhs, NULL);
|
|
|
|
}
|
|
|
|
|
2018-07-17 00:29:18 +02:00
|
|
|
/*
|
|
|
|
* 802.11 Section 4.10.4.3:
|
|
|
|
* Because in an IBSS there are two 4-way handshakes between
|
|
|
|
* any two Supplicants and Authenticators, the pairwise key used
|
|
|
|
* between any two STAs is from the 4-way handshake initiated
|
|
|
|
* by the STA Authenticator with the higher MAC address...
|
|
|
|
*/
|
|
|
|
if (netdev->type == NL80211_IFTYPE_ADHOC &&
|
2018-10-26 18:44:59 +02:00
|
|
|
memcmp(nhs->super.aa, nhs->super.spa, 6) < 0) {
|
|
|
|
nhs->ptk_installed = true;
|
|
|
|
try_handshake_complete(nhs);
|
2018-07-17 00:29:18 +02:00
|
|
|
return;
|
2018-10-26 18:44:59 +02:00
|
|
|
}
|
2016-06-16 21:45:56 +02:00
|
|
|
|
|
|
|
l_debug("%d", netdev->index);
|
|
|
|
|
2019-02-27 21:22:42 +01:00
|
|
|
err = -ENOENT;
|
2018-06-20 20:05:13 +02:00
|
|
|
if (!netdev_copy_tk(tk_buf, tk, cipher, false))
|
|
|
|
goto invalid_key;
|
2016-06-16 21:45:56 +02:00
|
|
|
|
2018-06-22 20:13:28 +02:00
|
|
|
msg = netdev_build_cmd_new_key_pairwise(netdev, cipher, addr, tk_buf,
|
2016-06-16 21:45:56 +02:00
|
|
|
crypto_cipher_key_len(cipher));
|
2018-06-22 02:32:55 +02:00
|
|
|
nhs->pairwise_new_key_cmd_id =
|
2018-06-22 02:32:54 +02:00
|
|
|
l_genl_family_send(nl80211, msg, netdev_new_pairwise_key_cb,
|
2018-06-22 02:32:55 +02:00
|
|
|
nhs, NULL);
|
|
|
|
if (nhs->pairwise_new_key_cmd_id > 0)
|
2016-06-16 21:45:56 +02:00
|
|
|
return;
|
|
|
|
|
2019-02-27 21:22:42 +01:00
|
|
|
err = -EIO;
|
2016-06-16 21:45:56 +02:00
|
|
|
l_genl_msg_unref(msg);
|
2018-06-20 20:05:13 +02:00
|
|
|
invalid_key:
|
2019-02-27 21:22:42 +01:00
|
|
|
netdev_setting_keys_failed(nhs, err);
|
2016-06-16 21:45:56 +02:00
|
|
|
}
|
|
|
|
|
2018-07-03 23:36:33 +02:00
|
|
|
void netdev_handshake_failed(struct handshake_state *hs, uint16_t reason_code)
|
2016-06-16 18:35:45 +02:00
|
|
|
{
|
2018-07-03 23:36:33 +02:00
|
|
|
struct netdev_handshake_state *nhs =
|
2019-04-03 18:47:09 +02:00
|
|
|
l_container_of(hs, struct netdev_handshake_state, super);
|
2018-07-03 23:36:33 +02:00
|
|
|
struct netdev *netdev = nhs->netdev;
|
2016-06-16 18:35:45 +02:00
|
|
|
struct l_genl_msg *msg;
|
|
|
|
|
2018-05-02 00:05:42 +02:00
|
|
|
l_error("4-Way handshake failed for ifindex: %d, reason: %u",
|
2018-06-27 23:08:24 +02:00
|
|
|
netdev->index, reason_code);
|
2016-06-16 18:35:45 +02:00
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
netdev->sm = NULL;
|
2016-07-20 00:45:48 +02:00
|
|
|
|
2016-09-21 23:19:47 +02:00
|
|
|
netdev->result = NETDEV_RESULT_HANDSHAKE_FAILED;
|
2019-02-28 18:32:40 +01:00
|
|
|
netdev->last_code = reason_code;
|
2018-06-22 20:13:28 +02:00
|
|
|
|
2018-07-03 23:36:33 +02:00
|
|
|
switch (netdev->type) {
|
|
|
|
case NL80211_IFTYPE_STATION:
|
2019-10-21 15:55:02 +02:00
|
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
2018-07-03 23:36:33 +02:00
|
|
|
msg = netdev_build_cmd_disconnect(netdev, reason_code);
|
|
|
|
netdev->disconnect_cmd_id = l_genl_family_send(nl80211, msg,
|
2019-01-24 21:56:23 +01:00
|
|
|
netdev_disconnect_cb,
|
2018-07-03 23:36:33 +02:00
|
|
|
netdev, NULL);
|
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_AP:
|
2019-10-21 15:55:02 +02:00
|
|
|
case NL80211_IFTYPE_P2P_GO:
|
2018-07-03 23:36:33 +02:00
|
|
|
msg = netdev_build_cmd_del_station(netdev, nhs->super.spa,
|
|
|
|
reason_code, false);
|
|
|
|
if (!l_genl_family_send(nl80211, msg, NULL, NULL, NULL))
|
|
|
|
l_error("error sending DEL_STATION");
|
|
|
|
}
|
2020-07-15 23:29:17 +02:00
|
|
|
|
|
|
|
if (netdev->work.id)
|
|
|
|
wiphy_radio_work_done(netdev->wiphy, netdev->work.id);
|
2016-06-16 18:35:45 +02:00
|
|
|
}
|
|
|
|
|
2016-06-23 00:49:16 +02:00
|
|
|
static void hardware_rekey_cb(struct l_genl_msg *msg, void *data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = data;
|
|
|
|
int err;
|
|
|
|
|
2018-10-19 23:19:26 +02:00
|
|
|
netdev->rekey_offload_cmd_id = 0;
|
|
|
|
|
2016-06-23 00:49:16 +02:00
|
|
|
err = l_genl_msg_get_error(msg);
|
|
|
|
if (err < 0) {
|
|
|
|
if (err == -EOPNOTSUPP) {
|
|
|
|
l_error("hardware_rekey not supported");
|
|
|
|
netdev->rekey_offload_support = false;
|
|
|
|
}
|
2018-10-20 17:36:42 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* TODO: Ignore all other errors for now, until WoWLAN is
|
|
|
|
* supported properly
|
|
|
|
*/
|
2016-06-23 00:49:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct l_genl_msg *netdev_build_cmd_replay_counter(struct netdev *netdev,
|
2018-11-19 19:09:27 +01:00
|
|
|
const uint8_t *kek,
|
|
|
|
const uint8_t *kck,
|
|
|
|
uint64_t replay_ctr)
|
2016-06-23 00:49:16 +02:00
|
|
|
{
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
|
|
|
|
msg = l_genl_msg_new_sized(NL80211_CMD_SET_REKEY_OFFLOAD, 512);
|
|
|
|
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
|
|
|
|
|
|
|
l_genl_msg_enter_nested(msg, NL80211_ATTR_REKEY_DATA);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_REKEY_DATA_KEK,
|
|
|
|
NL80211_KEK_LEN, kek);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_REKEY_DATA_KCK,
|
|
|
|
NL80211_KCK_LEN, kck);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_REKEY_DATA_REPLAY_CTR,
|
|
|
|
NL80211_REPLAY_CTR_LEN, &replay_ctr);
|
|
|
|
|
|
|
|
l_genl_msg_leave_nested(msg);
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_set_rekey_offload(uint32_t ifindex,
|
|
|
|
const uint8_t *kek,
|
|
|
|
const uint8_t *kck,
|
|
|
|
uint64_t replay_counter,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev;
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
|
|
|
|
netdev = netdev_find(ifindex);
|
|
|
|
if (!netdev)
|
|
|
|
return;
|
|
|
|
|
2018-06-22 20:13:28 +02:00
|
|
|
if (netdev->type != NL80211_IFTYPE_STATION)
|
|
|
|
return;
|
|
|
|
|
2016-06-23 00:49:16 +02:00
|
|
|
if (!netdev->rekey_offload_support)
|
|
|
|
return;
|
|
|
|
|
|
|
|
l_debug("%d", netdev->index);
|
2018-10-19 23:19:26 +02:00
|
|
|
msg = netdev_build_cmd_replay_counter(netdev, kek, kck, replay_counter);
|
|
|
|
netdev->rekey_offload_cmd_id = l_genl_family_send(nl80211, msg,
|
|
|
|
hardware_rekey_cb,
|
|
|
|
netdev, NULL);
|
2016-06-23 00:49:16 +02:00
|
|
|
}
|
|
|
|
|
2019-09-14 00:26:31 +02:00
|
|
|
static void netdev_qos_map_cb(struct l_genl_msg *msg, void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
int err = l_genl_msg_get_error(msg);
|
2020-04-02 07:30:17 +02:00
|
|
|
const char *ext_error;
|
2019-09-14 00:26:31 +02:00
|
|
|
|
|
|
|
netdev->qos_map_cmd_id = 0;
|
2020-04-02 07:30:17 +02:00
|
|
|
|
|
|
|
if (err >= 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ext_error = l_genl_msg_get_extended_error(msg);
|
|
|
|
l_error("Couuld not set QoS Map in kernel: %s",
|
|
|
|
ext_error ? ext_error : strerror(-err));
|
2019-09-14 00:26:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TODO: Fix this in the kernel:
|
|
|
|
*
|
|
|
|
* The QoS Map is really of no use to IWD. The kernel requires it to map QoS
|
|
|
|
* network values properly to what it puts into the IP header. The way we have
|
|
|
|
* to let the kernel know is to receive the IE, then give it right back...
|
|
|
|
*
|
|
|
|
* The kernel/driver/firmware *could* simply obtain this information as the
|
2020-01-21 07:21:38 +01:00
|
|
|
* frame comes in and not require userspace to forward it back... but that's a
|
2019-09-14 00:26:31 +02:00
|
|
|
* battle for another day.
|
|
|
|
*/
|
|
|
|
static void netdev_send_qos_map_set(struct netdev *netdev,
|
|
|
|
const uint8_t *qos_set, size_t qos_len)
|
|
|
|
{
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
|
|
|
|
if (!wiphy_supports_qos_set_map(netdev->wiphy)) {
|
|
|
|
l_warn("AP sent QoS Map, but capability was not advertised!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Since this IE comes in on either a management frame or during
|
|
|
|
* Association response we could have potentially already set this.
|
|
|
|
*/
|
|
|
|
if (netdev->qos_map_cmd_id)
|
|
|
|
return;
|
|
|
|
|
|
|
|
msg = l_genl_msg_new_sized(NL80211_CMD_SET_QOS_MAP, 128 + qos_len);
|
|
|
|
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_QOS_MAP, qos_len, qos_set);
|
|
|
|
|
|
|
|
netdev->qos_map_cmd_id = l_genl_family_send(nl80211, msg,
|
|
|
|
netdev_qos_map_cb,
|
|
|
|
netdev, NULL);
|
|
|
|
}
|
|
|
|
|
2018-11-19 19:09:27 +01:00
|
|
|
static void netdev_connect_event(struct l_genl_msg *msg, struct netdev *netdev)
|
2016-06-16 17:54:20 +02:00
|
|
|
{
|
2016-06-27 23:35:31 +02:00
|
|
|
struct l_genl_attr attr;
|
|
|
|
uint16_t type, len;
|
|
|
|
const void *data;
|
|
|
|
const uint16_t *status_code = NULL;
|
2019-03-19 01:25:16 +01:00
|
|
|
const uint8_t *ies = NULL;
|
|
|
|
size_t ies_len = 0;
|
|
|
|
struct ie_tlv_iter iter;
|
2019-04-15 22:32:28 +02:00
|
|
|
const uint8_t *resp_ies = NULL;
|
|
|
|
size_t resp_ies_len;
|
2016-06-16 17:54:20 +02:00
|
|
|
|
|
|
|
l_debug("");
|
|
|
|
|
2019-03-08 23:09:42 +01:00
|
|
|
if (netdev->aborting)
|
|
|
|
return;
|
|
|
|
|
netdev: process association in netdev_associate_event
Apart from OWE, the association event was disregarded and all association
processing was done in netdev_connect_event. This led to
netdev_connect_event having to handle all the logic of both success and
failure, as well as parsing the association for FT and OWE. Also, without
checking the status code in the associate frame there is the potential
for the kernel to think we are connected even if association failed
(e.g. rogue AP).
This change introduces two flags into netdev, expect_connect_failure and
ignore_connect_event. All the FT processing that was once in
netdev_connect_event has now been moved into netdev_associate_event, as
well as non-FT associate frame processing. The connect event now only
handles failure cases for soft/half MAC cards.
Note: Since fullmac cards rely on the connect event, the eapol_start
and netdev_connect_ok were left in netdev_connect_event. Since neither
auth/assoc events come in on fullmac we shouldn't have any conflict with
the new flags.
Once a connection has completed association, EAPoL is started from
netdev_associate_event (if required) and the ignore_connect_event flag can
be set. This will bypass the connect event.
If a connection has failed during association for whatever reason, we can
set expect_connect_failure, the netdev reason, and the MPDU status code.
This allows netdev_connect_event to both handle the error, and, if required,
send a deauth telling the kernel that we have failed (protecting against the
rogue AP situation).
2019-03-05 22:42:33 +01:00
|
|
|
if (netdev->ignore_connect_event)
|
2019-03-01 23:58:12 +01:00
|
|
|
return;
|
|
|
|
|
2017-03-24 17:44:26 +01:00
|
|
|
if (!netdev->connected) {
|
|
|
|
l_warn("Unexpected connection related event -- "
|
|
|
|
"is another supplicant running?");
|
2016-08-02 22:48:11 +02:00
|
|
|
return;
|
2017-03-24 17:44:26 +01:00
|
|
|
}
|
2016-08-02 22:48:11 +02:00
|
|
|
|
2016-06-27 23:35:31 +02:00
|
|
|
if (!l_genl_attr_init(&attr, msg)) {
|
|
|
|
l_debug("attr init failed");
|
2016-06-16 17:54:20 +02:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2016-06-27 23:35:31 +02:00
|
|
|
while (l_genl_attr_next(&attr, &type, &len, &data)) {
|
|
|
|
switch (type) {
|
|
|
|
case NL80211_ATTR_TIMED_OUT:
|
|
|
|
l_warn("authentication timed out");
|
|
|
|
goto error;
|
|
|
|
case NL80211_ATTR_STATUS_CODE:
|
|
|
|
if (len == sizeof(uint16_t))
|
|
|
|
status_code = data;
|
2016-11-02 23:46:16 +01:00
|
|
|
break;
|
2019-03-19 01:25:16 +01:00
|
|
|
case NL80211_ATTR_REQ_IE:
|
|
|
|
ies = data;
|
|
|
|
ies_len = len;
|
|
|
|
break;
|
2019-04-15 22:32:28 +02:00
|
|
|
case NL80211_ATTR_RESP_IE:
|
|
|
|
resp_ies = data;
|
|
|
|
resp_ies_len = len;
|
|
|
|
break;
|
2016-06-27 23:35:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
netdev: process association in netdev_associate_event
Apart from OWE, the association event was disregarded and all association
processing was done in netdev_connect_event. This led to
netdev_connect_event having to handle all the logic of both success and
failure, as well as parsing the association for FT and OWE. Also, without
checking the status code in the associate frame there is the potential
for the kernel to think we are connected even if association failed
(e.g. rogue AP).
This change introduces two flags into netdev, expect_connect_failure and
ignore_connect_event. All the FT processing that was once in
netdev_connect_event has now been moved into netdev_associate_event, as
well as non-FT associate frame processing. The connect event now only
handles failure cases for soft/half MAC cards.
Note: Since fullmac cards rely on the connect event, the eapol_start
and netdev_connect_ok were left in netdev_connect_event. Since neither
auth/assoc events come in on fullmac we shouldn't have any conflict with
the new flags.
Once a connection has completed association, EAPoL is started from
netdev_associate_event (if required) and the ignore_connect_event flag can
be set. This will bypass the connect event.
If a connection has failed during association for whatever reason, we can
set expect_connect_failure, the netdev reason, and the MPDU status code.
This allows netdev_connect_event to both handle the error, and, if required,
send a deauth telling the kernel that we have failed (protecting against the
rogue AP situation).
2019-03-05 22:42:33 +01:00
|
|
|
if (netdev->expect_connect_failure) {
|
|
|
|
/*
|
|
|
|
* The kernel may think we are connected when we are actually
|
|
|
|
* expecting a failure here, e.g. if Authenticate/Associate had
|
|
|
|
* previously failed. If so we need to deauth to let the kernel
|
|
|
|
* know.
|
|
|
|
*/
|
|
|
|
if (status_code && *status_code == 0)
|
|
|
|
goto deauth;
|
|
|
|
else
|
|
|
|
goto error;
|
2016-11-02 23:46:16 +01:00
|
|
|
}
|
|
|
|
|
netdev: process association in netdev_associate_event
Apart from OWE, the association event was disregarded and all association
processing was done in netdev_connect_event. This led to
netdev_connect_event having to handle all the logic of both success and
failure, as well as parsing the association for FT and OWE. Also, without
checking the status code in the associate frame there is the potential
for the kernel to think we are connected even if association failed
(e.g. rogue AP).
This change introduces two flags into netdev, expect_connect_failure and
ignore_connect_event. All the FT processing that was once in
netdev_connect_event has now been moved into netdev_associate_event, as
well as non-FT associate frame processing. The connect event now only
handles failure cases for soft/half MAC cards.
Note: Since fullmac cards rely on the connect event, the eapol_start
and netdev_connect_ok were left in netdev_connect_event. Since neither
auth/assoc events come in on fullmac we shouldn't have any conflict with
the new flags.
Once a connection has completed association, EAPoL is started from
netdev_associate_event (if required) and the ignore_connect_event flag can
be set. This will bypass the connect event.
If a connection has failed during association for whatever reason, we can
set expect_connect_failure, the netdev reason, and the MPDU status code.
This allows netdev_connect_event to both handle the error, and, if required,
send a deauth telling the kernel that we have failed (protecting against the
rogue AP situation).
2019-03-05 22:42:33 +01:00
|
|
|
/* AP Rejected the authenticate / associate */
|
|
|
|
if (!status_code || *status_code != 0)
|
2017-01-12 09:36:02 +01:00
|
|
|
goto error;
|
|
|
|
|
2019-10-18 00:30:11 +02:00
|
|
|
if (!ies)
|
|
|
|
goto process_resp_ies;
|
2019-03-19 01:25:16 +01:00
|
|
|
/*
|
|
|
|
* The driver may have modified the IEs we passed to CMD_CONNECT
|
|
|
|
* before sending them out, the actual IE sent is reflected in the
|
|
|
|
* ATTR_REQ_IE sequence. These are the values EAPoL will need to use.
|
|
|
|
*/
|
|
|
|
ie_tlv_iter_init(&iter, ies, ies_len);
|
|
|
|
|
|
|
|
while (ie_tlv_iter_next(&iter)) {
|
|
|
|
data = ie_tlv_iter_get_data(&iter);
|
|
|
|
|
|
|
|
switch (ie_tlv_iter_get_tag(&iter)) {
|
|
|
|
case IE_TYPE_RSN:
|
2019-06-07 21:17:00 +02:00
|
|
|
handshake_state_set_supplicant_ie(netdev->handshake,
|
2019-03-19 01:25:16 +01:00
|
|
|
data - 2);
|
|
|
|
break;
|
|
|
|
case IE_TYPE_VENDOR_SPECIFIC:
|
|
|
|
if (!is_ie_wpa_ie(data, ie_tlv_iter_get_length(&iter)))
|
|
|
|
break;
|
|
|
|
|
2019-06-07 21:17:00 +02:00
|
|
|
handshake_state_set_supplicant_ie(netdev->handshake,
|
2019-03-19 01:25:16 +01:00
|
|
|
data - 2);
|
|
|
|
break;
|
|
|
|
case IE_TYPE_MOBILITY_DOMAIN:
|
|
|
|
handshake_state_set_mde(netdev->handshake, data - 2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-18 00:30:11 +02:00
|
|
|
process_resp_ies:
|
2019-09-14 00:26:31 +02:00
|
|
|
if (resp_ies) {
|
2019-04-15 22:32:28 +02:00
|
|
|
const uint8_t *fte = NULL;
|
2019-09-14 00:26:31 +02:00
|
|
|
const uint8_t *qos_set = NULL;
|
|
|
|
size_t qos_len = 0;
|
2019-04-15 22:32:28 +02:00
|
|
|
struct ie_ft_info ft_info;
|
|
|
|
|
|
|
|
ie_tlv_iter_init(&iter, resp_ies, resp_ies_len);
|
|
|
|
|
|
|
|
while (ie_tlv_iter_next(&iter)) {
|
|
|
|
data = ie_tlv_iter_get_data(&iter);
|
|
|
|
|
|
|
|
switch (ie_tlv_iter_get_tag(&iter)) {
|
|
|
|
case IE_TYPE_FAST_BSS_TRANSITION:
|
|
|
|
fte = data - 2;
|
|
|
|
break;
|
2019-09-14 00:26:31 +02:00
|
|
|
case IE_TYPE_QOS_MAP_SET:
|
|
|
|
qos_set = data;
|
|
|
|
qos_len = ie_tlv_iter_get_length(&iter);
|
|
|
|
break;
|
2019-04-15 22:32:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-14 00:26:31 +02:00
|
|
|
/* FILS handles its own FT key derivation */
|
|
|
|
if (fte && !(netdev->handshake->akm_suite &
|
|
|
|
(IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256 |
|
|
|
|
IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384))) {
|
2019-05-23 00:24:00 +02:00
|
|
|
uint32_t kck_len =
|
|
|
|
handshake_state_get_kck_len(netdev->handshake);
|
2019-04-15 22:32:28 +02:00
|
|
|
/*
|
|
|
|
* If we are here, then most likely we have a FullMac
|
|
|
|
* hw performing initial mobility association. We need
|
|
|
|
* to set the FTE element or the handshake will fail
|
|
|
|
* The firmware accepted the FTE element, so do not
|
|
|
|
* sanitize the contents and just assume they're okay.
|
|
|
|
*/
|
|
|
|
if (ie_parse_fast_bss_transition_from_data(fte,
|
2019-05-23 00:24:00 +02:00
|
|
|
fte[1] + 2, kck_len, &ft_info) >= 0) {
|
2019-04-15 22:32:28 +02:00
|
|
|
handshake_state_set_fte(netdev->handshake, fte);
|
|
|
|
handshake_state_set_kh_ids(netdev->handshake,
|
|
|
|
ft_info.r0khid,
|
|
|
|
ft_info.r0khid_len,
|
|
|
|
ft_info.r1khid);
|
|
|
|
} else {
|
|
|
|
l_info("CMD_CONNECT Succeeded, but parsing FTE"
|
|
|
|
" failed. Expect handshake failure");
|
|
|
|
}
|
|
|
|
}
|
2019-09-14 00:26:31 +02:00
|
|
|
|
|
|
|
if (qos_set)
|
|
|
|
netdev_send_qos_map_set(netdev, qos_set, qos_len);
|
2019-04-15 22:32:28 +02:00
|
|
|
}
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
if (netdev->sm) {
|
2016-10-04 05:48:08 +02:00
|
|
|
/*
|
|
|
|
* Start processing EAPoL frames now that the state machine
|
|
|
|
* has all the input data even in FT mode.
|
|
|
|
*/
|
2017-10-27 23:41:01 +02:00
|
|
|
if (!eapol_start(netdev->sm))
|
|
|
|
goto error;
|
2017-05-30 14:26:18 +02:00
|
|
|
|
netdev: process association in netdev_associate_event
Apart from OWE, the association event was disregarded and all association
processing was done in netdev_connect_event. This led to
netdev_connect_event having to handle all the logic of both success and
failure, as well as parsing the association for FT and OWE. Also, without
checking the status code in the associate frame there is the potential
for the kernel to think we are connected even if association failed
(e.g. rogue AP).
This change introduces two flags into netdev, expect_connect_failure and
ignore_connect_event. All the FT processing that was once in
netdev_connect_event has now been moved into netdev_associate_event, as
well as non-FT associate frame processing. The connect event now only
handles failure cases for soft/half MAC cards.
Note: Since fullmac cards rely on the connect event, the eapol_start
and netdev_connect_ok were left in netdev_connect_event. Since neither
auth/assoc events come in on fullmac we shouldn't have any conflict with
the new flags.
Once a connection has completed association, EAPoL is started from
netdev_associate_event (if required) and the ignore_connect_event flag can
be set. This will bypass the connect event.
If a connection has failed during association for whatever reason, we can
set expect_connect_failure, the netdev reason, and the MPDU status code.
This allows netdev_connect_event to both handle the error, and, if required,
send a deauth telling the kernel that we have failed (protecting against the
rogue AP situation).
2019-03-05 22:42:33 +01:00
|
|
|
return;
|
2017-05-30 14:26:18 +02:00
|
|
|
}
|
2017-01-12 09:36:02 +01:00
|
|
|
|
2017-05-30 14:26:19 +02:00
|
|
|
netdev_connect_ok(netdev);
|
2016-06-16 17:54:20 +02:00
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
error:
|
2019-01-24 21:56:23 +01:00
|
|
|
netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED,
|
netdev: process association in netdev_associate_event
Apart from OWE, the association event was disregarded and all association
processing was done in netdev_connect_event. This led to
netdev_connect_event having to handle all the logic of both success and
failure, as well as parsing the association for FT and OWE. Also, without
checking the status code in the associate frame there is the potential
for the kernel to think we are connected even if association failed
(e.g. rogue AP).
This change introduces two flags into netdev, expect_connect_failure and
ignore_connect_event. All the FT processing that was once in
netdev_connect_event has now been moved into netdev_associate_event, as
well as non-FT associate frame processing. The connect event now only
handles failure cases for soft/half MAC cards.
Note: Since fullmac cards rely on the connect event, the eapol_start
and netdev_connect_ok were left in netdev_connect_event. Since neither
auth/assoc events come in on fullmac we shouldn't have any conflict with
the new flags.
Once a connection has completed association, EAPoL is started from
netdev_associate_event (if required) and the ignore_connect_event flag can
be set. This will bypass the connect event.
If a connection has failed during association for whatever reason, we can
set expect_connect_failure, the netdev reason, and the MPDU status code.
This allows netdev_connect_event to both handle the error, and, if required,
send a deauth telling the kernel that we have failed (protecting against the
rogue AP situation).
2019-03-05 22:42:33 +01:00
|
|
|
(status_code) ? *status_code :
|
|
|
|
MMPDU_STATUS_CODE_UNSPECIFIED);
|
|
|
|
return;
|
|
|
|
|
|
|
|
deauth:
|
|
|
|
msg = netdev_build_cmd_deauthenticate(netdev,
|
|
|
|
MMPDU_REASON_CODE_UNSPECIFIED);
|
|
|
|
netdev->disconnect_cmd_id = l_genl_family_send(nl80211,
|
|
|
|
msg,
|
|
|
|
netdev_disconnect_cb,
|
|
|
|
netdev, NULL);
|
2016-06-16 17:54:20 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 20:13:57 +02:00
|
|
|
static unsigned int ie_rsn_akm_suite_to_nl80211(enum ie_rsn_akm_suite akm)
|
2016-06-27 23:35:31 +02:00
|
|
|
{
|
2018-08-09 20:13:57 +02:00
|
|
|
switch (akm) {
|
|
|
|
case IE_RSN_AKM_SUITE_8021X:
|
|
|
|
return CRYPTO_AKM_8021X;
|
|
|
|
case IE_RSN_AKM_SUITE_PSK:
|
|
|
|
return CRYPTO_AKM_PSK;
|
|
|
|
case IE_RSN_AKM_SUITE_FT_OVER_8021X:
|
|
|
|
return CRYPTO_AKM_FT_OVER_8021X;
|
|
|
|
case IE_RSN_AKM_SUITE_FT_USING_PSK:
|
|
|
|
return CRYPTO_AKM_FT_USING_PSK;
|
|
|
|
case IE_RSN_AKM_SUITE_8021X_SHA256:
|
|
|
|
return CRYPTO_AKM_8021X_SHA256;
|
|
|
|
case IE_RSN_AKM_SUITE_PSK_SHA256:
|
|
|
|
return CRYPTO_AKM_PSK_SHA256;
|
|
|
|
case IE_RSN_AKM_SUITE_TDLS:
|
|
|
|
return CRYPTO_AKM_TDLS;
|
|
|
|
case IE_RSN_AKM_SUITE_SAE_SHA256:
|
|
|
|
return CRYPTO_AKM_SAE_SHA256;
|
|
|
|
case IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256:
|
|
|
|
return CRYPTO_AKM_FT_OVER_SAE_SHA256;
|
|
|
|
case IE_RSN_AKM_SUITE_AP_PEER_KEY_SHA256:
|
|
|
|
return CRYPTO_AKM_AP_PEER_KEY_SHA256;
|
|
|
|
case IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA256:
|
|
|
|
return CRYPTO_AKM_8021X_SUITE_B_SHA256;
|
|
|
|
case IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA384:
|
|
|
|
return CRYPTO_AKM_8021X_SUITE_B_SHA384;
|
|
|
|
case IE_RSN_AKM_SUITE_FT_OVER_8021X_SHA384:
|
|
|
|
return CRYPTO_AKM_FT_OVER_8021X_SHA384;
|
2019-04-04 22:43:13 +02:00
|
|
|
case IE_RSN_AKM_SUITE_FILS_SHA256:
|
|
|
|
return CRYPTO_AKM_FILS_SHA256;
|
|
|
|
case IE_RSN_AKM_SUITE_FILS_SHA384:
|
|
|
|
return CRYPTO_AKM_FILS_SHA384;
|
|
|
|
case IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256:
|
|
|
|
return CRYPTO_AKM_FT_OVER_FILS_SHA256;
|
|
|
|
case IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384:
|
|
|
|
return CRYPTO_AKM_FT_OVER_FILS_SHA384;
|
2018-11-16 23:22:44 +01:00
|
|
|
case IE_RSN_AKM_SUITE_OWE:
|
|
|
|
return CRYPTO_AKM_OWE;
|
2019-06-07 22:58:43 +02:00
|
|
|
case IE_RSN_AKM_SUITE_OSEN:
|
|
|
|
return CRYPTO_AKM_OSEN;
|
2018-08-09 20:13:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct l_genl_msg *netdev_build_cmd_associate_common(
|
|
|
|
struct netdev *netdev)
|
|
|
|
{
|
|
|
|
struct handshake_state *hs = netdev->handshake;
|
2018-08-25 03:54:24 +02:00
|
|
|
bool is_rsn = hs->supplicant_ie != NULL;
|
2018-08-09 20:13:57 +02:00
|
|
|
struct l_genl_msg *msg;
|
2016-06-27 23:35:31 +02:00
|
|
|
|
2017-01-12 09:36:01 +01:00
|
|
|
msg = l_genl_msg_new_sized(NL80211_CMD_ASSOCIATE, 600);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
2018-08-09 20:13:57 +02:00
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY_FREQ, 4,
|
|
|
|
&netdev->frequency);
|
2017-01-12 09:36:01 +01:00
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, hs->aa);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_SSID, hs->ssid_len, hs->ssid);
|
2017-05-22 10:49:48 +02:00
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_SOCKET_OWNER, 0, NULL);
|
2017-01-12 09:36:01 +01:00
|
|
|
|
|
|
|
if (is_rsn) {
|
|
|
|
uint32_t nl_cipher;
|
|
|
|
uint32_t nl_akm;
|
|
|
|
uint32_t wpa_version;
|
2018-08-09 20:13:57 +02:00
|
|
|
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_CONTROL_PORT, 0, NULL);
|
2017-01-12 09:36:01 +01:00
|
|
|
|
2018-08-09 22:16:45 +02:00
|
|
|
if (netdev->pae_over_nl80211)
|
|
|
|
l_genl_msg_append_attr(msg,
|
|
|
|
NL80211_ATTR_CONTROL_PORT_OVER_NL80211,
|
|
|
|
0, NULL);
|
|
|
|
|
2017-01-12 09:36:01 +01:00
|
|
|
if (hs->pairwise_cipher == IE_RSN_CIPHER_SUITE_CCMP)
|
|
|
|
nl_cipher = CRYPTO_CIPHER_CCMP;
|
|
|
|
else
|
|
|
|
nl_cipher = CRYPTO_CIPHER_TKIP;
|
|
|
|
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
|
|
|
|
4, &nl_cipher);
|
|
|
|
|
|
|
|
if (hs->group_cipher == IE_RSN_CIPHER_SUITE_CCMP)
|
|
|
|
nl_cipher = CRYPTO_CIPHER_CCMP;
|
|
|
|
else
|
|
|
|
nl_cipher = CRYPTO_CIPHER_TKIP;
|
|
|
|
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
|
|
|
|
4, &nl_cipher);
|
|
|
|
|
|
|
|
if (hs->mfp) {
|
|
|
|
uint32_t use_mfp = NL80211_MFP_REQUIRED;
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_USE_MFP,
|
|
|
|
4, &use_mfp);
|
|
|
|
}
|
|
|
|
|
2018-08-09 20:13:57 +02:00
|
|
|
nl_akm = ie_rsn_akm_suite_to_nl80211(hs->akm_suite);
|
|
|
|
if (nl_akm)
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_AKM_SUITES,
|
2017-01-12 09:36:01 +01:00
|
|
|
4, &nl_akm);
|
|
|
|
|
2018-08-09 20:13:57 +02:00
|
|
|
if (hs->wpa_ie)
|
|
|
|
wpa_version = NL80211_WPA_VERSION_1;
|
|
|
|
else
|
|
|
|
wpa_version = NL80211_WPA_VERSION_2;
|
|
|
|
|
2017-01-12 09:36:01 +01:00
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_WPA_VERSIONS,
|
|
|
|
4, &wpa_version);
|
2018-08-09 20:13:57 +02:00
|
|
|
}
|
2017-01-12 09:36:01 +01:00
|
|
|
|
2018-08-09 20:13:57 +02:00
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2017-01-12 09:36:01 +01:00
|
|
|
static void netdev_cmd_ft_reassociate_cb(struct l_genl_msg *msg,
|
|
|
|
void *user_data)
|
2016-06-15 17:54:13 +02:00
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
|
2016-08-05 14:25:34 +02:00
|
|
|
netdev->connect_cmd_id = 0;
|
|
|
|
|
2017-01-12 09:36:01 +01:00
|
|
|
if (l_genl_msg_get_error(msg) < 0) {
|
|
|
|
struct l_genl_msg *cmd_deauth;
|
2016-06-15 17:54:13 +02:00
|
|
|
|
2017-01-12 09:36:01 +01:00
|
|
|
netdev->result = NETDEV_RESULT_ASSOCIATION_FAILED;
|
2019-02-28 18:32:40 +01:00
|
|
|
netdev->last_code = MMPDU_STATUS_CODE_UNSPECIFIED;
|
2017-01-12 09:36:01 +01:00
|
|
|
cmd_deauth = netdev_build_cmd_deauthenticate(netdev,
|
2017-08-31 04:04:41 +02:00
|
|
|
MMPDU_REASON_CODE_UNSPECIFIED);
|
2017-01-12 09:36:01 +01:00
|
|
|
netdev->disconnect_cmd_id = l_genl_family_send(nl80211,
|
|
|
|
cmd_deauth,
|
2019-01-24 21:56:23 +01:00
|
|
|
netdev_disconnect_cb,
|
2017-01-12 09:36:01 +01:00
|
|
|
netdev, NULL);
|
|
|
|
}
|
|
|
|
}
|
2016-09-23 00:34:27 +02:00
|
|
|
|
2018-08-14 01:25:48 +02:00
|
|
|
static void netdev_authenticate_event(struct l_genl_msg *msg,
|
|
|
|
struct netdev *netdev)
|
|
|
|
{
|
|
|
|
struct l_genl_attr attr;
|
|
|
|
uint16_t type, len;
|
|
|
|
const void *data;
|
|
|
|
const uint8_t *frame = NULL;
|
|
|
|
size_t frame_len = 0;
|
2019-05-03 20:59:51 +02:00
|
|
|
int ret;
|
|
|
|
uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
|
2018-08-14 01:25:48 +02:00
|
|
|
|
|
|
|
l_debug("");
|
|
|
|
|
2019-03-08 23:09:42 +01:00
|
|
|
if (netdev->aborting)
|
|
|
|
return;
|
|
|
|
|
2018-08-14 01:25:48 +02:00
|
|
|
if (!netdev->connected) {
|
|
|
|
l_warn("Unexpected connection related event -- "
|
|
|
|
"is another supplicant running?");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* During Fast Transition we use the authenticate event to start the
|
|
|
|
* reassociation step because the FTE necessary before we can build
|
|
|
|
* the FT Associate command is included in the attached frame and is
|
|
|
|
* not available in the Authenticate command callback.
|
|
|
|
*/
|
2019-05-07 21:46:48 +02:00
|
|
|
if (!netdev->ap)
|
2018-08-14 01:25:48 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (!l_genl_attr_init(&attr, msg)) {
|
|
|
|
l_debug("attr init failed");
|
|
|
|
|
|
|
|
goto auth_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (l_genl_attr_next(&attr, &type, &len, &data)) {
|
|
|
|
switch (type) {
|
|
|
|
case NL80211_ATTR_TIMED_OUT:
|
|
|
|
l_warn("authentication timed out");
|
|
|
|
|
2019-05-03 20:59:54 +02:00
|
|
|
if (auth_proto_auth_timeout(netdev->ap))
|
2019-05-03 20:59:51 +02:00
|
|
|
return;
|
2018-08-14 01:25:48 +02:00
|
|
|
|
|
|
|
goto auth_error;
|
|
|
|
|
|
|
|
case NL80211_ATTR_FRAME:
|
|
|
|
if (frame)
|
|
|
|
goto auth_error;
|
|
|
|
|
|
|
|
frame = data;
|
|
|
|
frame_len = len;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!frame)
|
|
|
|
goto auth_error;
|
|
|
|
|
2019-05-07 20:53:42 +02:00
|
|
|
if (netdev->ap) {
|
2019-05-03 20:59:51 +02:00
|
|
|
ret = auth_proto_rx_authenticate(netdev->ap, frame, frame_len);
|
|
|
|
if (ret == 0 || ret == -EAGAIN)
|
|
|
|
return;
|
|
|
|
else if (ret > 0)
|
|
|
|
status_code = (uint16_t)ret;
|
|
|
|
|
|
|
|
goto auth_error;
|
|
|
|
} else
|
2018-08-14 01:25:48 +02:00
|
|
|
goto auth_error;
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
auth_error:
|
2019-01-24 21:56:23 +01:00
|
|
|
netdev_connect_failed(netdev, NETDEV_RESULT_AUTHENTICATION_FAILED,
|
2019-05-03 20:59:51 +02:00
|
|
|
status_code);
|
2018-08-14 01:25:48 +02:00
|
|
|
}
|
|
|
|
|
2017-01-12 09:36:01 +01:00
|
|
|
static void netdev_associate_event(struct l_genl_msg *msg,
|
|
|
|
struct netdev *netdev)
|
|
|
|
{
|
2018-11-16 23:22:55 +01:00
|
|
|
struct l_genl_attr attr;
|
|
|
|
uint16_t type, len;
|
|
|
|
const void *data;
|
|
|
|
size_t frame_len = 0;
|
netdev: process association in netdev_associate_event
Apart from OWE, the association event was disregarded and all association
processing was done in netdev_connect_event. This led to
netdev_connect_event having to handle all the logic of both success and
failure, as well as parsing the association for FT and OWE. Also, without
checking the status code in the associate frame there is the potential
for the kernel to think we are connected even if association failed
(e.g. rogue AP).
This change introduces two flags into netdev, expect_connect_failure and
ignore_connect_event. All the FT processing that was once in
netdev_connect_event has now been moved into netdev_associate_event, as
well as non-FT associate frame processing. The connect event now only
handles failure cases for soft/half MAC cards.
Note: Since fullmac cards rely on the connect event, the eapol_start
and netdev_connect_ok were left in netdev_connect_event. Since neither
auth/assoc events come in on fullmac we shouldn't have any conflict with
the new flags.
Once a connection has completed association, EAPoL is started from
netdev_associate_event (if required) and the ignore_connect_event flag can
be set. This will bypass the connect event.
If a connection has failed during association for whatever reason, we can
set expect_connect_failure, the netdev reason, and the MPDU status code.
This allows netdev_connect_event to both handle the error, and, if required,
send a deauth telling the kernel that we have failed (protecting against the
rogue AP situation).
2019-03-05 22:42:33 +01:00
|
|
|
const uint8_t *frame = NULL;
|
|
|
|
uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
|
2019-05-03 20:59:51 +02:00
|
|
|
int ret;
|
2018-11-16 23:22:55 +01:00
|
|
|
|
2017-01-12 09:36:01 +01:00
|
|
|
l_debug("");
|
2018-11-16 23:22:55 +01:00
|
|
|
|
2019-05-02 12:55:18 +02:00
|
|
|
if (!netdev->connected || netdev->aborting)
|
2019-03-08 23:09:42 +01:00
|
|
|
return;
|
|
|
|
|
2019-05-07 21:46:48 +02:00
|
|
|
if (!netdev->ap)
|
2019-04-06 00:32:54 +02:00
|
|
|
return;
|
|
|
|
|
2018-11-16 23:22:55 +01:00
|
|
|
if (!l_genl_attr_init(&attr, msg)) {
|
|
|
|
l_debug("attr init failed");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (l_genl_attr_next(&attr, &type, &len, &data)) {
|
|
|
|
switch (type) {
|
|
|
|
case NL80211_ATTR_TIMED_OUT:
|
2018-11-19 18:53:30 +01:00
|
|
|
l_warn("association timed out");
|
2019-05-03 20:59:51 +02:00
|
|
|
|
|
|
|
if (auth_proto_assoc_timeout(netdev->ap))
|
|
|
|
return;
|
|
|
|
|
2018-11-16 23:22:55 +01:00
|
|
|
goto assoc_failed;
|
|
|
|
|
|
|
|
case NL80211_ATTR_FRAME:
|
|
|
|
frame = data;
|
|
|
|
frame_len = len;
|
|
|
|
|
netdev: process association in netdev_associate_event
Apart from OWE, the association event was disregarded and all association
processing was done in netdev_connect_event. This led to
netdev_connect_event having to handle all the logic of both success and
failure, as well as parsing the association for FT and OWE. Also, without
checking the status code in the associate frame there is the potential
for the kernel to think we are connected even if association failed
(e.g. rogue AP).
This change introduces two flags into netdev, expect_connect_failure and
ignore_connect_event. All the FT processing that was once in
netdev_connect_event has now been moved into netdev_associate_event, as
well as non-FT associate frame processing. The connect event now only
handles failure cases for soft/half MAC cards.
Note: Since fullmac cards rely on the connect event, the eapol_start
and netdev_connect_ok were left in netdev_connect_event. Since neither
auth/assoc events come in on fullmac we shouldn't have any conflict with
the new flags.
Once a connection has completed association, EAPoL is started from
netdev_associate_event (if required) and the ignore_connect_event flag can
be set. This will bypass the connect event.
If a connection has failed during association for whatever reason, we can
set expect_connect_failure, the netdev reason, and the MPDU status code.
This allows netdev_connect_event to both handle the error, and, if required,
send a deauth telling the kernel that we have failed (protecting against the
rogue AP situation).
2019-03-05 22:42:33 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!frame)
|
|
|
|
goto assoc_failed;
|
|
|
|
|
2019-05-03 20:59:53 +02:00
|
|
|
if (netdev->ap) {
|
2019-05-03 20:59:51 +02:00
|
|
|
ret = auth_proto_rx_associate(netdev->ap, frame, frame_len);
|
|
|
|
if (ret == 0) {
|
2020-08-13 02:50:12 +02:00
|
|
|
bool fils = !!(netdev->handshake->akm_suite &
|
|
|
|
(IE_RSN_AKM_SUITE_FILS_SHA256 |
|
|
|
|
IE_RSN_AKM_SUITE_FILS_SHA384 |
|
|
|
|
IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384 |
|
|
|
|
IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256));
|
|
|
|
|
2019-05-03 20:59:51 +02:00
|
|
|
auth_proto_free(netdev->ap);
|
|
|
|
netdev->ap = NULL;
|
|
|
|
|
|
|
|
netdev->sm = eapol_sm_new(netdev->handshake);
|
|
|
|
eapol_register(netdev->sm);
|
|
|
|
|
2019-05-07 18:50:47 +02:00
|
|
|
/* Just in case this was a retry */
|
|
|
|
netdev->ignore_connect_event = false;
|
|
|
|
|
2019-05-07 20:53:42 +02:00
|
|
|
/*
|
2020-08-13 02:50:12 +02:00
|
|
|
* If in FT and/or FILS we don't force an initial 4-way
|
|
|
|
* handshake and instead just keep the EAPoL state
|
|
|
|
* machine for the rekeys.
|
2019-05-07 20:53:42 +02:00
|
|
|
*/
|
2020-08-13 02:50:12 +02:00
|
|
|
if (netdev->in_ft || fils)
|
2019-05-07 20:53:42 +02:00
|
|
|
eapol_sm_set_require_handshake(netdev->sm,
|
|
|
|
false);
|
|
|
|
|
2020-08-13 02:50:12 +02:00
|
|
|
netdev->in_ft = false;
|
2019-05-07 18:50:47 +02:00
|
|
|
return;
|
2019-05-03 20:59:51 +02:00
|
|
|
} else if (ret == -EAGAIN) {
|
|
|
|
/*
|
|
|
|
* Here to support OWE retries. OWE will retry
|
|
|
|
* internally, but a connect even will still be emitted.
|
|
|
|
*/
|
|
|
|
netdev->ignore_connect_event = true;
|
|
|
|
return;
|
|
|
|
} else if (ret > 0)
|
|
|
|
status_code = (uint16_t)ret;
|
|
|
|
|
|
|
|
goto assoc_failed;
|
netdev: process association in netdev_associate_event
Apart from OWE, the association event was disregarded and all association
processing was done in netdev_connect_event. This led to
netdev_connect_event having to handle all the logic of both success and
failure, as well as parsing the association for FT and OWE. Also, without
checking the status code in the associate frame there is the potential
for the kernel to think we are connected even if association failed
(e.g. rogue AP).
This change introduces two flags into netdev, expect_connect_failure and
ignore_connect_event. All the FT processing that was once in
netdev_connect_event has now been moved into netdev_associate_event, as
well as non-FT associate frame processing. The connect event now only
handles failure cases for soft/half MAC cards.
Note: Since fullmac cards rely on the connect event, the eapol_start
and netdev_connect_ok were left in netdev_connect_event. Since neither
auth/assoc events come in on fullmac we shouldn't have any conflict with
the new flags.
Once a connection has completed association, EAPoL is started from
netdev_associate_event (if required) and the ignore_connect_event flag can
be set. This will bypass the connect event.
If a connection has failed during association for whatever reason, we can
set expect_connect_failure, the netdev reason, and the MPDU status code.
This allows netdev_connect_event to both handle the error, and, if required,
send a deauth telling the kernel that we have failed (protecting against the
rogue AP situation).
2019-03-05 22:42:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
2018-11-16 23:22:55 +01:00
|
|
|
assoc_failed:
|
netdev: process association in netdev_associate_event
Apart from OWE, the association event was disregarded and all association
processing was done in netdev_connect_event. This led to
netdev_connect_event having to handle all the logic of both success and
failure, as well as parsing the association for FT and OWE. Also, without
checking the status code in the associate frame there is the potential
for the kernel to think we are connected even if association failed
(e.g. rogue AP).
This change introduces two flags into netdev, expect_connect_failure and
ignore_connect_event. All the FT processing that was once in
netdev_connect_event has now been moved into netdev_associate_event, as
well as non-FT associate frame processing. The connect event now only
handles failure cases for soft/half MAC cards.
Note: Since fullmac cards rely on the connect event, the eapol_start
and netdev_connect_ok were left in netdev_connect_event. Since neither
auth/assoc events come in on fullmac we shouldn't have any conflict with
the new flags.
Once a connection has completed association, EAPoL is started from
netdev_associate_event (if required) and the ignore_connect_event flag can
be set. This will bypass the connect event.
If a connection has failed during association for whatever reason, we can
set expect_connect_failure, the netdev reason, and the MPDU status code.
This allows netdev_connect_event to both handle the error, and, if required,
send a deauth telling the kernel that we have failed (protecting against the
rogue AP situation).
2019-03-05 22:42:33 +01:00
|
|
|
netdev->result = NETDEV_RESULT_ASSOCIATION_FAILED;
|
|
|
|
netdev->last_code = status_code;
|
|
|
|
netdev->expect_connect_failure = true;
|
2016-06-15 17:54:13 +02:00
|
|
|
}
|
|
|
|
|
2017-01-12 09:36:01 +01:00
|
|
|
static void netdev_cmd_connect_cb(struct l_genl_msg *msg, void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
|
|
|
|
netdev->connect_cmd_id = 0;
|
|
|
|
|
|
|
|
/* Wait for connect event */
|
|
|
|
if (l_genl_msg_get_error(msg) >= 0) {
|
2020-02-14 23:01:04 +01:00
|
|
|
netdev->connected = true;
|
|
|
|
|
2017-01-12 09:36:01 +01:00
|
|
|
if (netdev->event_filter)
|
|
|
|
netdev->event_filter(netdev,
|
|
|
|
NETDEV_EVENT_ASSOCIATING,
|
2019-03-01 00:41:51 +01:00
|
|
|
NULL,
|
2017-01-12 09:36:01 +01:00
|
|
|
netdev->user_data);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We register the eapol state machine here, in case the PAE
|
|
|
|
* socket receives EAPoL packets before the nl80211 socket
|
|
|
|
* receives the connected event. The logical sequence of
|
|
|
|
* events can be reversed (e.g. connect_event, then PAE data)
|
|
|
|
* due to scheduling
|
|
|
|
*/
|
|
|
|
if (netdev->sm)
|
|
|
|
eapol_register(netdev->sm);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-01-24 21:56:23 +01:00
|
|
|
netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED,
|
2019-02-28 18:32:40 +01:00
|
|
|
MMPDU_STATUS_CODE_UNSPECIFIED);
|
2017-01-12 09:36:01 +01:00
|
|
|
}
|
|
|
|
|
2018-11-19 19:09:27 +01:00
|
|
|
static struct l_genl_msg *netdev_build_cmd_authenticate(struct netdev *netdev,
|
2020-05-07 23:50:58 +02:00
|
|
|
uint32_t auth_type)
|
2018-11-16 23:22:55 +01:00
|
|
|
{
|
|
|
|
struct handshake_state *hs = netdev->handshake;
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
|
|
|
|
msg = l_genl_msg_new_sized(NL80211_CMD_AUTHENTICATE, 512);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY_FREQ,
|
|
|
|
4, &netdev->frequency);
|
2020-05-07 23:50:58 +02:00
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN,
|
|
|
|
netdev->handshake->aa);
|
2018-11-16 23:22:55 +01:00
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_SSID, hs->ssid_len, hs->ssid);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_AUTH_TYPE, 4, &auth_type);
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2019-05-03 20:59:54 +02:00
|
|
|
static void netdev_auth_cb(struct l_genl_msg *msg, void *user_data)
|
2018-08-14 01:25:48 +02:00
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
|
2019-05-03 20:59:54 +02:00
|
|
|
if (l_genl_msg_get_error(msg) < 0) {
|
|
|
|
l_error("Error sending CMD_AUTHENTICATE");
|
2018-09-19 20:30:36 +02:00
|
|
|
|
2019-05-03 20:59:54 +02:00
|
|
|
netdev_connect_failed(netdev,
|
|
|
|
NETDEV_RESULT_AUTHENTICATION_FAILED,
|
|
|
|
MMPDU_STATUS_CODE_UNSPECIFIED);
|
|
|
|
return;
|
2018-09-19 20:30:36 +02:00
|
|
|
}
|
2018-08-14 01:25:48 +02:00
|
|
|
}
|
|
|
|
|
2019-05-03 20:59:54 +02:00
|
|
|
static void netdev_assoc_cb(struct l_genl_msg *msg, void *user_data)
|
2018-08-14 01:25:48 +02:00
|
|
|
{
|
2019-05-03 20:59:54 +02:00
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
|
|
|
|
if (l_genl_msg_get_error(msg) < 0) {
|
|
|
|
l_error("Error sending CMD_ASSOCIATE");
|
2018-08-14 01:25:48 +02:00
|
|
|
|
2019-05-03 20:59:54 +02:00
|
|
|
netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED,
|
|
|
|
MMPDU_STATUS_CODE_UNSPECIFIED);
|
|
|
|
}
|
2018-08-14 01:25:48 +02:00
|
|
|
}
|
|
|
|
|
2019-05-03 20:59:54 +02:00
|
|
|
static void netdev_sae_tx_authenticate(const uint8_t *body,
|
2018-08-14 01:25:48 +02:00
|
|
|
size_t body_len, void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
|
2020-05-07 23:50:58 +02:00
|
|
|
msg = netdev_build_cmd_authenticate(netdev, NL80211_AUTHTYPE_SAE);
|
2018-08-14 01:25:48 +02:00
|
|
|
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_AUTH_DATA, body_len, body);
|
|
|
|
|
2019-05-03 20:59:54 +02:00
|
|
|
if (!l_genl_family_send(nl80211, msg, netdev_auth_cb, netdev, NULL)) {
|
2018-08-14 01:25:48 +02:00
|
|
|
l_genl_msg_unref(msg);
|
2019-05-03 20:59:54 +02:00
|
|
|
netdev_connect_failed(netdev,
|
|
|
|
NETDEV_RESULT_AUTHENTICATION_FAILED,
|
|
|
|
MMPDU_STATUS_CODE_UNSPECIFIED);
|
2018-08-14 01:25:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-03 20:59:54 +02:00
|
|
|
static void netdev_sae_tx_associate(void *user_data)
|
2018-11-16 23:22:55 +01:00
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
2019-05-03 20:59:54 +02:00
|
|
|
struct l_genl_msg *msg;
|
|
|
|
struct iovec iov[2];
|
|
|
|
int iov_elems = 0;
|
2018-11-16 23:22:55 +01:00
|
|
|
|
2019-05-03 20:59:54 +02:00
|
|
|
msg = netdev_build_cmd_associate_common(netdev);
|
2018-11-16 23:22:55 +01:00
|
|
|
|
2019-05-03 20:59:54 +02:00
|
|
|
iov[iov_elems].iov_base = netdev->handshake->supplicant_ie;
|
|
|
|
iov[iov_elems].iov_len = netdev->handshake->supplicant_ie[1] + 2;
|
|
|
|
iov_elems++;
|
|
|
|
|
|
|
|
if (netdev->handshake->mde) {
|
|
|
|
iov[iov_elems].iov_base = netdev->handshake->mde;
|
|
|
|
iov[iov_elems].iov_len = netdev->handshake->mde[1] + 2;
|
|
|
|
iov_elems++;
|
|
|
|
}
|
|
|
|
|
|
|
|
l_genl_msg_append_attrv(msg, NL80211_ATTR_IE, iov, iov_elems);
|
|
|
|
|
|
|
|
if (!l_genl_family_send(nl80211, msg, netdev_assoc_cb, netdev, NULL)) {
|
|
|
|
l_genl_msg_unref(msg);
|
|
|
|
netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED,
|
2019-02-28 18:32:40 +01:00
|
|
|
MMPDU_STATUS_CODE_UNSPECIFIED);
|
2018-11-16 23:22:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_owe_tx_authenticate(void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
|
|
|
|
msg = netdev_build_cmd_authenticate(netdev,
|
2020-05-07 23:50:58 +02:00
|
|
|
NL80211_AUTHTYPE_OPEN_SYSTEM);
|
2018-11-16 23:22:55 +01:00
|
|
|
|
2019-04-22 19:11:45 +02:00
|
|
|
if (!l_genl_family_send(nl80211, msg, netdev_auth_cb,
|
2018-11-19 19:09:27 +01:00
|
|
|
netdev, NULL)) {
|
2018-11-16 23:22:55 +01:00
|
|
|
l_genl_msg_unref(msg);
|
2019-01-24 21:56:23 +01:00
|
|
|
netdev_connect_failed(netdev,
|
|
|
|
NETDEV_RESULT_AUTHENTICATION_FAILED,
|
2019-02-28 18:32:40 +01:00
|
|
|
MMPDU_STATUS_CODE_UNSPECIFIED);
|
2018-11-16 23:22:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_owe_tx_associate(struct iovec *ie_iov, size_t iov_len,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
|
|
|
|
msg = netdev_build_cmd_associate_common(netdev);
|
|
|
|
|
|
|
|
l_genl_msg_append_attrv(msg, NL80211_ATTR_IE, ie_iov, iov_len);
|
|
|
|
|
2019-04-22 19:11:45 +02:00
|
|
|
if (!l_genl_family_send(nl80211, msg, netdev_assoc_cb,
|
2018-11-16 23:22:55 +01:00
|
|
|
netdev, NULL)) {
|
|
|
|
l_genl_msg_unref(msg);
|
2019-01-24 21:56:23 +01:00
|
|
|
netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED,
|
2019-02-28 18:32:40 +01:00
|
|
|
MMPDU_STATUS_CODE_UNSPECIFIED);
|
2018-11-16 23:22:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-22 19:11:45 +02:00
|
|
|
static void netdev_fils_tx_authenticate(const uint8_t *body,
|
|
|
|
size_t body_len,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
|
2020-05-07 23:50:58 +02:00
|
|
|
msg = netdev_build_cmd_authenticate(netdev, NL80211_AUTHTYPE_FILS_SK);
|
2019-04-22 19:11:45 +02:00
|
|
|
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_AUTH_DATA, body_len, body);
|
|
|
|
|
|
|
|
if (!l_genl_family_send(nl80211, msg, netdev_auth_cb,
|
|
|
|
netdev, NULL)) {
|
|
|
|
l_genl_msg_unref(msg);
|
|
|
|
netdev_connect_failed(netdev,
|
|
|
|
NETDEV_RESULT_AUTHENTICATION_FAILED,
|
|
|
|
MMPDU_STATUS_CODE_UNSPECIFIED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_fils_tx_associate(struct iovec *iov, size_t iov_len,
|
|
|
|
const uint8_t *kek, size_t kek_len,
|
|
|
|
const uint8_t *nonces, size_t nonces_len,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
|
|
|
|
msg = netdev_build_cmd_associate_common(netdev);
|
|
|
|
|
|
|
|
l_genl_msg_append_attrv(msg, NL80211_ATTR_IE, iov, iov_len);
|
|
|
|
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_FILS_KEK, kek_len, kek);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_FILS_NONCES, nonces_len, nonces);
|
|
|
|
|
|
|
|
if (!l_genl_family_send(nl80211, msg, netdev_assoc_cb,
|
|
|
|
netdev, NULL)) {
|
|
|
|
l_genl_msg_unref(msg);
|
|
|
|
netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED,
|
|
|
|
MMPDU_STATUS_CODE_UNSPECIFIED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-27 23:35:31 +02:00
|
|
|
static struct l_genl_msg *netdev_build_cmd_connect(struct netdev *netdev,
|
2016-11-02 23:46:19 +01:00
|
|
|
struct scan_bss *bss,
|
2017-03-31 14:43:01 +02:00
|
|
|
struct handshake_state *hs,
|
2019-07-12 23:00:01 +02:00
|
|
|
const uint8_t *prev_bssid,
|
2019-09-06 20:11:05 +02:00
|
|
|
const struct iovec *vendor_ies,
|
2019-07-12 23:00:01 +02:00
|
|
|
size_t num_vendor_ies)
|
2016-06-15 17:54:13 +02:00
|
|
|
{
|
2016-06-27 23:35:31 +02:00
|
|
|
uint32_t auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM;
|
2016-06-15 17:54:13 +02:00
|
|
|
struct l_genl_msg *msg;
|
2019-07-12 23:00:01 +02:00
|
|
|
struct iovec iov[4 + num_vendor_ies];
|
2016-11-02 23:46:16 +01:00
|
|
|
int iov_elems = 0;
|
2018-08-25 03:54:24 +02:00
|
|
|
bool is_rsn = hs->supplicant_ie != NULL;
|
2019-07-10 17:53:38 +02:00
|
|
|
const uint8_t *extended_capabilities;
|
2019-08-23 16:11:51 +02:00
|
|
|
const uint8_t *rm_enabled_capabilities;
|
2016-06-15 17:54:13 +02:00
|
|
|
|
2016-06-27 23:35:31 +02:00
|
|
|
msg = l_genl_msg_new_sized(NL80211_CMD_CONNECT, 512);
|
2016-06-15 17:54:13 +02:00
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY_FREQ,
|
|
|
|
4, &bss->frequency);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, bss->addr);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_SSID,
|
|
|
|
bss->ssid_len, bss->ssid);
|
2016-06-27 23:35:31 +02:00
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_AUTH_TYPE, 4, &auth_type);
|
|
|
|
|
2017-03-31 14:43:01 +02:00
|
|
|
if (prev_bssid)
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN,
|
|
|
|
prev_bssid);
|
|
|
|
|
2016-06-27 23:35:31 +02:00
|
|
|
if (bss->capability & IE_BSS_CAP_PRIVACY)
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_PRIVACY, 0, NULL);
|
2016-06-15 17:54:13 +02:00
|
|
|
|
2017-05-22 10:49:48 +02:00
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_SOCKET_OWNER, 0, NULL);
|
|
|
|
|
2016-12-12 18:34:19 +01:00
|
|
|
if (is_rsn) {
|
2016-06-15 17:54:13 +02:00
|
|
|
uint32_t nl_cipher;
|
2016-11-15 20:15:45 +01:00
|
|
|
uint32_t nl_akm;
|
|
|
|
uint32_t wpa_version;
|
2016-06-15 17:54:13 +02:00
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
if (hs->pairwise_cipher == IE_RSN_CIPHER_SUITE_CCMP)
|
2016-06-15 17:54:13 +02:00
|
|
|
nl_cipher = CRYPTO_CIPHER_CCMP;
|
|
|
|
else
|
|
|
|
nl_cipher = CRYPTO_CIPHER_TKIP;
|
|
|
|
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
|
|
|
|
4, &nl_cipher);
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
if (hs->group_cipher == IE_RSN_CIPHER_SUITE_CCMP)
|
2016-06-15 17:54:13 +02:00
|
|
|
nl_cipher = CRYPTO_CIPHER_CCMP;
|
|
|
|
else
|
|
|
|
nl_cipher = CRYPTO_CIPHER_TKIP;
|
|
|
|
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
|
|
|
|
4, &nl_cipher);
|
|
|
|
|
2016-12-08 15:26:16 +01:00
|
|
|
if (hs->mfp) {
|
2016-12-13 16:26:42 +01:00
|
|
|
uint32_t use_mfp = NL80211_MFP_REQUIRED;
|
2016-12-08 15:26:16 +01:00
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_USE_MFP,
|
2016-12-13 16:26:42 +01:00
|
|
|
4, &use_mfp);
|
2016-12-08 15:26:16 +01:00
|
|
|
}
|
|
|
|
|
2016-11-15 20:15:45 +01:00
|
|
|
nl_akm = ie_rsn_akm_suite_to_nl80211(hs->akm_suite);
|
|
|
|
if (nl_akm)
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_AKM_SUITES,
|
|
|
|
4, &nl_akm);
|
|
|
|
|
|
|
|
if (hs->wpa_ie)
|
|
|
|
wpa_version = NL80211_WPA_VERSION_1;
|
|
|
|
else
|
|
|
|
wpa_version = NL80211_WPA_VERSION_2;
|
|
|
|
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_WPA_VERSIONS,
|
|
|
|
4, &wpa_version);
|
|
|
|
|
2016-06-15 17:54:13 +02:00
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_CONTROL_PORT, 0, NULL);
|
|
|
|
|
2018-08-25 03:54:24 +02:00
|
|
|
iov[iov_elems].iov_base = (void *) hs->supplicant_ie;
|
|
|
|
iov[iov_elems].iov_len = hs->supplicant_ie[1] + 2;
|
2016-12-12 18:34:19 +01:00
|
|
|
iov_elems += 1;
|
2016-06-15 17:54:13 +02:00
|
|
|
}
|
|
|
|
|
2019-03-19 19:45:03 +01:00
|
|
|
if (netdev->pae_over_nl80211)
|
|
|
|
l_genl_msg_append_attr(msg,
|
|
|
|
NL80211_ATTR_CONTROL_PORT_OVER_NL80211,
|
|
|
|
0, NULL);
|
|
|
|
|
2019-08-23 16:11:51 +02:00
|
|
|
rm_enabled_capabilities =
|
|
|
|
wiphy_get_rm_enabled_capabilities(netdev->wiphy);
|
|
|
|
if (rm_enabled_capabilities && bss->capability & IE_BSS_CAP_RM) {
|
|
|
|
iov[iov_elems].iov_base = (void *) rm_enabled_capabilities;
|
|
|
|
iov[iov_elems].iov_len = rm_enabled_capabilities[1] + 2;
|
2019-07-10 23:42:06 +02:00
|
|
|
iov_elems += 1;
|
|
|
|
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_USE_RRM, 0, NULL);
|
|
|
|
}
|
|
|
|
|
2016-12-12 18:34:20 +01:00
|
|
|
if (hs->mde) {
|
|
|
|
iov[iov_elems].iov_base = (void *) hs->mde;
|
|
|
|
iov[iov_elems].iov_len = hs->mde[1] + 2;
|
2016-11-02 23:46:16 +01:00
|
|
|
iov_elems += 1;
|
|
|
|
}
|
|
|
|
|
2019-07-10 17:53:38 +02:00
|
|
|
/*
|
|
|
|
* This element should be added after MDE
|
|
|
|
* See 802.11-2016, Section 9.3.3.6
|
|
|
|
*/
|
|
|
|
extended_capabilities = wiphy_get_extended_capabilities(netdev->wiphy,
|
|
|
|
netdev->type);
|
|
|
|
iov[iov_elems].iov_base = (void *) extended_capabilities;
|
|
|
|
iov[iov_elems].iov_len = extended_capabilities[1] + 2;
|
|
|
|
iov_elems += 1;
|
|
|
|
|
2019-07-12 23:00:01 +02:00
|
|
|
if (vendor_ies) {
|
|
|
|
memcpy(iov + iov_elems, vendor_ies,
|
|
|
|
sizeof(*vendor_ies) * num_vendor_ies);
|
|
|
|
iov_elems += num_vendor_ies;
|
|
|
|
}
|
|
|
|
|
2016-11-02 23:46:16 +01:00
|
|
|
if (iov_elems)
|
|
|
|
l_genl_msg_append_attrv(msg, NL80211_ATTR_IE, iov, iov_elems);
|
|
|
|
|
2016-06-15 17:54:13 +02:00
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2020-03-18 19:54:19 +01:00
|
|
|
struct rtnl_data {
|
|
|
|
struct netdev *netdev;
|
|
|
|
uint8_t addr[ETH_ALEN];
|
|
|
|
int ref;
|
|
|
|
};
|
|
|
|
|
2020-07-15 23:29:17 +02:00
|
|
|
static int netdev_begin_connection(struct netdev *netdev)
|
2016-06-14 18:17:43 +02:00
|
|
|
{
|
2020-07-15 23:29:17 +02:00
|
|
|
if (netdev->connect_cmd) {
|
2018-08-14 01:25:48 +02:00
|
|
|
netdev->connect_cmd_id = l_genl_family_send(nl80211,
|
2020-07-15 23:29:17 +02:00
|
|
|
netdev->connect_cmd,
|
|
|
|
netdev_cmd_connect_cb, netdev,
|
|
|
|
NULL);
|
2016-08-05 14:25:34 +02:00
|
|
|
|
2020-07-15 23:29:17 +02:00
|
|
|
if (!netdev->connect_cmd_id)
|
|
|
|
goto failed;
|
|
|
|
|
|
|
|
netdev->connect_cmd = NULL;
|
2016-06-15 17:54:13 +02:00
|
|
|
}
|
2016-06-14 18:17:43 +02:00
|
|
|
|
2020-07-15 23:29:17 +02:00
|
|
|
/*
|
|
|
|
* Set the supplicant address now, this may have already been done for
|
|
|
|
* a non-randomized address connect, but if we are randomizing we need
|
|
|
|
* to set it again as the address should have now changed.
|
|
|
|
*/
|
2020-03-18 19:54:19 +01:00
|
|
|
handshake_state_set_supplicant_address(netdev->handshake, netdev->addr);
|
|
|
|
|
2020-07-15 23:29:17 +02:00
|
|
|
if (netdev->ap) {
|
|
|
|
if (!auth_proto_start(netdev->ap))
|
|
|
|
goto failed;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set connected since the auth protocols cannot do
|
|
|
|
* so internally
|
|
|
|
*/
|
2020-03-18 19:54:19 +01:00
|
|
|
netdev->connected = true;
|
2020-07-15 23:29:17 +02:00
|
|
|
}
|
2020-03-18 19:54:19 +01:00
|
|
|
|
|
|
|
return 0;
|
2020-07-15 23:29:17 +02:00
|
|
|
|
|
|
|
failed:
|
|
|
|
netdev_connect_failed(netdev, NETDEV_RESULT_ABORTED,
|
|
|
|
MMPDU_STATUS_CODE_UNSPECIFIED);
|
|
|
|
return -EIO;
|
2020-03-18 19:54:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_mac_change_failed(struct netdev *netdev,
|
|
|
|
struct rtnl_data *req, int error)
|
|
|
|
{
|
|
|
|
l_error("Error setting mac address on %d: %s", netdev->index,
|
|
|
|
strerror(-error));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the interface is down and we failed to up it we need to notify
|
|
|
|
* any watchers since we have been skipping the notification while
|
|
|
|
* mac_change_cmd_id was set.
|
|
|
|
*/
|
|
|
|
if (!netdev_get_is_up(netdev)) {
|
|
|
|
WATCHLIST_NOTIFY(&netdev_watches, netdev_watch_func_t,
|
|
|
|
netdev, NETDEV_WATCH_EVENT_DOWN);
|
|
|
|
|
|
|
|
goto failed;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* If the interface is up we can still try and connect. This
|
|
|
|
* is a very rare case and most likely will never happen.
|
|
|
|
*/
|
|
|
|
l_info("Interface still up after failing to change the MAC, "
|
|
|
|
"continuing with connection");
|
2020-07-15 23:29:17 +02:00
|
|
|
if (netdev_begin_connection(netdev) < 0)
|
2020-03-18 19:54:19 +01:00
|
|
|
goto failed;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
failed:
|
|
|
|
netdev_connect_failed(netdev, NETDEV_RESULT_ABORTED,
|
|
|
|
MMPDU_STATUS_CODE_UNSPECIFIED);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_mac_destroy(void *user_data)
|
|
|
|
{
|
|
|
|
struct rtnl_data *req = user_data;
|
|
|
|
|
|
|
|
req->ref--;
|
|
|
|
|
|
|
|
/* still pending requests? */
|
|
|
|
if (req->ref)
|
|
|
|
return;
|
|
|
|
|
|
|
|
l_free(req);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_mac_power_up_cb(int error, uint16_t type,
|
|
|
|
const void *data, uint32_t len,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct rtnl_data *req = user_data;
|
|
|
|
struct netdev *netdev = req->netdev;
|
|
|
|
|
|
|
|
netdev->mac_change_cmd_id = 0;
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
l_error("Error taking interface %u up for per-network MAC "
|
|
|
|
"generation: %s", netdev->index, strerror(-error));
|
|
|
|
netdev_mac_change_failed(netdev, req, error);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Pick up where we left off in netdev_connect_commmon.
|
|
|
|
*/
|
2020-07-15 23:29:17 +02:00
|
|
|
if (netdev_begin_connection(netdev) < 0) {
|
2020-03-18 19:54:19 +01:00
|
|
|
l_error("Failed to connect after changing MAC");
|
|
|
|
netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED,
|
|
|
|
MMPDU_STATUS_CODE_UNSPECIFIED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_mac_power_down_cb(int error, uint16_t type,
|
|
|
|
const void *data, uint32_t len,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct rtnl_data *req = user_data;
|
|
|
|
struct netdev *netdev = req->netdev;
|
|
|
|
|
|
|
|
netdev->mac_change_cmd_id = 0;
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
l_error("Error taking interface %u down for per-network MAC "
|
|
|
|
"generation: %s", netdev->index, strerror(-error));
|
|
|
|
netdev_mac_change_failed(netdev, req, error);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
l_debug("Setting generated address on ifindex: %d to: "MAC,
|
|
|
|
netdev->index, MAC_STR(req->addr));
|
|
|
|
netdev->mac_change_cmd_id = l_rtnl_set_mac(rtnl, netdev->index,
|
|
|
|
req->addr, true,
|
|
|
|
netdev_mac_power_up_cb, req,
|
|
|
|
netdev_mac_destroy);
|
|
|
|
if (!netdev->mac_change_cmd_id) {
|
|
|
|
netdev_mac_change_failed(netdev, req, -EIO);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
req->ref++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TODO: There are some potential race conditions that are being ignored. There
|
|
|
|
* is nothing that IWD itself can do to solve these, they require kernel
|
|
|
|
* changes:
|
|
|
|
*
|
|
|
|
* 1. A perfectly timed ifdown could be ignored. If an external process
|
|
|
|
* brings down an interface just before calling this function we would only
|
|
|
|
* get a single newlink event since there is no state change doing a second
|
|
|
|
* ifdown (nor an error from the kernel). This newlink event would be ignored
|
|
|
|
* since IWD thinks its from our own doing. This would result in IWD changing
|
|
|
|
* the MAC and bringing the interface back up which would look very strange
|
|
|
|
* and unexpected to someone who just tried to ifdown an interface.
|
|
|
|
*
|
|
|
|
* 2. A perfectly timed ifup could result in a failed connection. If an external
|
|
|
|
* process ifup's just after IWD ifdown's but before changing the MAC this
|
|
|
|
* would cause the MAC change to fail. This failure would result in a failed
|
|
|
|
* connection.
|
|
|
|
*
|
|
|
|
* Returns 0 if a MAC change procedure was started.
|
|
|
|
* Returns -EALREADY if the requested MAC matched our current MAC
|
|
|
|
* Returns -EIO if there was an IO error when powering down
|
|
|
|
*/
|
2020-07-15 23:29:17 +02:00
|
|
|
static int netdev_start_powered_mac_change(struct netdev *netdev)
|
2020-03-18 19:54:19 +01:00
|
|
|
{
|
|
|
|
struct rtnl_data *req;
|
|
|
|
uint8_t new_addr[6];
|
|
|
|
|
2020-03-19 23:59:00 +01:00
|
|
|
/* No address set in handshake, use per-network MAC generation */
|
|
|
|
if (util_mem_is_zero(netdev->handshake->spa, ETH_ALEN))
|
|
|
|
wiphy_generate_address_from_ssid(netdev->wiphy,
|
2020-07-15 23:29:17 +02:00
|
|
|
(const char *)netdev->handshake->ssid,
|
|
|
|
new_addr);
|
2020-03-19 23:59:00 +01:00
|
|
|
else
|
|
|
|
memcpy(new_addr, netdev->handshake->spa, ETH_ALEN);
|
2020-03-18 19:54:19 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* MAC has already been changed previously, no need to again
|
|
|
|
*/
|
|
|
|
if (!memcmp(new_addr, netdev->addr, sizeof(new_addr)))
|
|
|
|
return -EALREADY;
|
|
|
|
|
|
|
|
req = l_new(struct rtnl_data, 1);
|
|
|
|
req->netdev = netdev;
|
|
|
|
/* This message will need to be unreffed upon any error */
|
|
|
|
req->ref++;
|
|
|
|
memcpy(req->addr, new_addr, sizeof(req->addr));
|
|
|
|
|
|
|
|
netdev->mac_change_cmd_id = l_rtnl_set_powered(rtnl, netdev->index,
|
|
|
|
false, netdev_mac_power_down_cb,
|
|
|
|
req, netdev_mac_destroy);
|
|
|
|
|
|
|
|
if (!netdev->mac_change_cmd_id) {
|
|
|
|
l_free(req);
|
|
|
|
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-07-15 23:29:17 +02:00
|
|
|
static bool netdev_connection_work_ready(struct wiphy_radio_work_item *item)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = l_container_of(item, struct netdev, work);
|
|
|
|
|
|
|
|
if (mac_per_ssid) {
|
|
|
|
int ret = netdev_start_powered_mac_change(netdev);
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
return false;
|
|
|
|
else if (ret != -EALREADY)
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (netdev_begin_connection(netdev) < 0)
|
|
|
|
goto failed;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
failed:
|
|
|
|
netdev_connect_failed(netdev, NETDEV_RESULT_ABORTED,
|
|
|
|
MMPDU_STATUS_CODE_UNSPECIFIED);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct wiphy_radio_work_item_ops connect_work_ops = {
|
|
|
|
.do_work = netdev_connection_work_ready,
|
|
|
|
};
|
|
|
|
|
2020-03-18 19:54:19 +01:00
|
|
|
static int netdev_connect_common(struct netdev *netdev,
|
|
|
|
struct l_genl_msg *cmd_connect,
|
|
|
|
struct scan_bss *bss,
|
|
|
|
struct handshake_state *hs,
|
|
|
|
struct eapol_sm *sm,
|
|
|
|
netdev_event_func_t event_filter,
|
|
|
|
netdev_connect_cb_t cb, void *user_data)
|
|
|
|
{
|
2020-07-15 23:29:17 +02:00
|
|
|
netdev->connect_cmd = cmd_connect;
|
2016-06-14 18:17:43 +02:00
|
|
|
netdev->event_filter = event_filter;
|
|
|
|
netdev->connect_cb = cb;
|
|
|
|
netdev->user_data = user_data;
|
2016-11-02 23:46:19 +01:00
|
|
|
netdev->handshake = hs;
|
2016-06-28 23:58:17 +02:00
|
|
|
netdev->sm = sm;
|
2016-12-23 11:38:01 +01:00
|
|
|
netdev->frequency = bss->frequency;
|
2020-01-21 07:21:38 +01:00
|
|
|
netdev->cur_rssi_low = false; /* Gets updated on the 1st CQM event */
|
2017-05-20 16:33:13 +02:00
|
|
|
netdev->cur_rssi = bss->signal_strength / 100;
|
|
|
|
netdev_rssi_level_init(netdev);
|
2016-06-28 23:58:17 +02:00
|
|
|
|
2016-12-12 18:34:19 +01:00
|
|
|
handshake_state_set_authenticator_address(hs, bss->addr);
|
2016-11-02 23:46:19 +01:00
|
|
|
|
2019-01-25 20:23:12 +01:00
|
|
|
if (!wiphy_has_ext_feature(netdev->wiphy,
|
|
|
|
NL80211_EXT_FEATURE_CAN_REPLACE_PTK0))
|
|
|
|
handshake_state_set_no_rekey(hs, true);
|
|
|
|
|
2020-07-15 23:29:17 +02:00
|
|
|
wiphy_radio_work_insert(netdev->wiphy, &netdev->work, 1,
|
|
|
|
&connect_work_ops);
|
|
|
|
return 0;
|
2016-09-14 03:50:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int netdev_connect(struct netdev *netdev, struct scan_bss *bss,
|
2016-12-12 18:34:20 +01:00
|
|
|
struct handshake_state *hs,
|
2019-09-06 20:11:05 +02:00
|
|
|
const struct iovec *vendor_ies,
|
2019-07-12 23:00:01 +02:00
|
|
|
size_t num_vendor_ies,
|
2016-09-14 03:50:24 +02:00
|
|
|
netdev_event_func_t event_filter,
|
|
|
|
netdev_connect_cb_t cb, void *user_data)
|
|
|
|
{
|
2018-08-14 01:25:48 +02:00
|
|
|
struct l_genl_msg *cmd_connect = NULL;
|
2016-12-12 18:34:18 +01:00
|
|
|
struct eapol_sm *sm = NULL;
|
2018-08-25 03:54:24 +02:00
|
|
|
bool is_rsn = hs->supplicant_ie != NULL;
|
2016-09-14 03:50:24 +02:00
|
|
|
|
2019-10-21 15:55:02 +02:00
|
|
|
if (netdev->type != NL80211_IFTYPE_STATION &&
|
|
|
|
netdev->type != NL80211_IFTYPE_P2P_CLIENT)
|
2018-07-17 23:15:58 +02:00
|
|
|
return -ENOTSUP;
|
|
|
|
|
2020-07-15 23:29:17 +02:00
|
|
|
if (netdev->connected || netdev->connect_cmd_id || netdev->work.id)
|
2016-09-14 03:50:24 +02:00
|
|
|
return -EISCONN;
|
|
|
|
|
2018-11-16 23:22:55 +01:00
|
|
|
switch (hs->akm_suite) {
|
|
|
|
case IE_RSN_AKM_SUITE_SAE_SHA256:
|
|
|
|
case IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256:
|
2019-05-03 20:59:54 +02:00
|
|
|
netdev->ap = sae_sm_new(hs, netdev_sae_tx_authenticate,
|
|
|
|
netdev_sae_tx_associate,
|
|
|
|
netdev);
|
2018-11-16 23:22:55 +01:00
|
|
|
break;
|
|
|
|
case IE_RSN_AKM_SUITE_OWE:
|
2019-05-03 20:59:53 +02:00
|
|
|
netdev->ap = owe_sm_new(hs, netdev_owe_tx_authenticate,
|
2018-11-16 23:22:55 +01:00
|
|
|
netdev_owe_tx_associate,
|
|
|
|
netdev);
|
|
|
|
break;
|
2019-04-22 19:11:45 +02:00
|
|
|
case IE_RSN_AKM_SUITE_FILS_SHA256:
|
|
|
|
case IE_RSN_AKM_SUITE_FILS_SHA384:
|
2019-05-23 00:24:04 +02:00
|
|
|
case IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256:
|
|
|
|
case IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384:
|
2019-05-03 20:59:51 +02:00
|
|
|
netdev->ap = fils_sm_new(hs, netdev_fils_tx_authenticate,
|
2019-04-22 19:11:45 +02:00
|
|
|
netdev_fils_tx_associate,
|
2019-05-03 20:59:51 +02:00
|
|
|
netdev);
|
2019-04-22 19:11:45 +02:00
|
|
|
break;
|
2018-11-16 23:22:55 +01:00
|
|
|
default:
|
2019-07-12 23:00:01 +02:00
|
|
|
cmd_connect = netdev_build_cmd_connect(netdev, bss, hs,
|
|
|
|
NULL, vendor_ies, num_vendor_ies);
|
2016-09-14 03:50:24 +02:00
|
|
|
|
2018-08-14 01:25:48 +02:00
|
|
|
if (!cmd_connect)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2019-10-28 15:05:02 +01:00
|
|
|
if (is_rsn || hs->settings_8021x)
|
2018-08-14 01:25:48 +02:00
|
|
|
sm = eapol_sm_new(hs);
|
|
|
|
}
|
2016-12-12 18:34:18 +01:00
|
|
|
|
2016-12-12 18:34:20 +01:00
|
|
|
return netdev_connect_common(netdev, cmd_connect, bss, hs, sm,
|
2016-09-14 03:50:24 +02:00
|
|
|
event_filter, cb, user_data);
|
|
|
|
}
|
|
|
|
|
2016-06-14 19:15:18 +02:00
|
|
|
int netdev_disconnect(struct netdev *netdev,
|
|
|
|
netdev_disconnect_cb_t cb, void *user_data)
|
|
|
|
{
|
2017-05-31 18:08:40 +02:00
|
|
|
struct l_genl_msg *disconnect;
|
2020-07-15 23:29:17 +02:00
|
|
|
bool send_disconnect = true;
|
2016-06-16 23:05:34 +02:00
|
|
|
|
2019-10-21 15:55:02 +02:00
|
|
|
if (netdev->type != NL80211_IFTYPE_STATION &&
|
|
|
|
netdev->type != NL80211_IFTYPE_P2P_CLIENT)
|
2018-07-17 23:15:58 +02:00
|
|
|
return -ENOTSUP;
|
|
|
|
|
2016-09-22 18:48:32 +02:00
|
|
|
if (netdev->disconnect_cmd_id)
|
|
|
|
return -EINPROGRESS;
|
|
|
|
|
2016-09-22 22:55:07 +02:00
|
|
|
/* Only perform this if we haven't successfully fully associated yet */
|
|
|
|
if (!netdev->operational) {
|
2020-07-15 23:29:17 +02:00
|
|
|
/*
|
|
|
|
* Three possibilities here:
|
|
|
|
* 1. We do not actually have a connect in progress (work.id
|
|
|
|
* is zero), then we can bail out early with an error.
|
|
|
|
* 2. We have sent CMD_CONNECT but not fully connected. The
|
|
|
|
* CMD_CONNECT needs to be canceled and a disconnect should
|
|
|
|
* be sent.
|
|
|
|
* 3. Queued up the connect work, but haven't sent CMD_CONNECT
|
|
|
|
* to the kernel. This case we do not need to send a
|
|
|
|
* disconnect.
|
|
|
|
*/
|
|
|
|
if (!netdev->work.id)
|
|
|
|
return -ENOTCONN;
|
|
|
|
|
2019-03-08 23:09:42 +01:00
|
|
|
if (netdev->connect_cmd_id) {
|
|
|
|
l_genl_family_cancel(nl80211, netdev->connect_cmd_id);
|
|
|
|
netdev->connect_cmd_id = 0;
|
2020-07-15 23:29:17 +02:00
|
|
|
} else
|
|
|
|
send_disconnect = false;
|
2019-03-08 23:09:42 +01:00
|
|
|
|
2019-01-24 21:56:23 +01:00
|
|
|
netdev_connect_failed(netdev, NETDEV_RESULT_ABORTED,
|
|
|
|
MMPDU_REASON_CODE_UNSPECIFIED);
|
2016-09-22 22:55:07 +02:00
|
|
|
} else {
|
|
|
|
netdev_connect_free(netdev);
|
|
|
|
}
|
2016-08-02 22:48:11 +02:00
|
|
|
|
2020-07-15 23:29:17 +02:00
|
|
|
if (send_disconnect) {
|
|
|
|
disconnect = netdev_build_cmd_disconnect(netdev,
|
2017-08-31 04:04:41 +02:00
|
|
|
MMPDU_REASON_CODE_DEAUTH_LEAVING);
|
2020-07-15 23:29:17 +02:00
|
|
|
netdev->disconnect_cmd_id = l_genl_family_send(nl80211,
|
|
|
|
disconnect, netdev_cmd_disconnect_cb,
|
|
|
|
netdev, NULL);
|
2016-09-21 22:20:51 +02:00
|
|
|
|
2020-07-15 23:29:17 +02:00
|
|
|
if (!netdev->disconnect_cmd_id) {
|
|
|
|
l_genl_msg_unref(disconnect);
|
|
|
|
return -EIO;
|
|
|
|
}
|
2016-06-16 23:05:34 +02:00
|
|
|
|
2020-07-15 23:29:17 +02:00
|
|
|
netdev->disconnect_cb = cb;
|
|
|
|
netdev->user_data = user_data;
|
|
|
|
netdev->aborting = true;
|
|
|
|
} else if (cb)
|
|
|
|
cb(netdev, true, user_data);
|
2016-06-16 23:05:34 +02:00
|
|
|
|
2016-06-14 19:15:18 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-03-31 14:43:01 +02:00
|
|
|
int netdev_reassociate(struct netdev *netdev, struct scan_bss *target_bss,
|
2017-08-14 14:31:19 +02:00
|
|
|
struct scan_bss *orig_bss, struct handshake_state *hs,
|
|
|
|
netdev_event_func_t event_filter,
|
|
|
|
netdev_connect_cb_t cb, void *user_data)
|
2017-03-31 14:43:01 +02:00
|
|
|
{
|
|
|
|
struct l_genl_msg *cmd_connect;
|
2018-06-22 02:32:55 +02:00
|
|
|
struct netdev_handshake_state;
|
2017-03-31 14:43:01 +02:00
|
|
|
struct handshake_state *old_hs;
|
|
|
|
struct eapol_sm *sm = NULL, *old_sm;
|
2018-08-25 03:54:24 +02:00
|
|
|
bool is_rsn = hs->supplicant_ie != NULL;
|
2017-03-31 14:43:01 +02:00
|
|
|
int err;
|
|
|
|
|
|
|
|
cmd_connect = netdev_build_cmd_connect(netdev, target_bss, hs,
|
2019-07-12 23:00:01 +02:00
|
|
|
orig_bss->addr, NULL, 0);
|
2017-03-31 14:43:01 +02:00
|
|
|
if (!cmd_connect)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (is_rsn)
|
|
|
|
sm = eapol_sm_new(hs);
|
|
|
|
|
|
|
|
old_sm = netdev->sm;
|
|
|
|
old_hs = netdev->handshake;
|
|
|
|
|
|
|
|
err = netdev_connect_common(netdev, cmd_connect, target_bss, hs, sm,
|
2017-08-14 14:31:19 +02:00
|
|
|
event_filter, cb, user_data);
|
2017-03-31 14:43:01 +02:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
2017-08-14 14:31:19 +02:00
|
|
|
memcpy(netdev->prev_bssid, orig_bss->addr, ETH_ALEN);
|
2017-03-31 14:43:01 +02:00
|
|
|
|
|
|
|
netdev->operational = false;
|
|
|
|
|
2017-08-18 14:22:58 +02:00
|
|
|
netdev_rssi_polling_update(netdev);
|
|
|
|
|
2017-03-31 14:43:01 +02:00
|
|
|
if (old_sm)
|
|
|
|
eapol_sm_free(old_sm);
|
|
|
|
|
2017-08-14 14:31:19 +02:00
|
|
|
if (old_hs)
|
|
|
|
handshake_state_free(old_hs);
|
2017-03-31 14:43:01 +02:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2018-07-17 23:15:59 +02:00
|
|
|
static void netdev_join_adhoc_cb(struct l_genl_msg *msg, void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
|
|
|
|
netdev->join_adhoc_cmd_id = 0;
|
|
|
|
|
|
|
|
if (netdev->adhoc_cb)
|
|
|
|
netdev->adhoc_cb(netdev, l_genl_msg_get_error(msg),
|
|
|
|
netdev->user_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
int netdev_join_adhoc(struct netdev *netdev, const char *ssid,
|
|
|
|
struct iovec *extra_ie, size_t extra_ie_elems,
|
2018-08-20 01:25:23 +02:00
|
|
|
bool control_port, netdev_command_cb_t cb,
|
2018-07-17 23:15:59 +02:00
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct l_genl_msg *cmd;
|
2018-08-17 21:42:07 +02:00
|
|
|
uint32_t ifindex = netdev->index;
|
2018-07-17 23:15:59 +02:00
|
|
|
uint32_t ch_freq = scan_channel_to_freq(6, SCAN_BAND_2_4_GHZ);
|
|
|
|
uint32_t ch_type = NL80211_CHAN_HT20;
|
|
|
|
|
|
|
|
if (netdev->type != NL80211_IFTYPE_ADHOC) {
|
|
|
|
l_error("iftype is invalid for adhoc: %u",
|
|
|
|
netdev_get_iftype(netdev));
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (netdev->join_adhoc_cmd_id || netdev->leave_adhoc_cmd_id)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
netdev->adhoc_cb = cb;
|
|
|
|
netdev->user_data = user_data;
|
|
|
|
|
|
|
|
cmd = l_genl_msg_new_sized(NL80211_CMD_JOIN_IBSS, 128);
|
|
|
|
|
|
|
|
l_genl_msg_append_attr(cmd, NL80211_ATTR_IFINDEX, 4, &ifindex);
|
|
|
|
l_genl_msg_append_attr(cmd, NL80211_ATTR_SSID, strlen(ssid), ssid);
|
|
|
|
l_genl_msg_append_attr(cmd, NL80211_ATTR_WIPHY_FREQ, 4, &ch_freq);
|
|
|
|
l_genl_msg_append_attr(cmd, NL80211_ATTR_WIPHY_CHANNEL_TYPE, 4,
|
|
|
|
&ch_type);
|
|
|
|
l_genl_msg_append_attrv(cmd, NL80211_ATTR_IE, extra_ie, extra_ie_elems);
|
|
|
|
l_genl_msg_append_attr(cmd, NL80211_ATTR_SOCKET_OWNER, 0, NULL);
|
|
|
|
|
2018-08-09 22:16:45 +02:00
|
|
|
if (control_port) {
|
2018-07-17 23:15:59 +02:00
|
|
|
l_genl_msg_append_attr(cmd, NL80211_ATTR_CONTROL_PORT, 0, NULL);
|
|
|
|
|
2018-08-09 22:16:45 +02:00
|
|
|
if (netdev->pae_over_nl80211)
|
|
|
|
l_genl_msg_append_attr(cmd,
|
|
|
|
NL80211_ATTR_CONTROL_PORT_OVER_NL80211,
|
|
|
|
0, NULL);
|
|
|
|
}
|
2018-07-17 23:15:59 +02:00
|
|
|
|
|
|
|
netdev->join_adhoc_cmd_id = l_genl_family_send(nl80211, cmd,
|
|
|
|
netdev_join_adhoc_cb, netdev, NULL);
|
|
|
|
|
|
|
|
if (!netdev->join_adhoc_cmd_id) {
|
|
|
|
netdev->adhoc_cb = NULL;
|
|
|
|
netdev->user_data = NULL;
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_leave_adhoc_cb(struct l_genl_msg *msg, void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
|
|
|
|
netdev->leave_adhoc_cmd_id = 0;
|
|
|
|
|
|
|
|
if (netdev->adhoc_cb)
|
|
|
|
netdev->adhoc_cb(netdev, l_genl_msg_get_error(msg),
|
|
|
|
netdev->user_data);
|
|
|
|
|
|
|
|
netdev->adhoc_cb = NULL;
|
|
|
|
}
|
|
|
|
|
2018-08-20 01:25:23 +02:00
|
|
|
int netdev_leave_adhoc(struct netdev *netdev, netdev_command_cb_t cb,
|
2018-07-17 23:15:59 +02:00
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct l_genl_msg *cmd;
|
|
|
|
|
|
|
|
if (netdev->type != NL80211_IFTYPE_ADHOC) {
|
|
|
|
l_error("iftype is invalid for adhoc: %u",
|
|
|
|
netdev_get_iftype(netdev));
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (netdev->join_adhoc_cmd_id || netdev->leave_adhoc_cmd_id)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
netdev->adhoc_cb = cb;
|
|
|
|
netdev->user_data = user_data;
|
|
|
|
|
|
|
|
cmd = l_genl_msg_new_sized(NL80211_CMD_LEAVE_IBSS, 64);
|
|
|
|
|
|
|
|
l_genl_msg_append_attr(cmd, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
|
|
|
|
|
|
|
netdev->leave_adhoc_cmd_id = l_genl_family_send(nl80211, cmd,
|
|
|
|
netdev_leave_adhoc_cb, netdev,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
if (!netdev->leave_adhoc_cmd_id)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-06-14 23:56:13 +02:00
|
|
|
static uint32_t netdev_send_action_framev(struct netdev *netdev,
|
|
|
|
const uint8_t *to,
|
|
|
|
struct iovec *iov, size_t iov_len,
|
|
|
|
uint32_t freq,
|
|
|
|
l_genl_msg_func_t callback)
|
|
|
|
{
|
|
|
|
uint32_t id;
|
2019-06-27 00:15:53 +02:00
|
|
|
struct l_genl_msg *msg = nl80211_build_cmd_frame(netdev->index,
|
|
|
|
netdev->addr,
|
|
|
|
to, freq,
|
|
|
|
iov, iov_len);
|
2019-06-14 23:56:13 +02:00
|
|
|
|
2019-05-09 20:16:27 +02:00
|
|
|
id = l_genl_family_send(nl80211, msg, callback, netdev, NULL);
|
|
|
|
|
|
|
|
if (!id)
|
|
|
|
l_genl_msg_unref(msg);
|
|
|
|
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t netdev_send_action_frame(struct netdev *netdev,
|
|
|
|
const uint8_t *to,
|
|
|
|
const uint8_t *body, size_t body_len,
|
|
|
|
uint32_t freq,
|
|
|
|
l_genl_msg_func_t callback)
|
|
|
|
{
|
|
|
|
struct iovec iov[1];
|
|
|
|
|
|
|
|
iov[0].iov_base = (void *)body;
|
|
|
|
iov[0].iov_len = body_len;
|
|
|
|
|
|
|
|
return netdev_send_action_framev(netdev, to, iov, 1, freq, callback);
|
|
|
|
}
|
|
|
|
|
2017-01-12 09:36:00 +01:00
|
|
|
static void netdev_cmd_authenticate_ft_cb(struct l_genl_msg *msg,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
|
|
|
|
netdev->connect_cmd_id = 0;
|
|
|
|
|
2019-01-24 21:56:23 +01:00
|
|
|
if (l_genl_msg_get_error(msg) < 0)
|
|
|
|
netdev_connect_failed(netdev,
|
|
|
|
NETDEV_RESULT_AUTHENTICATION_FAILED,
|
2019-02-28 18:32:40 +01:00
|
|
|
MMPDU_STATUS_CODE_UNSPECIFIED);
|
2017-01-12 09:36:00 +01:00
|
|
|
}
|
|
|
|
|
2019-05-09 19:14:12 +02:00
|
|
|
static void netdev_ft_tx_authenticate(struct iovec *iov,
|
|
|
|
size_t iov_len, void *user_data)
|
2019-05-07 20:53:42 +02:00
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
struct l_genl_msg *cmd_authenticate;
|
|
|
|
|
2020-05-07 23:50:58 +02:00
|
|
|
cmd_authenticate = netdev_build_cmd_authenticate(netdev,
|
|
|
|
NL80211_AUTHTYPE_FT);
|
2019-05-07 20:53:42 +02:00
|
|
|
if (!cmd_authenticate)
|
|
|
|
goto restore_snonce;
|
|
|
|
|
2019-05-09 19:14:12 +02:00
|
|
|
l_genl_msg_append_attrv(cmd_authenticate, NL80211_ATTR_IE, iov,
|
|
|
|
iov_len);
|
|
|
|
|
2019-05-07 20:53:42 +02:00
|
|
|
netdev->connect_cmd_id = l_genl_family_send(nl80211,
|
|
|
|
cmd_authenticate,
|
|
|
|
netdev_cmd_authenticate_ft_cb,
|
|
|
|
netdev, NULL);
|
|
|
|
if (!netdev->connect_cmd_id) {
|
|
|
|
l_genl_msg_unref(cmd_authenticate);
|
|
|
|
goto restore_snonce;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
restore_snonce:
|
|
|
|
memcpy(netdev->handshake->snonce, netdev->prev_snonce, 32);
|
|
|
|
|
|
|
|
netdev_connect_failed(netdev, NETDEV_RESULT_AUTHENTICATION_FAILED,
|
|
|
|
MMPDU_STATUS_CODE_UNSPECIFIED);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_ft_tx_associate(struct iovec *ie_iov, size_t iov_len,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
|
|
|
|
msg = netdev_build_cmd_associate_common(netdev);
|
|
|
|
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN,
|
|
|
|
netdev->prev_bssid);
|
|
|
|
l_genl_msg_append_attrv(msg, NL80211_ATTR_IE, ie_iov, iov_len);
|
|
|
|
|
|
|
|
netdev->connect_cmd_id = l_genl_family_send(nl80211, msg,
|
|
|
|
netdev_cmd_ft_reassociate_cb,
|
|
|
|
netdev, NULL);
|
|
|
|
if (!netdev->connect_cmd_id) {
|
|
|
|
l_genl_msg_unref(msg);
|
|
|
|
|
|
|
|
netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED,
|
|
|
|
MMPDU_STATUS_CODE_UNSPECIFIED);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-09 20:16:28 +02:00
|
|
|
static void netdev_ft_request_cb(struct l_genl_msg *msg, void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
|
|
|
|
if (l_genl_msg_get_error(msg) < 0) {
|
|
|
|
l_error("Could not send CMD_FRAME");
|
|
|
|
netdev_connect_failed(netdev,
|
|
|
|
NETDEV_RESULT_AUTHENTICATION_FAILED,
|
|
|
|
MMPDU_STATUS_CODE_UNSPECIFIED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-16 03:40:59 +01:00
|
|
|
static void netdev_ft_response_frame_event(const struct mmpdu_header *hdr,
|
2019-05-09 20:16:28 +02:00
|
|
|
const void *body, size_t body_len,
|
2020-01-16 03:40:59 +01:00
|
|
|
int rssi, void *user_data)
|
2019-05-09 20:16:28 +02:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
|
2020-01-16 03:40:59 +01:00
|
|
|
struct netdev *netdev = user_data;
|
2019-05-09 20:16:28 +02:00
|
|
|
|
|
|
|
if (!netdev->ap || !netdev->in_ft)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ret = auth_proto_rx_authenticate(netdev->ap, body, body_len);
|
|
|
|
if (ret < 0)
|
|
|
|
goto ft_error;
|
|
|
|
else if (ret > 0) {
|
|
|
|
status_code = (uint16_t)ret;
|
|
|
|
goto ft_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
ft_error:
|
|
|
|
netdev_connect_failed(netdev, NETDEV_RESULT_AUTHENTICATION_FAILED,
|
|
|
|
status_code);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-01-16 03:40:59 +01:00
|
|
|
static void netdev_qos_map_frame_event(const struct mmpdu_header *hdr,
|
2019-09-14 00:26:31 +02:00
|
|
|
const void *body, size_t body_len,
|
2020-01-16 03:40:59 +01:00
|
|
|
int rssi, void *user_data)
|
2019-09-14 00:26:31 +02:00
|
|
|
{
|
2020-01-16 03:40:59 +01:00
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
|
2019-09-14 00:26:31 +02:00
|
|
|
/* No point telling the kernel */
|
|
|
|
if (!netdev->connected)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (memcmp(netdev->handshake->aa, hdr->address_2, ETH_ALEN))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (body_len < 5)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (l_get_u8(body + 2) != IE_TYPE_QOS_MAP_SET)
|
|
|
|
return;
|
|
|
|
|
|
|
|
netdev_send_qos_map_set(netdev, body + 4, body_len - 4);
|
|
|
|
}
|
|
|
|
|
2019-05-09 20:16:28 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-07-15 23:29:17 +02:00
|
|
|
static bool netdev_ft_work_ready(struct wiphy_radio_work_item *item)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = l_container_of(item, struct netdev, work);
|
|
|
|
|
|
|
|
if (!auth_proto_start(netdev->ap)) {
|
|
|
|
/* Restore original nonce */
|
|
|
|
memcpy(netdev->handshake->snonce, netdev->prev_snonce, 32);
|
|
|
|
|
|
|
|
netdev_connect_failed(netdev, NETDEV_RESULT_ABORTED,
|
|
|
|
MMPDU_STATUS_CODE_UNSPECIFIED);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct wiphy_radio_work_item_ops ft_work_ops = {
|
|
|
|
.do_work = netdev_ft_work_ready,
|
|
|
|
};
|
|
|
|
|
2019-05-09 20:16:28 +02:00
|
|
|
static int fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
|
|
|
|
bool over_air,
|
2017-01-12 09:36:00 +01:00
|
|
|
netdev_connect_cb_t cb)
|
|
|
|
{
|
2018-06-22 02:32:55 +02:00
|
|
|
struct netdev_handshake_state *nhs;
|
2017-01-12 09:36:00 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We reuse the handshake_state object and reset what's needed.
|
|
|
|
* Could also create a new object and copy most of the state but
|
|
|
|
* we would end up doing more work.
|
|
|
|
*/
|
2019-05-07 20:53:42 +02:00
|
|
|
memcpy(netdev->prev_bssid, netdev->handshake->aa, ETH_ALEN);
|
|
|
|
memcpy(netdev->prev_snonce, netdev->handshake->snonce, 32);
|
2017-01-12 09:36:00 +01:00
|
|
|
|
|
|
|
handshake_state_new_snonce(netdev->handshake);
|
|
|
|
|
2019-05-09 20:16:28 +02:00
|
|
|
netdev->prev_frequency = netdev->frequency;
|
2019-05-07 20:53:42 +02:00
|
|
|
netdev->frequency = target_bss->frequency;
|
2017-01-12 09:36:00 +01:00
|
|
|
|
|
|
|
handshake_state_set_authenticator_address(netdev->handshake,
|
|
|
|
target_bss->addr);
|
2019-05-09 20:16:28 +02:00
|
|
|
|
fast_transition: fix crash by parsing RSN IE only if present
When performing a fast transition to another OPEN network the RSN
element won't be there and therefore the bss->rsne is gonna be NULL.
Fix crash by not accessing the rsne member when performing a fast
transition to an AP that doe snot advertise any RSN IE.
Crash caught with gdb:
src/station.c:station_transition_start() 186, target 34:8f:27:2f:b8:fc
Program received signal SIGSEGV, Segmentation fault.
handshake_state_set_authenticator_ie (s=0x555555626eb0, ie=0x0) at src/handshake.c:163
163 s->authenticator_ie = l_memdup(ie, ie[1] + 2u);
(gdb) bt
#0 handshake_state_set_authenticator_ie (s=0x555555626eb0, ie=0x0) at src/handshake.c:163
#1 0x0000555555561a98 in fast_transition (netdev=0x55555562fbe0, target_bss=0x55555561f4a0,
over_air=over_air@entry=true, cb=0x55555556d5b0 <station_fast_transition_cb>) at src/netdev.c:3164
#2 0x0000555555565dfd in netdev_fast_transition (netdev=<optimized out>, target_bss=<optimized out>,
cb=<optimized out>) at src/netdev.c:3232
#3 0x000055555556ccbd in station_transition_start (bss=0x55555561f4a0, station=0x555555617da0)
at src/station.c:1261
#4 station_roam_scan_notify (err=<optimized out>, bss_list=<optimized out>, userdata=0x555555617da0)
at src/station.c:1444
#5 0x0000555555579560 in scan_finished (sc=0x55555562bf80, err=err@entry=0, bss_list=0x55555561bd90,
sr=0x555555626b30, wiphy=<optimized out>) at src/scan.c:1234
#6 0x0000555555579620 in get_scan_done (user=0x555555618920) at src/scan.c:1264
#7 0x00005555555abd23 in destroy_request (data=0x55555561b000) at ell/genl.c:673
#8 0x00005555555ac129 in process_unicast (nlmsg=0x7fffffffc310, genl=0x55555560b7a0) at ell/genl.c:940
#9 received_data (io=<optimized out>, user_data=0x55555560b7a0) at ell/genl.c:1039
#10 0x00005555555a8aa3 in io_callback (fd=<optimized out>, events=1, user_data=0x55555560b840)
at ell/io.c:126
#11 0x00005555555a7ccd in l_main_iterate (timeout=<optimized out>) at ell/main.c:473
#12 0x00005555555a7d9c in l_main_run () at ell/main.c:520
#13 l_main_run () at ell/main.c:502
#14 0x00005555555a7fac in l_main_run_with_signal (callback=<optimized out>, user_data=0x0)
at ell/main.c:642
#15 0x000055555555e5b8 in main (argc=<optimized out>, argv=<optimized out>) at src/main.c:519
2019-08-28 21:06:15 +02:00
|
|
|
if (target_bss->rsne)
|
|
|
|
handshake_state_set_authenticator_ie(netdev->handshake,
|
2018-08-25 03:54:24 +02:00
|
|
|
target_bss->rsne);
|
2017-01-12 09:36:00 +01:00
|
|
|
memcpy(netdev->handshake->mde + 2, target_bss->mde, 3);
|
|
|
|
|
|
|
|
netdev->operational = false;
|
|
|
|
netdev->in_ft = true;
|
|
|
|
netdev->connect_cb = cb;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Cancel commands that could be running because of EAPoL activity
|
|
|
|
* like re-keying, this way the callbacks for those commands don't
|
|
|
|
* have to check if failures resulted from the transition.
|
|
|
|
*/
|
2019-04-03 18:47:09 +02:00
|
|
|
nhs = l_container_of(netdev->handshake,
|
2018-06-22 02:32:55 +02:00
|
|
|
struct netdev_handshake_state, super);
|
|
|
|
|
netdev: signal handshake complete after setting all keys
Currently, netdev triggers the HANDSHAKE_COMPLETE event after completing
the SET_STATION (after setting the pairwise key). Depending on the timing
this may happen before the GTK/IGTK are set which will result in group
traffic not working initially (the GTK/IGTK would still get set, but group
traffic would not work immediately after DBus said you were connected, this
mainly poses a problem with autotests).
In order to fix this, several flags were added in netdev_handshake_state:
ptk_installed, gtk_installed, igtk_installed, and completed. Each of these
flags are set true when their respective keys are set, and in each key
callback we try to trigger the handshake complete event (assuming all the
flags are true). Initially the gtk/igtk flags are set to true, for reasons
explained below.
In the WPA2 case, all the key setter functions are called sequentially from
eapol. With this change, the PTK is now set AFTER the gtk/igtk. This is
because the gtk/igtk are optional and only set if group traffic is allowed.
If the gtk/igtk are not used, we set the PTK and can immediately trigger the
handshake complete event (since gtk_installed/igtk_installed are initialized
as true). When the gtk/igtk are being set, we immediately set their flags to
false and wait for their callbacks in addition to the PTK callback. Doing it
this way handles both group traffic and non group traffic paths.
WPA1 throws a wrench into this since the group keys are obtained in a
separate handshake. For this case a new flag was added to the handshake_state,
'wait_for_gtk'. This allows netdev to set the PTK after the initial 4-way,
but still wait for the gtk/igtk setters to get called before triggering the
handshake complete event. As a precaution, netdev sets a timeout that will
trigger if the gtk/igtk setters are never called. In this case we can still
complete the connection, but print a warning that group traffic will not be
allowed.
2018-10-26 18:44:58 +02:00
|
|
|
/* reset key states just as we do in initialization */
|
|
|
|
nhs->complete = false;
|
|
|
|
nhs->ptk_installed = false;
|
|
|
|
nhs->gtk_installed = true;
|
|
|
|
nhs->igtk_installed = true;
|
|
|
|
|
2018-06-22 02:32:55 +02:00
|
|
|
if (nhs->group_new_key_cmd_id) {
|
|
|
|
l_genl_family_cancel(nl80211, nhs->group_new_key_cmd_id);
|
|
|
|
nhs->group_new_key_cmd_id = 0;
|
2017-01-12 09:36:00 +01:00
|
|
|
}
|
|
|
|
|
2018-06-22 02:32:55 +02:00
|
|
|
if (nhs->group_management_new_key_cmd_id) {
|
2017-01-12 09:36:00 +01:00
|
|
|
l_genl_family_cancel(nl80211,
|
2018-06-22 02:32:55 +02:00
|
|
|
nhs->group_management_new_key_cmd_id);
|
|
|
|
nhs->group_management_new_key_cmd_id = 0;
|
2017-01-12 09:36:00 +01:00
|
|
|
}
|
|
|
|
|
2018-10-20 17:38:56 +02:00
|
|
|
if (netdev->rekey_offload_cmd_id) {
|
|
|
|
l_genl_family_cancel(nl80211, netdev->rekey_offload_cmd_id);
|
|
|
|
netdev->rekey_offload_cmd_id = 0;
|
|
|
|
}
|
|
|
|
|
2017-08-18 14:22:58 +02:00
|
|
|
netdev_rssi_polling_update(netdev);
|
|
|
|
|
2019-05-07 20:53:42 +02:00
|
|
|
if (netdev->sm) {
|
|
|
|
eapol_sm_free(netdev->sm);
|
|
|
|
netdev->sm = NULL;
|
|
|
|
}
|
|
|
|
|
2019-05-09 20:16:28 +02:00
|
|
|
if (over_air)
|
|
|
|
netdev->ap = ft_over_air_sm_new(netdev->handshake,
|
|
|
|
netdev_ft_tx_authenticate,
|
|
|
|
netdev_ft_tx_associate, netdev);
|
|
|
|
else
|
|
|
|
netdev->ap = ft_over_ds_sm_new(netdev->handshake,
|
|
|
|
netdev_ft_over_ds_tx_authenticate,
|
|
|
|
netdev_ft_tx_associate, netdev);
|
|
|
|
|
2020-07-15 23:29:17 +02:00
|
|
|
wiphy_radio_work_insert(netdev->wiphy, &netdev->work, 1,
|
|
|
|
&ft_work_ops);
|
2019-05-09 20:16:28 +02:00
|
|
|
|
2017-01-12 09:36:00 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-05-09 20:16:28 +02:00
|
|
|
int netdev_fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
|
|
|
|
netdev_connect_cb_t cb)
|
|
|
|
{
|
|
|
|
return fast_transition(netdev, target_bss, true, cb);
|
|
|
|
}
|
|
|
|
|
|
|
|
int netdev_fast_transition_over_ds(struct netdev *netdev,
|
|
|
|
struct scan_bss *target_bss,
|
|
|
|
netdev_connect_cb_t cb)
|
|
|
|
{
|
|
|
|
return fast_transition(netdev, target_bss, false, cb);
|
|
|
|
}
|
|
|
|
|
2017-04-29 03:53:27 +02:00
|
|
|
static void netdev_preauth_cb(const uint8_t *pmk, void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev_preauth_state *preauth = user_data;
|
|
|
|
netdev_preauthenticate_cb_t cb = preauth->cb;
|
|
|
|
|
|
|
|
preauth->cb = NULL;
|
|
|
|
|
|
|
|
cb(preauth->netdev,
|
|
|
|
pmk ? NETDEV_RESULT_OK : NETDEV_RESULT_HANDSHAKE_FAILED,
|
|
|
|
pmk, preauth->user_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
int netdev_preauthenticate(struct netdev *netdev, struct scan_bss *target_bss,
|
|
|
|
netdev_preauthenticate_cb_t cb, void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev_preauth_state *preauth;
|
|
|
|
|
|
|
|
if (!netdev->operational)
|
|
|
|
return -ENOTCONN;
|
|
|
|
|
|
|
|
preauth = l_new(struct netdev_preauth_state, 1);
|
|
|
|
|
|
|
|
if (!eapol_preauth_start(target_bss->addr, netdev->handshake,
|
|
|
|
netdev_preauth_cb, preauth,
|
|
|
|
netdev_preauth_destroy)) {
|
|
|
|
l_free(preauth);
|
|
|
|
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
preauth->cb = cb;
|
|
|
|
preauth->user_data = user_data;
|
|
|
|
preauth->netdev = netdev;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-12-23 11:38:01 +01:00
|
|
|
static void netdev_neighbor_report_req_cb(struct l_genl_msg *msg,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
|
|
|
|
if (!netdev->neighbor_report_cb)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (l_genl_msg_get_error(msg) < 0) {
|
2017-01-23 12:38:40 +01:00
|
|
|
netdev->neighbor_report_cb(netdev, l_genl_msg_get_error(msg),
|
|
|
|
NULL, 0, netdev->user_data);
|
2016-12-23 11:38:01 +01:00
|
|
|
|
|
|
|
netdev->neighbor_report_cb = NULL;
|
|
|
|
|
|
|
|
l_timeout_remove(netdev->neighbor_report_timeout);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_neighbor_report_timeout(struct l_timeout *timeout,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
|
2017-01-23 12:38:40 +01:00
|
|
|
netdev->neighbor_report_cb(netdev, -ETIMEDOUT, NULL, 0,
|
|
|
|
netdev->user_data);
|
2016-12-23 11:38:01 +01:00
|
|
|
|
|
|
|
netdev->neighbor_report_cb = NULL;
|
2017-01-23 12:38:40 +01:00
|
|
|
|
|
|
|
l_timeout_remove(netdev->neighbor_report_timeout);
|
2016-12-23 11:38:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int netdev_neighbor_report_req(struct netdev *netdev,
|
|
|
|
netdev_neighbor_report_cb_t cb)
|
|
|
|
{
|
|
|
|
const uint8_t action_frame[] = {
|
|
|
|
0x05, /* Category: Radio Measurement */
|
|
|
|
0x04, /* Radio Measurement Action: Neighbor Report Request */
|
|
|
|
0x01, /* Dialog Token: a non-zero value (unused) */
|
|
|
|
};
|
|
|
|
|
|
|
|
if (netdev->neighbor_report_cb || !netdev->connected)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
if (!netdev_send_action_frame(netdev, netdev->handshake->aa,
|
|
|
|
action_frame, sizeof(action_frame),
|
2019-05-09 20:16:27 +02:00
|
|
|
netdev->frequency,
|
2016-12-23 11:38:01 +01:00
|
|
|
netdev_neighbor_report_req_cb))
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
netdev->neighbor_report_cb = cb;
|
|
|
|
|
|
|
|
/* Set a 3-second timeout */
|
|
|
|
netdev->neighbor_report_timeout =
|
|
|
|
l_timeout_create(3, netdev_neighbor_report_timeout,
|
|
|
|
netdev, NULL);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-01-16 03:40:59 +01:00
|
|
|
static void netdev_neighbor_report_frame_event(const struct mmpdu_header *hdr,
|
2017-08-31 04:04:45 +02:00
|
|
|
const void *body, size_t body_len,
|
2020-01-16 03:40:59 +01:00
|
|
|
int rssi, void *user_data)
|
2016-12-23 11:38:00 +01:00
|
|
|
{
|
2020-01-16 03:40:59 +01:00
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
|
2017-08-31 04:04:45 +02:00
|
|
|
if (body_len < 3) {
|
|
|
|
l_debug("Neighbor Report frame too short");
|
2016-12-23 11:38:00 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-08-31 04:04:45 +02:00
|
|
|
if (!netdev->neighbor_report_cb)
|
2016-12-23 11:38:00 +01:00
|
|
|
return;
|
|
|
|
|
2017-08-31 04:04:45 +02:00
|
|
|
/*
|
|
|
|
* Don't use the dialog token (byte 3), return the first Neighbor
|
|
|
|
* Report Response received.
|
|
|
|
*
|
|
|
|
* Byte 1 is 0x05 for Radio Measurement, byte 2 is 0x05 for
|
|
|
|
* Neighbor Report.
|
|
|
|
*/
|
2016-12-23 11:38:00 +01:00
|
|
|
|
2017-08-31 04:04:45 +02:00
|
|
|
netdev->neighbor_report_cb(netdev, 0, body + 3, body_len - 3,
|
|
|
|
netdev->user_data);
|
|
|
|
netdev->neighbor_report_cb = NULL;
|
2016-12-23 11:38:00 +01:00
|
|
|
|
2017-08-31 04:04:45 +02:00
|
|
|
l_timeout_remove(netdev->neighbor_report_timeout);
|
2016-12-23 11:38:00 +01:00
|
|
|
}
|
|
|
|
|
2020-04-02 07:30:17 +02:00
|
|
|
static void netdev_sa_query_resp_cb(struct l_genl_msg *msg, void *user_data)
|
2018-02-07 22:01:43 +01:00
|
|
|
{
|
2020-04-02 07:30:17 +02:00
|
|
|
int err = l_genl_msg_get_error(msg);
|
|
|
|
const char *ext_error;
|
|
|
|
|
|
|
|
if (err >= 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ext_error = l_genl_msg_get_extended_error(msg);
|
|
|
|
l_debug("error sending SA Query request: %s",
|
|
|
|
ext_error ? ext_error : strerror(-err));
|
2018-02-07 22:01:43 +01:00
|
|
|
}
|
|
|
|
|
2020-01-16 03:40:59 +01:00
|
|
|
static void netdev_sa_query_req_frame_event(const struct mmpdu_header *hdr,
|
|
|
|
const void *body, size_t body_len,
|
|
|
|
int rssi, void *user_data)
|
2018-02-07 22:01:43 +01:00
|
|
|
{
|
|
|
|
uint8_t sa_resp[4];
|
|
|
|
uint16_t transaction;
|
2020-01-16 03:40:59 +01:00
|
|
|
struct netdev *netdev = user_data;
|
2018-02-07 22:01:43 +01:00
|
|
|
|
|
|
|
if (body_len < 4) {
|
|
|
|
l_debug("SA Query request too short");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!netdev->connected)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* only care about SA Queries from our connected AP */
|
|
|
|
if (memcmp(hdr->address_2, netdev->handshake->aa, 6))
|
|
|
|
return;
|
|
|
|
|
|
|
|
transaction = l_get_u16(body + 2);
|
|
|
|
|
|
|
|
sa_resp[0] = 0x08; /* SA Query */
|
|
|
|
sa_resp[1] = 0x01; /* Response */
|
|
|
|
memcpy(sa_resp + 2, &transaction, 2);
|
|
|
|
|
|
|
|
l_info("received SA Query request from "MAC", transaction=%u",
|
|
|
|
MAC_STR(hdr->address_2), transaction);
|
|
|
|
|
|
|
|
if (!netdev_send_action_frame(netdev, netdev->handshake->aa,
|
|
|
|
sa_resp, sizeof(sa_resp),
|
2019-05-09 20:16:27 +02:00
|
|
|
netdev->frequency,
|
2018-02-07 22:01:43 +01:00
|
|
|
netdev_sa_query_resp_cb)) {
|
|
|
|
l_error("error sending SA Query response");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-16 03:40:59 +01:00
|
|
|
static void netdev_sa_query_resp_frame_event(const struct mmpdu_header *hdr,
|
|
|
|
const void *body, size_t body_len,
|
|
|
|
int rssi, void *user_data)
|
2018-02-01 18:22:39 +01:00
|
|
|
{
|
2020-01-16 03:40:59 +01:00
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
|
2018-02-01 18:22:39 +01:00
|
|
|
if (body_len < 4) {
|
|
|
|
l_debug("SA Query frame too short");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
l_debug("SA Query src="MAC" dest="MAC" bssid="MAC" transaction=%u",
|
|
|
|
MAC_STR(hdr->address_2), MAC_STR(hdr->address_1),
|
|
|
|
MAC_STR(hdr->address_3), l_get_u16(body + 2));
|
|
|
|
|
|
|
|
if (!netdev->sa_query_timeout) {
|
|
|
|
l_debug("no SA Query request sent");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if this is from our connected BSS */
|
|
|
|
if (memcmp(hdr->address_2, netdev->handshake->aa, 6)) {
|
|
|
|
l_debug("received SA Query from non-connected AP");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (memcmp(body + 2, &netdev->sa_query_id, 2)) {
|
|
|
|
l_debug("SA Query transaction ID's did not match");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
l_info("SA Query response from connected BSS received, "
|
|
|
|
"keeping the connection active");
|
|
|
|
|
|
|
|
l_timeout_remove(netdev->sa_query_timeout);
|
|
|
|
netdev->sa_query_timeout = NULL;
|
|
|
|
}
|
|
|
|
|
2020-04-02 07:30:17 +02:00
|
|
|
static void netdev_sa_query_req_cb(struct l_genl_msg *msg, void *user_data)
|
2018-02-01 18:22:39 +01:00
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
2020-04-02 07:30:17 +02:00
|
|
|
int err = l_genl_msg_get_error(msg);
|
|
|
|
const char *ext_error;
|
2018-02-01 18:22:39 +01:00
|
|
|
|
2020-04-02 07:30:17 +02:00
|
|
|
if (err >= 0)
|
|
|
|
return;
|
2018-02-01 18:22:39 +01:00
|
|
|
|
2020-04-02 07:30:17 +02:00
|
|
|
ext_error = l_genl_msg_get_extended_error(msg);
|
|
|
|
l_debug("error sending SA Query request: %s",
|
|
|
|
ext_error ? ext_error : strerror(-err));
|
|
|
|
|
|
|
|
l_timeout_remove(netdev->sa_query_timeout);
|
|
|
|
netdev->sa_query_timeout = NULL;
|
2018-02-01 18:22:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_sa_query_timeout(struct l_timeout *timeout,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
|
|
|
|
l_info("SA Query timed out, connection is invalid. Disconnecting...");
|
|
|
|
|
|
|
|
l_timeout_remove(netdev->sa_query_timeout);
|
|
|
|
netdev->sa_query_timeout = NULL;
|
|
|
|
|
|
|
|
msg = netdev_build_cmd_disconnect(netdev,
|
|
|
|
MMPDU_REASON_CODE_PREV_AUTH_NOT_VALID);
|
|
|
|
netdev->disconnect_cmd_id = l_genl_family_send(nl80211, msg,
|
2019-01-24 21:56:23 +01:00
|
|
|
netdev_disconnect_cb, netdev, NULL);
|
2018-02-01 18:22:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_unprot_disconnect_event(struct l_genl_msg *msg,
|
|
|
|
struct netdev *netdev)
|
|
|
|
{
|
|
|
|
const struct mmpdu_header *hdr = NULL;
|
|
|
|
struct l_genl_attr attr;
|
|
|
|
uint16_t type;
|
|
|
|
uint16_t len;
|
|
|
|
const void *data;
|
|
|
|
uint8_t action_frame[4];
|
|
|
|
uint8_t reason_code;
|
|
|
|
|
|
|
|
if (!netdev->connected)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* ignore excessive disassociate requests */
|
|
|
|
if (netdev->sa_query_timeout)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!l_genl_attr_init(&attr, msg))
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (l_genl_attr_next(&attr, &type, &len, &data)) {
|
|
|
|
switch (type) {
|
|
|
|
case NL80211_ATTR_FRAME:
|
|
|
|
hdr = mpdu_validate(data, len);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check that ATTR_FRAME was actually included */
|
|
|
|
if (!hdr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* get reason code, first byte of frame */
|
|
|
|
reason_code = l_get_u8(mmpdu_body(hdr));
|
|
|
|
|
|
|
|
l_info("disconnect event, src="MAC" dest="MAC" bssid="MAC" reason=%u",
|
|
|
|
MAC_STR(hdr->address_2), MAC_STR(hdr->address_1),
|
|
|
|
MAC_STR(hdr->address_3), reason_code);
|
|
|
|
|
|
|
|
if (memcmp(hdr->address_2, netdev->handshake->aa, 6)) {
|
|
|
|
l_debug("received invalid disassociate frame");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reason_code != MMPDU_REASON_CODE_CLASS2_FRAME_FROM_NONAUTH_STA &&
|
|
|
|
reason_code !=
|
|
|
|
MMPDU_REASON_CODE_CLASS3_FRAME_FROM_NONASSOC_STA) {
|
|
|
|
l_debug("invalid reason code %u", reason_code);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
action_frame[0] = 0x08; /* Category: SA Query */
|
|
|
|
action_frame[1] = 0x00; /* SA Query Action: Request */
|
|
|
|
|
|
|
|
/* Transaction ID */
|
|
|
|
l_getrandom(action_frame + 2, 2);
|
|
|
|
|
|
|
|
if (!netdev_send_action_frame(netdev, netdev->handshake->aa,
|
|
|
|
action_frame, sizeof(action_frame),
|
2019-05-09 20:16:27 +02:00
|
|
|
netdev->frequency,
|
2018-02-01 18:22:39 +01:00
|
|
|
netdev_sa_query_req_cb)) {
|
|
|
|
l_error("error sending SA Query action frame");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
netdev->sa_query_id = l_get_u16(action_frame + 2);
|
|
|
|
netdev->sa_query_timeout = l_timeout_create(3,
|
|
|
|
netdev_sa_query_timeout, netdev, NULL);
|
|
|
|
}
|
|
|
|
|
2018-07-17 00:29:15 +02:00
|
|
|
static void netdev_station_event(struct l_genl_msg *msg,
|
|
|
|
struct netdev *netdev, bool added)
|
|
|
|
{
|
|
|
|
struct l_genl_attr attr;
|
|
|
|
uint16_t type;
|
|
|
|
uint16_t len;
|
|
|
|
const void *data;
|
|
|
|
const uint8_t *mac = NULL;
|
|
|
|
|
|
|
|
if (netdev_get_iftype(netdev) != NETDEV_IFTYPE_ADHOC)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!l_genl_attr_init(&attr, msg))
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (l_genl_attr_next(&attr, &type, &len, &data)) {
|
|
|
|
switch (type) {
|
|
|
|
case NL80211_ATTR_MAC:
|
|
|
|
mac = data;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mac) {
|
|
|
|
l_error("%s station event did not include MAC attribute",
|
|
|
|
added ? "new" : "del");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
WATCHLIST_NOTIFY(&netdev->station_watches,
|
|
|
|
netdev_station_watch_func_t, netdev, mac, added);
|
|
|
|
}
|
|
|
|
|
2016-06-15 17:54:13 +02:00
|
|
|
static void netdev_mlme_notify(struct l_genl_msg *msg, void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = NULL;
|
|
|
|
struct l_genl_attr attr;
|
|
|
|
uint16_t type, len;
|
|
|
|
const void *data;
|
|
|
|
uint8_t cmd;
|
|
|
|
|
|
|
|
cmd = l_genl_msg_get_command(msg);
|
|
|
|
|
2019-07-15 21:04:43 +02:00
|
|
|
l_debug("MLME notification %s(%u)", nl80211cmd_to_string(cmd), cmd);
|
2016-06-15 17:54:13 +02:00
|
|
|
|
|
|
|
if (!l_genl_attr_init(&attr, msg))
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (l_genl_attr_next(&attr, &type, &len, &data)) {
|
|
|
|
switch (type) {
|
|
|
|
case NL80211_ATTR_IFINDEX:
|
|
|
|
if (len != sizeof(uint32_t)) {
|
|
|
|
l_warn("Invalid interface index attribute");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
netdev = netdev_find(*((uint32_t *) data));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-21 15:55:02 +02:00
|
|
|
if (!netdev)
|
2016-06-15 17:54:13 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case NL80211_CMD_AUTHENTICATE:
|
|
|
|
netdev_authenticate_event(msg, netdev);
|
2016-06-15 21:02:24 +02:00
|
|
|
break;
|
|
|
|
case NL80211_CMD_DEAUTHENTICATE:
|
|
|
|
netdev_deauthenticate_event(msg, netdev);
|
2016-06-15 17:54:13 +02:00
|
|
|
break;
|
2016-06-16 17:54:20 +02:00
|
|
|
case NL80211_CMD_ASSOCIATE:
|
|
|
|
netdev_associate_event(msg, netdev);
|
|
|
|
break;
|
2016-06-27 23:35:31 +02:00
|
|
|
case NL80211_CMD_CONNECT:
|
|
|
|
netdev_connect_event(msg, netdev);
|
|
|
|
break;
|
2016-06-16 18:21:12 +02:00
|
|
|
case NL80211_CMD_DISCONNECT:
|
|
|
|
netdev_disconnect_event(msg, netdev);
|
|
|
|
break;
|
2016-06-16 18:06:17 +02:00
|
|
|
case NL80211_CMD_NOTIFY_CQM:
|
|
|
|
netdev_cqm_event(msg, netdev);
|
|
|
|
break;
|
2016-06-23 00:49:16 +02:00
|
|
|
case NL80211_CMD_SET_REKEY_OFFLOAD:
|
|
|
|
netdev_rekey_offload_event(msg, netdev);
|
|
|
|
break;
|
2018-02-01 18:22:39 +01:00
|
|
|
case NL80211_CMD_UNPROT_DEAUTHENTICATE:
|
|
|
|
case NL80211_CMD_UNPROT_DISASSOCIATE:
|
|
|
|
netdev_unprot_disconnect_event(msg, netdev);
|
|
|
|
break;
|
2018-07-17 00:29:15 +02:00
|
|
|
case NL80211_CMD_NEW_STATION:
|
|
|
|
netdev_station_event(msg, netdev, true);
|
|
|
|
break;
|
|
|
|
case NL80211_CMD_DEL_STATION:
|
|
|
|
netdev_station_event(msg, netdev, false);
|
|
|
|
break;
|
2016-06-15 17:54:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-01 18:08:19 +02:00
|
|
|
static void netdev_pae_destroy(void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
|
|
|
|
netdev->pae_io = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool netdev_pae_read(struct l_io *io, void *user_data)
|
|
|
|
{
|
|
|
|
int fd = l_io_get_fd(io);
|
|
|
|
struct sockaddr_ll sll;
|
|
|
|
socklen_t sll_len;
|
|
|
|
ssize_t bytes;
|
|
|
|
uint8_t frame[IEEE80211_MAX_DATA_LEN];
|
|
|
|
|
|
|
|
memset(&sll, 0, sizeof(sll));
|
|
|
|
sll_len = sizeof(sll);
|
|
|
|
|
|
|
|
bytes = recvfrom(fd, frame, sizeof(frame), 0,
|
|
|
|
(struct sockaddr *) &sll, &sll_len);
|
|
|
|
if (bytes <= 0) {
|
|
|
|
l_error("EAPoL read socket: %s", strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sll.sll_halen != ETH_ALEN)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
__eapol_rx_packet(sll.sll_ifindex, sll.sll_addr,
|
|
|
|
ntohs(sll.sll_protocol), frame, bytes, false);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-03-13 21:27:20 +01:00
|
|
|
static void netdev_control_port_frame_event(struct l_genl_msg *msg,
|
|
|
|
struct netdev *netdev)
|
|
|
|
{
|
|
|
|
struct l_genl_attr attr;
|
|
|
|
uint16_t type;
|
|
|
|
uint16_t len;
|
|
|
|
const void *data;
|
|
|
|
const uint8_t *frame = NULL;
|
|
|
|
uint16_t frame_len = 0;
|
|
|
|
const uint8_t *src = NULL;
|
|
|
|
uint16_t proto = 0;
|
|
|
|
bool unencrypted = false;
|
|
|
|
|
|
|
|
l_debug("");
|
|
|
|
|
|
|
|
if (!l_genl_attr_init(&attr, msg))
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (l_genl_attr_next(&attr, &type, &len, &data)) {
|
|
|
|
switch (type) {
|
|
|
|
case NL80211_ATTR_FRAME:
|
|
|
|
if (frame)
|
|
|
|
return;
|
|
|
|
|
|
|
|
frame = data;
|
|
|
|
frame_len = len;
|
|
|
|
break;
|
|
|
|
case NL80211_ATTR_MAC:
|
|
|
|
if (src)
|
|
|
|
return;
|
|
|
|
|
|
|
|
src = data;
|
|
|
|
break;
|
|
|
|
case NL80211_ATTR_CONTROL_PORT_ETHERTYPE:
|
|
|
|
if (len != sizeof(proto))
|
|
|
|
return;
|
|
|
|
|
|
|
|
proto = *((const uint16_t *) data);
|
|
|
|
break;
|
|
|
|
case NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT:
|
|
|
|
unencrypted = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!src || !frame || !proto)
|
|
|
|
return;
|
|
|
|
|
|
|
|
__eapol_rx_packet(netdev->index, src, proto,
|
|
|
|
frame, frame_len, unencrypted);
|
|
|
|
}
|
|
|
|
|
2018-05-01 20:31:20 +02:00
|
|
|
static struct l_genl_msg *netdev_build_control_port_frame(struct netdev *netdev,
|
|
|
|
const uint8_t *to,
|
|
|
|
uint16_t proto,
|
|
|
|
bool unencrypted,
|
|
|
|
const void *body,
|
|
|
|
size_t body_len)
|
|
|
|
{
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
|
|
|
|
msg = l_genl_msg_new_sized(NL80211_CMD_CONTROL_PORT_FRAME,
|
|
|
|
128 + body_len);
|
|
|
|
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_FRAME, body_len, body);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, 2,
|
|
|
|
&proto);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, to);
|
|
|
|
|
|
|
|
if (unencrypted)
|
|
|
|
l_genl_msg_append_attr(msg,
|
|
|
|
NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, 0, NULL);
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_control_port_frame_cb(struct l_genl_msg *msg,
|
|
|
|
void *user_data)
|
|
|
|
{
|
2020-04-02 07:30:17 +02:00
|
|
|
int err = l_genl_msg_get_error(msg);
|
|
|
|
const char *ext_error;
|
2018-05-01 20:31:20 +02:00
|
|
|
|
2020-04-02 07:30:17 +02:00
|
|
|
if (err >= 0)
|
|
|
|
return;
|
2018-05-01 20:31:20 +02:00
|
|
|
|
2020-04-02 07:30:17 +02:00
|
|
|
ext_error = l_genl_msg_get_extended_error(msg);
|
|
|
|
l_error("CMD_CONTROL_PORT failed: %s",
|
|
|
|
ext_error ? ext_error : strerror(-err));
|
2018-05-01 20:31:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int netdev_control_port_write_pae(struct netdev *netdev,
|
|
|
|
const uint8_t *dest,
|
|
|
|
uint16_t proto,
|
|
|
|
const struct eapol_frame *ef,
|
|
|
|
bool noencrypt)
|
|
|
|
{
|
|
|
|
int fd = l_io_get_fd(netdev->pae_io);
|
|
|
|
struct sockaddr_ll sll;
|
|
|
|
size_t frame_size = sizeof(struct eapol_header) +
|
|
|
|
L_BE16_TO_CPU(ef->header.packet_len);
|
|
|
|
ssize_t r;
|
|
|
|
|
|
|
|
memset(&sll, 0, sizeof(sll));
|
|
|
|
sll.sll_family = AF_PACKET;
|
|
|
|
sll.sll_ifindex = netdev->index;
|
|
|
|
sll.sll_protocol = htons(proto);
|
|
|
|
sll.sll_halen = ETH_ALEN;
|
|
|
|
memcpy(sll.sll_addr, dest, ETH_ALEN);
|
|
|
|
|
|
|
|
r = sendto(fd, ef, frame_size, 0,
|
|
|
|
(struct sockaddr *) &sll, sizeof(sll));
|
|
|
|
if (r < 0)
|
|
|
|
l_error("EAPoL write socket: %s", strerror(errno));
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int netdev_control_port_frame(uint32_t ifindex,
|
|
|
|
const uint8_t *dest, uint16_t proto,
|
|
|
|
const struct eapol_frame *ef,
|
|
|
|
bool noencrypt,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
struct netdev *netdev;
|
|
|
|
size_t frame_size;
|
|
|
|
|
|
|
|
netdev = netdev_find(ifindex);
|
|
|
|
if (!netdev)
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
frame_size = sizeof(struct eapol_header) +
|
|
|
|
L_BE16_TO_CPU(ef->header.packet_len);
|
|
|
|
|
2018-07-02 03:35:22 +02:00
|
|
|
if (!netdev->pae_over_nl80211)
|
2018-05-01 20:31:20 +02:00
|
|
|
return netdev_control_port_write_pae(netdev, dest, proto,
|
|
|
|
ef, noencrypt);
|
|
|
|
|
|
|
|
msg = netdev_build_control_port_frame(netdev, dest, proto, noencrypt,
|
|
|
|
ef, frame_size);
|
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
if (!l_genl_family_send(nl80211, msg, netdev_control_port_frame_cb,
|
|
|
|
netdev, NULL)) {
|
|
|
|
l_genl_msg_unref(msg);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-12-23 11:38:00 +01:00
|
|
|
static void netdev_unicast_notify(struct l_genl_msg *msg, void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev *netdev = NULL;
|
|
|
|
struct l_genl_attr attr;
|
|
|
|
uint16_t type, len;
|
|
|
|
const void *data;
|
|
|
|
uint8_t cmd;
|
|
|
|
|
|
|
|
cmd = l_genl_msg_get_command(msg);
|
|
|
|
if (!cmd)
|
|
|
|
return;
|
|
|
|
|
|
|
|
l_debug("Unicast notification %u", cmd);
|
|
|
|
|
|
|
|
if (!l_genl_attr_init(&attr, msg))
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (l_genl_attr_next(&attr, &type, &len, &data)) {
|
|
|
|
switch (type) {
|
|
|
|
case NL80211_ATTR_IFINDEX:
|
|
|
|
if (len != sizeof(uint32_t)) {
|
|
|
|
l_warn("Invalid interface index attribute");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
netdev = netdev_find(*((uint32_t *) data));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-13 15:44:29 +02:00
|
|
|
if (!netdev)
|
2016-12-23 11:38:00 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
switch (cmd) {
|
2018-03-13 21:27:20 +01:00
|
|
|
case NL80211_CMD_CONTROL_PORT_FRAME:
|
|
|
|
netdev_control_port_frame_event(msg, netdev);
|
|
|
|
break;
|
2016-12-23 11:38:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-20 16:33:13 +02:00
|
|
|
static struct l_genl_msg *netdev_build_cmd_cqm_rssi_update(
|
|
|
|
struct netdev *netdev,
|
|
|
|
const int8_t *levels,
|
|
|
|
size_t levels_num)
|
|
|
|
{
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
uint32_t hyst = 5;
|
|
|
|
int thold_count;
|
|
|
|
int32_t thold_list[levels_num + 2];
|
|
|
|
|
|
|
|
if (levels_num == 0) {
|
|
|
|
thold_list[0] = LOW_SIGNAL_THRESHOLD;
|
|
|
|
thold_count = 1;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Build the list of all the threshold values we care about:
|
|
|
|
* - the low/high level threshold,
|
|
|
|
* - the value ranges requested by
|
|
|
|
* netdev_set_rssi_report_levels
|
|
|
|
*/
|
|
|
|
unsigned int i;
|
|
|
|
bool low_sig_added = false;
|
|
|
|
|
|
|
|
thold_count = 0;
|
|
|
|
for (i = 0; i < levels_num; i++) {
|
|
|
|
int32_t val = levels[levels_num - i - 1];
|
|
|
|
|
|
|
|
if (i && thold_list[thold_count - 1] >= val)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (val >= LOW_SIGNAL_THRESHOLD && !low_sig_added) {
|
|
|
|
thold_list[thold_count++] =
|
|
|
|
LOW_SIGNAL_THRESHOLD;
|
|
|
|
low_sig_added = true;
|
|
|
|
|
|
|
|
/* Duplicate values are not allowed */
|
|
|
|
if (val == LOW_SIGNAL_THRESHOLD)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
thold_list[thold_count++] = val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
msg = l_genl_msg_new_sized(NL80211_CMD_SET_CQM, 32 + thold_count * 4);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
|
|
|
l_genl_msg_enter_nested(msg, NL80211_ATTR_CQM);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_CQM_RSSI_THOLD,
|
|
|
|
thold_count * 4, thold_list);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_CQM_RSSI_HYST, 4, &hyst);
|
|
|
|
l_genl_msg_leave_nested(msg);
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_cmd_set_cqm_cb(struct l_genl_msg *msg, void *user_data)
|
|
|
|
{
|
2020-04-02 07:30:17 +02:00
|
|
|
int err = l_genl_msg_get_error(msg);
|
|
|
|
const char *ext_error;
|
2019-08-04 08:16:56 +02:00
|
|
|
|
2020-04-02 07:30:17 +02:00
|
|
|
if (err >= 0)
|
2019-08-04 08:16:56 +02:00
|
|
|
return;
|
|
|
|
|
2020-04-02 07:30:17 +02:00
|
|
|
ext_error = l_genl_msg_get_extended_error(msg);
|
|
|
|
l_error("CMD_SET_CQM failed: %s",
|
|
|
|
ext_error ? ext_error : strerror(-err));
|
2017-05-20 16:33:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int netdev_set_rssi_report_levels(struct netdev *netdev, const int8_t *levels,
|
|
|
|
size_t levels_num)
|
|
|
|
{
|
|
|
|
struct l_genl_msg *cmd_set_cqm;
|
|
|
|
|
2019-08-04 08:16:56 +02:00
|
|
|
l_debug("ifindex: %d, num_levels: %zu", netdev->index, levels_num);
|
|
|
|
|
2017-05-20 16:33:13 +02:00
|
|
|
if (levels_num > L_ARRAY_SIZE(netdev->rssi_levels))
|
|
|
|
return -ENOSPC;
|
|
|
|
|
2018-05-24 20:12:36 +02:00
|
|
|
if (!wiphy_has_ext_feature(netdev->wiphy,
|
2017-08-18 14:22:58 +02:00
|
|
|
NL80211_EXT_FEATURE_CQM_RSSI_LIST))
|
|
|
|
goto done;
|
|
|
|
|
2017-05-20 16:33:13 +02:00
|
|
|
cmd_set_cqm = netdev_build_cmd_cqm_rssi_update(netdev, levels,
|
|
|
|
levels_num);
|
|
|
|
if (!cmd_set_cqm)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!l_genl_family_send(nl80211, cmd_set_cqm, netdev_cmd_set_cqm_cb,
|
|
|
|
NULL, NULL)) {
|
|
|
|
l_genl_msg_unref(cmd_set_cqm);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2017-08-18 14:22:58 +02:00
|
|
|
done:
|
2019-01-15 07:34:06 +01:00
|
|
|
if (levels_num)
|
|
|
|
memcpy(netdev->rssi_levels, levels, levels_num);
|
|
|
|
|
2017-05-20 16:33:13 +02:00
|
|
|
netdev->rssi_levels_num = levels_num;
|
|
|
|
netdev_rssi_level_init(netdev);
|
|
|
|
|
2017-08-18 14:22:58 +02:00
|
|
|
netdev_rssi_polling_update(netdev);
|
|
|
|
|
2017-05-20 16:33:13 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int netdev_cqm_rssi_update(struct netdev *netdev)
|
|
|
|
{
|
2019-08-28 08:35:50 +02:00
|
|
|
struct l_genl_msg *msg;
|
2017-05-20 16:33:13 +02:00
|
|
|
|
2019-08-04 08:16:56 +02:00
|
|
|
l_debug("");
|
|
|
|
|
2019-08-28 08:35:50 +02:00
|
|
|
if (!wiphy_has_ext_feature(netdev->wiphy,
|
|
|
|
NL80211_EXT_FEATURE_CQM_RSSI_LIST))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
msg = netdev_build_cmd_cqm_rssi_update(netdev, netdev->rssi_levels,
|
|
|
|
netdev->rssi_levels_num);
|
2017-05-20 16:33:13 +02:00
|
|
|
if (!msg)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!l_genl_family_send(nl80211, msg, netdev_cmd_set_cqm_cb,
|
|
|
|
NULL, NULL)) {
|
|
|
|
l_genl_msg_unref(msg);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-08-20 05:07:15 +02:00
|
|
|
static struct l_genl_msg *netdev_build_cmd_set_interface(struct netdev *netdev,
|
|
|
|
uint32_t iftype)
|
2017-08-14 14:49:16 +02:00
|
|
|
{
|
2018-08-20 05:07:15 +02:00
|
|
|
struct l_genl_msg *msg =
|
|
|
|
l_genl_msg_new_sized(NL80211_CMD_SET_INTERFACE, 32);
|
|
|
|
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFTYPE, 4, &iftype);
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct netdev_set_iftype_request {
|
|
|
|
netdev_command_cb_t cb;
|
|
|
|
void *user_data;
|
|
|
|
netdev_destroy_func_t destroy;
|
|
|
|
uint32_t pending_type;
|
|
|
|
uint32_t ref;
|
|
|
|
struct netdev *netdev;
|
|
|
|
bool bring_up;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void netdev_set_iftype_request_destroy(void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev_set_iftype_request *req = user_data;
|
|
|
|
struct netdev *netdev = req->netdev;
|
|
|
|
|
|
|
|
req->ref--;
|
|
|
|
if (req->ref)
|
|
|
|
return;
|
|
|
|
|
|
|
|
netdev->set_powered_cmd_id = 0;
|
|
|
|
netdev->set_interface_cmd_id = 0;
|
|
|
|
|
|
|
|
if (req->destroy)
|
|
|
|
req->destroy(req->user_data);
|
|
|
|
|
|
|
|
l_free(req);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_set_iftype_up_cb(int error, uint16_t type,
|
|
|
|
const void *data,
|
|
|
|
uint32_t len, void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev_set_iftype_request *req = user_data;
|
|
|
|
struct netdev *netdev = req->netdev;
|
|
|
|
|
|
|
|
if (req->cb)
|
|
|
|
req->cb(netdev, error, req->user_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_set_iftype_cb(struct l_genl_msg *msg, void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev_set_iftype_request *req = user_data;
|
|
|
|
struct netdev *netdev = req->netdev;
|
|
|
|
int error = l_genl_msg_get_error(msg);
|
|
|
|
|
|
|
|
if (error != 0)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
netdev->type = req->pending_type;
|
|
|
|
|
2018-08-20 05:52:52 +02:00
|
|
|
/* Set RSSI threshold for CQM notifications */
|
|
|
|
if (netdev->type == NL80211_IFTYPE_STATION)
|
|
|
|
netdev_cqm_rssi_update(netdev);
|
|
|
|
|
2018-08-20 05:07:15 +02:00
|
|
|
/* If the netdev was down originally, we're done */
|
|
|
|
if (!req->bring_up)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
netdev->set_powered_cmd_id =
|
2020-02-15 10:48:39 +01:00
|
|
|
l_rtnl_set_powered(rtnl, netdev->index, true,
|
2018-08-20 05:07:15 +02:00
|
|
|
netdev_set_iftype_up_cb, req,
|
|
|
|
netdev_set_iftype_request_destroy);
|
|
|
|
if (!netdev->set_powered_cmd_id) {
|
|
|
|
error = -EIO;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
req->ref++;
|
|
|
|
netdev->set_interface_cmd_id = 0;
|
|
|
|
return;
|
|
|
|
|
|
|
|
done:
|
|
|
|
if (req->cb)
|
|
|
|
req->cb(netdev, error, req->user_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_set_iftype_down_cb(int error, uint16_t type,
|
|
|
|
const void *data,
|
|
|
|
uint32_t len, void *user_data)
|
|
|
|
{
|
|
|
|
struct netdev_set_iftype_request *req = user_data;
|
|
|
|
struct netdev *netdev = req->netdev;
|
2017-08-14 14:49:16 +02:00
|
|
|
struct l_genl_msg *msg;
|
2018-08-20 05:07:15 +02:00
|
|
|
|
|
|
|
if (error != 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
msg = netdev_build_cmd_set_interface(netdev, req->pending_type);
|
|
|
|
netdev->set_interface_cmd_id =
|
|
|
|
l_genl_family_send(nl80211, msg, netdev_set_iftype_cb, req,
|
|
|
|
netdev_set_iftype_request_destroy);
|
|
|
|
if (!netdev->set_interface_cmd_id) {
|
|
|
|
l_genl_msg_unref(msg);
|
|
|
|
error = -EIO;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
req->ref++;
|
|
|
|
netdev->set_powered_cmd_id = 0;
|
|
|
|
return;
|
|
|
|
|
|
|
|
error:
|
|
|
|
if (req->cb)
|
|
|
|
req->cb(netdev, error, req->user_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
int netdev_set_iftype(struct netdev *netdev, enum netdev_iftype type,
|
|
|
|
netdev_command_cb_t cb, void *user_data,
|
|
|
|
netdev_destroy_func_t destroy)
|
|
|
|
{
|
2018-07-17 00:29:14 +02:00
|
|
|
uint32_t iftype;
|
2018-08-20 05:07:15 +02:00
|
|
|
struct netdev_set_iftype_request *req;
|
2018-07-17 00:29:14 +02:00
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case NETDEV_IFTYPE_AP:
|
|
|
|
iftype = NL80211_IFTYPE_AP;
|
|
|
|
break;
|
|
|
|
case NETDEV_IFTYPE_ADHOC:
|
|
|
|
iftype = NL80211_IFTYPE_ADHOC;
|
|
|
|
break;
|
|
|
|
case NETDEV_IFTYPE_STATION:
|
|
|
|
iftype = NL80211_IFTYPE_STATION;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
l_error("unsupported iftype %u", type);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2017-08-14 14:49:16 +02:00
|
|
|
|
2018-08-20 05:07:15 +02:00
|
|
|
if (netdev->set_powered_cmd_id ||
|
|
|
|
netdev->set_interface_cmd_id)
|
|
|
|
return -EBUSY;
|
2017-08-14 14:49:16 +02:00
|
|
|
|
2018-08-20 05:07:15 +02:00
|
|
|
req = l_new(struct netdev_set_iftype_request, 1);
|
|
|
|
req->cb = cb;
|
|
|
|
req->user_data = user_data;
|
|
|
|
req->destroy = destroy;
|
|
|
|
req->pending_type = iftype;
|
|
|
|
req->netdev = netdev;
|
|
|
|
req->ref = 1;
|
|
|
|
req->bring_up = netdev_get_is_up(netdev);
|
2017-08-14 14:49:16 +02:00
|
|
|
|
2018-08-20 05:07:15 +02:00
|
|
|
if (!req->bring_up) {
|
|
|
|
struct l_genl_msg *msg =
|
|
|
|
netdev_build_cmd_set_interface(netdev, iftype);
|
2017-08-14 14:49:16 +02:00
|
|
|
|
2018-08-20 05:07:15 +02:00
|
|
|
netdev->set_interface_cmd_id =
|
|
|
|
l_genl_family_send(nl80211, msg,
|
|
|
|
netdev_set_iftype_cb, req,
|
|
|
|
netdev_set_iftype_request_destroy);
|
|
|
|
if (netdev->set_interface_cmd_id)
|
|
|
|
return 0;
|
2017-08-14 14:49:16 +02:00
|
|
|
|
2018-08-20 05:07:15 +02:00
|
|
|
l_genl_msg_unref(msg);
|
|
|
|
} else {
|
|
|
|
netdev->set_powered_cmd_id =
|
2020-02-15 10:48:39 +01:00
|
|
|
l_rtnl_set_powered(rtnl, netdev->index, false,
|
2018-08-20 05:07:15 +02:00
|
|
|
netdev_set_iftype_down_cb, req,
|
|
|
|
netdev_set_iftype_request_destroy);
|
|
|
|
if (netdev->set_powered_cmd_id)
|
|
|
|
return 0;
|
|
|
|
}
|
2018-06-19 22:28:01 +02:00
|
|
|
|
2018-08-20 05:07:15 +02:00
|
|
|
l_free(req);
|
|
|
|
return -EIO;
|
2017-08-14 14:49:16 +02:00
|
|
|
}
|
|
|
|
|
2018-06-12 06:20:18 +02:00
|
|
|
static void netdev_bridge_port_event(const struct ifinfomsg *ifi, int bytes,
|
|
|
|
bool added)
|
|
|
|
{
|
|
|
|
struct netdev *netdev;
|
|
|
|
struct rtattr *attr;
|
|
|
|
uint32_t master = 0;
|
|
|
|
|
|
|
|
netdev = netdev_find(ifi->ifi_index);
|
|
|
|
if (!netdev)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (attr = IFLA_RTA(ifi); RTA_OK(attr, bytes);
|
|
|
|
attr = RTA_NEXT(attr, bytes)) {
|
|
|
|
switch (attr->rta_type) {
|
|
|
|
case IFLA_MASTER:
|
|
|
|
memcpy(&master, RTA_DATA(attr), sizeof(master));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
l_debug("netdev: %d %s bridge: %d", ifi->ifi_index,
|
|
|
|
(added ? "added to" : "removed from"), master);
|
|
|
|
}
|
|
|
|
|
2018-06-14 04:11:23 +02:00
|
|
|
struct set_4addr_cb_data {
|
|
|
|
struct netdev *netdev;
|
|
|
|
bool value;
|
2018-08-20 01:25:23 +02:00
|
|
|
netdev_command_cb_t callback;
|
2018-06-14 04:11:23 +02:00
|
|
|
void *user_data;
|
|
|
|
netdev_destroy_func_t destroy;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void netdev_set_4addr_cb(struct l_genl_msg *msg, void *user_data)
|
|
|
|
{
|
|
|
|
struct set_4addr_cb_data *cb_data = user_data;
|
|
|
|
int error = l_genl_msg_get_error(msg);
|
|
|
|
|
|
|
|
if (!cb_data)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* cache the value that has just been set */
|
|
|
|
if (!error)
|
|
|
|
cb_data->netdev->use_4addr = cb_data->value;
|
|
|
|
|
2018-06-14 04:25:01 +02:00
|
|
|
cb_data->callback(cb_data->netdev, error, cb_data->user_data);
|
2018-06-14 04:11:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void netdev_set_4addr_destroy(void *user_data)
|
|
|
|
{
|
|
|
|
struct set_4addr_cb_data *cb_data = user_data;
|
|
|
|
|
|
|
|
if (!cb_data)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (cb_data->destroy)
|
|
|
|
cb_data->destroy(cb_data->user_data);
|
|
|
|
|
|
|
|
l_free(cb_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
int netdev_set_4addr(struct netdev *netdev, bool use_4addr,
|
2018-08-20 01:25:23 +02:00
|
|
|
netdev_command_cb_t cb, void *user_data,
|
2018-06-14 04:11:23 +02:00
|
|
|
netdev_destroy_func_t destroy)
|
|
|
|
{
|
|
|
|
struct set_4addr_cb_data *cb_data = NULL;
|
|
|
|
uint8_t attr_4addr = (use_4addr ? 1 : 0);
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
|
|
|
|
l_debug("netdev: %d use_4addr: %d", netdev->index, use_4addr);
|
|
|
|
|
|
|
|
msg = l_genl_msg_new_sized(NL80211_CMD_SET_INTERFACE, 32);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_4ADDR, 1, &attr_4addr);
|
|
|
|
|
|
|
|
if (cb) {
|
|
|
|
cb_data = l_new(struct set_4addr_cb_data, 1);
|
|
|
|
cb_data->netdev = netdev;
|
|
|
|
cb_data->value = use_4addr;
|
|
|
|
cb_data->callback = cb;
|
|
|
|
cb_data->user_data = user_data;
|
|
|
|
cb_data->destroy = destroy;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!l_genl_family_send(nl80211, msg, netdev_set_4addr_cb, cb_data,
|
|
|
|
netdev_set_4addr_destroy)) {
|
|
|
|
l_error("CMD_SET_INTERFACE (4addr) failed");
|
|
|
|
|
|
|
|
l_genl_msg_unref(msg);
|
|
|
|
l_free(cb_data);
|
|
|
|
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool netdev_get_4addr(struct netdev *netdev)
|
|
|
|
{
|
|
|
|
return netdev->use_4addr;
|
|
|
|
}
|
|
|
|
|
2016-06-20 12:42:03 +02:00
|
|
|
static void netdev_newlink_notify(const struct ifinfomsg *ifi, int bytes)
|
|
|
|
{
|
|
|
|
struct netdev *netdev;
|
2016-07-13 04:26:27 +02:00
|
|
|
bool old_up, new_up;
|
2016-07-14 02:38:05 +02:00
|
|
|
char old_name[IFNAMSIZ];
|
2017-03-08 15:07:14 +01:00
|
|
|
uint8_t old_addr[ETH_ALEN];
|
2016-07-14 02:38:05 +02:00
|
|
|
struct rtattr *attr;
|
2016-06-20 12:42:03 +02:00
|
|
|
|
2018-06-12 06:20:18 +02:00
|
|
|
if (ifi->ifi_family == AF_BRIDGE) {
|
|
|
|
netdev_bridge_port_event(ifi, bytes, true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-06-20 12:42:03 +02:00
|
|
|
netdev = netdev_find(ifi->ifi_index);
|
|
|
|
if (!netdev)
|
|
|
|
return;
|
|
|
|
|
2016-07-13 04:26:27 +02:00
|
|
|
old_up = netdev_get_is_up(netdev);
|
2016-07-14 02:38:05 +02:00
|
|
|
strcpy(old_name, netdev->name);
|
2017-03-08 15:07:14 +01:00
|
|
|
memcpy(old_addr, netdev->addr, ETH_ALEN);
|
2016-07-13 04:26:27 +02:00
|
|
|
|
|
|
|
netdev->ifi_flags = ifi->ifi_flags;
|
2016-06-20 12:42:03 +02:00
|
|
|
|
2016-07-14 02:38:05 +02:00
|
|
|
for (attr = IFLA_RTA(ifi); RTA_OK(attr, bytes);
|
|
|
|
attr = RTA_NEXT(attr, bytes)) {
|
2017-03-08 15:07:14 +01:00
|
|
|
switch (attr->rta_type) {
|
|
|
|
case IFLA_IFNAME:
|
|
|
|
strcpy(netdev->name, RTA_DATA(attr));
|
|
|
|
break;
|
|
|
|
case IFLA_ADDRESS:
|
|
|
|
if (RTA_PAYLOAD(attr) < ETH_ALEN)
|
|
|
|
break;
|
2016-07-14 02:38:05 +02:00
|
|
|
|
2017-03-08 15:07:14 +01:00
|
|
|
memcpy(netdev->addr, RTA_DATA(attr), ETH_ALEN);
|
|
|
|
break;
|
|
|
|
}
|
2016-07-14 02:38:05 +02:00
|
|
|
}
|
|
|
|
|
2019-11-20 23:23:42 +01:00
|
|
|
if (!netdev->events_ready) /* Did we send NETDEV_WATCH_EVENT_NEW yet? */
|
2018-09-22 18:48:21 +02:00
|
|
|
return;
|
|
|
|
|
2016-07-13 04:26:27 +02:00
|
|
|
new_up = netdev_get_is_up(netdev);
|
|
|
|
|
2020-03-18 19:54:19 +01:00
|
|
|
/*
|
|
|
|
* If mac_change_cmd_id is set we are in the process of changing the
|
|
|
|
* MAC address and this event is a result of powering down/up. In this
|
|
|
|
* case we do not want to emit a netdev DOWN/UP event as this would
|
|
|
|
* cause other modules to behave as such. We do, however, want to emit
|
|
|
|
* address changes so other modules get the new MAC address updated.
|
|
|
|
*/
|
|
|
|
if (old_up != new_up && !netdev->mac_change_cmd_id)
|
2018-08-18 06:40:49 +02:00
|
|
|
WATCHLIST_NOTIFY(&netdev_watches, netdev_watch_func_t,
|
2017-09-01 01:18:41 +02:00
|
|
|
netdev, new_up ? NETDEV_WATCH_EVENT_UP :
|
|
|
|
NETDEV_WATCH_EVENT_DOWN);
|
2016-07-14 02:38:05 +02:00
|
|
|
|
2017-09-01 01:18:41 +02:00
|
|
|
if (strcmp(old_name, netdev->name))
|
2018-08-18 06:40:49 +02:00
|
|
|
WATCHLIST_NOTIFY(&netdev_watches, netdev_watch_func_t,
|
2017-09-01 01:18:41 +02:00
|
|
|
netdev, NETDEV_WATCH_EVENT_NAME_CHANGE);
|
2016-07-14 02:38:05 +02:00
|
|
|
|
2017-09-01 01:18:41 +02:00
|
|
|
if (memcmp(old_addr, netdev->addr, ETH_ALEN))
|
2018-08-18 06:40:49 +02:00
|
|
|
WATCHLIST_NOTIFY(&netdev_watches, netdev_watch_func_t,
|
2017-09-01 01:18:41 +02:00
|
|
|
netdev, NETDEV_WATCH_EVENT_ADDRESS_CHANGE);
|
2016-06-20 12:42:03 +02:00
|
|
|
}
|
|
|
|
|
2016-07-01 21:49:34 +02:00
|
|
|
static void netdev_dellink_notify(const struct ifinfomsg *ifi, int bytes)
|
|
|
|
{
|
|
|
|
struct netdev *netdev;
|
|
|
|
|
2018-06-12 06:20:18 +02:00
|
|
|
if (ifi->ifi_family == AF_BRIDGE) {
|
|
|
|
netdev_bridge_port_event(ifi, bytes, false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-07-01 21:49:34 +02:00
|
|
|
netdev = l_queue_remove_if(netdev_list, netdev_match,
|
|
|
|
L_UINT_TO_PTR(ifi->ifi_index));
|
|
|
|
if (!netdev)
|
|
|
|
return;
|
|
|
|
|
|
|
|
netdev_free(netdev);
|
|
|
|
}
|
|
|
|
|
2018-08-17 19:37:47 +02:00
|
|
|
static void netdev_initial_up_cb(int error, uint16_t type, const void *data,
|
|
|
|
uint32_t len, void *user_data)
|
2016-07-13 04:29:52 +02:00
|
|
|
{
|
2018-08-17 19:37:47 +02:00
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
|
|
|
|
netdev->set_powered_cmd_id = 0;
|
|
|
|
|
2018-09-22 18:48:20 +02:00
|
|
|
if (!error)
|
|
|
|
netdev->ifi_flags |= IFF_UP;
|
|
|
|
else {
|
2016-07-13 04:29:52 +02:00
|
|
|
l_error("Error bringing interface %i up: %s", netdev->index,
|
2018-08-17 19:37:47 +02:00
|
|
|
strerror(-error));
|
2016-07-13 04:29:52 +02:00
|
|
|
|
2018-08-17 19:37:47 +02:00
|
|
|
if (error != -ERFKILL)
|
2016-07-26 14:42:20 +02:00
|
|
|
return;
|
2016-07-13 04:29:52 +02:00
|
|
|
}
|
|
|
|
|
2020-02-15 10:48:39 +01:00
|
|
|
l_rtnl_set_linkmode_and_operstate(rtnl, netdev->index,
|
2019-05-06 20:17:53 +02:00
|
|
|
IF_LINK_MODE_DORMANT, IF_OPER_DOWN,
|
|
|
|
netdev_operstate_cb,
|
2018-08-17 21:04:44 +02:00
|
|
|
L_UINT_TO_PTR(netdev->index), NULL);
|
2016-07-13 04:29:52 +02:00
|
|
|
|
2018-06-14 04:11:23 +02:00
|
|
|
/*
|
|
|
|
* we don't know the initial status of the 4addr property on this
|
|
|
|
* netdev, therefore we set it to zero by default.
|
|
|
|
*/
|
|
|
|
netdev_set_4addr(netdev, netdev->use_4addr, NULL, NULL, NULL);
|
|
|
|
|
2016-07-13 04:29:52 +02:00
|
|
|
l_debug("Interface %i initialized", netdev->index);
|
|
|
|
|
2019-12-06 17:11:51 +01:00
|
|
|
scan_wdev_add(netdev->wdev_id);
|
|
|
|
|
2018-08-18 06:40:49 +02:00
|
|
|
WATCHLIST_NOTIFY(&netdev_watches, netdev_watch_func_t,
|
|
|
|
netdev, NETDEV_WATCH_EVENT_NEW);
|
2019-11-20 23:23:42 +01:00
|
|
|
netdev->events_ready = true;
|
2016-07-13 04:29:52 +02:00
|
|
|
}
|
|
|
|
|
2019-12-19 04:50:56 +01:00
|
|
|
static bool netdev_check_set_mac(struct netdev *netdev)
|
|
|
|
{
|
|
|
|
if (util_mem_is_zero(netdev->set_mac_once, 6))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
l_debug("Setting initial address on ifindex: %d to: " MAC,
|
|
|
|
netdev->index, MAC_STR(netdev->set_mac_once));
|
|
|
|
netdev->set_powered_cmd_id =
|
2020-03-11 23:52:51 +01:00
|
|
|
l_rtnl_set_mac(rtnl, netdev->index, netdev->set_mac_once, true,
|
|
|
|
netdev_initial_up_cb, netdev, NULL);
|
2019-12-19 04:50:56 +01:00
|
|
|
memset(netdev->set_mac_once, 0, 6);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-08-17 19:37:47 +02:00
|
|
|
static void netdev_initial_down_cb(int error, uint16_t type, const void *data,
|
|
|
|
uint32_t len, void *user_data)
|
2016-07-13 04:29:52 +02:00
|
|
|
{
|
2018-08-17 19:37:47 +02:00
|
|
|
struct netdev *netdev = user_data;
|
|
|
|
|
2018-09-22 18:48:20 +02:00
|
|
|
if (!error)
|
|
|
|
netdev->ifi_flags &= ~IFF_UP;
|
|
|
|
else {
|
2016-07-13 04:29:52 +02:00
|
|
|
l_error("Error taking interface %i down: %s", netdev->index,
|
2018-08-17 19:37:47 +02:00
|
|
|
strerror(-error));
|
2016-07-13 04:29:52 +02:00
|
|
|
|
2018-08-17 19:37:47 +02:00
|
|
|
netdev->set_powered_cmd_id = 0;
|
2016-07-13 04:29:52 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-12-19 04:50:56 +01:00
|
|
|
if (netdev_check_set_mac(netdev))
|
2019-07-02 01:29:37 +02:00
|
|
|
return;
|
|
|
|
|
2018-08-17 19:37:47 +02:00
|
|
|
netdev->set_powered_cmd_id =
|
2020-02-15 10:48:39 +01:00
|
|
|
l_rtnl_set_powered(rtnl, netdev->index, true,
|
2019-11-20 23:23:44 +01:00
|
|
|
netdev_initial_up_cb, netdev, NULL);
|
2016-07-13 04:29:52 +02:00
|
|
|
}
|
|
|
|
|
2016-06-20 12:42:03 +02:00
|
|
|
static void netdev_getlink_cb(int error, uint16_t type, const void *data,
|
|
|
|
uint32_t len, void *user_data)
|
|
|
|
{
|
|
|
|
const struct ifinfomsg *ifi = data;
|
|
|
|
unsigned int bytes;
|
2016-07-13 04:29:52 +02:00
|
|
|
struct netdev *netdev;
|
2018-08-17 19:37:47 +02:00
|
|
|
l_netlink_command_func_t cb;
|
|
|
|
bool powered;
|
2016-06-20 12:42:03 +02:00
|
|
|
|
2019-04-16 21:23:43 +02:00
|
|
|
if (error != 0) {
|
|
|
|
l_error("RTM_GETLINK error %i: %s", error, strerror(-error));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ifi->ifi_type != ARPHRD_ETHER || type != RTM_NEWLINK) {
|
|
|
|
l_debug("Non-ethernet address or not newlink message -- "
|
|
|
|
"ifi_type: %i, type: %i", ifi->ifi_type, type);
|
2016-06-20 12:42:03 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-07-13 04:29:52 +02:00
|
|
|
netdev = netdev_find(ifi->ifi_index);
|
|
|
|
if (!netdev)
|
|
|
|
return;
|
|
|
|
|
2016-06-20 12:42:03 +02:00
|
|
|
bytes = len - NLMSG_ALIGN(sizeof(struct ifinfomsg));
|
|
|
|
|
|
|
|
netdev_newlink_notify(ifi, bytes);
|
2016-07-13 04:29:52 +02:00
|
|
|
|
|
|
|
/*
|
2019-07-02 01:29:37 +02:00
|
|
|
* If the interface is UP, reset it to ensure a clean state.
|
|
|
|
* Otherwise, if we need to set a random mac, do so. If not, just
|
|
|
|
* bring the interface UP.
|
2016-07-13 04:29:52 +02:00
|
|
|
*/
|
2018-08-17 19:37:47 +02:00
|
|
|
powered = netdev_get_is_up(netdev);
|
2019-07-02 01:29:37 +02:00
|
|
|
|
2019-12-19 04:50:56 +01:00
|
|
|
if (!powered && netdev_check_set_mac(netdev))
|
2019-07-02 01:29:37 +02:00
|
|
|
return;
|
|
|
|
|
2018-08-17 19:37:47 +02:00
|
|
|
cb = powered ? netdev_initial_down_cb : netdev_initial_up_cb;
|
|
|
|
|
|
|
|
netdev->set_powered_cmd_id =
|
2020-02-15 10:48:39 +01:00
|
|
|
l_rtnl_set_powered(rtnl, ifi->ifi_index, !powered, cb, netdev,
|
2019-11-20 23:23:44 +01:00
|
|
|
NULL);
|
2016-06-20 12:42:03 +02:00
|
|
|
}
|
|
|
|
|
2018-05-01 18:08:19 +02:00
|
|
|
static struct l_io *pae_open(uint32_t ifindex)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* BPF filter to match skb->dev->type == 1 (ARPHRD_ETHER) and
|
|
|
|
* match skb->protocol == 0x888e (PAE) or 0x88c7 (preauthentication).
|
|
|
|
*/
|
|
|
|
struct sock_filter pae_filter[] = {
|
|
|
|
{ 0x20, 0, 0, 0xfffff008 }, /* ld #ifidx */
|
|
|
|
{ 0x15, 0, 6, 0x00000000 }, /* jne #0, drop */
|
|
|
|
{ 0x28, 0, 0, 0xfffff01c }, /* ldh #hatype */
|
|
|
|
{ 0x15, 0, 4, 0x00000001 }, /* jne #1, drop */
|
|
|
|
{ 0x28, 0, 0, 0xfffff000 }, /* ldh #proto */
|
|
|
|
{ 0x15, 1, 0, 0x0000888e }, /* je #0x888e, keep */
|
|
|
|
{ 0x15, 0, 1, 0x000088c7 }, /* jne #0x88c7, drop */
|
|
|
|
{ 0x06, 0, 0, 0xffffffff }, /* keep: ret #-1 */
|
|
|
|
{ 0x06, 0, 0, 0000000000 }, /* drop: ret #0 */
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct sock_fprog pae_fprog = {
|
|
|
|
.len = L_ARRAY_SIZE(pae_filter),
|
|
|
|
.filter = pae_filter
|
|
|
|
};
|
|
|
|
|
|
|
|
struct l_io *io;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
|
|
|
|
htons(ETH_P_ALL));
|
|
|
|
if (fd < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Here we modify the k value in the BPF program above to match the
|
|
|
|
* given ifindex. We do it this way instead of using bind to attach
|
|
|
|
* to a specific interface index to avoid having to re-open the fd
|
|
|
|
* whenever the device is powered down / up
|
|
|
|
*/
|
|
|
|
|
|
|
|
pae_filter[1].k = ifindex;
|
|
|
|
|
|
|
|
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER,
|
|
|
|
&pae_fprog, sizeof(pae_fprog)) < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
io = l_io_new(fd);
|
|
|
|
l_io_set_close_on_destroy(io, true);
|
|
|
|
|
|
|
|
return io;
|
|
|
|
|
|
|
|
error:
|
|
|
|
close(fd);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-12-19 04:50:56 +01:00
|
|
|
struct netdev *netdev_create_from_genl(struct l_genl_msg *msg,
|
|
|
|
const uint8_t *set_mac)
|
2016-06-02 00:23:33 +02:00
|
|
|
{
|
2019-12-17 23:57:21 +01:00
|
|
|
const char *ifname;
|
|
|
|
const uint8_t *ifaddr;
|
|
|
|
uint32_t ifindex;
|
|
|
|
uint32_t iftype;
|
|
|
|
uint64_t wdev;
|
|
|
|
uint32_t wiphy_id;
|
2016-06-02 00:23:33 +02:00
|
|
|
struct netdev *netdev;
|
|
|
|
struct wiphy *wiphy = NULL;
|
2016-06-20 12:42:03 +02:00
|
|
|
struct ifinfomsg *rtmmsg;
|
|
|
|
size_t bufsize;
|
2016-12-23 11:38:02 +01:00
|
|
|
const uint8_t action_neighbor_report_prefix[2] = { 0x05, 0x05 };
|
2018-02-01 18:22:39 +01:00
|
|
|
const uint8_t action_sa_query_resp_prefix[2] = { 0x08, 0x01 };
|
2018-02-07 22:01:43 +01:00
|
|
|
const uint8_t action_sa_query_req_prefix[2] = { 0x08, 0x00 };
|
2019-05-09 20:16:28 +02:00
|
|
|
const uint8_t action_ft_response_prefix[] = { 0x06, 0x02 };
|
2019-09-14 00:26:31 +02:00
|
|
|
const uint8_t action_qos_map_prefix[] = { 0x01, 0x04 };
|
2018-05-01 18:08:19 +02:00
|
|
|
struct l_io *pae_io = NULL;
|
2016-06-02 00:23:33 +02:00
|
|
|
|
2019-12-17 23:57:21 +01:00
|
|
|
if (nl80211_parse_attrs(msg, NL80211_ATTR_IFINDEX, &ifindex,
|
|
|
|
NL80211_ATTR_WDEV, &wdev,
|
|
|
|
NL80211_ATTR_IFNAME, &ifname,
|
|
|
|
NL80211_ATTR_WIPHY, &wiphy_id,
|
|
|
|
NL80211_ATTR_IFTYPE, &iftype,
|
|
|
|
NL80211_ATTR_MAC, &ifaddr,
|
|
|
|
NL80211_ATTR_UNSPEC) < 0) {
|
|
|
|
l_warn("Required attributes missing");
|
2019-04-11 03:10:20 +02:00
|
|
|
return NULL;
|
2016-07-19 23:03:26 +02:00
|
|
|
}
|
|
|
|
|
2019-12-17 23:57:21 +01:00
|
|
|
wiphy = wiphy_find(wiphy_id);
|
|
|
|
if (!wiphy) {
|
|
|
|
l_warn("No wiphy: %d", wiphy_id);
|
2019-04-11 03:10:20 +02:00
|
|
|
return NULL;
|
2016-06-02 00:23:33 +02:00
|
|
|
}
|
|
|
|
|
2019-12-17 23:57:21 +01:00
|
|
|
if (netdev_find(ifindex)) {
|
|
|
|
l_debug("Skipping duplicate netdev %s[%d]", ifname, ifindex);
|
2019-04-11 03:10:20 +02:00
|
|
|
return NULL;
|
2016-06-02 00:23:33 +02:00
|
|
|
}
|
|
|
|
|
2018-05-24 20:12:36 +02:00
|
|
|
if (!wiphy_has_ext_feature(wiphy,
|
2019-10-25 04:36:03 +02:00
|
|
|
NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211) ||
|
|
|
|
!pae_over_nl80211) {
|
2019-12-17 23:57:21 +01:00
|
|
|
pae_io = pae_open(ifindex);
|
2018-05-01 18:08:19 +02:00
|
|
|
if (!pae_io) {
|
|
|
|
l_error("Unable to open PAE interface");
|
2019-04-11 03:10:20 +02:00
|
|
|
return NULL;
|
2018-05-01 18:08:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-02 00:23:33 +02:00
|
|
|
netdev = l_new(struct netdev, 1);
|
2019-12-17 23:57:21 +01:00
|
|
|
netdev->index = ifindex;
|
|
|
|
netdev->wdev_id = wdev;
|
|
|
|
netdev->type = iftype;
|
2016-06-23 00:49:16 +02:00
|
|
|
netdev->rekey_offload_support = true;
|
2016-06-02 00:23:33 +02:00
|
|
|
memcpy(netdev->addr, ifaddr, sizeof(netdev->addr));
|
2019-12-17 23:57:21 +01:00
|
|
|
l_strlcpy(netdev->name, ifname, IFNAMSIZ);
|
2016-07-13 04:29:52 +02:00
|
|
|
netdev->wiphy = wiphy;
|
2019-10-25 04:36:03 +02:00
|
|
|
netdev->pae_over_nl80211 = pae_io == NULL;
|
2019-12-19 04:50:56 +01:00
|
|
|
|
|
|
|
if (set_mac)
|
|
|
|
memcpy(netdev->set_mac_once, set_mac, 6);
|
2018-05-01 18:08:19 +02:00
|
|
|
|
|
|
|
if (pae_io) {
|
|
|
|
netdev->pae_io = pae_io;
|
|
|
|
l_io_set_read_handler(netdev->pae_io, netdev_pae_read, netdev,
|
|
|
|
netdev_pae_destroy);
|
|
|
|
}
|
|
|
|
|
2018-07-17 00:29:15 +02:00
|
|
|
watchlist_init(&netdev->station_watches, NULL);
|
2016-06-02 00:23:33 +02:00
|
|
|
|
|
|
|
l_queue_push_tail(netdev_list, netdev);
|
|
|
|
|
2019-07-08 16:02:58 +02:00
|
|
|
l_debug("Created interface %s[%d %" PRIx64 "]", netdev->name,
|
|
|
|
netdev->index, netdev->wdev_id);
|
2016-06-20 12:42:03 +02:00
|
|
|
|
|
|
|
/* Query interface flags */
|
2016-11-07 16:42:02 +01:00
|
|
|
bufsize = NLMSG_ALIGN(sizeof(struct ifinfomsg));
|
2016-06-20 12:42:03 +02:00
|
|
|
rtmmsg = l_malloc(bufsize);
|
|
|
|
memset(rtmmsg, 0, bufsize);
|
|
|
|
|
|
|
|
rtmmsg->ifi_family = AF_UNSPEC;
|
2019-12-17 23:57:21 +01:00
|
|
|
rtmmsg->ifi_index = ifindex;
|
2016-06-20 12:42:03 +02:00
|
|
|
|
2016-10-18 18:40:26 +02:00
|
|
|
l_netlink_send(rtnl, RTM_GETLINK, 0, rtmmsg, bufsize,
|
2018-08-17 20:29:05 +02:00
|
|
|
netdev_getlink_cb, NULL, NULL);
|
2016-06-20 12:42:03 +02:00
|
|
|
|
|
|
|
l_free(rtmmsg);
|
2016-12-23 11:38:02 +01:00
|
|
|
|
|
|
|
/* Subscribe to Management -> Action -> RM -> Neighbor Report frames */
|
2020-01-16 03:40:59 +01:00
|
|
|
frame_watch_add(wdev, 0, 0x00d0, action_neighbor_report_prefix,
|
|
|
|
sizeof(action_neighbor_report_prefix),
|
|
|
|
netdev_neighbor_report_frame_event, netdev, NULL);
|
2016-12-23 11:38:03 +01:00
|
|
|
|
2020-01-16 03:40:59 +01:00
|
|
|
frame_watch_add(wdev, 0, 0x00d0, action_sa_query_resp_prefix,
|
|
|
|
sizeof(action_sa_query_resp_prefix),
|
|
|
|
netdev_sa_query_resp_frame_event, netdev, NULL);
|
2018-02-01 18:22:39 +01:00
|
|
|
|
2020-01-16 03:40:59 +01:00
|
|
|
frame_watch_add(wdev, 0, 0x00d0, action_sa_query_req_prefix,
|
|
|
|
sizeof(action_sa_query_req_prefix),
|
|
|
|
netdev_sa_query_req_frame_event, netdev, NULL);
|
2018-02-07 22:01:43 +01:00
|
|
|
|
2020-01-16 03:40:59 +01:00
|
|
|
frame_watch_add(wdev, 0, 0x00d0, action_ft_response_prefix,
|
|
|
|
sizeof(action_ft_response_prefix),
|
|
|
|
netdev_ft_response_frame_event, netdev, NULL);
|
2019-05-09 20:16:28 +02:00
|
|
|
|
2019-09-14 00:26:31 +02:00
|
|
|
if (wiphy_supports_qos_set_map(netdev->wiphy))
|
2020-01-16 03:40:59 +01:00
|
|
|
frame_watch_add(wdev, 0, 0x00d0, action_qos_map_prefix,
|
|
|
|
sizeof(action_qos_map_prefix),
|
|
|
|
netdev_qos_map_frame_event, netdev, NULL);
|
2019-09-14 00:26:31 +02:00
|
|
|
|
2016-12-23 11:38:03 +01:00
|
|
|
/* Set RSSI threshold for CQM notifications */
|
2018-08-20 05:52:52 +02:00
|
|
|
if (netdev->type == NL80211_IFTYPE_STATION)
|
|
|
|
netdev_cqm_rssi_update(netdev);
|
2019-04-11 03:10:20 +02:00
|
|
|
|
|
|
|
return netdev;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool netdev_destroy(struct netdev *netdev)
|
|
|
|
{
|
|
|
|
if (!l_queue_remove(netdev_list, netdev))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
netdev_free(netdev);
|
|
|
|
return true;
|
2016-06-02 00:23:33 +02:00
|
|
|
}
|
|
|
|
|
2016-06-20 12:42:03 +02:00
|
|
|
static void netdev_link_notify(uint16_t type, const void *data, uint32_t len,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
const struct ifinfomsg *ifi = data;
|
|
|
|
unsigned int bytes;
|
|
|
|
|
|
|
|
if (ifi->ifi_type != ARPHRD_ETHER)
|
|
|
|
return;
|
|
|
|
|
2019-11-20 23:23:44 +01:00
|
|
|
l_debug("event %u on ifindex %u", type, ifi->ifi_index);
|
|
|
|
|
2016-06-20 12:42:03 +02:00
|
|
|
bytes = len - NLMSG_ALIGN(sizeof(struct ifinfomsg));
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case RTM_NEWLINK:
|
|
|
|
netdev_newlink_notify(ifi, bytes);
|
|
|
|
break;
|
2016-07-01 21:49:34 +02:00
|
|
|
case RTM_DELLINK:
|
|
|
|
netdev_dellink_notify(ifi, bytes);
|
|
|
|
break;
|
2016-06-20 12:42:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-18 06:40:49 +02:00
|
|
|
uint32_t netdev_station_watch_add(struct netdev *netdev,
|
|
|
|
netdev_station_watch_func_t func, void *user_data)
|
2016-06-20 12:42:04 +02:00
|
|
|
{
|
2018-08-18 06:40:49 +02:00
|
|
|
return watchlist_add(&netdev->station_watches, func, user_data, NULL);
|
2016-06-20 12:42:04 +02:00
|
|
|
}
|
|
|
|
|
2018-08-18 06:40:49 +02:00
|
|
|
bool netdev_station_watch_remove(struct netdev *netdev, uint32_t id)
|
2016-06-20 12:42:04 +02:00
|
|
|
{
|
2018-08-18 06:40:49 +02:00
|
|
|
return watchlist_remove(&netdev->station_watches, id);
|
2016-06-20 12:42:04 +02:00
|
|
|
}
|
|
|
|
|
2018-08-18 06:40:49 +02:00
|
|
|
uint32_t netdev_watch_add(netdev_watch_func_t func,
|
|
|
|
void *user_data, netdev_destroy_func_t destroy)
|
2018-07-17 00:29:15 +02:00
|
|
|
{
|
2018-08-18 06:40:49 +02:00
|
|
|
return watchlist_add(&netdev_watches, func, user_data, destroy);
|
2018-07-17 00:29:15 +02:00
|
|
|
}
|
|
|
|
|
2018-08-18 06:40:49 +02:00
|
|
|
bool netdev_watch_remove(uint32_t id)
|
2018-07-17 00:29:15 +02:00
|
|
|
{
|
2018-08-18 06:40:49 +02:00
|
|
|
return watchlist_remove(&netdev_watches, id);
|
2018-07-17 00:29:15 +02:00
|
|
|
}
|
|
|
|
|
2019-10-11 21:29:23 +02:00
|
|
|
static int netdev_init(void)
|
2014-06-21 13:41:40 +02:00
|
|
|
{
|
2019-05-18 00:05:52 +02:00
|
|
|
struct l_genl *genl = iwd_get_genl();
|
2019-03-21 17:03:45 +01:00
|
|
|
const struct l_settings *settings = iwd_get_config();
|
2020-03-18 19:54:19 +01:00
|
|
|
const char *rand_addr_str;
|
2019-03-21 17:03:45 +01:00
|
|
|
|
2014-06-21 13:41:40 +02:00
|
|
|
if (rtnl)
|
2019-10-11 21:29:23 +02:00
|
|
|
return -EALREADY;
|
2014-06-21 13:41:40 +02:00
|
|
|
|
2020-04-08 19:28:00 +02:00
|
|
|
rtnl = iwd_get_rtnl();
|
2014-06-21 13:41:40 +02:00
|
|
|
|
2016-06-20 12:42:03 +02:00
|
|
|
if (!l_netlink_register(rtnl, RTNLGRP_LINK,
|
|
|
|
netdev_link_notify, NULL, NULL)) {
|
|
|
|
l_error("Failed to register for RTNL link notifications");
|
2019-10-11 22:34:54 +02:00
|
|
|
goto fail_netlink;
|
|
|
|
}
|
|
|
|
|
|
|
|
nl80211 = l_genl_family_new(genl, NL80211_GENL_NAME);
|
|
|
|
if (!nl80211) {
|
|
|
|
l_error("Failed to obtain nl80211");
|
|
|
|
goto fail_netlink;
|
2016-06-20 12:42:03 +02:00
|
|
|
}
|
|
|
|
|
2019-10-25 04:16:49 +02:00
|
|
|
if (!l_settings_get_int(settings, "General", "RoamThreshold",
|
2019-03-21 17:03:45 +01:00
|
|
|
&LOW_SIGNAL_THRESHOLD))
|
|
|
|
LOW_SIGNAL_THRESHOLD = -70;
|
|
|
|
|
2019-10-25 04:36:03 +02:00
|
|
|
if (!l_settings_get_bool(settings, "General", "ControlPortOverNL80211",
|
|
|
|
&pae_over_nl80211))
|
|
|
|
pae_over_nl80211 = true;
|
|
|
|
|
2020-03-18 19:54:19 +01:00
|
|
|
rand_addr_str = l_settings_get_value(settings, "General",
|
|
|
|
"AddressRandomization");
|
|
|
|
if (rand_addr_str && !strcmp(rand_addr_str, "network"))
|
|
|
|
mac_per_ssid = true;
|
|
|
|
|
2018-08-18 06:40:49 +02:00
|
|
|
watchlist_init(&netdev_watches, NULL);
|
2016-06-01 22:35:26 +02:00
|
|
|
netdev_list = l_queue_new();
|
|
|
|
|
2018-08-18 06:23:19 +02:00
|
|
|
__handshake_set_install_tk_func(netdev_set_tk);
|
|
|
|
__handshake_set_install_gtk_func(netdev_set_gtk);
|
|
|
|
__handshake_set_install_igtk_func(netdev_set_igtk);
|
|
|
|
|
|
|
|
__eapol_set_rekey_offload_func(netdev_set_rekey_offload);
|
|
|
|
__eapol_set_tx_packet_func(netdev_control_port_frame);
|
|
|
|
|
2019-05-18 00:05:52 +02:00
|
|
|
unicast_watch = l_genl_add_unicast_watch(genl, NL80211_GENL_NAME,
|
|
|
|
netdev_unicast_notify,
|
|
|
|
NULL, NULL);
|
|
|
|
if (!unicast_watch)
|
|
|
|
l_error("Registering for unicast notification failed");
|
|
|
|
|
2016-06-15 17:54:13 +02:00
|
|
|
if (!l_genl_family_register(nl80211, "mlme", netdev_mlme_notify,
|
|
|
|
NULL, NULL))
|
|
|
|
l_error("Registering for MLME notification failed");
|
2019-10-11 21:29:23 +02:00
|
|
|
|
|
|
|
return 0;
|
2019-10-11 22:34:54 +02:00
|
|
|
|
|
|
|
fail_netlink:
|
|
|
|
rtnl = NULL;
|
|
|
|
|
|
|
|
return -EIO;
|
2014-06-21 13:41:40 +02:00
|
|
|
}
|
|
|
|
|
2019-10-11 21:29:23 +02:00
|
|
|
static void netdev_exit(void)
|
2014-06-21 13:41:40 +02:00
|
|
|
{
|
2019-05-18 00:05:52 +02:00
|
|
|
struct l_genl *genl = iwd_get_genl();
|
|
|
|
|
2014-06-21 13:41:40 +02:00
|
|
|
if (!rtnl)
|
2018-08-18 06:23:19 +02:00
|
|
|
return;
|
2014-06-21 13:41:40 +02:00
|
|
|
|
2019-05-18 00:05:52 +02:00
|
|
|
l_genl_remove_unicast_watch(genl, unicast_watch);
|
|
|
|
|
2018-08-18 06:40:49 +02:00
|
|
|
watchlist_destroy(&netdev_watches);
|
2019-10-11 21:29:23 +02:00
|
|
|
|
|
|
|
l_genl_family_free(nl80211);
|
2016-06-01 21:58:51 +02:00
|
|
|
nl80211 = NULL;
|
|
|
|
|
2016-05-31 23:23:12 +02:00
|
|
|
rtnl = NULL;
|
2014-06-21 13:41:40 +02:00
|
|
|
}
|
2016-07-13 04:29:52 +02:00
|
|
|
|
|
|
|
void netdev_shutdown(void)
|
|
|
|
{
|
|
|
|
if (!rtnl)
|
|
|
|
return;
|
|
|
|
|
|
|
|
l_queue_foreach(netdev_list, netdev_shutdown_one, NULL);
|
2016-08-02 22:48:12 +02:00
|
|
|
|
|
|
|
l_queue_destroy(netdev_list, netdev_free);
|
|
|
|
netdev_list = NULL;
|
2016-07-13 04:29:52 +02:00
|
|
|
}
|
2019-10-11 21:29:23 +02:00
|
|
|
|
|
|
|
IWD_MODULE(netdev, netdev_init, netdev_exit);
|
2019-10-11 21:29:24 +02:00
|
|
|
IWD_MODULE_DEPENDS(netdev, eapol);
|