*
* Connection Manager
*
- * Copyright (C) 2007-2009 Intel Corporation. All rights reserved.
+ * Copyright (C) 2007-2010 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include <errno.h>
#include <string.h>
+#include <stdint.h>
#include <syslog.h>
#include <glib.h>
#define TIMEOUT 5000
+#define IEEE80211_CAP_ESS 0x0001
+#define IEEE80211_CAP_IBSS 0x0002
+#define IEEE80211_CAP_PRIVACY 0x0010
+
static DBusConnection *connection;
static const struct supplicant_callbacks *callbacks_pointer;
+static dbus_bool_t system_available = FALSE;
+static dbus_bool_t system_ready = FALSE;
+
+static dbus_int32_t debug_level = 0;
+static dbus_bool_t debug_timestamp = FALSE;
+static dbus_bool_t debug_showkeys = FALSE;
+
+static const char *debug_strings[] = {
+ "msgdump", "debug", "info", "warning", "error", NULL
+};
+
static unsigned int eap_methods;
-static struct {
+struct strvalmap {
const char *str;
unsigned int val;
-} eap_method_map[] = {
+};
+
+static struct strvalmap eap_method_map[] = {
{ "MD5", SUPPLICANT_EAP_METHOD_MD5 },
{ "TLS", SUPPLICANT_EAP_METHOD_TLS },
{ "MSCHAPV2", SUPPLICANT_EAP_METHOD_MSCHAPV2 },
{ "GTC", SUPPLICANT_EAP_METHOD_GTC },
{ "OTP", SUPPLICANT_EAP_METHOD_OTP },
{ "LEAP", SUPPLICANT_EAP_METHOD_LEAP },
+ { "WSC", SUPPLICANT_EAP_METHOD_WSC },
{ }
};
-static struct {
- const char *str;
- unsigned int val;
-} scan_capa_map[] = {
+static struct strvalmap keymgmt_capa_map[] = {
+ { "none", SUPPLICANT_CAPABILITY_KEYMGMT_NONE },
+ { "ieee8021x", SUPPLICANT_CAPABILITY_KEYMGMT_IEEE8021X },
+ { "wpa-none", SUPPLICANT_CAPABILITY_KEYMGMT_WPA_NONE },
+ { "wpa-psk", SUPPLICANT_CAPABILITY_KEYMGMT_WPA_PSK },
+ { "wpa-eap", SUPPLICANT_CAPABILITY_KEYMGMT_WPA_EAP },
+ { "wps", SUPPLICANT_CAPABILITY_KEYMGMT_WPS },
+ { }
+};
+
+static struct strvalmap authalg_capa_map[] = {
+ { "open", SUPPLICANT_CAPABILITY_AUTHALG_OPEN },
+ { "shared", SUPPLICANT_CAPABILITY_AUTHALG_SHARED },
+ { "leap", SUPPLICANT_CAPABILITY_AUTHALG_LEAP },
+ { }
+};
+
+static struct strvalmap proto_capa_map[] = {
+ { "wpa", SUPPLICANT_CAPABILITY_PROTO_WPA },
+ { "rsn", SUPPLICANT_CAPABILITY_PROTO_RSN },
+ { }
+};
+
+static struct strvalmap group_capa_map[] = {
+ { "wep40", SUPPLICANT_CAPABILITY_GROUP_WEP40 },
+ { "wep104", SUPPLICANT_CAPABILITY_GROUP_WEP104 },
+ { "tkip", SUPPLICANT_CAPABILITY_GROUP_TKIP },
+ { "ccmp", SUPPLICANT_CAPABILITY_GROUP_CCMP },
+ { }
+};
+
+static struct strvalmap pairwise_capa_map[] = {
+ { "none", SUPPLICANT_CAPABILITY_PAIRWISE_NONE },
+ { "tkip", SUPPLICANT_CAPABILITY_PAIRWISE_TKIP },
+ { "ccmp", SUPPLICANT_CAPABILITY_PAIRWISE_CCMP },
+ { }
+};
+
+static struct strvalmap scan_capa_map[] = {
{ "active", SUPPLICANT_CAPABILITY_SCAN_ACTIVE },
{ "passive", SUPPLICANT_CAPABILITY_SCAN_PASSIVE },
{ "ssid", SUPPLICANT_CAPABILITY_SCAN_SSID },
{ }
};
+static struct strvalmap mode_capa_map[] = {
+ { "infrastructure", SUPPLICANT_CAPABILITY_MODE_INFRA },
+ { "ad-hoc", SUPPLICANT_CAPABILITY_MODE_IBSS },
+ { "ap", SUPPLICANT_CAPABILITY_MODE_AP },
+ { }
+};
+
static GHashTable *interface_table;
+static GHashTable *bss_mapping;
struct supplicant_interface {
char *path;
+ unsigned int keymgmt_capa;
+ unsigned int authalg_capa;
+ unsigned int proto_capa;
+ unsigned int group_capa;
+ unsigned int pairwise_capa;
unsigned int scan_capa;
+ unsigned int mode_capa;
+ dbus_bool_t ready;
enum supplicant_state state;
dbus_bool_t scanning;
+ supplicant_interface_scan_callback scan_callback;
+ void *scan_data;
int apscan;
char *ifname;
char *driver;
char *bridge;
GHashTable *network_table;
+ GHashTable *net_mapping;
GHashTable *bss_mapping;
};
struct supplicant_network {
struct supplicant_interface *interface;
+ char *path;
char *group;
char *name;
- enum supplicant_network_mode mode;
+ enum supplicant_mode mode;
GHashTable *bss_table;
+ GHashTable *config_table;
};
struct supplicant_bss {
unsigned char bssid[6];
unsigned char ssid[32];
unsigned int ssid_len;
- unsigned int frequency;
+ dbus_uint16_t frequency;
+ dbus_uint32_t maxrate;
+ enum supplicant_mode mode;
+ enum supplicant_security security;
+ dbus_bool_t privacy;
+ dbus_bool_t psk;
+ dbus_bool_t ieee8021x;
};
+static enum supplicant_mode string2mode(const char *mode)
+{
+ if (mode == NULL)
+ return SUPPLICANT_MODE_UNKNOWN;
+
+ if (g_str_equal(mode, "infrastructure") == TRUE)
+ return SUPPLICANT_MODE_INFRA;
+ else if (g_str_equal(mode, "ad-hoc") == TRUE)
+ return SUPPLICANT_MODE_IBSS;
+
+ return SUPPLICANT_MODE_UNKNOWN;
+}
+
+static const char *mode2string(enum supplicant_mode mode)
+{
+ switch (mode) {
+ case SUPPLICANT_MODE_UNKNOWN:
+ break;
+ case SUPPLICANT_MODE_INFRA:
+ return "infra";
+ case SUPPLICANT_MODE_IBSS:
+ return "adhoc";
+ }
+
+ return NULL;
+}
+
+static const char *security2string(enum supplicant_security security)
+{
+ switch (security) {
+ case SUPPLICANT_SECURITY_UNKNOWN:
+ break;
+ case SUPPLICANT_SECURITY_NONE:
+ return "none";
+ case SUPPLICANT_SECURITY_WEP:
+ return "wep";
+ case SUPPLICANT_SECURITY_PSK:
+ return "psk";
+ case SUPPLICANT_SECURITY_IEEE8021X:
+ return "ieee8021x";
+ }
+
+ return NULL;
+}
+
static enum supplicant_state string2state(const char *state)
{
if (state == NULL)
return SUPPLICANT_STATE_UNKNOWN;
}
+static void callback_system_ready(void)
+{
+ if (system_ready == TRUE)
+ return;
+
+ system_ready = TRUE;
+
+ if (callbacks_pointer == NULL)
+ return;
+
+ if (callbacks_pointer->system_ready == NULL)
+ return;
+
+ callbacks_pointer->system_ready();
+}
+
+static void callback_system_killed(void)
+{
+ system_ready = FALSE;
+
+ if (callbacks_pointer == NULL)
+ return;
+
+ if (callbacks_pointer->system_killed == NULL)
+ return;
+
+ callbacks_pointer->system_killed();
+}
+
static void callback_interface_added(struct supplicant_interface *interface)
{
if (callbacks_pointer == NULL)
callbacks_pointer->interface_removed(interface);
}
+static void callback_scan_started(struct supplicant_interface *interface)
+{
+ if (callbacks_pointer == NULL)
+ return;
+
+ if (callbacks_pointer->scan_started == NULL)
+ return;
+
+ callbacks_pointer->scan_started(interface);
+}
+
+static void callback_scan_finished(struct supplicant_interface *interface)
+{
+ if (callbacks_pointer == NULL)
+ return;
+
+ if (callbacks_pointer->scan_finished == NULL)
+ return;
+
+ callbacks_pointer->scan_finished(interface);
+}
+
static void callback_network_added(struct supplicant_network *network)
{
if (callbacks_pointer == NULL)
{
struct supplicant_interface *interface = data;
- callback_interface_removed(interface);
-
g_hash_table_destroy(interface->bss_mapping);
+ g_hash_table_destroy(interface->net_mapping);
g_hash_table_destroy(interface->network_table);
+ callback_interface_removed(interface);
+
g_free(interface->path);
g_free(interface->ifname);
g_free(interface->driver);
{
struct supplicant_network *network = data;
+ g_hash_table_destroy(network->bss_table);
+
callback_network_removed(network);
+ g_hash_table_destroy(network->config_table);
+
g_free(network->group);
g_free(network->name);
g_free(network);
g_free(bss);
}
-static void debug_eap_methods(void)
+static void debug_strvalmap(const char *label, struct strvalmap *map,
+ unsigned int val)
{
int i;
- for (i = 0; eap_method_map[i].str != NULL; i++) {
- if (eap_methods & eap_method_map[i].val)
- DBG("EAP Method: %s", eap_method_map[i].str);
+ for (i = 0; map[i].str != NULL; i++) {
+ if (val & map[i].val)
+ DBG("%s: %s", label, map[i].str);
}
}
-static void debug_scan_capabilities(struct supplicant_interface *interface)
+static void interface_capability_keymgmt(DBusMessageIter *iter, void *user_data)
{
+ struct supplicant_interface *interface = user_data;
+ const char *str = NULL;
int i;
- for (i = 0; scan_capa_map[i].str != NULL; i++) {
- if (interface->scan_capa & scan_capa_map[i].val)
- DBG("Scan Capability: %s", scan_capa_map[i].str);
- }
+ dbus_message_iter_get_basic(iter, &str);
+ if (str == NULL)
+ return;
+
+ for (i = 0; keymgmt_capa_map[i].str != NULL; i++)
+ if (strcmp(str, keymgmt_capa_map[i].str) == 0) {
+ interface->keymgmt_capa |= keymgmt_capa_map[i].val;
+ break;
+ }
+}
+
+static void interface_capability_authalg(DBusMessageIter *iter, void *user_data)
+{
+ struct supplicant_interface *interface = user_data;
+ const char *str = NULL;
+ int i;
+
+ dbus_message_iter_get_basic(iter, &str);
+ if (str == NULL)
+ return;
+
+ for (i = 0; authalg_capa_map[i].str != NULL; i++)
+ if (strcmp(str, authalg_capa_map[i].str) == 0) {
+ interface->authalg_capa |= authalg_capa_map[i].val;
+ break;
+ }
+}
+
+static void interface_capability_proto(DBusMessageIter *iter, void *user_data)
+{
+ struct supplicant_interface *interface = user_data;
+ const char *str = NULL;
+ int i;
+
+ dbus_message_iter_get_basic(iter, &str);
+ if (str == NULL)
+ return;
+
+ for (i = 0; proto_capa_map[i].str != NULL; i++)
+ if (strcmp(str, proto_capa_map[i].str) == 0) {
+ interface->proto_capa |= proto_capa_map[i].val;
+ break;
+ }
+}
+
+static void interface_capability_pairwise(DBusMessageIter *iter, void *user_data)
+{
+ struct supplicant_interface *interface = user_data;
+ const char *str = NULL;
+ int i;
+
+ dbus_message_iter_get_basic(iter, &str);
+ if (str == NULL)
+ return;
+
+ for (i = 0; pairwise_capa_map[i].str != NULL; i++)
+ if (strcmp(str, pairwise_capa_map[i].str) == 0) {
+ interface->pairwise_capa |= pairwise_capa_map[i].val;
+ break;
+ }
+}
+
+static void interface_capability_group(DBusMessageIter *iter, void *user_data)
+{
+ struct supplicant_interface *interface = user_data;
+ const char *str = NULL;
+ int i;
+
+ dbus_message_iter_get_basic(iter, &str);
+ if (str == NULL)
+ return;
+
+ for (i = 0; group_capa_map[i].str != NULL; i++)
+ if (strcmp(str, group_capa_map[i].str) == 0) {
+ interface->group_capa |= group_capa_map[i].val;
+ break;
+ }
}
static void interface_capability_scan(DBusMessageIter *iter, void *user_data)
}
}
+static void interface_capability_mode(DBusMessageIter *iter, void *user_data)
+{
+ struct supplicant_interface *interface = user_data;
+ const char *str = NULL;
+ int i;
+
+ dbus_message_iter_get_basic(iter, &str);
+ if (str == NULL)
+ return;
+
+ for (i = 0; mode_capa_map[i].str != NULL; i++)
+ if (strcmp(str, mode_capa_map[i].str) == 0) {
+ interface->mode_capa |= mode_capa_map[i].val;
+ break;
+ }
+}
+
static void interface_capability(const char *key, DBusMessageIter *iter,
void *user_data)
{
if (key == NULL)
return;
- if (g_strcmp0(key, "Scan") == 0)
- supplicant_dbus_array_foreach(iter, interface_capability_scan,
- interface);
+ if (g_strcmp0(key, "KeyMgmt") == 0)
+ supplicant_dbus_array_foreach(iter,
+ interface_capability_keymgmt, interface);
+ else if (g_strcmp0(key, "AuthAlg") == 0)
+ supplicant_dbus_array_foreach(iter,
+ interface_capability_authalg, interface);
+ else if (g_strcmp0(key, "Protocol") == 0)
+ supplicant_dbus_array_foreach(iter,
+ interface_capability_proto, interface);
+ else if (g_strcmp0(key, "Pairwise") == 0)
+ supplicant_dbus_array_foreach(iter,
+ interface_capability_pairwise, interface);
+ else if (g_strcmp0(key, "Group") == 0)
+ supplicant_dbus_array_foreach(iter,
+ interface_capability_group, interface);
+ else if (g_strcmp0(key, "Scan") == 0)
+ supplicant_dbus_array_foreach(iter,
+ interface_capability_scan, interface);
+ else if (g_strcmp0(key, "Modes") == 0)
+ supplicant_dbus_array_foreach(iter,
+ interface_capability_mode, interface);
else
DBG("key %s type %c",
key, dbus_message_iter_get_arg_type(iter));
return interface->ifname;
}
+const char *supplicant_interface_get_driver(struct supplicant_interface *interface)
+{
+ if (interface == NULL)
+ return NULL;
+
+ return interface->driver;
+}
+
struct supplicant_interface *supplicant_network_get_interface(struct supplicant_network *network)
{
if (network == NULL)
return network->name;
}
-enum supplicant_network_mode supplicant_network_get_mode(struct supplicant_network *network)
+const char *supplicant_network_get_identifier(struct supplicant_network *network)
+{
+ if (network == NULL || network->group == NULL)
+ return "";
+
+ return network->group;
+}
+
+enum supplicant_mode supplicant_network_get_mode(struct supplicant_network *network)
{
if (network == NULL)
- return SUPPLICANT_NETWORK_MODE_UNKNOWN;
+ return SUPPLICANT_MODE_UNKNOWN;
return network->mode;
}
+static void merge_network(struct supplicant_network *network)
+{
+ GString *str;
+ const char *ssid, *mode, *key_mgmt;
+ unsigned int i, ssid_len;
+ char *group;
+
+ ssid = g_hash_table_lookup(network->config_table, "ssid");
+ mode = g_hash_table_lookup(network->config_table, "mode");
+ key_mgmt = g_hash_table_lookup(network->config_table, "key_mgmt");
+
+ DBG("ssid %s mode %s", ssid, mode);
+
+ if (ssid != NULL)
+ ssid_len = strlen(ssid);
+ else
+ ssid_len = 0;
+
+ str = g_string_sized_new((ssid_len * 2) + 24);
+ if (str == NULL)
+ return;
+
+ for (i = 0; i < ssid_len; i++)
+ g_string_append_printf(str, "%02x", ssid[i]);
+
+ if (g_strcmp0(mode, "0") == 0)
+ g_string_append_printf(str, "_infra");
+ else if (g_strcmp0(mode, "1") == 0)
+ g_string_append_printf(str, "_adhoc");
+
+ if (g_strcmp0(key_mgmt, "WPA-PSK") == 0)
+ g_string_append_printf(str, "_psk");
+
+ group = g_string_free(str, FALSE);
+
+ DBG("%s", group);
+
+ g_free(group);
+
+ g_hash_table_destroy(network->config_table);
+
+ g_free(network->path);
+ g_free(network);
+}
+
static void network_property(const char *key, DBusMessageIter *iter,
void *user_data)
{
- if (key == NULL)
+ struct supplicant_network *network = user_data;
+
+ if (network->interface == NULL)
+ return;
+
+ if (key == NULL) {
+ merge_network(network);
return;
+ }
+
+ if (g_strcmp0(key, "Enabled") == 0) {
+ dbus_bool_t enabled = FALSE;
- DBG("key %s type %c", key, dbus_message_iter_get_arg_type(iter));
+ dbus_message_iter_get_basic(iter, &enabled);
+ } else if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) {
+ const char *str = NULL;
+
+ dbus_message_iter_get_basic(iter, &str);
+ if (str != NULL)
+ g_hash_table_replace(network->config_table,
+ g_strdup(key), g_strdup(str));
+ } else
+ DBG("key %s type %c",
+ key, dbus_message_iter_get_arg_type(iter));
}
static void interface_network_added(DBusMessageIter *iter, void *user_data)
{
+ struct supplicant_interface *interface = user_data;
+ struct supplicant_network *network;
const char *path = NULL;
dbus_message_iter_get_basic(iter, &path);
if (path == NULL)
return;
+ if (g_strcmp0(path, "/") == 0)
+ return;
+
+ network = g_hash_table_lookup(interface->net_mapping, path);
+ if (network != NULL)
+ return;
+
+ network = g_try_new0(struct supplicant_network, 1);
+ if (network == NULL)
+ return;
+
+ network->interface = interface;
+ network->path = g_strdup(path);
+
+ network->config_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, g_free);
+
+ dbus_message_iter_next(iter);
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INVALID) {
+ supplicant_dbus_property_foreach(iter, network_property,
+ network);
+ network_property(NULL, NULL, network);
+ return;
+ }
+
supplicant_dbus_property_get_all(path,
SUPPLICANT_INTERFACE ".Interface.Network",
- network_property, NULL);
+ network_property, network);
+}
+
+static void interface_network_removed(DBusMessageIter *iter, void *user_data)
+{
+ struct supplicant_interface *interface = user_data;
+ struct supplicant_network *network;
+ const char *path = NULL;
+
+ dbus_message_iter_get_basic(iter, &path);
+ if (path == NULL)
+ return;
+
+ network = g_hash_table_lookup(interface->net_mapping, path);
+ if (network == NULL)
+ return;
+
+ g_hash_table_remove(interface->net_mapping, path);
}
static char *create_name(unsigned char *ssid, int ssid_len)
return name;
}
-static void add_bss_to_network(struct supplicant_bss *bss)
+static char *create_group(struct supplicant_bss *bss)
{
- struct supplicant_interface *interface = bss->interface;
- struct supplicant_network *network;
GString *str;
- char *group;
unsigned int i;
+ const char *mode, *security;
str = g_string_sized_new((bss->ssid_len * 2) + 24);
if (str == NULL)
- return;
+ return NULL;
if (bss->ssid_len > 0 && bss->ssid[0] != '\0') {
for (i = 0; i < bss->ssid_len; i++)
} else
g_string_append_printf(str, "hidden");
- group = g_string_free(str, FALSE);
+ mode = mode2string(bss->mode);
+ if (mode != NULL)
+ g_string_append_printf(str, "_%s", mode);
- network = g_hash_table_lookup(interface->network_table, group);
- if (network != NULL) {
- g_free(group);
- goto done;
- }
+ security = security2string(bss->security);
+ if (security != NULL)
+ g_string_append_printf(str, "_%s", security);
- network = g_try_new0(struct supplicant_network, 1);
- if (network == NULL) {
- g_free(group);
- return;
+ return g_string_free(str, FALSE);
+}
+
+static void add_bss_to_network(struct supplicant_bss *bss)
+{
+ struct supplicant_interface *interface = bss->interface;
+ struct supplicant_network *network;
+ char *group;
+
+ group = create_group(bss);
+ if (group == NULL)
+ return;
+
+ network = g_hash_table_lookup(interface->network_table, group);
+ if (network != NULL) {
+ g_free(group);
+ goto done;
+ }
+
+ network = g_try_new0(struct supplicant_network, 1);
+ if (network == NULL) {
+ g_free(group);
+ return;
}
network->group = group;
network->name = create_name(bss->ssid, bss->ssid_len);
+ network->mode = bss->mode;
network->bss_table = g_hash_table_new_full(g_str_hash, g_str_equal,
NULL, remove_bss);
+ network->config_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, g_free);
+
g_hash_table_replace(interface->network_table,
network->group, network);
done:
g_hash_table_replace(interface->bss_mapping, bss->path, network);
g_hash_table_replace(network->bss_table, bss->path, bss);
+
+ g_hash_table_replace(bss_mapping, bss->path, interface);
+}
+
+static unsigned char wifi_oui[3] = { 0x00, 0x50, 0xf2 };
+static unsigned char ieee80211_oui[3] = { 0x00, 0x0f, 0xac };
+
+static void extract_rsn(struct supplicant_bss *bss,
+ const unsigned char *buf, int len)
+{
+ uint16_t count;
+ int i;
+
+ /* Version */
+ if (len < 2)
+ return;
+
+ buf += 2;
+ len -= 2;
+
+ /* Group cipher */
+ if (len < 4)
+ return;
+
+ buf += 4;
+ len -= 4;
+
+ /* Pairwise cipher */
+ if (len < 2)
+ return;
+
+ count = buf[0] | (buf[1] << 8);
+ if (2 + (count * 4) > len)
+ return;
+
+ buf += 2 + (count * 4);
+ len -= 2 + (count * 4);
+
+ /* Authentication */
+ if (len < 2)
+ return;
+
+ count = buf[0] | (buf[1] << 8);
+ if (2 + (count * 4) > len)
+ return;
+
+ for (i = 0; i < count; i++) {
+ const unsigned char *ptr = buf + 2 + (i * 4);
+
+ if (memcmp(ptr, wifi_oui, 3) == 0) {
+ switch (ptr[3]) {
+ case 1:
+ bss->ieee8021x = TRUE;
+ break;
+ case 2:
+ bss->psk = TRUE;
+ break;
+ }
+ } else if (memcmp(ptr, ieee80211_oui, 3) == 0) {
+ switch (ptr[3]) {
+ case 1:
+ bss->ieee8021x = TRUE;
+ break;
+ case 2:
+ bss->psk = TRUE;
+ break;
+ }
+ }
+ }
+
+ buf += 2 + (count * 4);
+ len -= 2 + (count * 4);
+}
+
+static void bss_rates(DBusMessageIter *iter, void *user_data)
+{
+ struct supplicant_bss *bss = user_data;
+ dbus_uint32_t rate = 0;
+
+ dbus_message_iter_get_basic(iter, &rate);
+ if (rate == 0)
+ return;
+
+ if (rate > bss->maxrate)
+ bss->maxrate = rate;
}
static void bss_property(const char *key, DBusMessageIter *iter,
return;
if (key == NULL) {
+ if (bss->ieee8021x == TRUE)
+ bss->security = SUPPLICANT_SECURITY_IEEE8021X;
+ else if (bss->psk == TRUE)
+ bss->security = SUPPLICANT_SECURITY_PSK;
+ else if (bss->privacy == TRUE)
+ bss->security = SUPPLICANT_SECURITY_WEP;
+ else
+ bss->security = SUPPLICANT_SECURITY_NONE;
+
add_bss_to_network(bss);
return;
}
bss->ssid_len = 0;
}
} else if (g_strcmp0(key, "Capabilities") == 0) {
- unsigned char capabilities = 0x00;
+ dbus_uint16_t capabilities = 0x0000;
dbus_message_iter_get_basic(iter, &capabilities);
+
+ if (capabilities & IEEE80211_CAP_ESS)
+ bss->mode = SUPPLICANT_MODE_INFRA;
+ else if (capabilities & IEEE80211_CAP_IBSS)
+ bss->mode = SUPPLICANT_MODE_IBSS;
+
+ if (capabilities & IEEE80211_CAP_PRIVACY)
+ bss->privacy = TRUE;
+ } else if (g_strcmp0(key, "Mode") == 0) {
+ const char *mode = NULL;
+
+ dbus_message_iter_get_basic(iter, &mode);
+ bss->mode = string2mode(mode);
} else if (g_strcmp0(key, "Frequency") == 0) {
- dbus_int32_t frequency = 0;
+ dbus_uint16_t frequency = 0;
dbus_message_iter_get_basic(iter, &frequency);
bss->frequency = frequency;
+ } else if (g_strcmp0(key, "Signal") == 0) {
+ dbus_int16_t signal = 0;
+
+ dbus_message_iter_get_basic(iter, &signal);
} else if (g_strcmp0(key, "Level") == 0) {
dbus_int32_t level = 0;
dbus_message_iter_get_basic(iter, &level);
+ } else if (g_strcmp0(key, "Rates") == 0) {
+ supplicant_dbus_array_foreach(iter, bss_rates, bss);
} else if (g_strcmp0(key, "MaxRate") == 0) {
- dbus_int32_t maxrate = 0;
+ dbus_uint32_t maxrate = 0;
dbus_message_iter_get_basic(iter, &maxrate);
+ if (maxrate != 0)
+ bss->maxrate =maxrate;
+ } else if (g_strcmp0(key, "Privacy") == 0) {
+ dbus_bool_t privacy = FALSE;
+
+ dbus_message_iter_get_basic(iter, &privacy);
+ bss->privacy = privacy;
} else if (g_strcmp0(key, "RSNIE") == 0) {
DBusMessageIter array;
unsigned char *ie;
dbus_message_iter_recurse(iter, &array);
dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
+
+ if (ie_len > 2)
+ extract_rsn(bss, ie + 2, ie_len - 2);
} else if (g_strcmp0(key, "WPAIE") == 0) {
DBusMessageIter array;
unsigned char *ie;
dbus_message_iter_recurse(iter, &array);
dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
+
+ if (ie_len > 6)
+ extract_rsn(bss, ie + 6, ie_len - 6);
} else if (g_strcmp0(key, "WPSIE") == 0) {
DBusMessageIter array;
unsigned char *ie;
if (path == NULL)
return;
+ if (g_strcmp0(path, "/") == 0)
+ return;
+
network = g_hash_table_lookup(interface->bss_mapping, path);
if (network != NULL) {
bss = g_hash_table_lookup(network->bss_table, path);
bss->interface = interface;
bss->path = g_strdup(path);
+ dbus_message_iter_next(iter);
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INVALID) {
+ supplicant_dbus_property_foreach(iter, bss_property, bss);
+ bss_property(NULL, NULL, bss);
+ return;
+ }
+
supplicant_dbus_property_get_all(path,
SUPPLICANT_INTERFACE ".Interface.BSS",
bss_property, bss);
if (network == NULL)
return;
+ g_hash_table_remove(bss_mapping, path);
+
g_hash_table_remove(interface->bss_mapping, path);
g_hash_table_remove(network->bss_table, path);
return;
if (key == NULL) {
- debug_scan_capabilities(interface);
-
- g_hash_table_replace(interface_table,
- interface->path, interface);
-
+ debug_strvalmap("KeyMgmt capability", keymgmt_capa_map,
+ interface->keymgmt_capa);
+ debug_strvalmap("AuthAlg capability", authalg_capa_map,
+ interface->authalg_capa);
+ debug_strvalmap("Protocol capability", proto_capa_map,
+ interface->proto_capa);
+ debug_strvalmap("Pairwise capability", pairwise_capa_map,
+ interface->pairwise_capa);
+ debug_strvalmap("Group capability", group_capa_map,
+ interface->group_capa);
+ debug_strvalmap("Scan capability", scan_capa_map,
+ interface->scan_capa);
+ debug_strvalmap("Mode capability", mode_capa_map,
+ interface->mode_capa);
+
+ interface->ready = TRUE;
callback_interface_added(interface);
return;
}
dbus_message_iter_get_basic(iter, &str);
if (str != NULL)
interface->state = string2state(str);
+
+ DBG("state %s (%d)", str, interface->state);
} else if (g_strcmp0(key, "Scanning") == 0) {
dbus_bool_t scanning = FALSE;
dbus_message_iter_get_basic(iter, &scanning);
interface->scanning = scanning;
+
+ if (interface->ready == TRUE) {
+ if (interface->scanning == TRUE)
+ callback_scan_started(interface);
+ else
+ callback_scan_finished(interface);
+ }
} else if (g_strcmp0(key, "ApScan") == 0) {
- int apscan;
+ int apscan = 1;
dbus_message_iter_get_basic(iter, &apscan);
interface->apscan = apscan;
key, dbus_message_iter_get_arg_type(iter));
}
-static void interface_path(DBusMessageIter *iter, void *user_data)
+static struct supplicant_interface *interface_alloc(const char *path)
+{
+ struct supplicant_interface *interface;
+
+ interface = g_try_new0(struct supplicant_interface, 1);
+ if (interface == NULL)
+ return NULL;
+
+ interface->path = g_strdup(path);
+
+ interface->network_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, remove_network);
+
+ interface->net_mapping = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, NULL);
+ interface->bss_mapping = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, NULL);
+
+ g_hash_table_replace(interface_table, interface->path, interface);
+
+ return interface;
+}
+
+static void interface_added(DBusMessageIter *iter, void *user_data)
{
struct supplicant_interface *interface;
const char *path = NULL;
if (path == NULL)
return;
+ if (g_strcmp0(path, "/") == 0)
+ return;
+
interface = g_hash_table_lookup(interface_table, path);
if (interface != NULL)
return;
- interface = g_try_new0(struct supplicant_interface, 1);
+ interface = interface_alloc(path);
if (interface == NULL)
return;
- interface->path = g_strdup(path);
-
- interface->network_table = g_hash_table_new_full(g_str_hash, g_str_equal,
- NULL, remove_network);
-
- interface->bss_mapping = g_hash_table_new_full(g_str_hash, g_str_equal,
- NULL, NULL);
+ dbus_message_iter_next(iter);
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INVALID) {
+ supplicant_dbus_property_foreach(iter, interface_property,
+ interface);
+ interface_property(NULL, NULL, interface);
+ return;
+ }
supplicant_dbus_property_get_all(path,
SUPPLICANT_INTERFACE ".Interface",
interface_property, interface);
}
+static void interface_removed(DBusMessageIter *iter, void *user_data)
+{
+ const char *path = NULL;
+
+ dbus_message_iter_get_basic(iter, &path);
+ if (path == NULL)
+ return;
+
+ g_hash_table_remove(interface_table, path);
+}
+
static void eap_method(DBusMessageIter *iter, void *user_data)
{
const char *str = NULL;
static void service_property(const char *key, DBusMessageIter *iter,
void *user_data)
{
- if (key == NULL)
+ if (key == NULL) {
+ callback_system_ready();
+ return;
+ }
+
+ if (g_strcmp0(key, "DebugLevel") == 0) {
+ const char *str = NULL;
+ int i;
+
+ dbus_message_iter_get_basic(iter, &str);
+ for (i = 0; debug_strings[i] != NULL; i++)
+ if (g_strcmp0(debug_strings[i], str) == 0) {
+ debug_level = i;
+ break;
+ }
+ DBG("Debug level %d", debug_level);
+ } else if (g_strcmp0(key, "DebugTimestamp") == 0) {
+ dbus_message_iter_get_basic(iter, &debug_timestamp);
+ DBG("Debug timestamp %u", debug_timestamp);
+ } else if (g_strcmp0(key, "DebugShowKeys") == 0) {
+ dbus_message_iter_get_basic(iter, &debug_showkeys);
+ DBG("Debug show keys %u", debug_showkeys);
+ } else if (g_strcmp0(key, "Interfaces") == 0) {
+ supplicant_dbus_array_foreach(iter, interface_added, NULL);
+ } else if (g_strcmp0(key, "EapMethods") == 0) {
+ supplicant_dbus_array_foreach(iter, eap_method, NULL);
+ debug_strvalmap("EAP method", eap_method_map, eap_methods);
+ } else
+ DBG("key %s type %c",
+ key, dbus_message_iter_get_arg_type(iter));
+}
+
+static void supplicant_bootstrap(void)
+{
+ supplicant_dbus_property_get_all(SUPPLICANT_PATH,
+ SUPPLICANT_INTERFACE,
+ service_property, NULL);
+}
+
+static void signal_name_owner_changed(const char *path, DBusMessageIter *iter)
+{
+ const char *name = NULL, *old = NULL, *new = NULL;
+
+ if (g_strcmp0(path, DBUS_PATH_DBUS) != 0)
+ return;
+
+ dbus_message_iter_get_basic(iter, &name);
+ if (name == NULL)
+ return;
+
+ if (g_strcmp0(name, SUPPLICANT_SERVICE) != 0)
+ return;
+
+ dbus_message_iter_next(iter);
+ dbus_message_iter_get_basic(iter, &old);
+ dbus_message_iter_next(iter);
+ dbus_message_iter_get_basic(iter, &new);
+
+ if (old == NULL || new == NULL)
return;
- if (g_strcmp0(key, "Interfaces") == 0)
- supplicant_dbus_array_foreach(iter, interface_path, user_data);
- else if (g_strcmp0(key, "EapMethods") == 0) {
- supplicant_dbus_array_foreach(iter, eap_method, user_data);
- debug_eap_methods();
- } else if (g_strcmp0(key, "DebugParams") == 0) {
+ if (strlen(old) > 0 && strlen(new) == 0) {
+ system_available = FALSE;
+ g_hash_table_remove_all(bss_mapping);
+ g_hash_table_remove_all(interface_table);
+ callback_system_killed();
+ }
+
+ if (strlen(new) > 0 && strlen(old) == 0) {
+ system_available = TRUE;
+ supplicant_bootstrap();
}
}
+static void signal_properties_changed(const char *path, DBusMessageIter *iter)
+{
+ if (g_strcmp0(path, SUPPLICANT_PATH) != 0)
+ return;
+
+ supplicant_dbus_property_foreach(iter, service_property, NULL);
+}
+
static void signal_interface_added(const char *path, DBusMessageIter *iter)
{
- interface_path(iter, NULL);
+ if (g_strcmp0(path, SUPPLICANT_PATH) == 0)
+ interface_added(iter, NULL);
}
static void signal_interface_removed(const char *path, DBusMessageIter *iter)
{
- DBG("path %s", path);
+ if (g_strcmp0(path, SUPPLICANT_PATH) == 0)
+ interface_removed(iter, NULL);
+}
+
+static void signal_interface_changed(const char *path, DBusMessageIter *iter)
+{
+ struct supplicant_interface *interface;
+
+ interface = g_hash_table_lookup(interface_table, path);
+ if (interface == NULL)
+ return;
+
+ supplicant_dbus_property_foreach(iter, interface_property, interface);
+}
+
+static void signal_scan_done(const char *path, DBusMessageIter *iter)
+{
+ struct supplicant_interface *interface;
+ dbus_bool_t success = FALSE;
+
+ interface = g_hash_table_lookup(interface_table, path);
+ if (interface == NULL)
+ return;
+
+ dbus_message_iter_get_basic(iter, &success);
+
+ if (interface->scan_callback != NULL) {
+ int result = 0;
+
+ if (success == FALSE)
+ result = -EIO;
+
+ interface->scan_callback(result, interface->scan_data);
+ }
+
+ interface->scan_callback = NULL;
+ interface->scan_data = NULL;
}
static void signal_bss_added(const char *path, DBusMessageIter *iter)
interface_bss_removed(iter, interface);
}
+static void signal_network_added(const char *path, DBusMessageIter *iter)
+{
+ struct supplicant_interface *interface;
+
+ interface = g_hash_table_lookup(interface_table, path);
+ if (interface == NULL)
+ return;
+
+ interface_network_added(iter, interface);
+}
+
+static void signal_network_removed(const char *path, DBusMessageIter *iter)
+{
+ struct supplicant_interface *interface;
+
+ interface = g_hash_table_lookup(interface_table, path);
+ if (interface == NULL)
+ return;
+
+ interface_network_removed(iter, interface);
+}
+
+static void signal_bss_changed(const char *path, DBusMessageIter *iter)
+{
+ struct supplicant_interface *interface;
+ struct supplicant_network *network;
+ struct supplicant_bss *bss;
+
+ interface = g_hash_table_lookup(bss_mapping, path);
+ if (interface == NULL)
+ return;
+
+ network = g_hash_table_lookup(interface->bss_mapping, path);
+ if (network == NULL)
+ return;
+
+ bss = g_hash_table_lookup(network->bss_table, path);
+ if (bss == NULL)
+ return;
+
+ supplicant_dbus_property_foreach(iter, bss_property, bss);
+}
+
static struct {
const char *interface;
const char *member;
void (*function) (const char *path, DBusMessageIter *iter);
} signal_map[] = {
- { SUPPLICANT_INTERFACE, "InterfaceAdded", signal_interface_added },
- { SUPPLICANT_INTERFACE, "InterfaceRemoved", signal_interface_removed },
- { SUPPLICANT_INTERFACE ".Interface", "BSSAdded", signal_bss_added },
- { SUPPLICANT_INTERFACE ".Interface", "BSSRemoved", signal_bss_removed },
+ { DBUS_INTERFACE_DBUS, "NameOwnerChanged", signal_name_owner_changed },
+
+ { SUPPLICANT_INTERFACE, "PropertiesChanged", signal_properties_changed },
+ { SUPPLICANT_INTERFACE, "InterfaceAdded", signal_interface_added },
+ { SUPPLICANT_INTERFACE, "InterfaceCreated", signal_interface_added },
+ { SUPPLICANT_INTERFACE, "InterfaceRemoved", signal_interface_removed },
+
+ { SUPPLICANT_INTERFACE ".Interface", "PropertiesChanged", signal_interface_changed },
+ { SUPPLICANT_INTERFACE ".Interface", "ScanDone", signal_scan_done },
+ { SUPPLICANT_INTERFACE ".Interface", "BSSAdded", signal_bss_added },
+ { SUPPLICANT_INTERFACE ".Interface", "BSSRemoved", signal_bss_removed },
+ { SUPPLICANT_INTERFACE ".Interface", "NetworkAdded", signal_network_added },
+ { SUPPLICANT_INTERFACE ".Interface", "NetworkRemoved", signal_network_removed },
+
+ { SUPPLICANT_INTERFACE ".Interface.BSS", "PropertiesChanged", signal_bss_changed },
+
{ }
};
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
+static const char *supplicant_rule0 = "type=signal,"
+ "path=" DBUS_PATH_DBUS ","
+ "sender=" DBUS_SERVICE_DBUS ","
+ "interface=" DBUS_INTERFACE_DBUS ","
+ "member=NameOwnerChanged,"
+ "arg0=" SUPPLICANT_SERVICE;
static const char *supplicant_rule1 = "type=signal,"
"interface=" SUPPLICANT_INTERFACE;
static const char *supplicant_rule2 = "type=signal,"
interface_table = g_hash_table_new_full(g_str_hash, g_str_equal,
NULL, remove_interface);
+ bss_mapping = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, NULL);
+
supplicant_dbus_setup(connection);
+ dbus_bus_add_match(connection, supplicant_rule0, NULL);
dbus_bus_add_match(connection, supplicant_rule1, NULL);
dbus_bus_add_match(connection, supplicant_rule2, NULL);
dbus_bus_add_match(connection, supplicant_rule3, NULL);
dbus_bus_add_match(connection, supplicant_rule6, NULL);
dbus_connection_flush(connection);
- supplicant_dbus_property_get_all(SUPPLICANT_PATH,
- SUPPLICANT_INTERFACE,
- service_property, NULL);
+ if (dbus_bus_name_has_owner(connection,
+ SUPPLICANT_SERVICE, NULL) == TRUE) {
+ system_available = TRUE;
+ supplicant_bootstrap();
+ }
return 0;
}
dbus_bus_remove_match(connection, supplicant_rule3, NULL);
dbus_bus_remove_match(connection, supplicant_rule2, NULL);
dbus_bus_remove_match(connection, supplicant_rule1, NULL);
+ dbus_bus_remove_match(connection, supplicant_rule0, NULL);
dbus_connection_flush(connection);
dbus_connection_remove_filter(connection,
supplicant_filter, NULL);
}
+ if (bss_mapping != NULL) {
+ g_hash_table_destroy(bss_mapping);
+ bss_mapping = NULL;
+ }
+
if (interface_table != NULL) {
g_hash_table_destroy(interface_table);
interface_table = NULL;
}
+ if (system_available == TRUE)
+ callback_system_killed();
+
if (connection != NULL) {
dbus_connection_unref(connection);
connection = NULL;
callbacks_pointer = NULL;
eap_methods = 0;
}
+
+static void debug_level_result(const char *error,
+ DBusMessageIter *iter, void *user_data)
+{
+ if (error != NULL)
+ DBG("debug level failure: %s", error);
+}
+
+static void debug_level_params(DBusMessageIter *iter, void *user_data)
+{
+ guint level = GPOINTER_TO_UINT(user_data);
+ const char *str;
+
+ if (level > 4)
+ level = 4;
+
+ str = debug_strings[level];
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+}
+
+void supplicant_set_debug_level(unsigned int level)
+{
+ if (system_available == FALSE)
+ return;
+
+ supplicant_dbus_property_set(SUPPLICANT_PATH, SUPPLICANT_INTERFACE,
+ "DebugLevel", DBUS_TYPE_STRING_AS_STRING,
+ debug_level_params, debug_level_result,
+ GUINT_TO_POINTER(level));
+}
+
+struct interface_create_data {
+ const char *ifname;
+ const char *driver;
+ struct supplicant_interface *interface;
+ supplicant_interface_create_callback callback;
+ void *user_data;
+};
+
+static void interface_create_property(const char *key, DBusMessageIter *iter,
+ void *user_data)
+{
+ struct interface_create_data *data = user_data;
+ struct supplicant_interface *interface = data->interface;
+
+ if (key == NULL) {
+ if (data->callback != NULL)
+ data->callback(0, data->interface, data->user_data);
+
+ dbus_free(data);
+ }
+
+ interface_property(key, iter, interface);
+}
+
+static void interface_create_result(const char *error,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct interface_create_data *data = user_data;
+ const char *path = NULL;
+ int err;
+
+ if (error != NULL) {
+ err = -EIO;
+ goto done;
+ }
+
+ dbus_message_iter_get_basic(iter, &path);
+ if (path == NULL) {
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (system_available == FALSE) {
+ err = -EFAULT;
+ goto done;
+ }
+
+ data->interface = g_hash_table_lookup(interface_table, path);
+ if (data->interface == NULL) {
+ data->interface = interface_alloc(path);
+ if (data->interface == NULL) {
+ err = -ENOMEM;
+ goto done;
+ }
+ }
+
+ err = supplicant_dbus_property_get_all(path,
+ SUPPLICANT_INTERFACE ".Interface",
+ interface_create_property, data);
+ if (err == 0)
+ return;
+
+done:
+ if (data->callback != NULL)
+ data->callback(err, NULL, data->user_data);
+
+ dbus_free(data);
+}
+
+static void interface_create_params(DBusMessageIter *iter, void *user_data)
+{
+ struct interface_create_data *data = user_data;
+ DBusMessageIter dict;
+
+ supplicant_dbus_dict_open(iter, &dict);
+
+ supplicant_dbus_dict_append_basic(&dict, "Ifname",
+ DBUS_TYPE_STRING, &data->ifname);
+
+ if (data->driver != NULL)
+ supplicant_dbus_dict_append_basic(&dict, "Driver",
+ DBUS_TYPE_STRING, &data->driver);
+
+ supplicant_dbus_dict_close(iter, &dict);
+}
+
+static void interface_get_result(const char *error,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct interface_create_data *data = user_data;
+ struct supplicant_interface *interface;
+ const char *path = NULL;
+ int err;
+
+ if (error != NULL) {
+ err = -EIO;
+ goto create;
+ }
+
+ dbus_message_iter_get_basic(iter, &path);
+ if (path == NULL) {
+ err = -EINVAL;
+ goto done;
+ }
+
+ interface = g_hash_table_lookup(interface_table, path);
+ if (interface == NULL) {
+ err = -ENOENT;
+ goto done;
+ }
+
+ if (data->callback != NULL)
+ data->callback(0, interface, data->user_data);
+
+ dbus_free(data);
+
+ return;
+
+create:
+ if (system_available == FALSE) {
+ err = -EFAULT;
+ goto done;
+ }
+
+ err = supplicant_dbus_method_call(SUPPLICANT_PATH,
+ SUPPLICANT_INTERFACE,
+ "CreateInterface",
+ interface_create_params,
+ interface_create_result, data);
+ if (err == 0)
+ return;
+
+done:
+ if (data->callback != NULL)
+ data->callback(err, NULL, data->user_data);
+
+ dbus_free(data);
+}
+
+static void interface_get_params(DBusMessageIter *iter, void *user_data)
+{
+ struct interface_create_data *data = user_data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &data->ifname);
+}
+
+int supplicant_interface_create(const char *ifname, const char *driver,
+ supplicant_interface_create_callback callback,
+ void *user_data)
+{
+ struct interface_create_data *data;
+
+ if (ifname == NULL)
+ return -EINVAL;
+
+ if (system_available == FALSE)
+ return -EFAULT;
+
+ data = dbus_malloc0(sizeof(*data));
+ if (data == NULL)
+ return -ENOMEM;
+
+ data->ifname = ifname;
+ data->driver = driver;
+ data->callback = callback;
+ data->user_data = user_data;
+
+ return supplicant_dbus_method_call(SUPPLICANT_PATH,
+ SUPPLICANT_INTERFACE,
+ "GetInterface",
+ interface_get_params,
+ interface_get_result, data);
+}
+
+int supplicant_interface_remove(struct supplicant_interface *interface,
+ supplicant_interface_remove_callback callback,
+ void *user_data)
+{
+ if (interface == NULL)
+ return -EINVAL;
+
+ if (system_available == FALSE)
+ return -EFAULT;
+
+ return 0;
+}
+
+struct interface_scan_data {
+ struct supplicant_interface *interface;
+ supplicant_interface_scan_callback callback;
+ void *user_data;
+};
+
+static void interface_scan_result(const char *error,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct interface_scan_data *data = user_data;
+
+ if (error != NULL) {
+ if (data->callback != NULL)
+ data->callback(-EIO, data->user_data);
+ } else {
+ data->interface->scan_callback = data->callback;
+ data->interface->scan_data = data->user_data;
+ }
+
+ dbus_free(data);
+}
+
+static void interface_scan_params(DBusMessageIter *iter, void *user_data)
+{
+ DBusMessageIter dict;
+ const char *type = "passive";
+
+ supplicant_dbus_dict_open(iter, &dict);
+
+ supplicant_dbus_dict_append_basic(&dict, "Type",
+ DBUS_TYPE_STRING, &type);
+
+ supplicant_dbus_dict_close(iter, &dict);
+}
+
+int supplicant_interface_scan(struct supplicant_interface *interface,
+ supplicant_interface_scan_callback callback,
+ void *user_data)
+{
+ struct interface_scan_data *data;
+
+ if (interface == NULL)
+ return -EINVAL;
+
+ if (system_available == FALSE)
+ return -EFAULT;
+
+ if (interface->scanning == TRUE)
+ return -EALREADY;
+
+ data = dbus_malloc0(sizeof(*data));
+ if (data == NULL)
+ return -ENOMEM;
+
+ data->interface = interface;
+ data->callback = callback;
+ data->user_data = user_data;
+
+ return supplicant_dbus_method_call(interface->path,
+ SUPPLICANT_INTERFACE ".Interface", "Scan",
+ interface_scan_params, interface_scan_result, data);
+}
+
+struct interface_disconnect_data {
+ supplicant_interface_disconnect_callback callback;
+ void *user_data;
+};
+
+static void interface_disconnect_result(const char *error,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct interface_disconnect_data *data = user_data;
+ int result = 0;
+
+ if (error != NULL)
+ result = -EIO;
+
+ if (data->callback != NULL)
+ data->callback(result, data->user_data);
+
+ dbus_free(data);
+}
+
+int supplicant_interface_disconnect(struct supplicant_interface *interface,
+ supplicant_interface_disconnect_callback callback,
+ void *user_data)
+{
+ struct interface_disconnect_data *data;
+
+ if (interface == NULL)
+ return -EINVAL;
+
+ if (system_available == FALSE)
+ return -EFAULT;
+
+ data = dbus_malloc0(sizeof(*data));
+ if (data == NULL)
+ return -ENOMEM;
+
+ data->callback = callback;
+ data->user_data = user_data;
+
+ return supplicant_dbus_method_call(interface->path,
+ SUPPLICANT_INTERFACE ".Interface", "Disconnect",
+ NULL, interface_disconnect_result, data);
+}