diff options
author | Tomasz Bursztyka <tomasz.bursztyka@nokia.com> | 2011-01-26 10:09:35 +0200 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2011-01-27 12:13:09 +0100 |
commit | 09036724f966b168211af78791d5bd7f56434839 (patch) | |
tree | 99cc48eb2f0b5d3842e4ddde28758e511a8958e4 /plugins/wifi.c | |
parent | f55f8c71c75901d48d98f1d754a038eef55baf4f (diff) | |
download | connman-09036724f966b168211af78791d5bd7f56434839.tar.gz connman-09036724f966b168211af78791d5bd7f56434839.tar.bz2 connman-09036724f966b168211af78791d5bd7f56434839.zip |
wifi: WPS support
Diffstat (limited to 'plugins/wifi.c')
-rw-r--r-- | plugins/wifi.c | 449 |
1 files changed, 266 insertions, 183 deletions
diff --git a/plugins/wifi.c b/plugins/wifi.c index b7685eec..c606c667 100644 --- a/plugins/wifi.c +++ b/plugins/wifi.c @@ -300,6 +300,192 @@ static void system_killed(void) connman_device_driver_unregister(&wifi_ng_driver); } +static int network_probe(struct connman_network *network) +{ + DBG("network %p", network); + + return 0; +} + +static void network_remove(struct connman_network *network) +{ + DBG("network %p", network); +} + +static void connect_callback(int result, GSupplicantInterface *interface, + void *user_data) +{ + connman_error("%s", __func__); +} + +static GSupplicantSecurity network_security(const char *security) +{ + if (g_str_equal(security, "none") == TRUE) + return G_SUPPLICANT_SECURITY_NONE; + else if (g_str_equal(security, "wep") == TRUE) + return G_SUPPLICANT_SECURITY_WEP; + else if (g_str_equal(security, "psk") == TRUE) + return G_SUPPLICANT_SECURITY_PSK; + else if (g_str_equal(security, "wpa") == TRUE) + return G_SUPPLICANT_SECURITY_PSK; + else if (g_str_equal(security, "rsn") == TRUE) + return G_SUPPLICANT_SECURITY_PSK; + else if (g_str_equal(security, "ieee8021x") == TRUE) + return G_SUPPLICANT_SECURITY_IEEE8021X; + + return G_SUPPLICANT_SECURITY_UNKNOWN; +} + +static void ssid_init(GSupplicantSSID *ssid, struct connman_network *network) +{ + const char *security, *passphrase; + + memset(ssid, 0, sizeof(*ssid)); + ssid->ssid = connman_network_get_blob(network, "WiFi.SSID", + &ssid->ssid_len); + security = connman_network_get_string(network, "WiFi.Security"); + ssid->security = network_security(security); + passphrase = connman_network_get_string(network, + "WiFi.Passphrase"); + if (passphrase == NULL || strlen(passphrase) == 0) + ssid->passphrase = NULL; + else + ssid->passphrase = passphrase; + + ssid->eap = connman_network_get_string(network, "WiFi.EAP"); + + /* + * 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", + ssid->passphrase); + /* We must have an identity for both PEAP and TLS */ + ssid->identity = connman_network_get_string(network, "WiFi.Identity"); + ssid->ca_cert_path = connman_network_get_string(network, + "WiFi.CACertFile"); + ssid->client_cert_path = connman_network_get_string(network, + "WiFi.ClientCertFile"); + ssid->private_key_path = connman_network_get_string(network, + "WiFi.PrivateKeyFile"); + ssid->private_key_passphrase = connman_network_get_string(network, + "WiFi.PrivateKeyPassphrase"); + ssid->phase2_auth = connman_network_get_string(network, "WiFi.Phase2"); + + ssid->use_wps = connman_network_get_bool(network, "WiFi.UseWPS"); + ssid->pin_wps = connman_network_get_string(network, "WiFi.PinWPS"); + +} + +static int network_connect(struct connman_network *network) +{ + struct connman_device *device = connman_network_get_device(network); + struct wifi_data *wifi; + GSupplicantInterface *interface; + GSupplicantSSID *ssid; + + DBG("network %p", network); + + if (device == NULL) + return -ENODEV; + + wifi = connman_device_get_data(device); + if (wifi == NULL) + return -ENODEV; + + ssid = g_try_malloc0(sizeof(GSupplicantSSID)); + if (ssid == NULL) + return -ENOMEM; + + interface = wifi->interface; + + ssid_init(ssid, network); + + if (wifi->disconnecting == TRUE) + wifi->pending_network = connman_network_ref(network); + else { + wifi->network = connman_network_ref(network); + + return g_supplicant_interface_connect(interface, ssid, + connect_callback, NULL); + } + + return -EINPROGRESS; +} + +static void disconnect_callback(int result, GSupplicantInterface *interface, + void *user_data) +{ + struct wifi_data *wifi = user_data; + + if (wifi->network != NULL) { + /* + * if result < 0 supplican return an error because + * the network is not current. + * we wont receive G_SUPPLICANT_STATE_DISCONNECTED since it + * failed, call connman_network_set_connected to report + * disconnect is completed. + */ + if (result < 0) + connman_network_set_connected(wifi->network, FALSE); + + connman_network_unref(wifi->network); + } + + wifi->network = NULL; + + wifi->disconnecting = FALSE; + + if (wifi->pending_network != NULL) { + network_connect(wifi->pending_network); + connman_network_unref(wifi->pending_network); + wifi->pending_network = NULL; + } + +} + +static int network_disconnect(struct connman_network *network) +{ + struct connman_device *device = connman_network_get_device(network); + struct wifi_data *wifi; + int err; + + DBG("network %p", network); + + wifi = connman_device_get_data(device); + if (wifi == NULL || wifi->interface == NULL) + return -ENODEV; + + connman_network_set_associating(network, FALSE); + + if (wifi->disconnecting == TRUE) + return -EALREADY; + + wifi->disconnecting = TRUE; + + err = g_supplicant_interface_disconnect(wifi->interface, + disconnect_callback, wifi); + if (err < 0) + wifi->disconnecting = FALSE; + + return err; +} + +static struct connman_network_driver network_driver = { + .name = "wifi", + .type = CONNMAN_NETWORK_TYPE_WIFI, + .priority = CONNMAN_NETWORK_PRIORITY_LOW, + .probe = network_probe, + .remove = network_remove, + .connect = network_connect, + .disconnect = network_disconnect, +}; + static void interface_added(GSupplicantInterface *interface) { const char *ifname = g_supplicant_interface_get_ifname(interface); @@ -350,6 +536,73 @@ static connman_bool_t is_idle(struct wifi_data *wifi) return FALSE; } +static connman_bool_t is_idle_wps(GSupplicantInterface *interface, + struct wifi_data *wifi) +{ + /* First, let's check if WPS processing did not went wrong */ + if (g_supplicant_interface_get_wps_state(interface) == + G_SUPPLICANT_WPS_STATE_FAIL) + return FALSE; + + /* Unlike normal connection, being associated while processing wps + * actually means that we are idling. */ + switch (wifi->state) { + case G_SUPPLICANT_STATE_UNKNOWN: + case G_SUPPLICANT_STATE_DISCONNECTED: + case G_SUPPLICANT_STATE_INACTIVE: + case G_SUPPLICANT_STATE_SCANNING: + case G_SUPPLICANT_STATE_ASSOCIATED: + return TRUE; + case G_SUPPLICANT_STATE_AUTHENTICATING: + case G_SUPPLICANT_STATE_ASSOCIATING: + case G_SUPPLICANT_STATE_4WAY_HANDSHAKE: + case G_SUPPLICANT_STATE_GROUP_HANDSHAKE: + case G_SUPPLICANT_STATE_COMPLETED: + return FALSE; + } + + return FALSE; +} + +static connman_bool_t handle_wps_completion(GSupplicantInterface *interface, + struct connman_network *network, + struct connman_device *device, + struct wifi_data *wifi) +{ + connman_bool_t wps; + + wps = connman_network_get_bool(network, "WiFi.UseWPS"); + if (wps == TRUE) { + const unsigned char *ssid, *wps_ssid; + unsigned int ssid_len, wps_ssid_len; + const char *wps_key; + + /* Checking if we got associated with requested + * network */ + ssid = connman_network_get_blob(network, "WiFi.SSID", + &ssid_len); + + wps_ssid = g_supplicant_interface_get_wps_ssid( + interface, &wps_ssid_len); + + if (wps_ssid == NULL || wps_ssid_len != ssid_len || + memcmp(ssid, wps_ssid, ssid_len) != 0) { + connman_network_set_associating(network, FALSE); + g_supplicant_interface_disconnect(wifi->interface, + disconnect_callback, wifi); + return FALSE; + } + + wps_key = g_supplicant_interface_get_wps_key(interface); + connman_network_set_string(network, "WiFi.Passphrase", + wps_key); + + connman_network_set_string(network, "WiFi.PinWPS", NULL); + } + + return TRUE; +} + static void interface_state(GSupplicantInterface *interface) { struct connman_network *network; @@ -358,6 +611,7 @@ static void interface_state(GSupplicantInterface *interface) GSupplicantState state = g_supplicant_interface_get_state(interface); unsigned char bssid[ETH_ALEN]; unsigned int bssid_len; + connman_bool_t wps; wifi = g_supplicant_interface_get_data(interface); @@ -382,6 +636,10 @@ static void interface_state(GSupplicantInterface *interface) break; case G_SUPPLICANT_STATE_COMPLETED: + if (handle_wps_completion(interface, network, device, wifi) == + FALSE) + break; + /* reset scan trigger and schedule background scan */ connman_device_schedule_scan(device); @@ -398,6 +656,11 @@ static void interface_state(GSupplicantInterface *interface) * those ones to FALSE could cancel an association * in progress. */ + wps = connman_network_get_bool(network, "WiFi.UseWPS"); + if (wps == TRUE) + if (is_idle_wps(interface, wifi) == TRUE) + break; + if (is_idle(wifi)) break; connman_network_set_associating(network, FALSE); @@ -480,6 +743,7 @@ static void network_added(GSupplicantNetwork *supplicant_network) const char *name, *identifier, *mode, *security, *group; const unsigned char *ssid; unsigned int ssid_len; + connman_bool_t wps; DBG(""); @@ -490,6 +754,7 @@ static void network_added(GSupplicantNetwork *supplicant_network) mode = g_supplicant_network_get_mode(supplicant_network); security = g_supplicant_network_get_security(supplicant_network); group = g_supplicant_network_get_identifier(supplicant_network); + wps = g_supplicant_network_get_wps(supplicant_network); if (wifi == NULL) return; @@ -521,6 +786,7 @@ static void network_added(GSupplicantNetwork *supplicant_network) connman_network_set_string(network, "WiFi.Security", security); connman_network_set_strength(network, calculate_strength(supplicant_network)); + connman_network_set_bool(network, "WiFi.WPS", wps); connman_network_set_available(network, TRUE); @@ -565,189 +831,6 @@ static const GSupplicantCallbacks callbacks = { }; -static int network_probe(struct connman_network *network) -{ - DBG("network %p", network); - - return 0; -} - -static void network_remove(struct connman_network *network) -{ - DBG("network %p", network); -} - -static void connect_callback(int result, GSupplicantInterface *interface, - void *user_data) -{ - connman_error("%s", __func__); -} - -static GSupplicantSecurity network_security(const char *security) -{ - if (g_str_equal(security, "none") == TRUE) - return G_SUPPLICANT_SECURITY_NONE; - else if (g_str_equal(security, "wep") == TRUE) - return G_SUPPLICANT_SECURITY_WEP; - else if (g_str_equal(security, "psk") == TRUE) - return G_SUPPLICANT_SECURITY_PSK; - else if (g_str_equal(security, "wpa") == TRUE) - return G_SUPPLICANT_SECURITY_PSK; - else if (g_str_equal(security, "rsn") == TRUE) - return G_SUPPLICANT_SECURITY_PSK; - else if (g_str_equal(security, "ieee8021x") == TRUE) - return G_SUPPLICANT_SECURITY_IEEE8021X; - - return G_SUPPLICANT_SECURITY_UNKNOWN; -} - -static void ssid_init(GSupplicantSSID *ssid, struct connman_network *network) -{ - const char *security, *passphrase; - - memset(ssid, 0, sizeof(*ssid)); - ssid->ssid = connman_network_get_blob(network, "WiFi.SSID", - &ssid->ssid_len); - security = connman_network_get_string(network, "WiFi.Security"); - ssid->security = network_security(security); - passphrase = connman_network_get_string(network, - "WiFi.Passphrase"); - if (passphrase == NULL || strlen(passphrase) == 0) - ssid->passphrase = NULL; - else - ssid->passphrase = passphrase; - - ssid->eap = connman_network_get_string(network, "WiFi.EAP"); - - /* - * 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", - ssid->passphrase); - /* We must have an identity for both PEAP and TLS */ - ssid->identity = connman_network_get_string(network, "WiFi.Identity"); - ssid->ca_cert_path = connman_network_get_string(network, - "WiFi.CACertFile"); - ssid->client_cert_path = connman_network_get_string(network, - "WiFi.ClientCertFile"); - ssid->private_key_path = connman_network_get_string(network, - "WiFi.PrivateKeyFile"); - ssid->private_key_passphrase = connman_network_get_string(network, - "WiFi.PrivateKeyPassphrase"); - ssid->phase2_auth = connman_network_get_string(network, "WiFi.Phase2"); - -} - -static int network_connect(struct connman_network *network) -{ - struct connman_device *device = connman_network_get_device(network); - struct wifi_data *wifi; - GSupplicantInterface *interface; - GSupplicantSSID *ssid; - - DBG("network %p", network); - - if (device == NULL) - return -ENODEV; - - wifi = connman_device_get_data(device); - if (wifi == NULL) - return -ENODEV; - - ssid = g_try_malloc0(sizeof(GSupplicantSSID)); - if (ssid == NULL) - return -ENOMEM; - - interface = wifi->interface; - - ssid_init(ssid, network); - - if (wifi->disconnecting == TRUE) - wifi->pending_network = connman_network_ref(network); - else { - wifi->network = connman_network_ref(network); - - return g_supplicant_interface_connect(interface, ssid, - connect_callback, NULL); - } - - return -EINPROGRESS; -} - -static void disconnect_callback(int result, GSupplicantInterface *interface, - void *user_data) -{ - struct wifi_data *wifi = user_data; - - if (wifi->network != NULL) { - /* - * if result < 0 supplican return an error because - * the network is not current. - * we wont receive G_SUPPLICANT_STATE_DISCONNECTED since it - * failed, call connman_network_set_connected to report - * disconnect is completed. - */ - if (result < 0) - connman_network_set_connected(wifi->network, FALSE); - - connman_network_unref(wifi->network); - } - - wifi->network = NULL; - - wifi->disconnecting = FALSE; - - if (wifi->pending_network != NULL) { - network_connect(wifi->pending_network); - connman_network_unref(wifi->pending_network); - wifi->pending_network = NULL; - } - -} - -static int network_disconnect(struct connman_network *network) -{ - struct connman_device *device = connman_network_get_device(network); - struct wifi_data *wifi; - int err; - - DBG("network %p", network); - - wifi = connman_device_get_data(device); - if (wifi == NULL || wifi->interface == NULL) - return -ENODEV; - - connman_network_set_associating(network, FALSE); - - if (wifi->disconnecting == TRUE) - return -EALREADY; - - wifi->disconnecting = TRUE; - - err = g_supplicant_interface_disconnect(wifi->interface, - disconnect_callback, wifi); - if (err < 0) - wifi->disconnecting = FALSE; - - return err; -} - -static struct connman_network_driver network_driver = { - .name = "wifi", - .type = CONNMAN_NETWORK_TYPE_WIFI, - .priority = CONNMAN_NETWORK_PRIORITY_LOW, - .probe = network_probe, - .remove = network_remove, - .connect = network_connect, - .disconnect = network_disconnect, -}; - static int tech_probe(struct connman_technology *technology) { wifi_technology = technology; |