X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fnetwork.c;h=f2ab16bd265bf6e9986cc11c1158aa1c07169b94;hb=refs%2Ftags%2Fupstream%2F1.38;hp=55c49f183110182348daf684dab5fa0cf1bede58;hpb=9e68b80433556b992f8514324aa376a4173379a8;p=platform%2Fupstream%2Fconnman.git diff --git a/src/network.c b/src/network.c index 55c49f1..f2ab16b 100644 --- a/src/network.c +++ b/src/network.c @@ -2,7 +2,7 @@ * * Connection Manager * - * Copyright (C) 2007-2012 Intel Corporation. All rights reserved. + * Copyright (C) 2007-2014 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 @@ -27,6 +27,30 @@ #include #include "connman.h" +#include +#include "src/shared/arp.h" + +/* + * How many times to send RS with the purpose of + * refreshing RDNSS entries before they actually expire. + * With a value of 1, one RS will be sent, with no retries. + */ +#define RS_REFRESH_COUNT 1 + +/* + * Value in seconds to wait for RA after RS was sent. + * After this time elapsed, we can send another RS. + */ +#define RS_REFRESH_TIMEOUT 3 + +/* + * As per RFC 4861, a host should transmit up to MAX_RTR_SOLICITATIONS(3) + * Router Solicitation messages, each separated by at least + * RTR_SOLICITATION_INTERVAL(4) seconds to obtain RA for IPv6 auto-configuration. + */ +#define RTR_SOLICITATION_INTERVAL 4 + +#define DHCP_RETRY_TIMEOUT 10 static GSList *network_list = NULL; static GSList *driver_list = NULL; @@ -34,11 +58,11 @@ static GSList *driver_list = NULL; struct connman_network { int refcount; enum connman_network_type type; - connman_bool_t available; - connman_bool_t connected; - connman_bool_t roaming; - connman_uint8_t strength; - connman_uint16_t frequency; + bool available; + bool connected; + bool roaming; + uint8_t strength; + uint16_t frequency; char *identifier; char *name; char *node; @@ -46,12 +70,16 @@ struct connman_network { char *path; int index; int router_solicit_count; + int router_solicit_refresh_count; + struct acd_host *acd_host; + guint ipv4ll_timeout; + guint dhcp_timeout; struct connman_network_driver *driver; void *driver_data; - connman_bool_t connecting; - connman_bool_t associating; + bool connecting; + bool associating; struct connman_device *device; @@ -62,24 +90,25 @@ struct connman_network { unsigned short channel; char *security; char *passphrase; - char *agent_passphrase; char *eap; char *identity; + char *anonymous_identity; char *agent_identity; char *ca_cert_path; + char *subject_match; + char *altsubject_match; + char *domain_suffix_match; + char *domain_match; char *client_cert_path; char *private_key_path; char *private_key_passphrase; char *phase2_auth; - connman_bool_t wps; - connman_bool_t use_wps; + bool wps; + bool wps_advertizing; + bool use_wps; char *pin_wps; } wifi; - struct { - char *nsp_name; - int nsp_name_len; - } wimax; }; static const char *type2string(enum connman_network_type type) @@ -90,10 +119,10 @@ static const char *type2string(enum connman_network_type type) break; case CONNMAN_NETWORK_TYPE_ETHERNET: return "ethernet"; + case CONNMAN_NETWORK_TYPE_GADGET: + return "gadget"; case CONNMAN_NETWORK_TYPE_WIFI: return "wifi"; - case CONNMAN_NETWORK_TYPE_WIMAX: - return "wimax"; case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN: case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN: return "bluetooth"; @@ -104,1234 +133,1536 @@ static const char *type2string(enum connman_network_type type) return NULL; } -static gboolean match_driver(struct connman_network *network, +static bool match_driver(struct connman_network *network, struct connman_network_driver *driver) { if (network->type == driver->type || driver->type == CONNMAN_NETWORK_TYPE_UNKNOWN) - return TRUE; + return true; - return FALSE; + return false; } -static int network_probe(struct connman_network *network) +static void set_configuration(struct connman_network *network, + enum connman_ipconfig_type type) { - GSList *list; - struct connman_network_driver *driver = NULL; - - DBG("network %p name %s", network, network->name); - - if (network->driver != NULL) - return -EALREADY; + struct connman_service *service; - for (list = driver_list; list; list = list->next) { - driver = list->data; + DBG("network %p", network); - if (match_driver(network, driver) == FALSE) - continue; + if (!network->device) + return; - DBG("driver %p name %s", driver, driver->name); + __connman_device_set_network(network->device, network); - if (driver->probe(network) == 0) - break; + service = connman_service_lookup_from_network(network); + __connman_service_ipconfig_indicate_state(service, + CONNMAN_SERVICE_STATE_CONFIGURATION, + type); +} - driver = NULL; - } +void connman_network_append_acddbus(DBusMessageIter *dict, + struct connman_network *network) +{ + if (!network->acd_host) + return; - if (driver == NULL) - return -ENODEV; + acd_host_append_dbus_property(network->acd_host, dict); +} - if (network->group == NULL) - return -EINVAL; +static int start_acd(struct connman_network *network); - switch (network->type) { - case CONNMAN_NETWORK_TYPE_UNKNOWN: - case CONNMAN_NETWORK_TYPE_VENDOR: - return 0; - case CONNMAN_NETWORK_TYPE_ETHERNET: - case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN: - case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN: - case CONNMAN_NETWORK_TYPE_CELLULAR: - case CONNMAN_NETWORK_TYPE_WIFI: - case CONNMAN_NETWORK_TYPE_WIMAX: - network->driver = driver; - if (__connman_service_create_from_network(network) == NULL) { - network->driver = NULL; - return -EINVAL; - } +static void remove_ipv4ll_timeout(struct connman_network *network) +{ + if (network->ipv4ll_timeout > 0) { + g_source_remove(network->ipv4ll_timeout); + network->ipv4ll_timeout = 0; } - - return 0; } -static void network_remove(struct connman_network *network) +static void acd_host_ipv4_available(struct acd_host *acd, gpointer user_data) { - DBG("network %p name %s", network, network->name); + struct connman_network *network = user_data; + struct connman_service *service; + struct connman_ipconfig *ipconfig_ipv4; + int err; - if (network->driver == NULL) + if (!network) return; - connman_network_set_connected(network, FALSE); - - switch (network->type) { - case CONNMAN_NETWORK_TYPE_UNKNOWN: - case CONNMAN_NETWORK_TYPE_VENDOR: - break; - case CONNMAN_NETWORK_TYPE_ETHERNET: - case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN: - case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN: - case CONNMAN_NETWORK_TYPE_CELLULAR: - case CONNMAN_NETWORK_TYPE_WIFI: - case CONNMAN_NETWORK_TYPE_WIMAX: - if (network->group != NULL) { - __connman_service_remove_from_network(network); + service = connman_service_lookup_from_network(network); + if (!service) + return; - g_free(network->group); - network->group = NULL; - } - break; + ipconfig_ipv4 = __connman_service_get_ip4config(service); + if (!ipconfig_ipv4) { + connman_error("Service has no IPv4 configuration"); + return; } - if (network->driver->remove) - network->driver->remove(network); + err = __connman_ipconfig_address_add(ipconfig_ipv4); + if (err < 0) + goto err; - network->driver = NULL; + err = __connman_ipconfig_gateway_add(ipconfig_ipv4); + if (err < 0) + goto err; + + __connman_service_save(service); + + return; + +err: + connman_network_set_error(__connman_service_get_network(service), + CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL); } -static void network_change(struct connman_network *network) +static int start_ipv4ll(struct connman_network *network) { - DBG("network %p name %s", network, network->name); + struct connman_service *service; + struct connman_ipconfig *ipconfig_ipv4; + struct in_addr addr; + char *address; - if (network->connected == FALSE) - return; + service = connman_service_lookup_from_network(network); + if (!service) + return -EINVAL; - connman_device_set_disconnected(network->device, TRUE); + ipconfig_ipv4 = __connman_service_get_ip4config(service); + if (!ipconfig_ipv4) { + connman_error("Service has no IPv4 configuration"); + return -EINVAL; + } - if (network->driver && network->driver->disconnect) { - network->driver->disconnect(network); - return; + /* Apply random IPv4 address. */ + addr.s_addr = htonl(arp_random_ip()); + address = inet_ntoa(addr); + if (!address) { + connman_error("Could not convert IPv4LL random address %u", + addr.s_addr); + return -EINVAL; } + __connman_ipconfig_set_local(ipconfig_ipv4, address); - network->connected = FALSE; + connman_info("Probing IPv4LL address %s", address); + return start_acd(network); } -static void probe_driver(struct connman_network_driver *driver) +static gboolean start_ipv4ll_ontimeout(gpointer data) { - GSList *list; + struct connman_network *network = data; - DBG("driver %p name %s", driver, driver->name); + if (!network) + return FALSE; - for (list = network_list; list != NULL; list = list->next) { - struct connman_network *network = list->data; + /* Start IPv4LL ACD. */ + start_ipv4ll(network); - if (network->driver != NULL) - continue; + return FALSE; +} - if (driver->type != network->type) - continue; +static void acd_host_ipv4_lost(struct acd_host *acd, gpointer user_data) +{ + struct connman_network *network = user_data; + struct connman_service *service; + struct connman_ipconfig *ipconfig_ipv4; + enum connman_ipconfig_type type; + enum connman_ipconfig_method method; - if (driver->probe(network) < 0) - continue; + if (!network) + return; - network->driver = driver; + service = connman_service_lookup_from_network(network); + if (!service) + return; + + ipconfig_ipv4 = __connman_service_get_ip4config(service); + if (!ipconfig_ipv4) { + connman_error("Service has no IPv4 configuration"); + return; + } + + type = __connman_ipconfig_get_config_type(ipconfig_ipv4); + if (type != CONNMAN_IPCONFIG_TYPE_IPV4) + return; + + __connman_ipconfig_address_remove(ipconfig_ipv4); + + method = __connman_ipconfig_get_method(ipconfig_ipv4); + if (method == CONNMAN_IPCONFIG_METHOD_DHCP) { + /* + * We have one more chance for DHCP. If this fails + * acd_host_ipv4_conflict will be called. + */ + network = __connman_service_get_network(service); + if (network) + __connman_network_enable_ipconfig(network, ipconfig_ipv4); + } else { + /* Start IPv4LL ACD. */ + start_ipv4ll(network); } } -static void remove_driver(struct connman_network_driver *driver) +static void acd_host_ipv4_conflict(struct acd_host *acd, gpointer user_data) { - GSList *list; + struct connman_network *network = user_data; + struct connman_service *service; + struct connman_ipconfig *ipconfig_ipv4; + enum connman_ipconfig_method method; - DBG("driver %p name %s", driver, driver->name); + service = connman_service_lookup_from_network(network); + if (!service) + return; - for (list = network_list; list != NULL; list = list->next) { - struct connman_network *network = list->data; + ipconfig_ipv4 = __connman_service_get_ip4config(service); + if (!ipconfig_ipv4) { + connman_error("Service has no IPv4 configuration"); + return; + } - if (network->driver == driver) - network_remove(network); + method = __connman_ipconfig_get_method(ipconfig_ipv4); + connman_info("%s conflict counts=%u", __FUNCTION__, + acd_host_get_conflicts_count(acd)); + + if (method == CONNMAN_IPCONFIG_METHOD_DHCP && + acd_host_get_conflicts_count(acd) < 2) { + connman_info("%s Sending DHCP decline", __FUNCTION__); + __connman_dhcp_decline(ipconfig_ipv4); + + connman_network_set_connected_dhcp_later(network, DHCP_RETRY_TIMEOUT); + __connman_ipconfig_set_local(ipconfig_ipv4, NULL); + } else { + if (method == CONNMAN_IPCONFIG_METHOD_DHCP) { + __connman_ipconfig_set_method(ipconfig_ipv4, + CONNMAN_IPCONFIG_METHOD_AUTO); + __connman_dhcp_decline(ipconfig_ipv4); + } + /* Start IPv4LL ACD. */ + start_ipv4ll(network); } } -static gint compare_priority(gconstpointer a, gconstpointer b) +static void acd_host_ipv4_maxconflict(struct acd_host *acd, gpointer user_data) { - const struct connman_network_driver *driver1 = a; - const struct connman_network_driver *driver2 = b; + struct connman_network *network = user_data; - return driver2->priority - driver1->priority; + remove_ipv4ll_timeout(network); + connman_info("Had maximum number of conflicts. Next IPv4LL address will be " + "tried in %d seconds", RATE_LIMIT_INTERVAL); + /* Wait, then start IPv4LL ACD. */ + network->ipv4ll_timeout = + g_timeout_add_seconds_full(G_PRIORITY_HIGH, + RATE_LIMIT_INTERVAL, + start_ipv4ll_ontimeout, + network, + NULL); } -/** - * connman_network_driver_register: - * @driver: network driver definition - * - * Register a new network driver - * - * Returns: %0 on success - */ -int connman_network_driver_register(struct connman_network_driver *driver) +static int start_acd(struct connman_network *network) { - GSList *list; - - DBG("driver %p name %s", driver, driver->name); + struct connman_service *service; + struct connman_ipconfig *ipconfig_ipv4; + const char* address; + struct in_addr addr; - for (list = driver_list; list; list = list->next) { - struct connman_network_driver *tmp = list->data; + remove_ipv4ll_timeout(network); - if (tmp->type == driver->type) - return -EALREADY; + service = connman_service_lookup_from_network(network); + if (!service) + return -EINVAL; + ipconfig_ipv4 = __connman_service_get_ip4config(service); + if (!ipconfig_ipv4) { + connman_error("Service has no IPv4 configuration"); + return -EINVAL; } - driver_list = g_slist_insert_sorted(driver_list, driver, - compare_priority); + if (!network->acd_host) { + int index; - probe_driver(driver); + index = __connman_ipconfig_get_index(ipconfig_ipv4); + network->acd_host = acd_host_new(index, + connman_service_get_dbuspath(service)); + if (!network->acd_host) { + connman_error("Could not create ACD data structure"); + return -EINVAL; + } - return 0; -} + acd_host_register_event(network->acd_host, + ACD_HOST_EVENT_IPV4_AVAILABLE, + acd_host_ipv4_available, network); + acd_host_register_event(network->acd_host, + ACD_HOST_EVENT_IPV4_LOST, + acd_host_ipv4_lost, network); + acd_host_register_event(network->acd_host, + ACD_HOST_EVENT_IPV4_CONFLICT, + acd_host_ipv4_conflict, network); + acd_host_register_event(network->acd_host, + ACD_HOST_EVENT_IPV4_MAXCONFLICT, + acd_host_ipv4_maxconflict, network); + } -/** - * connman_network_driver_unregister: - * @driver: network driver definition - * - * Remove a previously registered network driver - */ -void connman_network_driver_unregister(struct connman_network_driver *driver) -{ - DBG("driver %p name %s", driver, driver->name); + address = __connman_ipconfig_get_local(ipconfig_ipv4); + if (!address) + return -EINVAL; - driver_list = g_slist_remove(driver_list, driver); + connman_info("Starting ACD for address %s", address); + if (inet_pton(AF_INET, address, &addr) != 1) + connman_error("Could not convert address %s", address); + + acd_host_start(network->acd_host, htonl(addr.s_addr)); - remove_driver(driver); + return 0; } -static void network_destruct(struct connman_network *network) +static void dhcp_success(struct connman_network *network) { - DBG("network %p name %s", network, network->name); + struct connman_service *service; + struct connman_ipconfig *ipconfig_ipv4; + int err; - g_free(network->wifi.ssid); - g_free(network->wifi.mode); - g_free(network->wifi.security); - g_free(network->wifi.passphrase); - g_free(network->wifi.agent_passphrase); - g_free(network->wifi.eap); - g_free(network->wifi.identity); - g_free(network->wifi.agent_identity); - g_free(network->wifi.ca_cert_path); - g_free(network->wifi.client_cert_path); - g_free(network->wifi.private_key_path); - g_free(network->wifi.private_key_passphrase); - g_free(network->wifi.phase2_auth); - g_free(network->wifi.pin_wps); + service = connman_service_lookup_from_network(network); + if (!service) + goto err; - g_free(network->path); - g_free(network->group); - g_free(network->node); - g_free(network->name); - g_free(network->identifier); + ipconfig_ipv4 = __connman_service_get_ip4config(service); - network->device = NULL; + DBG("lease acquired for ipconfig %p", ipconfig_ipv4); - g_free(network); -} + if (!ipconfig_ipv4) + return; -/** - * connman_network_create: - * @identifier: network identifier (for example an unqiue name) - * - * Allocate a new network and assign the #identifier to it. - * - * Returns: a newly-allocated #connman_network structure - */ -struct connman_network *connman_network_create(const char *identifier, - enum connman_network_type type) -{ - struct connman_network *network; - char *ident; + if (connman_setting_get_bool("AddressConflictDetection")) { + err = start_acd(network); + if (!err) + return; - DBG("identifier %s type %d", identifier, type); + /* On error proceed without ACD. */ + } - network = g_try_new0(struct connman_network, 1); - if (network == NULL) - return NULL; + err = __connman_ipconfig_address_add(ipconfig_ipv4); + if (err < 0) + goto err; - DBG("network %p", network); + err = __connman_ipconfig_gateway_add(ipconfig_ipv4); + if (err < 0) + goto err; - network->refcount = 1; + __connman_service_save(service); - ident = g_strdup(identifier); + return; - if (ident == NULL) { - g_free(network); - return NULL; - } - - network->type = type; - network->identifier = ident; - - network_list = g_slist_append(network_list, network); - - return network; +err: + connman_network_set_error(network, + CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL); } -/** - * connman_network_ref: - * @network: network structure - * - * Increase reference counter of network - */ -struct connman_network * -connman_network_ref_debug(struct connman_network *network, - const char *file, int line, const char *caller) +static void dhcp_failure(struct connman_network *network) { - DBG("%p name %s ref %d by %s:%d:%s()", network, network->name, - network->refcount + 1, file, line, caller); + struct connman_service *service; + struct connman_ipconfig *ipconfig_ipv4; - __sync_fetch_and_add(&network->refcount, 1); + service = connman_service_lookup_from_network(network); + if (!service) + return; - return network; -} + ipconfig_ipv4 = __connman_service_get_ip4config(service); -/** - * connman_network_unref: - * @network: network structure - * - * Decrease reference counter of network - */ -void connman_network_unref_debug(struct connman_network *network, - const char *file, int line, const char *caller) -{ - DBG("%p name %s ref %d by %s:%d:%s()", network, network->name, - network->refcount - 1, file, line, caller); + DBG("lease lost for ipconfig %p", ipconfig_ipv4); - if (__sync_fetch_and_sub(&network->refcount, 1) != 1) + if (!ipconfig_ipv4) return; - network_list = g_slist_remove(network_list, network); - - network_destruct(network); -} - -const char *__connman_network_get_type(struct connman_network *network) -{ - return type2string(network->type); + __connman_ipconfig_address_remove(ipconfig_ipv4); + __connman_ipconfig_gateway_remove(ipconfig_ipv4); } -/** - * connman_network_get_type: - * @network: network structure - * - * Get type of network - */ -enum connman_network_type connman_network_get_type(struct connman_network *network) +static void dhcp_callback(struct connman_ipconfig *ipconfig, + struct connman_network *network, + bool success, gpointer data) { - return network->type; -} + network->connecting = false; -/** - * connman_network_get_identifier: - * @network: network structure - * - * Get identifier of network - */ -const char *connman_network_get_identifier(struct connman_network *network) -{ - return network->identifier; + if (success) + dhcp_success(network); + else + dhcp_failure(network); } -/** - * connman_network_set_index: - * @network: network structure - * @index: index number - * - * Set index number of network - */ -void connman_network_set_index(struct connman_network *network, int index) +static int set_connected_manual(struct connman_network *network) { + int err = 0; struct connman_service *service; struct connman_ipconfig *ipconfig; - service = __connman_service_lookup_from_network(network); - if (service == NULL) - goto done; + DBG("network %p", network); + + network->connecting = false; + service = connman_service_lookup_from_network(network); ipconfig = __connman_service_get_ip4config(service); + __connman_ipconfig_enable(ipconfig); - DBG("index %d service %p ip4config %p", network->index, - service, ipconfig); + if (!__connman_ipconfig_get_local(ipconfig)) + __connman_service_read_ip4config(service); - if (network->index < 0 && ipconfig == NULL) { + if (connman_setting_get_bool("AddressConflictDetection")) { + err = start_acd(network); + if (!err) + return 0; - ipconfig = __connman_service_get_ip4config(service); - if (ipconfig == NULL) - /* - * This is needed for plugins that havent set their - * ipconfig layer yet, due to not being able to get - * a network index prior to creating a service. - */ - __connman_service_create_ip4config(service, index); - else - __connman_ipconfig_set_index(ipconfig, index); + /* On error proceed without ACD. */ + } - } else { - /* If index changed, the index of ipconfig must be reset. */ - if (ipconfig == NULL) - goto done; + err = __connman_ipconfig_address_add(ipconfig); + if (err < 0) + goto err; - __connman_ipconfig_set_index(ipconfig, index); - } + err = __connman_ipconfig_gateway_add(ipconfig); + if (err < 0) + goto err; -done: - network->index = index; +err: + return err; } -/** - * connman_network_get_index: - * @network: network structure - * - * Get index number of network - */ -int connman_network_get_index(struct connman_network *network) +static void remove_dhcp_timeout(struct connman_network *network) { - return network->index; + if (network->dhcp_timeout > 0) { + g_source_remove(network->dhcp_timeout); + network->dhcp_timeout = 0; + } } -/** - * connman_network_set_group: - * @network: network structure - * @group: group name - * - * Set group name for automatic clustering - */ -void connman_network_set_group(struct connman_network *network, - const char *group) +static int set_connected_dhcp(struct connman_network *network) { - switch (network->type) { - case CONNMAN_NETWORK_TYPE_UNKNOWN: - case CONNMAN_NETWORK_TYPE_VENDOR: - return; - case CONNMAN_NETWORK_TYPE_ETHERNET: - case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN: - case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN: - case CONNMAN_NETWORK_TYPE_CELLULAR: - case CONNMAN_NETWORK_TYPE_WIFI: - case CONNMAN_NETWORK_TYPE_WIMAX: - break; - } + struct connman_service *service; + struct connman_ipconfig *ipconfig_ipv4; + int err; - if (g_strcmp0(network->group, group) == 0) { - if (group != NULL) - __connman_service_update_from_network(network); - return; - } + DBG("network %p", network); + remove_dhcp_timeout(network); - if (network->group != NULL) { - __connman_service_remove_from_network(network); + service = connman_service_lookup_from_network(network); + ipconfig_ipv4 = __connman_service_get_ip4config(service); + __connman_ipconfig_enable(ipconfig_ipv4); - g_free(network->group); + err = __connman_dhcp_start(ipconfig_ipv4, network, + dhcp_callback, NULL); + if (err < 0) { + connman_error("Can not request DHCP lease"); + return err; } - network->group = g_strdup(group); - - if (network->group != NULL) - network_probe(network); + return 0; } -/** - * connman_network_get_group: - * @network: network structure - * - * Get group name for automatic clustering - */ -const char *connman_network_get_group(struct connman_network *network) +static gboolean set_connected_dhcp_timout(gpointer data) { - return network->group; -} + struct connman_network *network = data; + struct connman_service *service; + struct connman_ipconfig *ipconfig; + enum connman_ipconfig_method method; -const char *__connman_network_get_ident(struct connman_network *network) -{ - if (network->device == NULL) - return NULL; + network->dhcp_timeout = 0; - return connman_device_get_ident(network->device); -} + service = connman_service_lookup_from_network(network); + if (!service) + return FALSE; -connman_bool_t __connman_network_get_weakness(struct connman_network *network) -{ - switch (network->type) { - case CONNMAN_NETWORK_TYPE_UNKNOWN: - case CONNMAN_NETWORK_TYPE_VENDOR: - case CONNMAN_NETWORK_TYPE_ETHERNET: - case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN: - case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN: - case CONNMAN_NETWORK_TYPE_CELLULAR: - case CONNMAN_NETWORK_TYPE_WIMAX: - break; - case CONNMAN_NETWORK_TYPE_WIFI: - if (g_strcmp0(network->wifi.mode, "adhoc") == 0) - return TRUE; - if (network->strength > 0 && network->strength < 20) - return TRUE; - break; - } + ipconfig = __connman_service_get_ip4config(service); + if (!ipconfig) + return FALSE; + + /* Method is still DHCP? */ + method = __connman_ipconfig_get_method(ipconfig); + if (method == CONNMAN_IPCONFIG_METHOD_DHCP) + set_connected_dhcp(network); return FALSE; } -connman_bool_t connman_network_get_connecting(struct connman_network *network) +void connman_network_set_connected_dhcp_later(struct connman_network *network, + uint32_t sec) { - return network->connecting; + remove_dhcp_timeout(network); + + network->dhcp_timeout = + g_timeout_add_seconds_full(G_PRIORITY_HIGH, + sec, + set_connected_dhcp_timout, + network, + NULL); } -/** - * connman_network_set_available: - * @network: network structure - * @available: availability state - * - * Change availability state of network (in range) - */ -int connman_network_set_available(struct connman_network *network, - connman_bool_t available) +static int manual_ipv6_set(struct connman_network *network, + struct connman_ipconfig *ipconfig_ipv6) { - DBG("network %p available %d", network, available); + struct connman_service *service; + int err; - if (network->available == available) - return -EALREADY; + DBG("network %p ipv6 %p", network, ipconfig_ipv6); - network->available = available; + service = connman_service_lookup_from_network(network); + if (!service) + return -EINVAL; - return 0; -} + if (!__connman_ipconfig_get_local(ipconfig_ipv6)) + __connman_service_read_ip6config(service); -/** - * connman_network_get_available: - * @network: network structure - * - * Get network available setting - */ -connman_bool_t connman_network_get_available(struct connman_network *network) -{ - return network->available; -} + __connman_ipconfig_enable_ipv6(ipconfig_ipv6); -/** - * connman_network_set_associating: - * @network: network structure - * @associating: associating state - * - * Change associating state of network - */ -int connman_network_set_associating(struct connman_network *network, - connman_bool_t associating) -{ - DBG("network %p associating %d", network, associating); + err = __connman_ipconfig_address_add(ipconfig_ipv6); + if (err < 0) { + connman_network_set_error(network, + CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL); + return err; + } - if (network->associating == associating) - return -EALREADY; + err = __connman_ipconfig_gateway_add(ipconfig_ipv6); + if (err < 0) + return err; - network->associating = associating; + __connman_device_set_network(network->device, network); - if (associating == TRUE) { - struct connman_service *service; + connman_network_set_associating(network, false); - service = __connman_service_lookup_from_network(network); - __connman_service_ipconfig_indicate_state(service, - CONNMAN_SERVICE_STATE_ASSOCIATION, - CONNMAN_IPCONFIG_TYPE_IPV4); - __connman_service_ipconfig_indicate_state(service, - CONNMAN_SERVICE_STATE_ASSOCIATION, - CONNMAN_IPCONFIG_TYPE_IPV6); - } + network->connecting = false; return 0; } -static void set_associate_error(struct connman_network *network) +static void stop_dhcpv6(struct connman_network *network) { - struct connman_service *service; + network->connecting = false; - service = __connman_service_lookup_from_network(network); - - __connman_service_indicate_error(service, - CONNMAN_SERVICE_ERROR_CONNECT_FAILED); + __connman_dhcpv6_stop(network); } -static void set_configure_error(struct connman_network *network) +static void dhcpv6_release_callback(struct connman_network *network, + enum __connman_dhcpv6_status status, + gpointer data) { - struct connman_service *service; - - service = __connman_service_lookup_from_network(network); + DBG("status %d", status); - __connman_service_indicate_error(service, - CONNMAN_SERVICE_ERROR_CONNECT_FAILED); + stop_dhcpv6(network); } -static void set_invalid_key_error(struct connman_network *network) +static void release_dhcpv6(struct connman_network *network) { - struct connman_service *service; - - service = __connman_service_lookup_from_network(network); - - __connman_service_indicate_error(service, - CONNMAN_SERVICE_ERROR_INVALID_KEY); + __connman_dhcpv6_start_release(network, dhcpv6_release_callback); + stop_dhcpv6(network); } -static void set_connect_error(struct connman_network *network) +static void dhcpv6_info_callback(struct connman_network *network, + enum __connman_dhcpv6_status status, + gpointer data) { - struct connman_service *service; - - service = __connman_service_lookup_from_network(network); + DBG("status %d", status); - __connman_service_indicate_error(service, - CONNMAN_SERVICE_ERROR_CONNECT_FAILED); + stop_dhcpv6(network); } -void connman_network_set_ipv4_method(struct connman_network *network, - enum connman_ipconfig_method method) +static int dhcpv6_set_addresses(struct connman_network *network) { struct connman_service *service; - struct connman_ipconfig *ipconfig; - - service = __connman_service_lookup_from_network(network); - if (service == NULL) - return; - - ipconfig = __connman_service_get_ip4config(service); - if (ipconfig == NULL) - return; + struct connman_ipconfig *ipconfig_ipv6; + int err = -EINVAL; - __connman_ipconfig_set_method(ipconfig, method); -} + service = connman_service_lookup_from_network(network); + if (!service) + goto err; -void connman_network_set_ipv6_method(struct connman_network *network, - enum connman_ipconfig_method method) -{ - struct connman_service *service; - struct connman_ipconfig *ipconfig; + network->connecting = false; - service = __connman_service_lookup_from_network(network); - if (service == NULL) - return; + ipconfig_ipv6 = __connman_service_get_ip6config(service); + err = __connman_ipconfig_address_add(ipconfig_ipv6); + if (err < 0) + goto err; - ipconfig = __connman_service_get_ip6config(service); - if (ipconfig == NULL) - return; + return 0; - __connman_ipconfig_set_method(ipconfig, method); +err: + connman_network_set_error(network, + CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL); + return err; } -void connman_network_set_error(struct connman_network *network, - enum connman_network_error error) -{ - DBG("nework %p, error %d", network, error); - - network->connecting = FALSE; - network->associating = FALSE; +static void autoconf_ipv6_set(struct connman_network *network); +static void dhcpv6_callback(struct connman_network *network, + enum __connman_dhcpv6_status status, gpointer data); - switch (error) { - case CONNMAN_NETWORK_ERROR_UNKNOWN: - return; - case CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL: - set_associate_error(network); - break; - case CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL: - set_configure_error(network); - break; - case CONNMAN_NETWORK_ERROR_INVALID_KEY: - set_invalid_key_error(network); +/* + * Have a separate callback for renew so that we do not do autoconf + * in wrong phase as the dhcpv6_callback() is also called when doing + * DHCPv6 solicitation. + */ +static void dhcpv6_renew_callback(struct connman_network *network, + enum __connman_dhcpv6_status status, + gpointer data) +{ + switch (status) { + case CONNMAN_DHCPV6_STATUS_SUCCEED: + dhcpv6_callback(network, status, data); break; - case CONNMAN_NETWORK_ERROR_CONNECT_FAIL: - set_connect_error(network); + case CONNMAN_DHCPV6_STATUS_FAIL: + case CONNMAN_DHCPV6_STATUS_RESTART: + stop_dhcpv6(network); + + /* restart and do solicit again. */ + autoconf_ipv6_set(network); break; } - - network_change(network); } -void connman_network_clear_error(struct connman_network *network) +static void dhcpv6_callback(struct connman_network *network, + enum __connman_dhcpv6_status status, gpointer data) { - struct connman_service *service; + DBG("status %d", status); - DBG("network %p", network); + /* Start the renew process if necessary */ + if (status == CONNMAN_DHCPV6_STATUS_SUCCEED) { - if (network == NULL) - return; + if (dhcpv6_set_addresses(network) < 0) { + stop_dhcpv6(network); + return; + } - if (network->connecting == TRUE || network->associating == TRUE) - return; + if (__connman_dhcpv6_start_renew(network, + dhcpv6_renew_callback) == -ETIMEDOUT) + dhcpv6_renew_callback(network, + CONNMAN_DHCPV6_STATUS_FAIL, + data); - service = __connman_service_lookup_from_network(network); - __connman_service_clear_error(service); + } else if (status == CONNMAN_DHCPV6_STATUS_RESTART) { + stop_dhcpv6(network); + autoconf_ipv6_set(network); + } else + stop_dhcpv6(network); } -static void set_configuration(struct connman_network *network, - enum connman_ipconfig_type type) +static void check_dhcpv6(struct nd_router_advert *reply, + unsigned int length, void *user_data) { + struct connman_network *network = user_data; struct connman_service *service; + GSList *prefixes; - DBG("network %p", network); + DBG("reply %p", reply); - if (network->device == NULL) + if (!reply) { + /* + * Router solicitation message seem to get lost easily so + * try to send it again. + */ + if (network->router_solicit_count > 0) { + DBG("re-send router solicitation %d", + network->router_solicit_count); + network->router_solicit_count--; + __connman_inet_ipv6_send_rs(network->index, RTR_SOLICITATION_INTERVAL, + check_dhcpv6, network); + return; + } + connman_network_unref(network); return; + } - __connman_device_set_network(network->device, network); - - connman_device_set_disconnected(network->device, FALSE); - - service = __connman_service_lookup_from_network(network); - __connman_service_ipconfig_indicate_state(service, - CONNMAN_SERVICE_STATE_CONFIGURATION, - type); -} + network->router_solicit_count = 0; -static void dhcp_success(struct connman_network *network) -{ - struct connman_service *service; - struct connman_ipconfig *ipconfig_ipv4; - int err; + /* + * If we were disconnected while waiting router advertisement, + * we just quit and do not start DHCPv6 + */ + if (!network->connected) { + connman_network_unref(network); + return; + } - service = __connman_service_lookup_from_network(network); - if (service == NULL) - goto err; + prefixes = __connman_inet_ipv6_get_prefixes(reply, length); - connman_network_set_associating(network, FALSE); + /* + * If IPv6 config is missing from service, then create it. + * The ipconfig might be missing if we got a rtnl message + * that disabled IPv6 config and thus removed it. This + * can happen if we are switching from one service to + * another in the same interface. The only way to get IPv6 + * config back is to re-create it here. + */ + service = connman_service_lookup_from_network(network); + if (service) { + connman_service_create_ip6config(service, network->index); - network->connecting = FALSE; + connman_network_set_associating(network, false); - ipconfig_ipv4 = __connman_service_get_ip4config(service); - err = __connman_ipconfig_address_add(ipconfig_ipv4); - if (err < 0) - goto err; + __connman_service_ipconfig_indicate_state(service, + CONNMAN_SERVICE_STATE_CONFIGURATION, + CONNMAN_IPCONFIG_TYPE_IPV6); + } - err = __connman_ipconfig_gateway_add(ipconfig_ipv4); - if (err < 0) - goto err; + /* + * We do stateful/stateless DHCPv6 if router advertisement says so. + */ + if (reply->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) { + __connman_dhcpv6_start(network, prefixes, dhcpv6_callback); + } else { + if (reply->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) + __connman_dhcpv6_start_info(network, + dhcpv6_info_callback); - return; + g_slist_free_full(prefixes, g_free); + network->connecting = false; + } -err: - connman_network_set_error(network, - CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL); + connman_network_unref(network); } -static void dhcp_failure(struct connman_network *network) +static void receive_refresh_rs_reply(struct nd_router_advert *reply, + unsigned int length, void *user_data) { - struct connman_service *service; + struct connman_network *network = user_data; - service = __connman_service_lookup_from_network(network); - if (service == NULL) - return; + DBG("reply %p", reply); - __connman_service_ipconfig_indicate_state(service, - CONNMAN_SERVICE_STATE_IDLE, - CONNMAN_IPCONFIG_TYPE_IPV4); -} + if (!reply) { + /* + * Router solicitation message seem to get lost easily so + * try to send it again. + */ + if (network->router_solicit_refresh_count > 1) { + network->router_solicit_refresh_count--; + DBG("re-send router solicitation %d", + network->router_solicit_refresh_count); + __connman_inet_ipv6_send_rs(network->index, + RS_REFRESH_TIMEOUT, + receive_refresh_rs_reply, + network); + return; + } + } -static void dhcp_callback(struct connman_network *network, - connman_bool_t success) -{ - DBG("success %d", success); + /* RS refresh not in progress anymore */ + network->router_solicit_refresh_count = 0; - if (success == TRUE) - dhcp_success(network); - else - dhcp_failure(network); + connman_network_unref(network); } -static int set_connected_fixed(struct connman_network *network) +int __connman_network_refresh_rs_ipv6(struct connman_network *network, + int index) { - struct connman_service *service; - struct connman_ipconfig *ipconfig_ipv4; - int err; - - DBG(""); - - service = __connman_service_lookup_from_network(network); - - ipconfig_ipv4 = __connman_service_get_ip4config(service); - - set_configuration(network, CONNMAN_IPCONFIG_TYPE_IPV4); - - network->connecting = FALSE; - - connman_network_set_associating(network, FALSE); + int ret = 0; - err = __connman_ipconfig_address_add(ipconfig_ipv4); - if (err < 0) - goto err; + DBG("network %p index %d", network, index); - err = __connman_ipconfig_gateway_add(ipconfig_ipv4); - if (err < 0) - goto err; + /* Send only one RS for all RDNSS entries which are about to expire */ + if (network->router_solicit_refresh_count > 0) { + DBG("RS refresh already started"); + return 0; + } - return 0; + network->router_solicit_refresh_count = RS_REFRESH_COUNT; -err: - connman_network_set_error(network, - CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL); + connman_network_ref(network); - return err; + ret = __connman_inet_ipv6_send_rs(index, RS_REFRESH_TIMEOUT, + receive_refresh_rs_reply, network); + return ret; } -static void set_connected_manual(struct connman_network *network) +static void autoconf_ipv6_set(struct connman_network *network) { struct connman_service *service; struct connman_ipconfig *ipconfig; - int err; + int index; DBG("network %p", network); - service = __connman_service_lookup_from_network(network); + if (network->router_solicit_count > 0) { + /* + * The autoconfiguration is already pending and we have sent + * router solicitation messages and are now waiting answers. + * There is no need to continue any further. + */ + DBG("autoconfiguration already started"); + return; + } - ipconfig = __connman_service_get_ip4config(service); + __connman_device_set_network(network->device, network); - if (__connman_ipconfig_get_local(ipconfig) == NULL) - __connman_service_read_ip4config(service); + service = connman_service_lookup_from_network(network); + if (!service) + return; - set_configuration(network, CONNMAN_IPCONFIG_TYPE_IPV4); + ipconfig = __connman_service_get_ip6config(service); + if (!ipconfig) + return; - err = __connman_ipconfig_address_add(ipconfig); - if (err < 0) - goto err; + __connman_ipconfig_enable(ipconfig); - err = __connman_ipconfig_gateway_add(ipconfig); - if (err < 0) - goto err; + __connman_ipconfig_enable_ipv6(ipconfig); - network->connecting = FALSE; + __connman_ipconfig_address_remove(ipconfig); - connman_network_set_associating(network, FALSE); + index = __connman_ipconfig_get_index(ipconfig); - return; + connman_network_ref(network); -err: - connman_network_set_error(network, - CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL); - return; + /* Try to get stateless DHCPv6 information, RFC 3736 */ + network->router_solicit_count = 3; + __connman_inet_ipv6_send_rs(index, RTR_SOLICITATION_INTERVAL, + check_dhcpv6, network); } -static int set_connected_dhcp(struct connman_network *network) +static void set_connected(struct connman_network *network) { - int err; + struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6; + struct connman_service *service; - DBG("network %p", network); + if (network->connected) + return; - set_configuration(network, CONNMAN_IPCONFIG_TYPE_IPV4); + connman_network_set_associating(network, false); - err = __connman_dhcp_start(network, dhcp_callback); - if (err < 0) { - connman_error("Can not request DHCP lease"); - return err; - } + network->connected = true; - return 0; -} + service = connman_service_lookup_from_network(network); -static int manual_ipv6_set(struct connman_network *network, - struct connman_ipconfig *ipconfig_ipv6) + ipconfig_ipv4 = __connman_service_get_ip4config(service); + ipconfig_ipv6 = __connman_service_get_ip6config(service); + + DBG("service %p ipv4 %p ipv6 %p", service, ipconfig_ipv4, + ipconfig_ipv6); + + __connman_network_enable_ipconfig(network, ipconfig_ipv4); + __connman_network_enable_ipconfig(network, ipconfig_ipv6); +} + +static void set_disconnected(struct connman_network *network) { + struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6; + enum connman_ipconfig_method ipv4_method, ipv6_method; + enum connman_service_state state; struct connman_service *service; - int err; - DBG("network %p ipv6 %p", network, ipconfig_ipv6); + service = connman_service_lookup_from_network(network); - service = __connman_service_lookup_from_network(network); - if (service == NULL) - return -EINVAL; + ipconfig_ipv4 = __connman_service_get_ip4config(service); + ipconfig_ipv6 = __connman_service_get_ip6config(service); - if (__connman_ipconfig_get_local(ipconfig_ipv6) == NULL) - __connman_service_read_ip6config(service); + DBG("service %p ipv4 %p ipv6 %p", service, ipconfig_ipv4, + ipconfig_ipv6); - err = __connman_ipconfig_address_add(ipconfig_ipv6); - if (err < 0) { - connman_network_set_error(network, - CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL); - return err; + ipv4_method = __connman_ipconfig_get_method(ipconfig_ipv4); + ipv6_method = __connman_ipconfig_get_method(ipconfig_ipv6); + + DBG("method ipv4 %d ipv6 %d", ipv4_method, ipv6_method); + + /* + * Resetting solicit count here will prevent the RS resend loop + * from sending packets in check_dhcpv6() + */ + network->router_solicit_count = 0; + + __connman_device_set_network(network->device, NULL); + + if (network->connected) { + switch (ipv6_method) { + case CONNMAN_IPCONFIG_METHOD_UNKNOWN: + case CONNMAN_IPCONFIG_METHOD_OFF: + case CONNMAN_IPCONFIG_METHOD_FIXED: + case CONNMAN_IPCONFIG_METHOD_MANUAL: + break; + case CONNMAN_IPCONFIG_METHOD_DHCP: + case CONNMAN_IPCONFIG_METHOD_AUTO: + release_dhcpv6(network); + break; + } + + switch (ipv4_method) { + case CONNMAN_IPCONFIG_METHOD_UNKNOWN: + case CONNMAN_IPCONFIG_METHOD_OFF: + case CONNMAN_IPCONFIG_METHOD_FIXED: + case CONNMAN_IPCONFIG_METHOD_MANUAL: + break; + case CONNMAN_IPCONFIG_METHOD_AUTO: + /* + * If the current method is AUTO then next time we + * try first DHCP. DHCP also needs to be stopped + * in this case because if we fell in AUTO means + * that DHCP was launched for IPv4 but it failed. + */ + __connman_ipconfig_set_method(ipconfig_ipv4, + CONNMAN_IPCONFIG_METHOD_DHCP); + __connman_service_notify_ipv4_configuration(service); + /* fall through */ + case CONNMAN_IPCONFIG_METHOD_DHCP: + remove_dhcp_timeout(network); + __connman_dhcp_stop(ipconfig_ipv4); + break; + } } - err = __connman_ipconfig_gateway_add(ipconfig_ipv6); - if (err < 0) - return err; + /* + * We only set the disconnect state if we were not in idle + * or in failure. It does not make sense to go to disconnect + * state if we were not connected. + */ + state = __connman_service_ipconfig_get_state(service, + CONNMAN_IPCONFIG_TYPE_IPV4); + if (state != CONNMAN_SERVICE_STATE_IDLE && + state != CONNMAN_SERVICE_STATE_FAILURE) + __connman_service_ipconfig_indicate_state(service, + CONNMAN_SERVICE_STATE_DISCONNECT, + CONNMAN_IPCONFIG_TYPE_IPV4); - __connman_connection_gateway_activate(service, + state = __connman_service_ipconfig_get_state(service, CONNMAN_IPCONFIG_TYPE_IPV6); + if (state != CONNMAN_SERVICE_STATE_IDLE && + state != CONNMAN_SERVICE_STATE_FAILURE) + __connman_service_ipconfig_indicate_state(service, + CONNMAN_SERVICE_STATE_DISCONNECT, + CONNMAN_IPCONFIG_TYPE_IPV6); - __connman_device_set_network(network->device, network); + if (network->connected) { + __connman_connection_gateway_remove(service, + CONNMAN_IPCONFIG_TYPE_ALL); + + __connman_ipconfig_address_unset(ipconfig_ipv4); + __connman_ipconfig_address_unset(ipconfig_ipv6); + + /* + * Special handling for IPv6 autoconfigured address. + * The simplest way to remove autoconfigured routes is to + * disable IPv6 temporarily so that kernel will do the cleanup + * automagically. + */ + if (ipv6_method == CONNMAN_IPCONFIG_METHOD_AUTO) { + __connman_ipconfig_disable_ipv6(ipconfig_ipv6); + __connman_ipconfig_enable_ipv6(ipconfig_ipv6); + } + } + + __connman_service_ipconfig_indicate_state(service, + CONNMAN_SERVICE_STATE_IDLE, + CONNMAN_IPCONFIG_TYPE_IPV4); + + __connman_service_ipconfig_indicate_state(service, + CONNMAN_SERVICE_STATE_IDLE, + CONNMAN_IPCONFIG_TYPE_IPV6); + + network->connecting = false; + network->connected = false; + + connman_network_set_associating(network, false); +} + + + +static int network_probe(struct connman_network *network) +{ + GSList *list; + struct connman_network_driver *driver = NULL; + + DBG("network %p name %s", network, network->name); + + if (network->driver) + return -EALREADY; + + for (list = driver_list; list; list = list->next) { + driver = list->data; + + if (!match_driver(network, driver)) { + driver = NULL; + continue; + } + + DBG("driver %p name %s", driver, driver->name); + + if (driver->probe(network) == 0) + break; + + driver = NULL; + } + + if (!driver) + return -ENODEV; + + if (!network->group) + return -EINVAL; + + switch (network->type) { + case CONNMAN_NETWORK_TYPE_UNKNOWN: + case CONNMAN_NETWORK_TYPE_VENDOR: + return 0; + case CONNMAN_NETWORK_TYPE_ETHERNET: + case CONNMAN_NETWORK_TYPE_GADGET: + case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN: + case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN: + case CONNMAN_NETWORK_TYPE_CELLULAR: + case CONNMAN_NETWORK_TYPE_WIFI: + network->driver = driver; + if (!__connman_service_create_from_network(network)) { + network->driver = NULL; + return -EINVAL; + } + } + + return 0; +} + +static void network_remove(struct connman_network *network) +{ + DBG("network %p name %s", network, network->name); + + if (!network->driver) + return; + + if (network->connected) + set_disconnected(network); + + switch (network->type) { + case CONNMAN_NETWORK_TYPE_UNKNOWN: + case CONNMAN_NETWORK_TYPE_VENDOR: + break; + case CONNMAN_NETWORK_TYPE_ETHERNET: + case CONNMAN_NETWORK_TYPE_GADGET: + case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN: + case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN: + case CONNMAN_NETWORK_TYPE_CELLULAR: + case CONNMAN_NETWORK_TYPE_WIFI: + if (network->group) { + __connman_service_remove_from_network(network); + + g_free(network->group); + network->group = NULL; + } + break; + } + + if (network->driver->remove) + network->driver->remove(network); + + network->driver = NULL; +} + +static void probe_driver(struct connman_network_driver *driver) +{ + GSList *list; + + DBG("driver %p name %s", driver, driver->name); + + for (list = network_list; list; list = list->next) { + struct connman_network *network = list->data; + + if (network->driver) + continue; + + if (driver->type != network->type) + continue; + + if (driver->probe(network) < 0) + continue; + + network->driver = driver; + } +} + +static gint compare_priority(gconstpointer a, gconstpointer b) +{ + const struct connman_network_driver *driver1 = a; + const struct connman_network_driver *driver2 = b; + + return driver2->priority - driver1->priority; +} + +/** + * connman_network_driver_register: + * @driver: network driver definition + * + * Register a new network driver + * + * Returns: %0 on success + */ +int connman_network_driver_register(struct connman_network_driver *driver) +{ + DBG("driver %p name %s", driver, driver->name); - connman_device_set_disconnected(network->device, FALSE); + driver_list = g_slist_insert_sorted(driver_list, driver, + compare_priority); - network->connecting = FALSE; + probe_driver(driver); return 0; } -static void stop_dhcpv6(struct connman_network *network) -{ - __connman_dhcpv6_stop(network); +/** + * connman_network_driver_unregister: + * @driver: network driver definition + * + * Remove a previously registered network driver + */ +void connman_network_driver_unregister(struct connman_network_driver *driver) +{ + GSList *list; + + DBG("driver %p name %s", driver, driver->name); + + driver_list = g_slist_remove(driver_list, driver); + + for (list = network_list; list; list = list->next) { + struct connman_network *network = list->data; + + if (network->driver == driver) + network_remove(network); + } +} + +static void network_destruct(struct connman_network *network) +{ + DBG("network %p name %s", network, network->name); + + g_free(network->wifi.ssid); + g_free(network->wifi.mode); + g_free(network->wifi.security); + g_free(network->wifi.passphrase); + g_free(network->wifi.eap); + g_free(network->wifi.identity); + g_free(network->wifi.anonymous_identity); + g_free(network->wifi.agent_identity); + g_free(network->wifi.ca_cert_path); + g_free(network->wifi.subject_match); + g_free(network->wifi.altsubject_match); + g_free(network->wifi.domain_suffix_match); + g_free(network->wifi.domain_match); + g_free(network->wifi.client_cert_path); + g_free(network->wifi.private_key_path); + g_free(network->wifi.private_key_passphrase); + g_free(network->wifi.phase2_auth); + g_free(network->wifi.pin_wps); + + g_free(network->path); + g_free(network->group); + g_free(network->node); + g_free(network->name); + g_free(network->identifier); + acd_host_free(network->acd_host); + + network->device = NULL; + + g_free(network); +} + +/** + * connman_network_create: + * @identifier: network identifier (for example an unique name) + * + * Allocate a new network and assign the #identifier to it. + * + * Returns: a newly-allocated #connman_network structure + */ +struct connman_network *connman_network_create(const char *identifier, + enum connman_network_type type) +{ + struct connman_network *network; + char *ident; + + network = g_try_new0(struct connman_network, 1); + if (!network) + return NULL; + + network->refcount = 1; + + ident = g_strdup(identifier); + + if (!ident) { + g_free(network); + return NULL; + } + + network->type = type; + network->identifier = ident; + network->acd_host = NULL; + network->ipv4ll_timeout = 0; + + network_list = g_slist_prepend(network_list, network); + + network->dhcp_timeout = 0; + + DBG("network %p identifier %s type %s", network, identifier, + type2string(type)); + return network; +} + +/** + * connman_network_ref: + * @network: network structure + * + * Increase reference counter of network + */ +struct connman_network * +connman_network_ref_debug(struct connman_network *network, + const char *file, int line, const char *caller) +{ + DBG("%p name %s ref %d by %s:%d:%s()", network, network->name, + network->refcount + 1, file, line, caller); + + __sync_fetch_and_add(&network->refcount, 1); + + return network; +} + +/** + * connman_network_unref: + * @network: network structure + * + * Decrease reference counter of network + */ +void connman_network_unref_debug(struct connman_network *network, + const char *file, int line, const char *caller) +{ + DBG("%p name %s ref %d by %s:%d:%s()", network, network->name, + network->refcount - 1, file, line, caller); + + if (__sync_fetch_and_sub(&network->refcount, 1) != 1) + return; + + network_list = g_slist_remove(network_list, network); + + network_destruct(network); } -static void dhcpv6_release_callback(struct connman_network *network, - connman_bool_t success) +const char *__connman_network_get_type(struct connman_network *network) { - DBG("success %d", success); - - stop_dhcpv6(network); + return type2string(network->type); } -static void release_dhcpv6(struct connman_network *network) +/** + * connman_network_get_type: + * @network: network structure + * + * Get type of network + */ +enum connman_network_type connman_network_get_type( + struct connman_network *network) { - __connman_dhcpv6_start_release(network, dhcpv6_release_callback); - stop_dhcpv6(network); + return network->type; } -static void dhcpv6_info_callback(struct connman_network *network, - connman_bool_t success) +/** + * connman_network_get_identifier: + * @network: network structure + * + * Get identifier of network + */ +const char *connman_network_get_identifier(struct connman_network *network) { - DBG("success %d", success); - - stop_dhcpv6(network); + return network->identifier; } -static gboolean dhcpv6_set_addresses(struct connman_network *network) +/** + * connman_network_set_index: + * @network: network structure + * @index: index number + * + * Set index number of network + */ +void connman_network_set_index(struct connman_network *network, int index) { struct connman_service *service; - struct connman_ipconfig *ipconfig_ipv6; - int err = -EINVAL; - - service = __connman_service_lookup_from_network(network); - if (service == NULL) - goto err; + struct connman_ipconfig *ipconfig; - connman_network_set_associating(network, FALSE); + service = connman_service_lookup_from_network(network); + if (!service) + goto done; - network->connecting = FALSE; + ipconfig = __connman_service_get_ip4config(service); + if (ipconfig) { + __connman_ipconfig_set_index(ipconfig, index); - ipconfig_ipv6 = __connman_service_get_ip6config(service); - err = __connman_ipconfig_address_add(ipconfig_ipv6); - if (err < 0) - goto err; + DBG("index %d service %p ip4config %p", network->index, + service, ipconfig); + } - err = __connman_ipconfig_gateway_add(ipconfig_ipv6); - if (err < 0) - goto err; + ipconfig = __connman_service_get_ip6config(service); + if (ipconfig) { + __connman_ipconfig_set_index(ipconfig, index); - return 0; + DBG("index %d service %p ip6config %p", network->index, + service, ipconfig); + } -err: - connman_network_set_error(network, - CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL); - return err; +done: + network->index = index; } -static void autoconf_ipv6_set(struct connman_network *network); -static void dhcpv6_callback(struct connman_network *network, - connman_bool_t success); +/** + * connman_network_get_index: + * @network: network structure + * + * Get index number of network + */ +int connman_network_get_index(struct connman_network *network) +{ + return network->index; +} -/* - * Have a separate callback for renew so that we do not do autoconf - * in wrong phase as the dhcpv6_callback() is also called when doing - * DHCPv6 solicitation. +/** + * connman_network_set_group: + * @network: network structure + * @group: group name + * + * Set group name for automatic clustering */ -static void dhcpv6_renew_callback(struct connman_network *network, - connman_bool_t success) +void connman_network_set_group(struct connman_network *network, + const char *group) { - if (success == TRUE) - dhcpv6_callback(network, success); - else { - stop_dhcpv6(network); + switch (network->type) { + case CONNMAN_NETWORK_TYPE_UNKNOWN: + case CONNMAN_NETWORK_TYPE_VENDOR: + return; + case CONNMAN_NETWORK_TYPE_ETHERNET: + case CONNMAN_NETWORK_TYPE_GADGET: + case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN: + case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN: + case CONNMAN_NETWORK_TYPE_CELLULAR: + case CONNMAN_NETWORK_TYPE_WIFI: + break; + } - /* restart and do solicit again. */ - autoconf_ipv6_set(network); + if (g_strcmp0(network->group, group) == 0) { + if (group) + __connman_service_update_from_network(network); + return; } -} -static void dhcpv6_callback(struct connman_network *network, - connman_bool_t success) -{ - DBG("success %d", success); + if (network->group) { + __connman_service_remove_from_network(network); - /* Start the renew process if necessary */ - if (success == TRUE) { + g_free(network->group); + } - if (dhcpv6_set_addresses(network) < 0) { - stop_dhcpv6(network); - return; - } + network->group = g_strdup(group); - if (__connman_dhcpv6_start_renew(network, - dhcpv6_renew_callback) == -ETIMEDOUT) - dhcpv6_renew_callback(network, FALSE); - } else - stop_dhcpv6(network); + if (network->group) + network_probe(network); } -static void check_dhcpv6(struct nd_router_advert *reply, - unsigned int length, void *user_data) +/** + * connman_network_get_group: + * @network: network structure + * + * Get group name for automatic clustering + */ +const char *connman_network_get_group(struct connman_network *network) { - struct connman_network *network = user_data; - GSList *prefixes; + return network->group; +} - DBG("reply %p", reply); +const char *__connman_network_get_ident(struct connman_network *network) +{ + if (!network->device) + return NULL; - if (reply == NULL) { - /* - * Router solicitation message seem to get lost easily so - * try to send it again. - */ - if (network->router_solicit_count > 0) { - DBG("re-send router solicitation %d", - network->router_solicit_count); - network->router_solicit_count--; - __connman_inet_ipv6_send_rs(network->index, 1, - check_dhcpv6, network); - return; - } - connman_network_unref(network); - return; + return connman_device_get_ident(network->device); +} + +bool __connman_network_get_weakness(struct connman_network *network) +{ + switch (network->type) { + case CONNMAN_NETWORK_TYPE_UNKNOWN: + case CONNMAN_NETWORK_TYPE_VENDOR: + case CONNMAN_NETWORK_TYPE_ETHERNET: + case CONNMAN_NETWORK_TYPE_GADGET: + case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN: + case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN: + case CONNMAN_NETWORK_TYPE_CELLULAR: + break; + case CONNMAN_NETWORK_TYPE_WIFI: + if (network->strength > 0 && network->strength < 20) + return true; + break; } - network->router_solicit_count = 0; + return false; +} - /* - * If we were disconnected while waiting router advertisement, - * we just quit and do not start DHCPv6 - */ - if (network->connected == FALSE) { - connman_network_unref(network); - return; - } +bool connman_network_get_connecting(struct connman_network *network) +{ + return network->connecting; +} - prefixes = __connman_inet_ipv6_get_prefixes(reply, length); +/** + * connman_network_set_available: + * @network: network structure + * @available: availability state + * + * Change availability state of network (in range) + */ +int connman_network_set_available(struct connman_network *network, + bool available) +{ + DBG("network %p available %d", network, available); - /* - * We do stateful/stateless DHCPv6 if router advertisement says so. - */ - if (reply->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) - __connman_dhcpv6_start(network, prefixes, dhcpv6_callback); - else if (reply->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) - __connman_dhcpv6_start_info(network, dhcpv6_info_callback); + if (network->available == available) + return -EALREADY; - connman_network_unref(network); + network->available = available; + + return 0; } -static void autoconf_ipv6_set(struct connman_network *network) +/** + * connman_network_get_available: + * @network: network structure + * + * Get network available setting + */ +bool connman_network_get_available(struct connman_network *network) { - struct connman_service *service; - struct connman_ipconfig *ipconfig; - int index; - - DBG("network %p", network); + return network->available; +} - if (network->router_solicit_count > 0) { - /* - * The autoconfiguration is already pending and we have sent - * router solicitation messages and are now waiting answers. - * There is no need to continue any further. - */ - DBG("autoconfiguration already started"); - return; - } +/** + * connman_network_set_associating: + * @network: network structure + * @associating: associating state + * + * Change associating state of network + */ +int connman_network_set_associating(struct connman_network *network, + bool associating) +{ + DBG("network %p associating %d", network, associating); - __connman_device_set_network(network->device, network); + if (network->associating == associating) + return -EALREADY; - connman_device_set_disconnected(network->device, FALSE); + network->associating = associating; - network->connecting = FALSE; + if (associating) { + struct connman_service *service; - service = __connman_service_lookup_from_network(network); - if (service == NULL) - return; + service = connman_service_lookup_from_network(network); + __connman_service_ipconfig_indicate_state(service, + CONNMAN_SERVICE_STATE_ASSOCIATION, + CONNMAN_IPCONFIG_TYPE_IPV4); + __connman_service_ipconfig_indicate_state(service, + CONNMAN_SERVICE_STATE_ASSOCIATION, + CONNMAN_IPCONFIG_TYPE_IPV6); + } - ipconfig = __connman_service_get_ip6config(service); - if (ipconfig == NULL) - return; + return 0; +} - index = __connman_ipconfig_get_index(ipconfig); +static void set_associate_error(struct connman_network *network) +{ + struct connman_service *service; - connman_network_ref(network); + service = connman_service_lookup_from_network(network); - /* Try to get stateless DHCPv6 information, RFC 3736 */ - network->router_solicit_count = 3; - __connman_inet_ipv6_send_rs(index, 1, check_dhcpv6, network); + __connman_service_indicate_error(service, + CONNMAN_SERVICE_ERROR_CONNECT_FAILED); } -static void set_connected(struct connman_network *network) +static void set_configure_error(struct connman_network *network) { - struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6; - enum connman_ipconfig_method ipv4_method, ipv6_method; struct connman_service *service; - int ret; - if (network->connected == TRUE) - return; + service = connman_service_lookup_from_network(network); - network->connected = TRUE; + __connman_service_indicate_error(service, + CONNMAN_SERVICE_ERROR_CONNECT_FAILED); +} - service = __connman_service_lookup_from_network(network); +static void set_invalid_key_error(struct connman_network *network) +{ + struct connman_service *service; - ipconfig_ipv4 = __connman_service_get_ip4config(service); - ipconfig_ipv6 = __connman_service_get_ip6config(service); + service = connman_service_lookup_from_network(network); - DBG("service %p ipv4 %p ipv6 %p", service, ipconfig_ipv4, - ipconfig_ipv6); + __connman_service_indicate_error(service, + CONNMAN_SERVICE_ERROR_INVALID_KEY); +} - ipv4_method = __connman_ipconfig_get_method(ipconfig_ipv4); - ipv6_method = __connman_ipconfig_get_method(ipconfig_ipv6); +static void set_connect_error(struct connman_network *network) +{ + struct connman_service *service; - DBG("method ipv4 %d ipv6 %d", ipv4_method, ipv6_method); + service = connman_service_lookup_from_network(network); - switch (ipv6_method) { - case CONNMAN_IPCONFIG_METHOD_UNKNOWN: - case CONNMAN_IPCONFIG_METHOD_OFF: - break; - case CONNMAN_IPCONFIG_METHOD_DHCP: - case CONNMAN_IPCONFIG_METHOD_AUTO: - autoconf_ipv6_set(network); - break; - case CONNMAN_IPCONFIG_METHOD_FIXED: - case CONNMAN_IPCONFIG_METHOD_MANUAL: - ret = manual_ipv6_set(network, ipconfig_ipv6); - if (ret != 0) { - connman_network_set_error(network, - CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL); - return; - } - break; - } + __connman_service_indicate_error(service, + CONNMAN_SERVICE_ERROR_CONNECT_FAILED); +} - switch (ipv4_method) { - case CONNMAN_IPCONFIG_METHOD_UNKNOWN: - case CONNMAN_IPCONFIG_METHOD_OFF: - case CONNMAN_IPCONFIG_METHOD_AUTO: - return; - case CONNMAN_IPCONFIG_METHOD_FIXED: - if (set_connected_fixed(network) < 0) { - connman_network_set_error(network, - CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL); - return; - } - return; - case CONNMAN_IPCONFIG_METHOD_MANUAL: - set_connected_manual(network); - return; - case CONNMAN_IPCONFIG_METHOD_DHCP: - if (set_connected_dhcp(network) < 0) { - connman_network_set_error(network, - CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL); - return; - } - } +static void set_blocked_error(struct connman_network *network) +{ + struct connman_service *service; - network->connecting = FALSE; + service = connman_service_lookup_from_network(network); - connman_network_set_associating(network, FALSE); + __connman_service_indicate_error(service, + CONNMAN_SERVICE_ERROR_BLOCKED); } -static void set_disconnected(struct connman_network *network) +void connman_network_set_ipv4_method(struct connman_network *network, + enum connman_ipconfig_method method) { - struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6; - enum connman_ipconfig_method ipv4_method, ipv6_method; - enum connman_service_state state; struct connman_service *service; + struct connman_ipconfig *ipconfig; - if (network->connected == FALSE) + service = connman_service_lookup_from_network(network); + if (!service) return; - network->connected = FALSE; - - service = __connman_service_lookup_from_network(network); + ipconfig = __connman_service_get_ip4config(service); + if (!ipconfig) + return; - ipconfig_ipv4 = __connman_service_get_ip4config(service); - ipconfig_ipv6 = __connman_service_get_ip6config(service); + __connman_ipconfig_set_method(ipconfig, method); +} - DBG("service %p ipv4 %p ipv6 %p", service, ipconfig_ipv4, - ipconfig_ipv6); +void connman_network_set_ipv6_method(struct connman_network *network, + enum connman_ipconfig_method method) +{ + struct connman_service *service; + struct connman_ipconfig *ipconfig; - ipv4_method = __connman_ipconfig_get_method(ipconfig_ipv4); - ipv6_method = __connman_ipconfig_get_method(ipconfig_ipv6); + service = connman_service_lookup_from_network(network); + if (!service) + return; - DBG("method ipv4 %d ipv6 %d", ipv4_method, ipv6_method); + ipconfig = __connman_service_get_ip6config(service); + if (!ipconfig) + return; - /* - * Resetting solicit count here will prevent the RS resend loop - * from sending packets in check_dhcpv6() - */ - network->router_solicit_count = 0; + __connman_ipconfig_set_method(ipconfig, method); +} - __connman_device_set_network(network->device, NULL); +void connman_network_set_error(struct connman_network *network, + enum connman_network_error error) +{ + DBG("network %p error %d", network, error); - switch (ipv6_method) { - case CONNMAN_IPCONFIG_METHOD_UNKNOWN: - case CONNMAN_IPCONFIG_METHOD_OFF: - case CONNMAN_IPCONFIG_METHOD_FIXED: - case CONNMAN_IPCONFIG_METHOD_MANUAL: + switch (error) { + case CONNMAN_NETWORK_ERROR_UNKNOWN: + return; + case CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL: + set_associate_error(network); break; - case CONNMAN_IPCONFIG_METHOD_DHCP: - case CONNMAN_IPCONFIG_METHOD_AUTO: - release_dhcpv6(network); + case CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL: + set_configure_error(network); break; - } - - switch (ipv4_method) { - case CONNMAN_IPCONFIG_METHOD_UNKNOWN: - case CONNMAN_IPCONFIG_METHOD_OFF: - case CONNMAN_IPCONFIG_METHOD_AUTO: - case CONNMAN_IPCONFIG_METHOD_FIXED: - case CONNMAN_IPCONFIG_METHOD_MANUAL: + case CONNMAN_NETWORK_ERROR_INVALID_KEY: + set_invalid_key_error(network); break; - case CONNMAN_IPCONFIG_METHOD_DHCP: - __connman_dhcp_stop(network); + case CONNMAN_NETWORK_ERROR_CONNECT_FAIL: + set_connect_error(network); + break; + case CONNMAN_NETWORK_ERROR_BLOCKED: + set_blocked_error(network); break; } - /* - * We only set the disconnect state if we were not in idle - * or in failure. It does not make sense to go to disconnect - * state if we were not connected. - */ - state = __connman_service_ipconfig_get_state(service, - CONNMAN_IPCONFIG_TYPE_IPV4); - if (state != CONNMAN_SERVICE_STATE_IDLE && - state != CONNMAN_SERVICE_STATE_FAILURE) - __connman_service_ipconfig_indicate_state(service, - CONNMAN_SERVICE_STATE_DISCONNECT, - CONNMAN_IPCONFIG_TYPE_IPV4); - - state = __connman_service_ipconfig_get_state(service, - CONNMAN_IPCONFIG_TYPE_IPV6); - if (state != CONNMAN_SERVICE_STATE_IDLE && - state != CONNMAN_SERVICE_STATE_FAILURE) - __connman_service_ipconfig_indicate_state(service, - CONNMAN_SERVICE_STATE_DISCONNECT, - CONNMAN_IPCONFIG_TYPE_IPV6); - - __connman_connection_gateway_remove(service, - CONNMAN_IPCONFIG_TYPE_ALL); - - __connman_ipconfig_address_unset(ipconfig_ipv4); - __connman_ipconfig_address_unset(ipconfig_ipv6); - - /* - * Special handling for IPv6 autoconfigured address. - * The simplest way to remove autoconfigured routes is to - * disable IPv6 temporarily so that kernel will do the cleanup - * automagically. - */ - if (ipv6_method == CONNMAN_IPCONFIG_METHOD_AUTO) { - __connman_ipconfig_disable_ipv6(ipconfig_ipv6); - __connman_ipconfig_enable_ipv6(ipconfig_ipv6); - } - - __connman_service_ipconfig_indicate_state(service, - CONNMAN_SERVICE_STATE_IDLE, - CONNMAN_IPCONFIG_TYPE_IPV4); - - __connman_service_ipconfig_indicate_state(service, - CONNMAN_SERVICE_STATE_IDLE, - CONNMAN_IPCONFIG_TYPE_IPV6); - - network->connecting = FALSE; - - connman_network_set_associating(network, FALSE); + __connman_network_disconnect(network); } /** @@ -1342,24 +1673,23 @@ static void set_disconnected(struct connman_network *network) * Change connected state of network */ int connman_network_set_connected(struct connman_network *network, - connman_bool_t connected) + bool connected) { DBG("network %p connected %d/%d connecting %d associating %d", network, network->connected, connected, network->connecting, network->associating); - if ((network->connecting == TRUE || network->associating == TRUE) && - connected == FALSE) { + if ((network->connecting || network->associating) && + !connected) { connman_network_set_error(network, CONNMAN_NETWORK_ERROR_CONNECT_FAIL); - if (__connman_network_disconnect(network) == 0) - return 0; + return 0; } if (network->connected == connected) return -EALREADY; - if (connected == FALSE) + if (!connected) set_disconnected(network); else set_connected(network); @@ -1373,7 +1703,7 @@ int connman_network_set_connected(struct connman_network *network, * * Get network connection status */ -connman_bool_t connman_network_get_connected(struct connman_network *network) +bool connman_network_get_connected(struct connman_network *network) { return network->connected; } @@ -1384,37 +1714,60 @@ connman_bool_t connman_network_get_connected(struct connman_network *network) * * Get network associating status */ -connman_bool_t connman_network_get_associating(struct connman_network *network) +bool connman_network_get_associating(struct connman_network *network) { return network->associating; } +void connman_network_clear_hidden(void *user_data) +{ + if (!user_data) + return; + + DBG("user_data %p", user_data); + + /* + * Hidden service does not have a connect timeout so + * we do not need to remove it. We can just return + * error to the caller telling that we could not find + * any network that we could connect to. + */ + connman_dbus_reply_pending(user_data, EIO, NULL); +} + int connman_network_connect_hidden(struct connman_network *network, - char *identity, char* passphrase) + char *identity, char *passphrase, void *user_data) { int err = 0; struct connman_service *service; - DBG(""); + service = connman_service_lookup_from_network(network); + + DBG("network %p service %p user_data %p", network, service, user_data); - service = __connman_service_lookup_from_network(network); - if (service == NULL) + if (!service) return -EINVAL; - if (identity != NULL) + if (identity) __connman_service_set_agent_identity(service, identity); - if (passphrase != NULL) - err = __connman_service_add_passphrase(service, passphrase); + if (passphrase) + err = __connman_service_set_passphrase(service, passphrase); if (err == -ENOKEY) { __connman_service_indicate_error(service, CONNMAN_SERVICE_ERROR_INVALID_KEY); - return err; + goto out; } else { - __connman_service_set_userconnect(service, TRUE); - return __connman_service_connect(service); + __connman_service_set_hidden(service); + __connman_service_set_hidden_data(service, user_data); + return __connman_service_connect(service, + CONNMAN_SERVICE_CONNECT_REASON_USER); } + +out: + __connman_service_return_error(service, -err, user_data); + return err; } /** @@ -1429,32 +1782,31 @@ int __connman_network_connect(struct connman_network *network) DBG("network %p", network); - if (network->connected == TRUE) + if (network->connected) return -EISCONN; - if (network->connecting == TRUE || network->associating == TRUE) + if (network->connecting || network->associating) return -EALREADY; - if (network->driver == NULL) + if (!network->driver) return -EUNATCH; - if (network->driver->connect == NULL) + if (!network->driver->connect) return -ENOSYS; - if (network->device == NULL) + if (!network->device) return -ENODEV; - network->connecting = TRUE; - __connman_device_disconnect(network->device); + network->connecting = true; + err = network->driver->connect(network); if (err < 0) { if (err == -EINPROGRESS) - connman_network_set_associating(network, TRUE); - else { - network->connecting = FALSE; - } + connman_network_set_associating(network, true); + else + network->connecting = false; return err; } @@ -1472,60 +1824,45 @@ int __connman_network_connect(struct connman_network *network) */ int __connman_network_disconnect(struct connman_network *network) { - int err; + int err = 0; DBG("network %p", network); - if (network->connected == FALSE && network->connecting == FALSE && - network->associating == FALSE) + remove_ipv4ll_timeout(network); + if (network->acd_host) + acd_host_stop(network->acd_host); + + if (!network->connected && !network->connecting && + !network->associating) return -ENOTCONN; - if (network->driver == NULL) + if (!network->driver) return -EUNATCH; - if (network->driver->disconnect == NULL) - return -ENOSYS; + network->connecting = false; - network->connecting = FALSE; + if (network->driver->disconnect) + err = network->driver->disconnect(network); - err = network->driver->disconnect(network); - if (err == 0) + if (err != -EINPROGRESS) set_disconnected(network); return err; } -static int manual_ipv4_set(struct connman_network *network, - struct connman_ipconfig *ipconfig) -{ - struct connman_service *service; - int err; - - service = __connman_service_lookup_from_network(network); - if (service == NULL) - return -EINVAL; - - err = __connman_ipconfig_address_add(ipconfig); - if (err < 0) { - connman_network_set_error(network, - CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL); - return err; - } - - return __connman_ipconfig_gateway_add(ipconfig); -} - int __connman_network_clear_ipconfig(struct connman_network *network, struct connman_ipconfig *ipconfig) { struct connman_service *service; + struct connman_ipconfig *ipconfig_ipv4; enum connman_ipconfig_method method; enum connman_ipconfig_type type; - service = __connman_service_lookup_from_network(network); - if (service == NULL) + service = connman_service_lookup_from_network(network); + if (!service) return -EINVAL; + ipconfig_ipv4 = __connman_service_get_ip4config(service); method = __connman_ipconfig_get_method(ipconfig); type = __connman_ipconfig_get_config_type(ipconfig); @@ -1534,14 +1871,17 @@ int __connman_network_clear_ipconfig(struct connman_network *network, case CONNMAN_IPCONFIG_METHOD_OFF: case CONNMAN_IPCONFIG_METHOD_FIXED: return -EINVAL; - case CONNMAN_IPCONFIG_METHOD_AUTO: - release_dhcpv6(network); - break; case CONNMAN_IPCONFIG_METHOD_MANUAL: __connman_ipconfig_address_remove(ipconfig); break; + case CONNMAN_IPCONFIG_METHOD_AUTO: + release_dhcpv6(network); + if (type == CONNMAN_IPCONFIG_TYPE_IPV6) + break; + /* fall through */ case CONNMAN_IPCONFIG_METHOD_DHCP: - __connman_dhcp_stop(network); + remove_dhcp_timeout(network); + __connman_dhcp_stop(ipconfig_ipv4); break; } @@ -1557,57 +1897,88 @@ int __connman_network_clear_ipconfig(struct connman_network *network, return 0; } -int __connman_network_set_ipconfig(struct connman_network *network, - struct connman_ipconfig *ipconfig_ipv4, - struct connman_ipconfig *ipconfig_ipv6) +int __connman_network_enable_ipconfig(struct connman_network *network, + struct connman_ipconfig *ipconfig) { + int r = 0; + enum connman_ipconfig_type type; enum connman_ipconfig_method method; - int ret; - if (network == NULL) + if (!network || !ipconfig) return -EINVAL; - if (ipconfig_ipv6) { - method = __connman_ipconfig_get_method(ipconfig_ipv6); + type = __connman_ipconfig_get_config_type(ipconfig); + + switch (type) { + case CONNMAN_IPCONFIG_TYPE_UNKNOWN: + case CONNMAN_IPCONFIG_TYPE_ALL: + return -ENOSYS; + + case CONNMAN_IPCONFIG_TYPE_IPV6: + set_configuration(network, type); + + method = __connman_ipconfig_get_method(ipconfig); + + DBG("ipv6 ipconfig method %d", method); switch (method) { case CONNMAN_IPCONFIG_METHOD_UNKNOWN: + break; + case CONNMAN_IPCONFIG_METHOD_OFF: + __connman_ipconfig_disable_ipv6(ipconfig); break; + case CONNMAN_IPCONFIG_METHOD_AUTO: autoconf_ipv6_set(network); break; + case CONNMAN_IPCONFIG_METHOD_FIXED: case CONNMAN_IPCONFIG_METHOD_MANUAL: - ret = manual_ipv6_set(network, ipconfig_ipv6); - if (ret != 0) { - connman_network_set_error(network, - CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL); - return ret; - } + r = manual_ipv6_set(network, ipconfig); break; + case CONNMAN_IPCONFIG_METHOD_DHCP: + r = -ENOSYS; break; } - } - if (ipconfig_ipv4) { - method = __connman_ipconfig_get_method(ipconfig_ipv4); + break; + + case CONNMAN_IPCONFIG_TYPE_IPV4: + set_configuration(network, type); + + method = __connman_ipconfig_get_method(ipconfig); + + DBG("ipv4 ipconfig method %d", method); switch (method) { case CONNMAN_IPCONFIG_METHOD_UNKNOWN: case CONNMAN_IPCONFIG_METHOD_OFF: - case CONNMAN_IPCONFIG_METHOD_FIXED: + break; + case CONNMAN_IPCONFIG_METHOD_AUTO: - return -EINVAL; + r = -ENOSYS; + break; + + case CONNMAN_IPCONFIG_METHOD_FIXED: case CONNMAN_IPCONFIG_METHOD_MANUAL: - return manual_ipv4_set(network, ipconfig_ipv4); + r = set_connected_manual(network); + break; + case CONNMAN_IPCONFIG_METHOD_DHCP: - return __connman_dhcp_start(network, dhcp_callback); + r = set_connected_dhcp(network); + break; } + + break; } - return 0; + if (r < 0) + connman_network_set_error(network, + CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL); + + return r; } int connman_network_set_ipaddress(struct connman_network *network, @@ -1618,12 +1989,12 @@ int connman_network_set_ipaddress(struct connman_network *network, DBG("network %p", network); - service = __connman_service_lookup_from_network(network); - if (service == NULL) + service = connman_service_lookup_from_network(network); + if (!service) return -EINVAL; ipconfig = __connman_service_get_ipconfig(service, ipaddress->family); - if (ipconfig == NULL) + if (!ipconfig) return -EINVAL; __connman_ipconfig_set_local(ipconfig, ipaddress->local); @@ -1644,20 +2015,20 @@ int connman_network_set_nameservers(struct connman_network *network, DBG("network %p nameservers %s", network, nameservers); - service = __connman_service_lookup_from_network(network); - if (service == NULL) + service = connman_service_lookup_from_network(network); + if (!service) return -EINVAL; __connman_service_nameserver_clear(service); - if (nameservers == NULL) + if (!nameservers) return 0; nameservers_array = g_strsplit(nameservers, " ", 0); - for (i = 0; nameservers_array[i] != NULL; i++) { + for (i = 0; nameservers_array[i]; i++) { __connman_service_nameserver_append(service, - nameservers_array[i], FALSE); + nameservers_array[i], false); } g_strfreev(nameservers_array); @@ -1672,8 +2043,8 @@ int connman_network_set_domain(struct connman_network *network, DBG("network %p domain %s", network, domain); - service = __connman_service_lookup_from_network(network); - if (service == NULL) + service = connman_service_lookup_from_network(network); + if (!service) return -EINVAL; __connman_service_set_domainname(service, domain); @@ -1708,46 +2079,40 @@ int connman_network_set_name(struct connman_network *network, */ int connman_network_set_strength(struct connman_network *network, - connman_uint8_t strength) + uint8_t strength) { - DBG("network %p strengh %d", network, strength); - network->strength = strength; return 0; } -connman_uint8_t connman_network_get_strength(struct connman_network *network) +uint8_t connman_network_get_strength(struct connman_network *network) { return network->strength; } int connman_network_set_frequency(struct connman_network *network, - connman_uint16_t frequency) + uint16_t frequency) { - DBG("network %p frequency %d", network, frequency); - network->frequency = frequency; return 0; } -connman_uint16_t connman_network_get_frequency(struct connman_network *network) +uint16_t connman_network_get_frequency(struct connman_network *network) { return network->frequency; } int connman_network_set_wifi_channel(struct connman_network *network, - connman_uint16_t channel) + uint16_t channel) { - DBG("network %p wifi channel %d", network, channel); - network->wifi.channel = channel; return 0; } -connman_uint16_t connman_network_get_wifi_channel(struct connman_network *network) +uint16_t connman_network_get_wifi_channel(struct connman_network *network) { return network->wifi.channel; } @@ -1763,54 +2128,64 @@ connman_uint16_t connman_network_get_wifi_channel(struct connman_network *networ int connman_network_set_string(struct connman_network *network, const char *key, const char *value) { - DBG("network %p key %s value %s", network, key, value); - if (g_strcmp0(key, "Name") == 0) return connman_network_set_name(network, value); - if (g_str_equal(key, "Path") == TRUE) { + if (g_str_equal(key, "Path")) { g_free(network->path); network->path = g_strdup(value); - } else if (g_str_equal(key, "Node") == TRUE) { + } else if (g_str_equal(key, "Node")) { g_free(network->node); network->node = g_strdup(value); - } else if (g_str_equal(key, "WiFi.Mode") == TRUE) { + } else if (g_str_equal(key, "WiFi.Mode")) { g_free(network->wifi.mode); network->wifi.mode = g_strdup(value); - } else if (g_str_equal(key, "WiFi.Security") == TRUE) { + } else if (g_str_equal(key, "WiFi.Security")) { g_free(network->wifi.security); network->wifi.security = g_strdup(value); - } else if (g_str_equal(key, "WiFi.Passphrase") == TRUE) { + } else if (g_str_equal(key, "WiFi.Passphrase")) { g_free(network->wifi.passphrase); network->wifi.passphrase = g_strdup(value); - } else if (g_str_equal(key, "WiFi.AgentPassphrase") == TRUE) { - g_free(network->wifi.agent_passphrase); - network->wifi.agent_passphrase = g_strdup(value); - } else if (g_str_equal(key, "WiFi.EAP") == TRUE) { + } else if (g_str_equal(key, "WiFi.EAP")) { g_free(network->wifi.eap); network->wifi.eap = g_strdup(value); - } else if (g_str_equal(key, "WiFi.Identity") == TRUE) { + } else if (g_str_equal(key, "WiFi.Identity")) { g_free(network->wifi.identity); network->wifi.identity = g_strdup(value); - } else if (g_str_equal(key, "WiFi.AgentIdentity") == TRUE) { + } else if (g_str_equal(key, "WiFi.AnonymousIdentity")) { + g_free(network->wifi.anonymous_identity); + network->wifi.anonymous_identity = g_strdup(value); + } else if (g_str_equal(key, "WiFi.AgentIdentity")) { g_free(network->wifi.agent_identity); network->wifi.agent_identity = g_strdup(value); - } else if (g_str_equal(key, "WiFi.CACertFile") == TRUE) { + } else if (g_str_equal(key, "WiFi.CACertFile")) { g_free(network->wifi.ca_cert_path); network->wifi.ca_cert_path = g_strdup(value); - } else if (g_str_equal(key, "WiFi.ClientCertFile") == TRUE) { + } else if (g_str_equal(key, "WiFi.SubjectMatch")) { + g_free(network->wifi.subject_match); + network->wifi.subject_match = g_strdup(value); + } else if (g_str_equal(key, "WiFi.AltSubjectMatch")) { + g_free(network->wifi.altsubject_match); + network->wifi.altsubject_match = g_strdup(value); + } else if (g_str_equal(key, "WiFi.DomainSuffixMatch")) { + g_free(network->wifi.domain_suffix_match); + network->wifi.domain_suffix_match = g_strdup(value); + } else if (g_str_equal(key, "WiFi.DomainMatch")) { + g_free(network->wifi.domain_match); + network->wifi.domain_match = g_strdup(value); + } else if (g_str_equal(key, "WiFi.ClientCertFile")) { g_free(network->wifi.client_cert_path); network->wifi.client_cert_path = g_strdup(value); - } else if (g_str_equal(key, "WiFi.PrivateKeyFile") == TRUE) { + } else if (g_str_equal(key, "WiFi.PrivateKeyFile")) { g_free(network->wifi.private_key_path); network->wifi.private_key_path = g_strdup(value); - } else if (g_str_equal(key, "WiFi.PrivateKeyPassphrase") == TRUE) { + } else if (g_str_equal(key, "WiFi.PrivateKeyPassphrase")) { g_free(network->wifi.private_key_passphrase); network->wifi.private_key_passphrase = g_strdup(value); - } else if (g_str_equal(key, "WiFi.Phase2") == TRUE) { + } else if (g_str_equal(key, "WiFi.Phase2")) { g_free(network->wifi.phase2_auth); network->wifi.phase2_auth = g_strdup(value); - } else if (g_str_equal(key, "WiFi.PinWPS") == TRUE) { + } else if (g_str_equal(key, "WiFi.PinWPS")) { g_free(network->wifi.pin_wps); network->wifi.pin_wps = g_strdup(value); } else { @@ -1830,39 +2205,45 @@ int connman_network_set_string(struct connman_network *network, const char *connman_network_get_string(struct connman_network *network, const char *key) { - DBG("network %p key %s", network, key); - - if (g_str_equal(key, "Path") == TRUE) + if (g_str_equal(key, "Path")) return network->path; - else if (g_str_equal(key, "Name") == TRUE) + else if (g_str_equal(key, "Name")) return network->name; - else if (g_str_equal(key, "Node") == TRUE) + else if (g_str_equal(key, "Node")) return network->node; - else if (g_str_equal(key, "WiFi.Mode") == TRUE) + else if (g_str_equal(key, "WiFi.Mode")) return network->wifi.mode; - else if (g_str_equal(key, "WiFi.Security") == TRUE) + else if (g_str_equal(key, "WiFi.Security")) return network->wifi.security; - else if (g_str_equal(key, "WiFi.Passphrase") == TRUE) + else if (g_str_equal(key, "WiFi.Passphrase")) return network->wifi.passphrase; - else if (g_str_equal(key, "WiFi.AgentPassphrase") == TRUE) - return network->wifi.agent_passphrase; - else if (g_str_equal(key, "WiFi.EAP") == TRUE) + else if (g_str_equal(key, "WiFi.EAP")) return network->wifi.eap; - else if (g_str_equal(key, "WiFi.Identity") == TRUE) + else if (g_str_equal(key, "WiFi.Identity")) return network->wifi.identity; - else if (g_str_equal(key, "WiFi.AgentIdentity") == TRUE) + else if (g_str_equal(key, "WiFi.AnonymousIdentity")) + return network->wifi.anonymous_identity; + else if (g_str_equal(key, "WiFi.AgentIdentity")) return network->wifi.agent_identity; - else if (g_str_equal(key, "WiFi.CACertFile") == TRUE) + else if (g_str_equal(key, "WiFi.CACertFile")) return network->wifi.ca_cert_path; - else if (g_str_equal(key, "WiFi.ClientCertFile") == TRUE) + else if (g_str_equal(key, "WiFi.SubjectMatch")) + return network->wifi.subject_match; + else if (g_str_equal(key, "WiFi.AltSubjectMatch")) + return network->wifi.altsubject_match; + else if (g_str_equal(key, "WiFi.DomainSuffixMatch")) + return network->wifi.domain_suffix_match; + else if (g_str_equal(key, "WiFi.DomainMatch")) + return network->wifi.domain_match; + else if (g_str_equal(key, "WiFi.ClientCertFile")) return network->wifi.client_cert_path; - else if (g_str_equal(key, "WiFi.PrivateKeyFile") == TRUE) + else if (g_str_equal(key, "WiFi.PrivateKeyFile")) return network->wifi.private_key_path; - else if (g_str_equal(key, "WiFi.PrivateKeyPassphrase") == TRUE) + else if (g_str_equal(key, "WiFi.PrivateKeyPassphrase")) return network->wifi.private_key_passphrase; - else if (g_str_equal(key, "WiFi.Phase2") == TRUE) + else if (g_str_equal(key, "WiFi.Phase2")) return network->wifi.phase2_auth; - else if (g_str_equal(key, "WiFi.PinWPS") == TRUE) + else if (g_str_equal(key, "WiFi.PinWPS")) return network->wifi.pin_wps; return NULL; @@ -1877,14 +2258,14 @@ const char *connman_network_get_string(struct connman_network *network, * Set boolean value for specific key */ int connman_network_set_bool(struct connman_network *network, - const char *key, connman_bool_t value) + const char *key, bool value) { - DBG("network %p key %s value %d", network, key, value); - if (g_strcmp0(key, "Roaming") == 0) network->roaming = value; else if (g_strcmp0(key, "WiFi.WPS") == 0) network->wifi.wps = value; + else if (g_strcmp0(key, "WiFi.WPSAdvertising") == 0) + network->wifi.wps_advertizing = value; else if (g_strcmp0(key, "WiFi.UseWPS") == 0) network->wifi.use_wps = value; @@ -1898,19 +2279,19 @@ int connman_network_set_bool(struct connman_network *network, * * Get boolean value for specific key */ -connman_bool_t connman_network_get_bool(struct connman_network *network, +bool connman_network_get_bool(struct connman_network *network, const char *key) { - DBG("network %p key %s", network, key); - - if (g_str_equal(key, "Roaming") == TRUE) + if (g_str_equal(key, "Roaming")) return network->roaming; - else if (g_str_equal(key, "WiFi.WPS") == TRUE) + else if (g_str_equal(key, "WiFi.WPS")) return network->wifi.wps; - else if (g_str_equal(key, "WiFi.UseWPS") == TRUE) + else if (g_str_equal(key, "WiFi.WPSAdvertising")) + return network->wifi.wps_advertizing; + else if (g_str_equal(key, "WiFi.UseWPS")) return network->wifi.use_wps; - return FALSE; + return false; } /** @@ -1925,12 +2306,10 @@ connman_bool_t connman_network_get_bool(struct connman_network *network, int connman_network_set_blob(struct connman_network *network, const char *key, const void *data, unsigned int size) { - DBG("network %p key %s size %d", network, key, size); - - if (g_str_equal(key, "WiFi.SSID") == TRUE) { + if (g_str_equal(key, "WiFi.SSID")) { g_free(network->wifi.ssid); network->wifi.ssid = g_try_malloc(size); - if (network->wifi.ssid != NULL) { + if (network->wifi.ssid) { memcpy(network->wifi.ssid, data, size); network->wifi.ssid_len = size; } else @@ -1953,10 +2332,8 @@ int connman_network_set_blob(struct connman_network *network, const void *connman_network_get_blob(struct connman_network *network, const char *key, unsigned int *size) { - DBG("network %p key %s", network, key); - - if (g_str_equal(key, "WiFi.SSID") == TRUE) { - if (size != NULL) + if (g_str_equal(key, "WiFi.SSID")) { + if (size) *size = network->wifi.ssid_len; return network->wifi.ssid; } @@ -1970,12 +2347,12 @@ void __connman_network_set_device(struct connman_network *network, if (network->device == device) return; - if (network->device != NULL) + if (network->device) network_remove(network); network->device = device; - if (network->device != NULL) + if (network->device) network_probe(network); } @@ -2020,15 +2397,15 @@ void connman_network_update(struct connman_network *network) case CONNMAN_NETWORK_TYPE_VENDOR: return; case CONNMAN_NETWORK_TYPE_ETHERNET: + case CONNMAN_NETWORK_TYPE_GADGET: case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN: case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN: case CONNMAN_NETWORK_TYPE_CELLULAR: case CONNMAN_NETWORK_TYPE_WIFI: - case CONNMAN_NETWORK_TYPE_WIMAX: break; } - if (network->group != NULL) + if (network->group) __connman_service_update_from_network(network); }