wiphy: Move association logic out of wiphy.c

The eapol state machine parameters are now built inside device.c when
the network connection is attempted.  The reason is that the device
object knows about network settings, wiphy constraints and should
contain the main 'management' logic.

netdev now manages the actual low-level process of building association
messages, detecting authentication events, etc.
This commit is contained in:
Denis Kenzior 2016-06-15 10:54:13 -05:00
parent b93ae37325
commit 31a0e42772
3 changed files with 255 additions and 145 deletions

View File

@ -28,7 +28,12 @@
#include <ell/ell.h>
#include "src/iwd.h"
#include "src/common.h"
#include "src/ie.h"
#include "src/eapol.h"
#include "src/wiphy.h"
#include "src/scan.h"
#include "src/netdev.h"
#include "src/dbus.h"
#include "src/network.h"
@ -217,11 +222,65 @@ void device_connect_network(struct device *device, struct network *network,
struct scan_bss *bss,
struct l_dbus_message *message)
{
enum security security = network_get_security(network);
struct wiphy *wiphy = device->wiphy;
struct l_dbus *dbus = dbus_get_bus();
struct eapol_sm *sm = NULL;
if (security == SECURITY_PSK || security == SECURITY_8021X) {
uint16_t pairwise_ciphers, group_ciphers;
uint8_t rsne_buf[256];
struct ie_rsn_info info;
sm = eapol_sm_new();
eapol_sm_set_authenticator_address(sm, bss->addr);
eapol_sm_set_supplicant_address(sm,
netdev_get_address(device->netdev));
memset(&info, 0, sizeof(info));
if (security == SECURITY_PSK)
info.akm_suites =
bss->sha256 ? IE_RSN_AKM_SUITE_PSK_SHA256 :
IE_RSN_AKM_SUITE_PSK;
else
info.akm_suites =
bss->sha256 ? IE_RSN_AKM_SUITE_8021X_SHA256 :
IE_RSN_AKM_SUITE_8021X;
bss_get_supported_ciphers(bss,
&pairwise_ciphers, &group_ciphers);
info.pairwise_ciphers = wiphy_select_cipher(wiphy,
pairwise_ciphers);
info.group_cipher = wiphy_select_cipher(wiphy, group_ciphers);
/* RSN takes priority */
if (bss->rsne) {
ie_build_rsne(&info, rsne_buf);
eapol_sm_set_ap_rsn(sm, bss->rsne, bss->rsne[1] + 2);
eapol_sm_set_own_rsn(sm, rsne_buf, rsne_buf[1] + 2);
} else {
ie_build_wpa(&info, rsne_buf);
eapol_sm_set_ap_wpa(sm, bss->wpa, bss->wpa[1] + 2);
eapol_sm_set_own_wpa(sm, rsne_buf, rsne_buf[1] + 2);
}
if (security == SECURITY_PSK)
eapol_sm_set_pmk(sm, network_get_psk(network));
else
eapol_sm_set_8021x_config(sm,
network_get_settings(network));
eapol_sm_set_user_data(sm, device);
eapol_sm_set_tx_user_data(sm,
L_INT_TO_PTR(l_io_get_fd(device->eapol_io)));
}
device->connect_pending = l_dbus_message_ref(message);
if (netdev_connect(device->netdev, bss, NULL,
if (netdev_connect(device->netdev, bss, sm,
device_netdev_event,
device_connect_cb, device) < 0) {
dbus_pending_reply(&device->connect_pending,

View File

@ -34,7 +34,12 @@
#include <ell/ell.h>
#include "linux/nl80211.h"
#include "src/iwd.h"
#include "src/wiphy.h"
#include "src/ie.h"
#include "src/eapol.h"
#include "src/crypto.h"
#include "src/device.h"
#include "src/scan.h"
#include "src/netdev.h"
@ -48,6 +53,8 @@ struct netdev {
netdev_event_func_t event_filter;
netdev_connect_cb_t connect_cb;
void *user_data;
struct l_genl_msg *associate_msg;
struct eapol_sm *sm;
};
static struct l_netlink *rtnl = NULL;
@ -151,6 +158,17 @@ static void netdev_free(void *data)
struct netdev *netdev = data;
l_debug("Freeing netdev %s[%d]", netdev->name, netdev->index);
if (netdev->sm) {
eapol_sm_free(netdev->sm);
netdev->sm = NULL;
}
if (netdev->associate_msg) {
l_genl_msg_unref(netdev->associate_msg);
netdev->associate_msg = NULL;
}
l_free(netdev);
}
@ -167,6 +185,126 @@ struct netdev *netdev_find(int ifindex)
return l_queue_find(netdev_list, netdev_match, L_UINT_TO_PTR(ifindex));
}
static void netdev_cmd_associate_cb(struct l_genl_msg *msg, void *user_data)
{
struct netdev *netdev = user_data;
/* Wait for associate event */
if (l_genl_msg_get_error(msg) >= 0) {
if (netdev->event_filter)
netdev->event_filter(netdev,
NETDEV_EVENT_ASSOCIATING,
netdev->user_data);
if (netdev->sm) {
eapol_start(netdev->index, netdev->sm);
netdev->sm = NULL;
}
return;
}
if (netdev->connect_cb)
netdev->connect_cb(netdev, NETDEV_RESULT_ASSOCIATION_FAILED,
netdev->user_data);
}
static struct l_genl_msg *netdev_build_cmd_associate(struct netdev *netdev,
struct scan_bss *bss,
struct eapol_sm *sm)
{
struct l_genl_msg *msg;
msg = l_genl_msg_new_sized(NL80211_CMD_ASSOCIATE, 512);
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);
if (sm) {
uint32_t cipher;
uint32_t nl_cipher;
size_t ie_len;
const uint8_t *ie;
cipher = eapol_sm_get_pairwise_cipher(sm);
if (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);
cipher = eapol_sm_get_group_cipher(sm);
if (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);
l_genl_msg_append_attr(msg, NL80211_ATTR_CONTROL_PORT, 0, NULL);
ie = eapol_sm_get_own_ie(sm, &ie_len);
if (ie)
l_genl_msg_append_attr(msg, NL80211_ATTR_IE,
ie_len, ie);
}
return msg;
}
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;
int err;
l_debug("");
err = l_genl_msg_get_error(msg);
if (err < 0) {
l_error("authentication failed %s (%d)", strerror(-err), err);
goto error;
}
if (!l_genl_attr_init(&attr, msg)) {
l_debug("attr init failed");
goto error;
}
while (l_genl_attr_next(&attr, &type, &len, &data)) {
switch (type) {
case NL80211_ATTR_TIMED_OUT:
l_warn("authentication timed out");
goto error;
}
}
if (!netdev->associate_msg)
return;
if (l_genl_family_send(nl80211, netdev->associate_msg,
netdev_cmd_associate_cb, netdev, NULL) > 0) {
netdev->associate_msg = NULL;
return;
}
l_genl_msg_unref(netdev->associate_msg);
netdev->associate_msg = NULL;
error:
if (netdev->connect_cb)
netdev->connect_cb(netdev, NETDEV_RESULT_AUTHENTICATION_FAILED,
netdev->user_data);
}
static void netdev_cmd_authenticate_cb(struct l_genl_msg *msg, void *user_data)
{
struct netdev *netdev = user_data;
@ -209,18 +347,30 @@ int netdev_connect(struct netdev *netdev, struct scan_bss *bss,
netdev_connect_cb_t cb, void *user_data)
{
struct l_genl_msg *authenticate;
struct l_genl_msg *associate;
authenticate = netdev_build_cmd_authenticate(netdev, bss);
if (!authenticate)
return -EINVAL;
associate = netdev_build_cmd_associate(netdev, bss, sm);
if (!associate) {
l_genl_msg_unref(authenticate);
return -EINVAL;
}
if (!l_genl_family_send(nl80211, authenticate,
netdev_cmd_authenticate_cb, netdev, NULL))
netdev_cmd_authenticate_cb, netdev, NULL)) {
l_genl_msg_unref(associate);
l_genl_msg_unref(authenticate);
return -EIO;
}
netdev->event_filter = event_filter;
netdev->connect_cb = cb;
netdev->user_data = user_data;
netdev->sm = sm;
netdev->associate_msg = associate;
return 0;
}
@ -231,6 +381,46 @@ int netdev_disconnect(struct netdev *netdev,
return 0;
}
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);
l_debug("MLME 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;
}
}
if (!netdev) {
l_warn("MLME notification is missing ifindex attribute");
return;
}
switch (cmd) {
case NL80211_CMD_AUTHENTICATE:
netdev_authenticate_event(msg, netdev);
break;
}
}
static void netdev_get_interface_callback(struct l_genl_msg *msg,
void *user_data)
{
@ -411,6 +601,10 @@ bool netdev_init(struct l_genl_family *in)
NULL, NULL))
l_error("Getting all interface information failed");
if (!l_genl_family_register(nl80211, "mlme", netdev_mlme_notify,
NULL, NULL))
l_error("Registering for MLME notification failed");
return true;
}

