X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=plugins%2Fsupplicant.c;h=a9c524c27c99ea189ee5c12b3ac22856005d1786;hb=cd07fbeb1af81232dfb452ec7494bdc33051371e;hp=700a0c10b10d011fdb01e83e9d6d725a3cd6bd06;hpb=34f9f7ff8de621d91d612f1f8b723f7b60e59199;p=platform%2Fupstream%2Fconnman.git diff --git a/plugins/supplicant.c b/plugins/supplicant.c index 700a0c1..a9c524c 100644 --- a/plugins/supplicant.c +++ b/plugins/supplicant.c @@ -162,6 +162,8 @@ struct supplicant_result { dbus_uint16_t capabilities; gboolean adhoc; gboolean has_wep; + gboolean has_psk; + gboolean has_8021x; gboolean has_wpa; gboolean has_rsn; gboolean has_wps; @@ -175,6 +177,7 @@ 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; @@ -182,8 +185,10 @@ struct supplicant_task { 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; }; @@ -247,6 +252,15 @@ static int get_range(struct supplicant_task *task) 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; } @@ -329,6 +343,8 @@ static void add_interface_reply(DBusPendingCall *call, void *user_data) return; failed: + dbus_message_unref(reply); + task_list = g_slist_remove(task_list, task); connman_device_unref(task->device); @@ -350,20 +366,19 @@ static int add_interface(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_append_basic(&array, DBUS_TYPE_STRING, &task->ifname); - 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); - connman_dbus_dict_append_variant(&dict, "driver", + connman_dbus_dict_append_basic(&dict, "driver", DBUS_TYPE_STRING, &driver); - dbus_message_iter_close_container(&array, &dict); + connman_dbus_dict_close(&array, &dict); if (dbus_connection_send_with_reply(connection, message, &call, TIMEOUT) == FALSE) { @@ -438,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); @@ -500,6 +517,8 @@ static int remove_interface(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->path, DBUS_TYPE_INVALID); @@ -523,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; @@ -537,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); @@ -560,7 +580,6 @@ static int set_ap_scan(struct supplicant_task *task) return 0; } -#endif static int add_network(struct supplicant_task *task) { @@ -578,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, @@ -631,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); @@ -673,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); @@ -697,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, @@ -721,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; } @@ -733,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; } @@ -789,41 +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); - connman_dbus_dict_append_variant(&dict, "scan_ssid", + connman_dbus_dict_append_basic(&dict, "scan_ssid", DBUS_TYPE_UINT32, &scan_ssid); - if (address) - connman_dbus_dict_append_variant(&dict, "bssid", + 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); - connman_dbus_dict_append_array(&dict, "ssid", - DBUS_TYPE_BYTE, &network, len); - - 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"; - const char *auth_alg = "OPEN SHARED"; + const char *auth_alg = "OPEN"; const char *key_index = "0"; - connman_dbus_dict_append_variant(&dict, "auth_alg", + 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_variant(&dict, "key_mgmt", + connman_dbus_dict_append_basic(&dict, "key_mgmt", DBUS_TYPE_STRING, &key_mgmt); if (passphrase) { @@ -840,25 +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", + 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); @@ -879,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 { @@ -1040,6 +1252,76 @@ static void extract_ssid(DBusMessageIter *value, } } +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, struct supplicant_result *result) { @@ -1050,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, @@ -1064,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, @@ -1099,7 +1385,7 @@ static void extract_capabilites(DBusMessageIter *value, static unsigned char calculate_strength(struct supplicant_task *task, struct supplicant_result *result) { - if (task->range->max_qual.qual == 0) { + if (result->quality == -1 || task->range->max_qual.qual == 0) { unsigned char strength; if (result->level > 0) @@ -1137,7 +1423,6 @@ static void properties_reply(DBusPendingCall *call, void *user_data) unsigned short channel, frequency; const char *mode, *security; char *group = NULL; - unsigned int ssid_len; DBG("task %p", task); @@ -1217,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; @@ -1233,10 +1524,10 @@ static void properties_reply(DBusPendingCall *call, void *user_data) 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 @@ -1248,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; @@ -1275,10 +1573,8 @@ static void properties_reply(DBusPendingCall *call, void *user_data) if (result.name != NULL && result.name[0] != '\0') connman_network_set_name(network, result.name); - if (connman_network_get_blob(network, "WiFi.SSID", &ssid_len) == NULL) { - connman_network_set_blob(network, "WiFi.SSID", - result.ssid, result.ssid_len); - } + connman_network_set_blob(network, "WiFi.SSID", + result.ssid, result.ssid_len); connman_network_set_string(network, "WiFi.Mode", mode); @@ -1312,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); @@ -1329,27 +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: - connman_device_set_scanning(task->device, 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) @@ -1406,37 +1709,47 @@ done: dbus_message_unref(reply); noscan: - connman_device_set_scanning(task->device, 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); @@ -1469,6 +1782,9 @@ 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"); @@ -1476,8 +1792,7 @@ static int task_connect(struct supplicant_task *task) ssid = connman_network_get_blob(task->network, "WiFi.SSID", &ssid_len); - DBG("address %s security %s passphrase %s", - address, security, passphrase); + DBG("address %s security %s", address, security); if (security == NULL && passphrase == NULL) return -EINVAL; @@ -1485,25 +1800,51 @@ static int task_connect(struct supplicant_task *task) if (g_str_equal(security, "none") == FALSE && passphrase == NULL) return -EINVAL; - add_network(task); + remove_network(task); - select_network(task); - disable_network(task); + set_ap_scan(task); - set_network(task, ssid, ssid_len, address, security, passphrase); + add_network(task); - enable_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; unsigned char bssid[ETH_ALEN]; unsigned int bssid_len; - enum supplicant_state state; + enum supplicant_state state, prevstate; dbus_error_init(&error); @@ -1520,78 +1861,74 @@ 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, + 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_DISCONNECTED: - disable_network(task); - - if (task->disconnecting == TRUE) { - connman_network_set_connected(task->network, FALSE); - 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 { - /* 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); + switch (prevstate) { + case WPA_COMPLETED: + break; + case WPA_SCANNING: + connman_network_set_associating(task->network, TRUE); + break; + default: + goto badstate; + } break; case WPA_INACTIVE: - remove_network(task); + 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); if (task->disconnecting == TRUE) { - connman_network_set_connected(task->network, FALSE); connman_network_unref(task->network); task->disconnecting = FALSE; @@ -1599,7 +1936,8 @@ static void state_change(struct supplicant_task *task, DBusMessage *msg) task->network = task->pending_network; task->pending_network = NULL; task_connect(task); - } + } else + task->network = NULL; } break; @@ -1607,38 +1945,42 @@ static void state_change(struct supplicant_task *task, DBusMessage *msg) 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) @@ -1660,6 +2002,10 @@ int supplicant_start(struct connman_device *device) 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; @@ -1673,7 +2019,7 @@ int supplicant_start(struct connman_device *device) 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; @@ -1705,10 +2051,23 @@ int supplicant_stop(struct connman_device *device) 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); } @@ -1736,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; } @@ -1778,12 +2148,12 @@ int supplicant_disconnect(struct connman_network *network) return -ENODEV; if (task->disconnecting == TRUE) - return -EINPROGRESS; - - disable_network(task); + return -EALREADY; remove_network(task); + disconnect_network(task); + task->disconnecting = TRUE; return 0; @@ -1841,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) { @@ -1856,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; } @@ -1878,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; @@ -1902,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; }