X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=plugins%2Fsupplicant.c;h=a9c524c27c99ea189ee5c12b3ac22856005d1786;hb=cd07fbeb1af81232dfb452ec7494bdc33051371e;hp=2355333f93be23e56e6753250cc40969f0883dd3;hpb=c7bf3444433ca7b16bf503058b7738432522b095;p=platform%2Fupstream%2Fconnman.git diff --git a/plugins/supplicant.c b/plugins/supplicant.c index 2355333..a9c524c 100644 --- a/plugins/supplicant.c +++ b/plugins/supplicant.c @@ -24,19 +24,25 @@ #endif #include +#include #include #include #include +#include +#include +#include +#include #include #include #define CONNMAN_API_SUBJECT_TO_CHANGE #include +#include +#include #include #include -#include "inet.h" #include "supplicant.h" #define TIMEOUT 5000 @@ -149,12 +155,15 @@ enum supplicant_state { struct supplicant_result { char *path; char *name; - char *addr; + unsigned char *addr; + unsigned int addr_len; unsigned char *ssid; unsigned int ssid_len; dbus_uint16_t capabilities; gboolean adhoc; gboolean has_wep; + gboolean has_psk; + gboolean has_8021x; gboolean has_wpa; gboolean has_rsn; gboolean has_wps; @@ -168,14 +177,20 @@ struct supplicant_result { struct supplicant_task { int ifindex; char *ifname; + gboolean mac80211; struct connman_device *device; struct connman_network *network; + struct connman_network *pending_network; char *path; char *netpath; gboolean created; enum supplicant_state state; - gboolean noscan; + gboolean scanning; GSList *scan_results; + DBusPendingCall *scan_call; + DBusPendingCall *result_call; + struct iw_range *range; + gboolean disconnecting; }; static GSList *task_list = NULL; @@ -212,13 +227,82 @@ static struct supplicant_task *find_task_by_path(const char *path) for (list = task_list; list; list = list->next) { struct supplicant_task *task = list->data; - if (g_str_equal(task->path, path) == TRUE) + if (g_strcmp0(task->path, path) == 0) return task; } return NULL; } +static int get_range(struct supplicant_task *task) +{ + struct iwreq wrq; + int fd, err; + + fd = socket(PF_INET, SOCK_DGRAM, 0); + if (fd < 0) + return -1; + + memset(&wrq, 0, sizeof(struct iwreq)); + strncpy(wrq.ifr_name, task->ifname, IFNAMSIZ); + wrq.u.data.pointer = task->range; + wrq.u.data.length = sizeof(struct iw_range); + + err = ioctl(fd, SIOCGIWRANGE, &wrq); + + close(fd); + + if (err < 0) + task->range->max_qual.updated |= IW_QUAL_ALL_INVALID; + + connman_info("%s {scan} capabilities 0x%02x", task->ifname, + task->range->scan_capa); + + connman_info("%s {quality} flags 0x%02x", task->ifname, + task->range->max_qual.updated); + + return err; +} + +static int get_bssid(struct connman_device *device, + unsigned char *bssid, unsigned int *bssid_len) +{ + struct iwreq wrq; + char *ifname; + int ifindex; + int fd, err; + + ifindex = connman_device_get_index(device); + if (ifindex < 0) + return -EINVAL; + + ifname = connman_inet_ifname(ifindex); + if (ifname == NULL) + return -EINVAL; + + fd = socket(PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + g_free(ifname); + return -EINVAL; + } + + memset(&wrq, 0, sizeof(wrq)); + strncpy(wrq.ifr_name, ifname, IFNAMSIZ); + + err = ioctl(fd, SIOCGIWAP, &wrq); + + g_free(ifname); + close(fd); + + if (err < 0) + return -EIO; + + memcpy(bssid, wrq.u.ap_addr.sa_data, ETH_ALEN); + *bssid_len = ETH_ALEN; + + return 0; +} + static void add_interface_reply(DBusPendingCall *call, void *user_data) { struct supplicant_task *task = user_data; @@ -233,7 +317,7 @@ static void add_interface_reply(DBusPendingCall *call, void *user_data) return; if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) - goto done; + goto failed; dbus_error_init(&error); @@ -244,7 +328,7 @@ static void add_interface_reply(DBusPendingCall *call, void *user_data) dbus_error_free(&error); } else connman_error("Wrong arguments for add interface"); - goto done; + goto failed; } DBG("path %s", path); @@ -254,13 +338,25 @@ static void add_interface_reply(DBusPendingCall *call, void *user_data) connman_device_set_powered(task->device, TRUE); -done: dbus_message_unref(reply); + + return; + +failed: + dbus_message_unref(reply); + + task_list = g_slist_remove(task_list, task); + + connman_device_unref(task->device); + + free_task(task); } static int add_interface(struct supplicant_task *task) { + const char *driver = connman_option_get_string("wifi"); DBusMessage *message; + DBusMessageIter array, dict; DBusPendingCall *call; DBG("task %p", task); @@ -270,8 +366,19 @@ static int add_interface(struct supplicant_task *task) if (message == NULL) return -ENOMEM; - dbus_message_append_args(message, DBUS_TYPE_STRING, &task->ifname, - DBUS_TYPE_INVALID); + dbus_message_set_auto_start(message, FALSE); + + dbus_message_iter_init_append(message, &array); + + dbus_message_iter_append_basic(&array, + DBUS_TYPE_STRING, &task->ifname); + + connman_dbus_dict_open(&array, &dict); + + connman_dbus_dict_append_basic(&dict, "driver", + DBUS_TYPE_STRING, &driver); + + connman_dbus_dict_close(&array, &dict); if (dbus_connection_send_with_reply(connection, message, &call, TIMEOUT) == FALSE) { @@ -346,6 +453,8 @@ static int create_interface(struct supplicant_task *task) if (message == NULL) return -ENOMEM; + dbus_message_set_auto_start(message, FALSE); + dbus_message_append_args(message, DBUS_TYPE_STRING, &task->ifname, DBUS_TYPE_INVALID); @@ -382,7 +491,7 @@ static void remove_interface_reply(DBusPendingCall *call, void *user_data) connman_device_unref(task->device); - inet_ifdown(task->ifindex); + connman_inet_ifdown(task->ifindex); free_task(task); @@ -396,16 +505,20 @@ static int remove_interface(struct supplicant_task *task) DBG("task %p", task); +#if 0 if (task->created == FALSE) { connman_device_set_powered(task->device, FALSE); return 0; } +#endif message = dbus_message_new_method_call(SUPPLICANT_NAME, SUPPLICANT_PATH, SUPPLICANT_INTF, "removeInterface"); if (message == NULL) return -ENOMEM; + dbus_message_set_auto_start(message, FALSE); + dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &task->path, DBUS_TYPE_INVALID); @@ -429,7 +542,6 @@ static int remove_interface(struct supplicant_task *task) return -EINPROGRESS; } -#if 0 static int set_ap_scan(struct supplicant_task *task) { DBusMessage *message, *reply; @@ -443,6 +555,8 @@ static int set_ap_scan(struct supplicant_task *task) if (message == NULL) return -ENOMEM; + dbus_message_set_auto_start(message, FALSE); + dbus_message_append_args(message, DBUS_TYPE_UINT32, &ap_scan, DBUS_TYPE_INVALID); @@ -466,7 +580,6 @@ static int set_ap_scan(struct supplicant_task *task) return 0; } -#endif static int add_network(struct supplicant_task *task) { @@ -484,6 +597,8 @@ static int add_network(struct supplicant_task *task) if (message == NULL) return -ENOMEM; + dbus_message_set_auto_start(message, FALSE); + dbus_error_init(&error); reply = dbus_connection_send_with_reply_and_block(connection, @@ -537,6 +652,8 @@ static int remove_network(struct supplicant_task *task) if (message == NULL) return -ENOMEM; + dbus_message_set_auto_start(message, FALSE); + dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &task->netpath, DBUS_TYPE_INVALID); @@ -579,6 +696,8 @@ static int select_network(struct supplicant_task *task) if (message == NULL) return -ENOMEM; + dbus_message_set_auto_start(message, FALSE); + dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &task->netpath, DBUS_TYPE_INVALID); @@ -603,21 +722,20 @@ static int select_network(struct supplicant_task *task) return 0; } -static int enable_network(struct supplicant_task *task) +static int disconnect_network(struct supplicant_task *task) { DBusMessage *message, *reply; DBusError error; DBG("task %p", task); - if (task->netpath == NULL) - return -EINVAL; - - message = dbus_message_new_method_call(SUPPLICANT_NAME, task->netpath, - SUPPLICANT_INTF ".Network", "enable"); + message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path, + SUPPLICANT_INTF ".Interface", "disconnect"); if (message == NULL) return -ENOMEM; + dbus_message_set_auto_start(message, FALSE); + dbus_error_init(&error); reply = dbus_connection_send_with_reply_and_block(connection, @@ -627,7 +745,7 @@ static int enable_network(struct supplicant_task *task) connman_error("%s", error.message); dbus_error_free(&error); } else - connman_error("Failed to enable network"); + connman_error("Failed to disconnect network"); dbus_message_unref(message); return -EIO; } @@ -639,38 +757,114 @@ static int enable_network(struct supplicant_task *task) return 0; } -static int disable_network(struct supplicant_task *task) +static int set_network_tls(struct connman_network *network, + DBusMessageIter *dict) { - DBusMessage *message, *reply; - DBusError error; + const char *private_key, *client_cert, *ca_cert; + const char *private_key_password; - DBG("task %p", task); + /* + * For TLS, we at least need a key, the client cert, + * and a passhprase. + * Server cert is optional. + */ + client_cert = connman_network_get_string(network, + "WiFi.ClientCertFile"); + if (client_cert == NULL) + return -EINVAL; - if (task->netpath == NULL) + private_key = connman_network_get_string(network, + "WiFi.PrivateKeyFile"); + if (private_key == NULL) return -EINVAL; - message = dbus_message_new_method_call(SUPPLICANT_NAME, task->netpath, - SUPPLICANT_INTF ".Network", "disable"); - if (message == NULL) - return -ENOMEM; + private_key_password = connman_network_get_string(network, + "WiFi.PrivateKeyPassphrase"); + if (private_key_password == NULL) + return -EINVAL; - dbus_error_init(&error); + ca_cert = connman_network_get_string(network, "WiFi.CACertFile"); + if (ca_cert) + connman_dbus_dict_append_basic(dict, "ca_cert", + DBUS_TYPE_STRING, &ca_cert); - reply = dbus_connection_send_with_reply_and_block(connection, - message, -1, &error); - if (reply == NULL) { - if (dbus_error_is_set(&error) == TRUE) { - connman_error("%s", error.message); - dbus_error_free(&error); - } else - connman_error("Failed to disable network"); - dbus_message_unref(message); - return -EIO; + DBG("client cert %s private key %s", client_cert, private_key); + + connman_dbus_dict_append_basic(dict, "private_key", + DBUS_TYPE_STRING, &private_key); + connman_dbus_dict_append_basic(dict, "private_key_passwd", + DBUS_TYPE_STRING, + &private_key_password); + connman_dbus_dict_append_basic(dict, "client_cert", + DBUS_TYPE_STRING, &client_cert); + + return 0; +} + +static int set_network_peap(struct connman_network *network, + DBusMessageIter *dict, const char *passphrase) +{ + const char *client_cert, *ca_cert, *phase2; + char *phase2_auth; + + /* + * For PEAP, we at least need the sever cert, a 2nd + * phase authentication and a passhprase. + * Client cert is optional although strongly required + * When setting the client cert, we then need a private + * key as well. + */ + ca_cert = connman_network_get_string(network, "WiFi.CACertFile"); + if (ca_cert == NULL) + return -EINVAL; + + phase2 = connman_network_get_string(network, "WiFi.Phase2"); + if (phase2 == NULL) + return -EINVAL; + + DBG("CA cert %s phase2 auth %s", ca_cert, phase2); + + client_cert = connman_network_get_string(network, + "WiFi.ClientCertFile"); + if (client_cert) { + const char *private_key, *private_key_password; + + private_key = connman_network_get_string(network, + "WiFi.PrivateKeyFile"); + if (private_key == NULL) + return -EINVAL; + + private_key_password = + connman_network_get_string(network, + "WiFi.PrivateKeyPassphrase"); + if (private_key_password == NULL) + return -EINVAL; + + connman_dbus_dict_append_basic(dict, "client_cert", + DBUS_TYPE_STRING, &client_cert); + + connman_dbus_dict_append_basic(dict, "private_key", + DBUS_TYPE_STRING, &private_key); + + connman_dbus_dict_append_basic(dict, "private_key_passwd", + DBUS_TYPE_STRING, + &private_key_password); + + DBG("client cert %s private key %s", client_cert, private_key); } - dbus_message_unref(message); + phase2_auth = g_strdup_printf("\"auth=%s\"", phase2); - dbus_message_unref(reply); + connman_dbus_dict_append_basic(dict, "password", + DBUS_TYPE_STRING, &passphrase); + + connman_dbus_dict_append_basic(dict, "ca_cert", + DBUS_TYPE_STRING, &ca_cert); + + connman_dbus_dict_append_basic(dict, "phase2", + DBUS_TYPE_STRING, &phase2_auth); + + g_free(phase2_auth); return 0; } @@ -683,6 +877,7 @@ static int set_network(struct supplicant_task *task, DBusMessage *message, *reply; DBusMessageIter array, dict; DBusError error; + dbus_uint32_t scan_ssid = 1; DBG("task %p", task); @@ -694,36 +889,108 @@ static int set_network(struct supplicant_task *task, if (message == NULL) return -ENOMEM; + dbus_message_set_auto_start(message, FALSE); + dbus_message_iter_init_append(message, &array); - dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); + connman_dbus_dict_open(&array, &dict); - if (address == NULL) { - dbus_uint32_t scan_ssid = 1; - connman_dbus_dict_append_variant(&dict, "scan_ssid", - DBUS_TYPE_UINT32, &scan_ssid); - } else - connman_dbus_dict_append_variant(&dict, "bssid", - DBUS_TYPE_STRING, &address); + connman_dbus_dict_append_basic(&dict, "scan_ssid", + DBUS_TYPE_UINT32, &scan_ssid); - connman_dbus_dict_append_array(&dict, "ssid", - DBUS_TYPE_BYTE, &network, len); + if (network) + connman_dbus_dict_append_fixed_array(&dict, "ssid", + DBUS_TYPE_BYTE, &network, len); + else if (address) + connman_dbus_dict_append_basic(&dict, "bssid", + DBUS_TYPE_STRING, &address); - if (g_ascii_strcasecmp(security, "wpa") == 0 || + if (g_ascii_strcasecmp(security, "psk") == 0 || + g_ascii_strcasecmp(security, "wpa") == 0 || g_ascii_strcasecmp(security, "rsn") == 0) { const char *key_mgmt = "WPA-PSK"; - connman_dbus_dict_append_variant(&dict, "key_mgmt", + connman_dbus_dict_append_basic(&dict, "key_mgmt", DBUS_TYPE_STRING, &key_mgmt); if (passphrase && strlen(passphrase) > 0) - connman_dbus_dict_append_variant(&dict, "psk", + connman_dbus_dict_append_basic(&dict, "psk", DBUS_TYPE_STRING, &passphrase); + } else if (g_ascii_strcasecmp(security, "ieee8021x") == 0) { + struct connman_network *network = task->network; + const char *key_mgmt = "WPA-EAP", *eap, *identity; + char *eap_value; + + /* + * If our private key password is unset, + * we use the supplied passphrase. That is needed + * for PEAP where 2 passphrases (identity and client + * cert may have to be provided. + */ + if (connman_network_get_string(network, + "WiFi.PrivateKeyPassphrase") == NULL) + connman_network_set_string(network, + "WiFi.PrivateKeyPassphrase", + passphrase); + + eap = connman_network_get_string(network, "WiFi.EAP"); + if (eap == NULL) + goto invalid; + + /* We must have an identity for both PEAP and TLS */ + identity = connman_network_get_string(network, "WiFi.Identity"); + if (identity == NULL) + goto invalid; + + DBG("key_mgmt %s eap %s identity %s", key_mgmt, eap, identity); + + if (g_strcmp0(eap, "tls") == 0) { + int err; + + err = set_network_tls(network, &dict); + if (err < 0) { + dbus_message_unref(message); + return err; + } + } else if (g_strcmp0(eap, "peap") == 0) { + int err; + + err = set_network_peap(network, &dict, passphrase); + if (err < 0) { + dbus_message_unref(message); + return err; + } + } else { + connman_error("Unknown EAP %s", eap); + goto invalid; + } + + /* wpa_supplicant only accepts upper case EAPs */ + eap_value = g_ascii_strup(eap, -1); + + connman_dbus_dict_append_basic(&dict, "key_mgmt", + DBUS_TYPE_STRING, + &key_mgmt); + connman_dbus_dict_append_basic(&dict, "eap", + DBUS_TYPE_STRING, + &eap_value); + connman_dbus_dict_append_basic(&dict, "identity", + DBUS_TYPE_STRING, + &identity); + + g_free(eap_value); + } else if (g_ascii_strcasecmp(security, "wep") == 0) { - const char *key_mgmt = "NONE", *index = "0"; - connman_dbus_dict_append_variant(&dict, "key_mgmt", + const char *key_mgmt = "NONE"; + const char *auth_alg = "OPEN"; + const char *key_index = "0"; + + if (task->mac80211 == TRUE) + auth_alg = "OPEN SHARED"; + + connman_dbus_dict_append_basic(&dict, "auth_alg", + DBUS_TYPE_STRING, &auth_alg); + + connman_dbus_dict_append_basic(&dict, "key_mgmt", DBUS_TYPE_STRING, &key_mgmt); if (passphrase) { @@ -740,24 +1007,25 @@ static int set_network(struct supplicant_task *task, key[i] = (unsigned char) strtol(tmp, NULL, 16); } - connman_dbus_dict_append_array(&dict, + connman_dbus_dict_append_fixed_array(&dict, "wep_key0", DBUS_TYPE_BYTE, &key, size / 2); free(key); } else - connman_dbus_dict_append_variant(&dict, + connman_dbus_dict_append_basic(&dict, "wep_key0", DBUS_TYPE_STRING, &passphrase); - connman_dbus_dict_append_variant(&dict, "wep_tx_keyidx", - DBUS_TYPE_STRING, &index); + + connman_dbus_dict_append_basic(&dict, "wep_tx_keyidx", + DBUS_TYPE_STRING, &key_index); } } else { const char *key_mgmt = "NONE"; - connman_dbus_dict_append_variant(&dict, "key_mgmt", + connman_dbus_dict_append_basic(&dict, "key_mgmt", DBUS_TYPE_STRING, &key_mgmt); } - dbus_message_iter_close_container(&array, &dict); + connman_dbus_dict_close(&array, &dict); dbus_error_init(&error); @@ -778,30 +1046,75 @@ static int set_network(struct supplicant_task *task, dbus_message_unref(reply); return 0; + +invalid: + dbus_message_unref(message); + return -EINVAL; } +static void scan_reply(DBusPendingCall *call, void *user_data) +{ + struct supplicant_task *task = user_data; + DBusMessage *reply; + + DBG("task %p", task); + + task->scan_call = NULL; + + reply = dbus_pending_call_steal_reply(call); + if (reply == NULL) + return; + + if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) { + connman_device_set_scanning(task->device, FALSE); + goto done; + } + + if (task->scanning == TRUE) + connman_device_set_scanning(task->device, TRUE); + +done: + dbus_message_unref(reply); +} + + static int initiate_scan(struct supplicant_task *task) { DBusMessage *message; - DBusPendingCall *call; DBG("task %p", task); + if (task->path == NULL) + return -EINVAL; + + if (task->scan_call != NULL) + return -EALREADY; + message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path, SUPPLICANT_INTF ".Interface", "scan"); if (message == NULL) return -ENOMEM; + dbus_message_set_auto_start(message, FALSE); + if (dbus_connection_send_with_reply(connection, message, - &call, TIMEOUT) == FALSE) { + &task->scan_call, TIMEOUT) == FALSE) { connman_error("Failed to initiate scan"); dbus_message_unref(message); return -EIO; } + if (task->scan_call == NULL) { + connman_error("D-Bus connection not available"); + dbus_message_unref(message); + return -EIO; + } + + dbus_pending_call_set_notify(task->scan_call, scan_reply, task, NULL); + dbus_message_unref(message); - return 0; + return -EINPROGRESS; } static struct { @@ -817,8 +1130,10 @@ static struct { { "2wire" }, { "compaq" }, { "tsunami" }, - { "comcomcom" }, + { "comcomcom", "3com" }, + { "3Com", "3com" }, { "Symbol", "symbol" }, + { "Motorola", "motorola" }, { "Wireless" , "wireless" }, { "WLAN", "wlan" }, { } @@ -838,6 +1153,11 @@ static char *build_group(const char *addr, const char *name, if (str == NULL) return NULL; + if (ssid == NULL) { + g_string_append_printf(str, "hidden_%s", addr); + goto done; + } + for (i = 0; special_ssid[i].name; i++) { if (g_strcmp0(special_ssid[i].name, name) == 0) { if (special_ssid[i].value == NULL) @@ -866,7 +1186,7 @@ static void extract_addr(DBusMessageIter *value, struct supplicant_result *result) { DBusMessageIter array; - struct ether_addr *eth; + struct ether_addr eth; unsigned char *addr; int addr_len; @@ -876,31 +1196,25 @@ static void extract_addr(DBusMessageIter *value, if (addr_len != 6) return; - eth = (void *) addr; - - result->addr = g_try_malloc0(18); + result->addr = g_try_malloc(addr_len); if (result->addr == NULL) return; - snprintf(result->addr, 18, "%02X:%02X:%02X:%02X:%02X:%02X", - eth->ether_addr_octet[0], - eth->ether_addr_octet[1], - eth->ether_addr_octet[2], - eth->ether_addr_octet[3], - eth->ether_addr_octet[4], - eth->ether_addr_octet[5]); + memcpy(result->addr, addr, addr_len); + result->addr_len = addr_len; - result->path = g_try_malloc0(18); + result->path = g_try_malloc0(13); if (result->path == NULL) return; - snprintf(result->path, 18, "%02x%02x%02x%02x%02x%02x", - eth->ether_addr_octet[0], - eth->ether_addr_octet[1], - eth->ether_addr_octet[2], - eth->ether_addr_octet[3], - eth->ether_addr_octet[4], - eth->ether_addr_octet[5]); + memcpy(ð, addr, sizeof(eth)); + snprintf(result->path, 13, "%02x%02x%02x%02x%02x%02x", + eth.ether_addr_octet[0], + eth.ether_addr_octet[1], + eth.ether_addr_octet[2], + eth.ether_addr_octet[3], + eth.ether_addr_octet[4], + eth.ether_addr_octet[5]); } static void extract_ssid(DBusMessageIter *value, @@ -908,7 +1222,7 @@ static void extract_ssid(DBusMessageIter *value, { DBusMessageIter array; unsigned char *ssid; - int ssid_len; + int ssid_len, i; dbus_message_iter_recurse(value, &array); dbus_message_iter_get_fixed_array(&array, &ssid, &ssid_len); @@ -916,6 +1230,9 @@ static void extract_ssid(DBusMessageIter *value, if (ssid_len < 1) return; + if (ssid[0] == '\0') + return; + result->ssid = g_try_malloc(ssid_len); if (result->ssid == NULL) return; @@ -927,7 +1244,82 @@ static void extract_ssid(DBusMessageIter *value, if (result->name == NULL) return; - memcpy(result->name, ssid, ssid_len); + for (i = 0; i < ssid_len; i++) { + if (g_ascii_isprint(ssid[i])) + result->name[i] = ssid[i]; + else + result->name[i] = ' '; + } +} + +static unsigned char wifi_oui[3] = { 0x00, 0x50, 0xf2 }; +static unsigned char ieee80211_oui[3] = { 0x00, 0x0f, 0xac }; + +static void extract_rsn(struct supplicant_result *result, + 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: + result->has_8021x = TRUE; + break; + case 2: + result->has_psk = TRUE; + break; + } + } else if (memcmp(ptr, ieee80211_oui, 3) == 0) { + switch (ptr[3]) { + case 1: + result->has_8021x = TRUE; + break; + case 2: + result->has_psk = TRUE; + break; + } + } + } + + buf += 2 + (count * 4); + len -= 2 + (count * 4); } static void extract_wpaie(DBusMessageIter *value, @@ -940,8 +1332,10 @@ static void extract_wpaie(DBusMessageIter *value, dbus_message_iter_recurse(value, &array); dbus_message_iter_get_fixed_array(&array, &ie, &ie_len); - if (ie_len > 0) + if (ie_len > 6) { result->has_wpa = TRUE; + extract_rsn(result, ie + 6, ie_len - 6); + } } static void extract_rsnie(DBusMessageIter *value, @@ -954,8 +1348,10 @@ static void extract_rsnie(DBusMessageIter *value, dbus_message_iter_recurse(value, &array); dbus_message_iter_get_fixed_array(&array, &ie, &ie_len); - if (ie_len > 0) + if (ie_len > 2) { result->has_rsn = TRUE; + extract_rsn(result, ie + 2, ie_len - 2); + } } static void extract_wpsie(DBusMessageIter *value, @@ -986,12 +1382,24 @@ static void extract_capabilites(DBusMessageIter *value, result->has_wep = TRUE; } -static unsigned char calculate_strength(struct supplicant_result *result) +static unsigned char calculate_strength(struct supplicant_task *task, + struct supplicant_result *result) { - if (result->quality < 0) - return 0; + if (result->quality == -1 || task->range->max_qual.qual == 0) { + unsigned char strength; + + if (result->level > 0) + strength = 100 - result->level; + else + strength = 120 + result->level; + + if (strength > 100) + strength = 100; - return result->quality; + return strength; + } + + return (result->quality * 100) / task->range->max_qual.qual; } static unsigned short calculate_channel(struct supplicant_result *result) @@ -1014,7 +1422,7 @@ static void properties_reply(DBusPendingCall *call, void *user_data) unsigned char strength; unsigned short channel, frequency; const char *mode, *security; - char *group; + char *group = NULL; DBG("task %p", task); @@ -1033,8 +1441,8 @@ static void properties_reply(DBusPendingCall *call, void *user_data) memset(&result, 0, sizeof(result)); result.frequency = -1; result.quality = -1; - result.level = -1; - result.noise = -1; + result.level = 0; + result.noise = 0; dbus_message_iter_init(reply, &array); @@ -1094,6 +1502,12 @@ static void properties_reply(DBusPendingCall *call, void *user_data) dbus_message_iter_next(&dict); } + DBG("capabilties %u frequency %d " + "quality %d noise %d level %d maxrate %d", + result.capabilities, result.frequency, + result.quality, result.noise, + result.level, result.maxrate); + if (result.path == NULL) goto done; @@ -1105,15 +1519,15 @@ static void properties_reply(DBusPendingCall *call, void *user_data) else if (result.frequency == 14) result.frequency = 2484; - strength = calculate_strength(&result); + strength = calculate_strength(task, &result); channel = calculate_channel(&result); frequency = (result.frequency < 0) ? 0 : result.frequency; - if (result.has_rsn == TRUE) - security = "rsn"; - else if (result.has_wpa == TRUE) - security = "wpa"; + if (result.has_8021x == TRUE) + security = "ieee8021x"; + else if (result.has_psk == TRUE) + security = "psk"; else if (result.has_wep == TRUE) security = "wep"; else @@ -1125,6 +1539,13 @@ static void properties_reply(DBusPendingCall *call, void *user_data) result.ssid, result.ssid_len, mode, security); + if (result.has_psk == TRUE) { + if (result.has_rsn == TRUE) + security = "rsn"; + else if (result.has_wpa == TRUE) + security = "wpa"; + } + network = connman_device_get_network(task->device, result.path); if (network == NULL) { int index; @@ -1140,7 +1561,8 @@ static void properties_reply(DBusPendingCall *call, void *user_data) connman_network_set_protocol(network, CONNMAN_NETWORK_PROTOCOL_IP); - connman_network_set_string(network, "Address", result.addr); + connman_network_set_address(network, result.addr, + result.addr_len); if (connman_device_add_network(task->device, network) < 0) { connman_network_unref(network); @@ -1149,7 +1571,7 @@ static void properties_reply(DBusPendingCall *call, void *user_data) } if (result.name != NULL && result.name[0] != '\0') - connman_network_set_string(network, "Name", result.name); + connman_network_set_name(network, result.name); connman_network_set_blob(network, "WiFi.SSID", result.ssid, result.ssid_len); @@ -1161,17 +1583,18 @@ static void properties_reply(DBusPendingCall *call, void *user_data) (result.has_wps == TRUE) ? "WPS" : "no WPS"); connman_network_set_available(network, TRUE); - connman_network_set_uint8(network, "Strength", strength); - connman_network_set_uint16(network, "Frequency", frequency); + connman_network_set_strength(network, strength); + connman_network_set_uint16(network, "Frequency", frequency); connman_network_set_uint16(network, "WiFi.Channel", channel); connman_network_set_string(network, "WiFi.Security", security); - connman_network_set_group(network, group); + if (result.ssid != NULL) + connman_network_set_group(network, group); +done: g_free(group); -done: g_free(result.path); g_free(result.addr); g_free(result.name); @@ -1185,7 +1608,6 @@ done: static void get_properties(struct supplicant_task *task) { DBusMessage *message; - DBusPendingCall *call; char *path; path = g_slist_nth_data(task->scan_results, 0); @@ -1202,28 +1624,35 @@ static void get_properties(struct supplicant_task *task) if (message == NULL) goto noscan; + dbus_message_set_auto_start(message, FALSE); + if (dbus_connection_send_with_reply(connection, message, - &call, TIMEOUT) == FALSE) { + &task->result_call, TIMEOUT) == FALSE) { connman_error("Failed to get network properties"); dbus_message_unref(message); goto noscan; } - if (call == NULL) { + if (task->result_call == NULL) { connman_error("D-Bus connection not available"); dbus_message_unref(message); goto noscan; } - dbus_pending_call_set_notify(call, properties_reply, task, NULL); + dbus_pending_call_set_notify(task->result_call, + properties_reply, task, NULL); dbus_message_unref(message); return; noscan: - if (task->noscan == FALSE) + task->result_call = NULL; + + if (task->scanning == TRUE) { connman_device_set_scanning(task->device, FALSE); + task->scanning = FALSE; + } } static void scan_results_reply(DBusPendingCall *call, void *user_data) @@ -1280,38 +1709,47 @@ done: dbus_message_unref(reply); noscan: - if (task->noscan == FALSE) + task->result_call = NULL; + + if (task->scanning == TRUE) { connman_device_set_scanning(task->device, FALSE); + task->scanning = FALSE; + } } static void scan_results_available(struct supplicant_task *task) { DBusMessage *message; - DBusPendingCall *call; DBG("task %p", task); + if (task->result_call != NULL) + return; + message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path, SUPPLICANT_INTF ".Interface", "scanResults"); if (message == NULL) return; + dbus_message_set_auto_start(message, FALSE); + if (dbus_connection_send_with_reply(connection, message, - &call, TIMEOUT) == FALSE) { + &task->result_call, TIMEOUT) == FALSE) { connman_error("Failed to request scan result"); goto done; } - if (task->noscan == FALSE) - connman_device_set_scanning(task->device, TRUE); - - if (call == NULL) { + if (task->result_call == NULL) { connman_error("D-Bus connection not available"); goto done; } - dbus_pending_call_set_notify(call, scan_results_reply, task, NULL); + if (task->scanning == TRUE) + connman_device_set_scanning(task->device, TRUE); + + dbus_pending_call_set_notify(task->result_call, + scan_results_reply, task, NULL); done: dbus_message_unref(message); @@ -1339,11 +1777,74 @@ static enum supplicant_state string2state(const char *state) return WPA_INVALID; } +static int task_connect(struct supplicant_task *task) +{ + const char *address, *security, *passphrase; + const void *ssid; + unsigned int ssid_len; + int err; + + connman_inet_ifup(task->ifindex); + + address = connman_network_get_string(task->network, "Address"); + security = connman_network_get_string(task->network, "WiFi.Security"); + passphrase = connman_network_get_string(task->network, "WiFi.Passphrase"); + + ssid = connman_network_get_blob(task->network, "WiFi.SSID", &ssid_len); + + DBG("address %s security %s", address, security); + + if (security == NULL && passphrase == NULL) + return -EINVAL; + + if (g_str_equal(security, "none") == FALSE && passphrase == NULL) + return -EINVAL; + + remove_network(task); + + set_ap_scan(task); + + add_network(task); + + err = set_network(task, ssid, ssid_len, address, security, passphrase); + if (err < 0) + return err; + + err = select_network(task); + if (err < 0) + return err; + + return -EINPROGRESS; +} + +static void scanning(struct supplicant_task *task, DBusMessage *msg) +{ + DBusError error; + dbus_bool_t scanning; + + dbus_error_init(&error); + + if (dbus_message_get_args(msg, &error, DBUS_TYPE_BOOLEAN, &scanning, + DBUS_TYPE_INVALID) == FALSE) { + if (dbus_error_is_set(&error) == TRUE) { + connman_error("%s", error.message); + dbus_error_free(&error); + } else + connman_error("Wrong arguments for scanning"); + return; + } + + connman_info("%s scanning %s", task->ifname, + scanning == TRUE ? "started" : "finished"); +} + static void state_change(struct supplicant_task *task, DBusMessage *msg) { DBusError error; const char *newstate, *oldstate; - enum supplicant_state state; + unsigned char bssid[ETH_ALEN]; + unsigned int bssid_len; + enum supplicant_state state, prevstate; dbus_error_init(&error); @@ -1360,93 +1861,132 @@ static void state_change(struct supplicant_task *task, DBusMessage *msg) DBG("state %s ==> %s", oldstate, newstate); + connman_info("%s %s%s", task->ifname, newstate, + task->scanning == TRUE ? " (scanning)" : ""); + state = string2state(newstate); if (state == WPA_INVALID) return; - task->state = state; - - switch (task->state) { - case WPA_SCANNING: - task->noscan = TRUE; - connman_device_set_scanning(task->device, TRUE); - break; - case WPA_ASSOCIATING: - case WPA_ASSOCIATED: - case WPA_4WAY_HANDSHAKE: - case WPA_GROUP_HANDSHAKE: - task->noscan = TRUE; - break; - case WPA_COMPLETED: - case WPA_DISCONNECTED: - task->noscan = FALSE; - break; - case WPA_INACTIVE: - task->noscan = FALSE; - connman_device_set_scanning(task->device, FALSE); - break; - case WPA_INVALID: - break; + if (task->scanning == TRUE && state != WPA_SCANNING) { + connman_device_cleanup_scanning(task->device); + task->scanning = FALSE; } + prevstate = task->state; + task->state = state; + if (task->network == NULL) return; switch (task->state) { case WPA_COMPLETED: + switch (prevstate) { + case WPA_ASSOCIATED: + case WPA_GROUP_HANDSHAKE: + break; + default: + goto badstate; + } + + /* reset scan trigger and schedule background scan */ + connman_device_schedule_scan(task->device); + + if (get_bssid(task->device, bssid, &bssid_len) == 0) + connman_network_set_address(task->network, + bssid, bssid_len); + /* carrier on */ + connman_network_set_method(task->network, + CONNMAN_IPCONFIG_METHOD_DHCP); connman_network_set_connected(task->network, TRUE); - connman_device_set_scanning(task->device, FALSE); break; + + case WPA_ASSOCIATING: + switch (prevstate) { + case WPA_COMPLETED: + break; + case WPA_SCANNING: + connman_network_set_associating(task->network, TRUE); + break; + default: + goto badstate; + } + break; + + case WPA_INACTIVE: + switch (prevstate) { + case WPA_SCANNING: + case WPA_DISCONNECTED: + break; + default: + goto badstate; + } + /* fall through */ + case WPA_DISCONNECTED: /* carrier off */ connman_network_set_connected(task->network, FALSE); - connman_device_set_scanning(task->device, FALSE); - break; - case WPA_ASSOCIATING: - connman_network_set_associating(task->network, TRUE); + + if (task->disconnecting == TRUE) { + connman_network_unref(task->network); + task->disconnecting = FALSE; + + if (task->pending_network != NULL) { + task->network = task->pending_network; + task->pending_network = NULL; + task_connect(task); + } else + task->network = NULL; + } break; + default: connman_network_set_associating(task->network, FALSE); break; } + + return; + +badstate: + connman_error("%s invalid state change %s -> %s", task->ifname, + oldstate, newstate); } -static DBusHandlerResult supplicant_filter(DBusConnection *conn, +static gboolean supplicant_filter(DBusConnection *conn, DBusMessage *msg, void *data) { struct supplicant_task *task; const char *member, *path; - if (dbus_message_has_interface(msg, - SUPPLICANT_INTF ".Interface") == FALSE) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - member = dbus_message_get_member(msg); if (member == NULL) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + return TRUE; path = dbus_message_get_path(msg); if (path == NULL) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + return TRUE; task = find_task_by_path(path); if (task == NULL) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + return TRUE; DBG("task %p member %s", task, member); if (g_str_equal(member, "ScanResultsAvailable") == TRUE) scan_results_available(task); + else if (g_str_equal(member, "Scanning") == TRUE) + scanning(task, msg); else if (g_str_equal(member, "StateChange") == TRUE) state_change(task, msg); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + return TRUE; } int supplicant_start(struct connman_device *device) { struct supplicant_task *task; + int err; DBG("device %p", device); @@ -1455,22 +1995,45 @@ int supplicant_start(struct connman_device *device) return -ENOMEM; task->ifindex = connman_device_get_index(device); - task->ifname = inet_index2name(task->ifindex); + task->ifname = connman_inet_ifname(task->ifindex); if (task->ifname == NULL) { - g_free(task); - return -ENOMEM; + err = -ENOMEM; + goto failed; + } + + task->mac80211 = connman_inet_is_mac80211(task->ifindex); + if (task->mac80211 == FALSE) + connman_warn("Enabling quirks for unsupported driver"); + + task->range = g_try_malloc0(sizeof(struct iw_range)); + if (task->range == NULL) { + err = -ENOMEM; + goto failed; } + err = get_range(task); + if (err < 0) + goto failed; + task->device = connman_device_ref(device); task->created = FALSE; - task->noscan = FALSE; + task->scanning = FALSE; task->state = WPA_INVALID; + task->disconnecting = FALSE; + task->pending_network = NULL; task_list = g_slist_append(task_list, task); return create_interface(task); + +failed: + g_free(task->range); + g_free(task->ifname); + g_free(task); + + return err; } int supplicant_stop(struct connman_device *device) @@ -1484,12 +2047,27 @@ int supplicant_stop(struct connman_device *device) if (task == NULL) return -ENODEV; + g_free(task->range); + task_list = g_slist_remove(task_list, task); - disable_network(task); + if (task->scan_call != NULL) { + dbus_pending_call_cancel(task->scan_call); + task->scan_call = NULL; + } + + if (task->result_call != NULL) { + dbus_pending_call_cancel(task->result_call); + task->result_call = NULL; + } + + if (task->scanning == TRUE) + connman_device_set_scanning(task->device, FALSE); remove_network(task); + disconnect_network(task); + return remove_interface(task); } @@ -1517,7 +2095,18 @@ int supplicant_scan(struct connman_device *device) break; } + task->scanning = TRUE; + err = initiate_scan(task); + if (err < 0) { + if (err == -EINPROGRESS) + return 0; + + task->scanning = FALSE; + return err; + } + + connman_device_set_scanning(task->device, TRUE); return 0; } @@ -1525,48 +2114,24 @@ int supplicant_scan(struct connman_device *device) int supplicant_connect(struct connman_network *network) { struct supplicant_task *task; - const char *address, *security, *passphrase; - const void *ssid; - unsigned int ssid_len; int index; DBG("network %p", network); - address = connman_network_get_string(network, "Address"); - security = connman_network_get_string(network, "WiFi.Security"); - passphrase = connman_network_get_string(network, "WiFi.Passphrase"); - - ssid = connman_network_get_blob(network, "WiFi.SSID", &ssid_len); - - DBG("address %s security %s passphrase %s", - address, security, passphrase); - - if (security == NULL && passphrase == NULL) - return -EINVAL; - - if (g_str_equal(security, "none") == FALSE && passphrase == NULL) - return -EINVAL; - index = connman_network_get_index(network); task = find_task_by_index(index); if (task == NULL) return -ENODEV; - task->network = connman_network_ref(network); - - add_network(task); - - select_network(task); - disable_network(task); - - set_network(task, ssid, ssid_len, address, security, passphrase); - - enable_network(task); - - connman_network_set_associating(task->network, TRUE); + if (task->disconnecting == TRUE) + task->pending_network = connman_network_ref(network); + else { + task->network = connman_network_ref(network); + return task_connect(task); + } - return 0; + return -EINPROGRESS; } int supplicant_disconnect(struct connman_network *network) @@ -1582,13 +2147,14 @@ int supplicant_disconnect(struct connman_network *network) if (task == NULL) return -ENODEV; - disable_network(task); + if (task->disconnecting == TRUE) + return -EALREADY; remove_network(task); - connman_network_set_connected(task->network, FALSE); + disconnect_network(task); - connman_network_unref(task->network); + task->disconnecting = TRUE; return 0; } @@ -1645,9 +2211,8 @@ static void supplicant_remove(DBusConnection *conn, void *user_data) } } -static const char *supplicant_rule = "type=signal," - "interface=" SUPPLICANT_INTF ".Interface"; static guint watch; +static guint iface_watch; static int supplicant_create(void) { @@ -1660,18 +2225,20 @@ static int supplicant_create(void) DBG("connection %p", connection); - if (dbus_connection_add_filter(connection, - supplicant_filter, NULL, NULL) == FALSE) { - connection = connman_dbus_get_connection(); - return -EIO; - } - - dbus_bus_add_match(connection, supplicant_rule, NULL); - dbus_connection_flush(connection); - watch = g_dbus_add_service_watch(connection, SUPPLICANT_NAME, supplicant_probe, supplicant_remove, NULL, NULL); + iface_watch = g_dbus_add_signal_watch(connection, NULL, NULL, + SUPPLICANT_INTF ".Interface", + NULL, supplicant_filter, + NULL, NULL); + + if (watch == 0 || iface_watch == 0) { + g_dbus_remove_watch(connection, watch); + g_dbus_remove_watch(connection, iface_watch); + return -EIO; + } + return 0; } @@ -1682,13 +2249,8 @@ static void supplicant_destroy(void) DBG("connection %p", connection); - if (watch > 0) - g_dbus_remove_watch(connection, watch); - - dbus_bus_remove_match(connection, supplicant_rule, NULL); - dbus_connection_flush(connection); - - dbus_connection_remove_filter(connection, supplicant_filter, NULL); + g_dbus_remove_watch(connection, watch); + g_dbus_remove_watch(connection, iface_watch); dbus_connection_unref(connection); connection = NULL; @@ -1706,10 +2268,7 @@ int supplicant_register(struct supplicant_driver *driver) driver_list = g_slist_append(driver_list, driver); - if (g_dbus_check_service(connection, SUPPLICANT_NAME) == TRUE) - supplicant_probe(connection, NULL); - else - supplicant_activate(connection); + supplicant_activate(connection); return 0; }