View File

@ -836,146 +836,6 @@ static void mlme_associate_event(struct l_genl_msg *msg, struct device *device)
operstate_cb, device);
}
static void genl_associate_cb(struct l_genl_msg *msg, void *user_data)
{
struct device *device = user_data;
if (l_genl_msg_get_error(msg) < 0 && device->connect_pending)
dbus_pending_reply(&device->connect_pending,
dbus_error_failed(device->connect_pending));
}
static void mlme_associate_cmd(struct device *device)
{
struct l_genl_msg *msg;
struct scan_bss *bss = device->connected_bss;
struct network *network = device->connected_network;
struct wiphy *wiphy = device->wiphy;
const char *ssid = network_get_ssid(network);
enum security security = network_get_security(network);
l_debug("");
msg = l_genl_msg_new_sized(NL80211_CMD_ASSOCIATE, 512);
msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &device->index);
msg_append_attr(msg, NL80211_ATTR_WIPHY_FREQ, 4, &bss->frequency);
msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, bss->addr);
msg_append_attr(msg, NL80211_ATTR_SSID, strlen(ssid), ssid);
if (security == SECURITY_PSK || security == SECURITY_8021X) {
uint16_t pairwise_ciphers, group_ciphers;
uint32_t pairwise_cipher_attr;
uint32_t group_cipher_attr;
uint8_t rsne_buf[256];
struct ie_rsn_info info;
struct eapol_sm *sm = eapol_sm_new();
memset(&info, 0, sizeof(info));
if (security == SECURITY_PSK)
info.akm_suites =
bss->sha256 ? IE_RSN_AKM_SUITE_PSK_SHA256 :
IE_RSN_AKM_SUITE_PSK;
else
info.akm_suites =
bss->sha256 ? IE_RSN_AKM_SUITE_8021X_SHA256 :
IE_RSN_AKM_SUITE_8021X;
bss_get_supported_ciphers(bss, &pairwise_ciphers,
&group_ciphers);
info.pairwise_ciphers = wiphy_select_cipher(wiphy,
pairwise_ciphers);
if (info.pairwise_ciphers == IE_RSN_CIPHER_SUITE_CCMP)
pairwise_cipher_attr = CRYPTO_CIPHER_CCMP;
else
pairwise_cipher_attr = CRYPTO_CIPHER_TKIP;
info.group_cipher = wiphy_select_cipher(wiphy, group_ciphers);
if (info.group_cipher == IE_RSN_CIPHER_SUITE_CCMP)
group_cipher_attr = CRYPTO_CIPHER_CCMP;
else
group_cipher_attr = CRYPTO_CIPHER_TKIP;
/* RSN takes priority */
if (bss->rsne) {
ie_build_rsne(&info, rsne_buf);
eapol_sm_set_ap_rsn(sm, bss->rsne, bss->rsne[1] + 2);
eapol_sm_set_own_rsn(sm, rsne_buf, rsne_buf[1] + 2);
} else {
ie_build_wpa(&info, rsne_buf);
eapol_sm_set_ap_wpa(sm, bss->wpa, bss->wpa[1] + 2);
eapol_sm_set_own_wpa(sm, rsne_buf, rsne_buf[1] + 2);
}
if (security == SECURITY_PSK)
eapol_sm_set_pmk(sm, network_get_psk(network));
else
eapol_sm_set_8021x_config(sm,
network_get_settings(network));
eapol_sm_set_authenticator_address(sm, bss->addr);
eapol_sm_set_supplicant_address(sm,
netdev_get_address(device->netdev));
eapol_sm_set_user_data(sm, device);
eapol_sm_set_tx_user_data(sm,
L_INT_TO_PTR(l_io_get_fd(device->eapol_io)));
eapol_start(device->index, sm);
msg_append_attr(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
4, &pairwise_cipher_attr);
msg_append_attr(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
4, &group_cipher_attr);
msg_append_attr(msg, NL80211_ATTR_CONTROL_PORT, 0, NULL);
msg_append_attr(msg, NL80211_ATTR_IE,
rsne_buf[1] + 2, rsne_buf);
}
l_genl_family_send(nl80211, msg, genl_associate_cb, device, NULL);
}
static void mlme_authenticate_event(struct l_genl_msg *msg,
struct device *device)
{
struct l_genl_attr attr;
uint16_t type, len;
const void *data;
int err;
l_debug("");
err = l_genl_msg_get_error(msg);
if (err < 0) {
l_error("authentication failed %s (%d)", strerror(-err), err);
goto error;
}
if (!l_genl_attr_init(&attr, msg)) {
l_debug("attr init failed");
goto error;
}
while (l_genl_attr_next(&attr, &type, &len, &data)) {
switch (type) {
case NL80211_ATTR_TIMED_OUT:
l_warn("authentication timed out");
goto error;
}
}
l_info("Authentication completed");
mlme_associate_cmd(device);
return;
error:
if (device->connect_pending)
dbus_pending_reply(&device->connect_pending,
dbus_error_failed(device->connect_pending));
device_disassociated(device);
}
static void mlme_deauthenticate_event(struct l_genl_msg *msg,
struct device *device)
{
@ -1670,9 +1530,6 @@ static void wiphy_mlme_notify(struct l_genl_msg *msg, void *user_data)
switch (cmd) {
case NL80211_CMD_AUTHENTICATE:
mlme_authenticate_event(msg, device);
break;
case NL80211_CMD_ASSOCIATE:
mlme_associate_event(msg, device);
break;