dnsproxy: Only one copy of the relevant buffers will be made to a TCP request
[framework/connectivity/connman.git] / tools / supplicant.c
index 25dd75c..1c96823 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2009  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  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
@@ -25,6 +25,7 @@
 
 #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  },
@@ -57,40 +75,96 @@ static struct {
        { "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 {
@@ -99,9 +173,60 @@ 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)
@@ -131,6 +256,35 @@ static enum supplicant_state string2state(const char *state)
        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)
@@ -153,6 +307,28 @@ static void callback_interface_removed(struct supplicant_interface *interface)
        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)
@@ -179,11 +355,12 @@ static void remove_interface(gpointer data)
 {
        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);
@@ -195,8 +372,12 @@ static void remove_network(gpointer data)
 {
        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);
@@ -210,24 +391,100 @@ static void remove_bss(gpointer data)
        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)
@@ -247,6 +504,23 @@ 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)
 {
@@ -255,9 +529,27 @@ static void interface_capability(const char *key, DBusMessageIter *iter,
        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));
@@ -271,6 +563,14 @@ const char *supplicant_interface_get_ifname(struct supplicant_interface *interfa
        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)
@@ -287,34 +587,151 @@ const char *supplicant_network_get_name(struct supplicant_network *network)
        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)
@@ -340,17 +757,15 @@ 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++)
@@ -358,26 +773,49 @@ static void add_bss_to_network(struct supplicant_bss *bss)
        } 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 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);
 
@@ -386,6 +824,89 @@ static void add_bss_to_network(struct supplicant_bss *bss)
 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;
+                       }
+               }
+       }
+
+}
+
+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,
@@ -397,6 +918,15 @@ 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;
        }
@@ -427,22 +957,48 @@ static void bss_property(const char *key, DBusMessageIter *iter,
                        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;
@@ -450,6 +1006,9 @@ static void bss_property(const char *key, DBusMessageIter *iter,
 
                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;
@@ -457,6 +1016,9 @@ static void bss_property(const char *key, DBusMessageIter *iter,
 
                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;
@@ -480,6 +1042,9 @@ static void interface_bss_added(DBusMessageIter *iter, void *user_data)
        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);
@@ -494,6 +1059,13 @@ static void interface_bss_added(DBusMessageIter *iter, void *user_data)
        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);
@@ -513,6 +1085,8 @@ static void interface_bss_removed(DBusMessageIter *iter, void *user_data)
        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);
 
@@ -529,11 +1103,22 @@ static void interface_property(const char *key, DBusMessageIter *iter,
                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;
        }
@@ -547,13 +1132,22 @@ static void interface_property(const char *key, DBusMessageIter *iter,
                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;
@@ -591,6 +1185,29 @@ static void interface_property(const char *key, DBusMessageIter *iter,
                                key, dbus_message_iter_get_arg_type(iter));
 }
 
+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;
@@ -600,21 +1217,24 @@ static void interface_added(DBusMessageIter *iter, void *user_data)
        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",
@@ -651,16 +1271,36 @@ static void eap_method(DBusMessageIter *iter, void *user_data)
 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, "Interfaces") == 0)
-               supplicant_dbus_array_foreach(iter, interface_added, 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 (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)
@@ -692,11 +1332,25 @@ static void signal_name_owner_changed(const char *path, DBusMessageIter *iter)
        if (old == NULL || new == NULL)
                return;
 
-       if (strlen(old) > 0 && strlen(new) == 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)
+       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)
@@ -711,6 +1365,41 @@ static void signal_interface_removed(const char *path, DBusMessageIter *iter)
                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)
 {
        struct supplicant_interface *interface;
@@ -733,17 +1422,70 @@ static void signal_bss_removed(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[] = {
-       { DBUS_INTERFACE_DBUS,  "NameOwnerChanged", signal_name_owner_changed },
-       { SUPPLICANT_INTERFACE, "InterfaceAdded",   signal_interface_added    },
-       { SUPPLICANT_INTERFACE, "InterfaceCreated", 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   },
+
        { }
 };
 
@@ -815,6 +1557,9 @@ int supplicant_register(const struct supplicant_callbacks *callbacks)
        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);
@@ -826,7 +1571,11 @@ int supplicant_register(const struct supplicant_callbacks *callbacks)
        dbus_bus_add_match(connection, supplicant_rule6, NULL);
        dbus_connection_flush(connection);
 
-       supplicant_bootstrap();
+       if (dbus_bus_name_has_owner(connection,
+                                       SUPPLICANT_SERVICE, NULL) == TRUE) {
+               system_available = TRUE;
+               supplicant_bootstrap();
+       }
 
        return 0;
 }
@@ -847,11 +1596,19 @@ void supplicant_unregister(const struct supplicant_callbacks *callbacks)
                                                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;
@@ -860,3 +1617,326 @@ void supplicant_unregister(const struct supplicant_callbacks *callbacks)
        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)
+               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);
+}