| /* |
| * |
| * Connection Manager |
| * |
| * Copyright (C) 2007-2009 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 |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <stdio.h> |
| #include <errno.h> |
| #include <string.h> |
| |
| #include <gdbus.h> |
| |
| #include "connman.h" |
| |
| #define _DBG_NETWORK(fmt, arg...) DBG(DBG_NETWORK, fmt, ## arg) |
| |
| static unsigned int hidden_counter = 0; |
| |
| enum network_state { /* NB: order is important */ |
| NETWORK_STATE_IDLE, /* base state */ |
| NETWORK_STATE_DISCONNECTING, /* disconnect started */ |
| NETWORK_STATE_CONNECTING, /* connect started */ |
| NETWORK_STATE_ASSOCIATING, /* associate started */ |
| NETWORK_STATE_CONNECTED, /* connected, ready for use */ |
| }; |
| static const char *state_names[] = { |
| [NETWORK_STATE_IDLE] = "IDLE", |
| [NETWORK_STATE_DISCONNECTING] = "DISCONNECTING", |
| [NETWORK_STATE_CONNECTING] = "CONNECTING", |
| [NETWORK_STATE_ASSOCIATING] = "ASSOCIATING", |
| [NETWORK_STATE_CONNECTED] = "CONNECTED", |
| }; |
| |
| struct connman_network { |
| struct connman_element element; |
| enum connman_network_type type; |
| enum connman_network_protocol protocol; |
| enum network_state state; |
| connman_bool_t available; |
| connman_bool_t registered; /* registered in element hierarchy */ |
| connman_bool_t hidden; |
| connman_uint8_t strength; |
| connman_uint16_t frequency; |
| enum connman_network_phymode phymode; |
| int scangen; |
| char *identifier; |
| char *address; |
| char *name; |
| char *node; |
| char *group; |
| |
| struct connman_network_driver *driver; |
| void *driver_data; |
| |
| struct connman_device *device; |
| |
| struct { |
| void *ssid; |
| int ssid_len; |
| char *mode; |
| unsigned short channel; |
| char *security; |
| char *passphrase; |
| char *authmode; |
| unsigned int wep_key_len; |
| connman_uint8_t wep_key_idx; |
| connman_uint8_t wep_key_matter[MAX_WEP_KEYSPACE]; |
| } wifi; |
| }; |
| |
| static const char *type2string(enum connman_network_type type) |
| { |
| switch (type) { |
| case CONNMAN_NETWORK_TYPE_UNKNOWN: |
| case CONNMAN_NETWORK_TYPE_VENDOR: |
| break; |
| 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"; |
| case CONNMAN_NETWORK_TYPE_CELLULAR: |
| return "cellular"; |
| } |
| |
| return NULL; |
| } |
| |
| static void append_wifi_ssid(DBusMessageIter *dict, void *arg) |
| { |
| const struct connman_network *network = arg; |
| |
| dbus_message_iter_append_fixed_array(dict, DBUS_TYPE_BYTE, |
| &network->wifi.ssid, network->wifi.ssid_len); |
| } |
| |
| static void append_network_string(DBusMessageIter *dict, |
| struct connman_network *network, const char *property) |
| { |
| const char *string; |
| |
| string = connman_network_get_string(network, property); |
| if (string != NULL) |
| connman_dbus_dict_append_variant(dict, property, |
| DBUS_TYPE_STRING, &string); |
| } |
| |
| static DBusMessage *get_properties(DBusConnection *conn, |
| DBusMessage *msg, void *data) |
| { |
| struct connman_network *network = data; |
| DBusMessage *reply; |
| DBusMessageIter array, dict; |
| connman_bool_t connected; |
| |
| if (__connman_security_check_privilege(msg, |
| CONNMAN_SECURITY_PRIVILEGE_PUBLIC) < 0) |
| return __connman_error_permission_denied(msg); |
| |
| reply = dbus_message_new_method_return(msg); |
| if (reply == NULL) |
| return NULL; |
| |
| dbus_message_iter_init_append(reply, &array); |
| |
| dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY, |
| DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING |
| DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING |
| DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); |
| |
| if (network->device) { |
| const char *path = connman_device_get_path(network->device); |
| if (path != NULL) |
| connman_dbus_dict_append_variant(&dict, "Device", |
| DBUS_TYPE_OBJECT_PATH, &path); |
| } |
| |
| if (network->address != NULL) |
| connman_dbus_dict_append_variant(&dict, "Address", |
| DBUS_TYPE_STRING, &network->address); |
| |
| if (network->name != NULL) |
| connman_dbus_dict_append_variant(&dict, "Name", |
| DBUS_TYPE_STRING, &network->name); |
| |
| connected = (network->state == NETWORK_STATE_CONNECTED); |
| connman_dbus_dict_append_variant(&dict, "Connected", |
| DBUS_TYPE_BOOLEAN, &connected); |
| |
| if (network->strength > 0) |
| connman_dbus_dict_append_variant(&dict, "Strength", |
| DBUS_TYPE_BYTE, &network->strength); |
| |
| if (network->frequency > 0) |
| connman_dbus_dict_append_variant(&dict, "Frequency", |
| DBUS_TYPE_UINT16, &network->frequency); |
| |
| if (network->wifi.ssid != NULL && network->wifi.ssid_len > 0) |
| connman_dbus_dict_append_variant_array(&dict, "WiFi.SSID", |
| DBUS_TYPE_BYTE, append_wifi_ssid, network); |
| |
| if (network->wifi.mode != NULL) |
| connman_dbus_dict_append_variant(&dict, "WiFi.Mode", |
| DBUS_TYPE_STRING, &network->wifi.mode); |
| |
| if (network->wifi.channel > 0) |
| connman_dbus_dict_append_variant(&dict, "WiFi.Channel", |
| DBUS_TYPE_UINT16, &network->wifi.channel); |
| |
| if (network->phymode != CONNMAN_NETWORK_PHYMODE_UNDEF) { |
| const char *str = |
| connman_network_get_phymode_name(network->phymode); |
| connman_dbus_dict_append_variant(&dict, "WiFi.PhyMode", |
| DBUS_TYPE_STRING, &str); |
| } |
| |
| if (network->wifi.security != NULL) |
| connman_dbus_dict_append_variant(&dict, "WiFi.Security", |
| DBUS_TYPE_STRING, &network->wifi.security); |
| |
| if (network->wifi.authmode != NULL) |
| connman_dbus_dict_append_variant(&dict, CONNMAN_WIFI_AUTHMODE, |
| DBUS_TYPE_STRING, &network->wifi.authmode); |
| |
| if (__connman_security_check_privilege(msg, |
| CONNMAN_SECURITY_PRIVILEGE_READ_SECRET) == 0) { |
| if (network->wifi.passphrase != NULL) |
| connman_dbus_dict_append_variant(&dict, "WiFi.Passphrase", |
| DBUS_TYPE_STRING, &network->wifi.passphrase); |
| append_network_string(&dict, network, |
| CONNMAN_WIFI_EAP_IDENTITY); |
| append_network_string(&dict, network, CONNMAN_WIFI_EAP_EAP); |
| append_network_string(&dict, network, |
| CONNMAN_WIFI_EAP_INNEREAP); |
| append_network_string(&dict, network, |
| CONNMAN_WIFI_EAP_ANONYMOUSIDENTITY); |
| append_network_string(&dict, network, |
| CONNMAN_WIFI_EAP_CLIENTCERT); |
| append_network_string(&dict, network, CONNMAN_WIFI_EAP_CERTID); |
| append_network_string(&dict, network, |
| CONNMAN_WIFI_EAP_PRIVATEKEY); |
| append_network_string(&dict, network, |
| CONNMAN_WIFI_EAP_PRIVATEKEYPASSWORD); |
| append_network_string(&dict, network, CONNMAN_WIFI_EAP_KEYID); |
| append_network_string(&dict, network, CONNMAN_WIFI_EAP_CACERT); |
| append_network_string(&dict, network, |
| CONNMAN_WIFI_EAP_CACERTID); |
| append_network_string(&dict, network, CONNMAN_WIFI_EAP_PIN); |
| append_network_string(&dict, network, |
| CONNMAN_WIFI_EAP_PASSWORD); |
| append_network_string(&dict, network, |
| CONNMAN_WIFI_EAP_KEY_MGMT); |
| } |
| dbus_message_iter_close_container(&array, &dict); |
| |
| return reply; |
| } |
| |
| |
| |
| |
| static GDBusMethodTable network_methods[] = { |
| { "GetProperties", "", "a{sv}", get_properties }, |
| { }, |
| }; |
| |
| static GDBusSignalTable network_signals[] = { |
| { "PropertyChanged", "sv" }, |
| { }, |
| }; |
| |
| static DBusConnection *connection; |
| |
| static void append_networks(DBusMessageIter *iter, void *arg) |
| { |
| __connman_element_list(arg, CONNMAN_ELEMENT_TYPE_NETWORK, iter); |
| } |
| static void emit_networks_signal(struct connman_device *device) |
| { |
| connman_dbus_send_property_changed_array( |
| connman_device_get_path(device), CONNMAN_DEVICE_INTERFACE, |
| "Networks", DBUS_TYPE_OBJECT_PATH, append_networks, device); |
| } |
| |
| static int register_interface(struct connman_element *element) |
| { |
| struct connman_network *network = element->network; |
| |
| _DBG_NETWORK("element %p name %s", element, element->name); |
| |
| if (g_dbus_register_interface(connection, element->path, |
| CONNMAN_NETWORK_INTERFACE, |
| network_methods, network_signals, |
| NULL, network, NULL) == FALSE) { |
| connman_error("Failed to register %s network", element->path); |
| return -EIO; |
| } |
| |
| network->registered = TRUE; |
| |
| emit_networks_signal(network->device); |
| |
| return 0; |
| } |
| |
| static void unregister_interface(struct connman_element *element) |
| { |
| struct connman_network * network = element->network; |
| |
| _DBG_NETWORK("element %p name %s", element, element->name); |
| |
| network->registered = FALSE; |
| |
| emit_networks_signal(network->device); |
| |
| g_dbus_unregister_interface(connection, element->path, |
| CONNMAN_NETWORK_INTERFACE); |
| } |
| |
| connman_bool_t __connman_network_has_driver(struct connman_network *network) |
| { |
| if (network == NULL || network->driver == NULL) |
| return FALSE; |
| |
| return network->registered; |
| } |
| |
| static GSList *driver_list = NULL; |
| |
| 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_NETWORK("driver %p name %s", driver, driver->name); |
| |
| driver_list = g_slist_insert_sorted(driver_list, driver, |
| compare_priority); |
| |
| return 0; |
| } |
| |
| /** |
| * 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_NETWORK("driver %p name %s", driver, driver->name); |
| |
| driver_list = g_slist_remove(driver_list, driver); |
| } |
| |
| static void network_destruct(struct connman_element *element) |
| { |
| struct connman_network *network = element->network; |
| |
| _DBG_NETWORK("element %p name %s", element, element->name); |
| |
| g_free(network->wifi.ssid); |
| g_free(network->wifi.mode); |
| g_free(network->wifi.security); |
| g_free(network->wifi.authmode); |
| g_free(network->wifi.passphrase); |
| |
| g_free(network->group); |
| g_free(network->node); |
| g_free(network->name); |
| g_free(network->address); |
| g_free(network->identifier); |
| } |
| |
| /** |
| * connman_network_create: |
| * @identifier: network identifier (for example an unique name) |
| * @type: network type |
| * |
| * Allocate a new network of type #type and assign its #identifier. |
| * |
| * 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; |
| const char *str; |
| char *temp; |
| |
| _DBG_NETWORK("identifier %s type %d", identifier, type); |
| |
| network = g_try_new0(struct connman_network, 1); |
| if (network == NULL) |
| return NULL; |
| |
| _DBG_NETWORK("network %p", network); |
| |
| __connman_element_initialize(&network->element); |
| |
| //temp = connman_dbus_encode_string(identifier); |
| if (identifier == NULL) { |
| temp = g_strdup_printf("hidden_%d", hidden_counter++); |
| network->hidden = TRUE; |
| } else |
| temp = g_strdup(identifier); |
| |
| if (temp == NULL) { |
| g_free(network); |
| return NULL; |
| } |
| |
| network->element.name = temp; |
| network->element.type = CONNMAN_ELEMENT_TYPE_NETWORK; |
| |
| network->element.network = network; |
| network->element.destruct = network_destruct; |
| |
| str = type2string(type); |
| if (str != NULL) |
| connman_element_set_string(&network->element, "Type", str); |
| |
| network->state = NETWORK_STATE_IDLE; |
| network->type = type; |
| network->identifier = g_strdup(temp); |
| |
| return network; |
| } |
| |
| /** |
| * connman_network_ref: |
| * @network: network structure |
| * |
| * Increase reference counter of network |
| */ |
| struct connman_network *connman_network_ref(struct connman_network *network) |
| { |
| if (connman_element_ref(&network->element) == NULL) |
| return NULL; |
| |
| return network; |
| } |
| |
| /** |
| * connman_network_unref: |
| * @network: network structure |
| * |
| * Decrease reference counter of network |
| */ |
| void connman_network_unref(struct connman_network *network) |
| { |
| connman_element_unref(&network->element); |
| } |
| |
| const char *__connman_network_get_type(struct connman_network *network) |
| { |
| return type2string(network->type); |
| } |
| |
| /** |
| * connman_network_set_error: |
| * @network: network structure |
| * @error: error code |
| * |
| * Mark a network error. The error is sent to the element which causes an |
| * external signal. |
| */ |
| void connman_network_set_error(struct connman_network *network, |
| enum connman_element_error error) |
| { |
| connman_element_set_error(&network->element, error); |
| } |
| |
| /** |
| * connman_network_get_type: |
| * @network: network structure |
| * |
| * Get type of network |
| */ |
| enum connman_network_type connman_network_get_type(struct connman_network *network) |
| { |
| return network->type; |
| } |
| |
| /** |
| * connman_network_get_identifier: |
| * @network: network structure |
| * |
| * Get identifier of network |
| */ |
| const char *connman_network_get_identifier(struct connman_network *network) |
| { |
| return network->identifier; |
| } |
| |
| /** |
| * connman_network_get_path: |
| * @network: network structure |
| * |
| * Get path name of network |
| */ |
| const char *connman_network_get_path(struct connman_network *network) |
| { |
| return network->element.path; |
| } |
| |
| /** |
| * 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) |
| { |
| network->element.index = index; |
| } |
| |
| /** |
| * connman_network_get_index: |
| * @network: network structure |
| * |
| * Get index number of network |
| */ |
| int connman_network_get_index(struct connman_network *network) |
| { |
| return network->element.index; |
| } |
| |
| /** |
| * connman_network_set_protocol: |
| * @network: network structure |
| * @protocol: network protocol |
| * |
| * Change protocol of network |
| */ |
| void connman_network_set_protocol(struct connman_network *network, |
| enum connman_network_protocol protocol) |
| { |
| network->protocol = protocol; |
| } |
| |
| /** |
| * 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) |
| { |
| switch (network->type) { |
| case CONNMAN_NETWORK_TYPE_UNKNOWN: |
| case CONNMAN_NETWORK_TYPE_VENDOR: |
| return; |
| 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; |
| } |
| |
| _DBG_NETWORK("network %p group %s", network, group); |
| |
| if (g_strcmp0(network->group, group) == 0) { |
| if (group != NULL) |
| __connman_profile_update_network(network); |
| return; |
| } |
| |
| if (network->group != NULL) { |
| __connman_profile_remove_network(network); |
| |
| g_free(network->group); |
| } |
| |
| network->group = g_strdup(group); |
| |
| if (network->group != NULL) |
| __connman_profile_add_network(network); |
| } |
| |
| /** |
| * connman_network_get_group: |
| * @network: network structure |
| * |
| * Get group name for automatic clustering |
| */ |
| const char *connman_network_get_group(struct connman_network *network) |
| { |
| return network->group; |
| } |
| |
| const char *__connman_network_get_ident(struct connman_network *network) |
| { |
| if (network->device == NULL) |
| return NULL; |
| |
| return __connman_device_get_ident(network->device); |
| } |
| |
| 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_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 (network->strength > 0 && network->strength < 20) |
| return TRUE; |
| break; |
| } |
| |
| return FALSE; |
| } |
| |
| /** |
| * 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) |
| { |
| _DBG_NETWORK("network %p available %d", network, available); |
| |
| if (network->available == available) |
| return -EALREADY; |
| |
| network->available = available; |
| |
| return 0; |
| } |
| |
| /** |
| * connman_network_get_available: |
| * @network: network structure |
| * |
| * Get network available setting |
| */ |
| connman_bool_t connman_network_get_available(struct connman_network *network) |
| { |
| if (network->hidden == TRUE) |
| return TRUE; |
| |
| return network->available; |
| } |
| |
| static connman_bool_t |
| signal_state(struct connman_network *network) |
| { |
| dbus_bool_t connected = (network->state == NETWORK_STATE_CONNECTED); |
| return connman_dbus_send_property_changed_variant(network->element.path, |
| CONNMAN_NETWORK_INTERFACE, "Connected", |
| DBUS_TYPE_BOOLEAN, &connected); |
| } |
| |
| static gboolean set_connected(gpointer user_data) |
| { |
| struct connman_network *network = user_data; |
| struct connman_device *device = network->device; |
| struct connman_service *service; |
| |
| service = connman_service_lookup_from_network(network); |
| |
| switch (network->state) { |
| case NETWORK_STATE_CONNECTED: |
| /* NB: this kicks off L3 address configuration */ |
| __connman_device_set_current_network(device, network); |
| |
| /* XXX only do this if >1 addr config setup? */ |
| __connman_service_indicate_state(service, |
| CONNMAN_SERVICE_STATE_CONFIGURATION); |
| break; |
| case NETWORK_STATE_IDLE: |
| if (__connman_device_get_current_network(device) != NULL) { |
| connman_element_unregister_children(&network->element); |
| |
| /* NB: L3 address state cleared on last connection */ |
| __connman_device_set_current_network(device, NULL); |
| } |
| |
| network->hidden = FALSE; /* XXX */ |
| |
| __connman_service_indicate_state(service, |
| CONNMAN_SERVICE_STATE_IDLE); |
| break; |
| default: |
| break; |
| } |
| return FALSE; /* NB: meant for g_idle_add usage */ |
| } |
| |
| static void state_change(struct connman_network *network, |
| enum network_state state) |
| { |
| _DBG_NETWORK("network state change (%p) %s -> %s", network, |
| state_names[network->state], state_names[state]); |
| network->state = state; |
| } |
| |
| static void invalid_state(const struct connman_network *network, |
| enum network_state state) |
| { |
| connman_error("%s: INVALID network state change (%p), %s -> %s", |
| connman_device_get_interface(network->device), network, |
| state_names[network->state], state_names[state]); |
| } |
| |
| /** |
| * connman_network_get_connected: |
| * @network: network structure |
| * |
| * Get network connection status |
| */ |
| connman_bool_t connman_network_get_connected(struct connman_network *network) |
| { |
| return (network->state == NETWORK_STATE_CONNECTED); |
| } |
| |
| /** |
| * connman_network_set_connected: |
| * @network: network structure |
| * |
| * Mark network connected. |
| */ |
| int connman_network_set_connected(struct connman_network *network) |
| { |
| _DBG_NETWORK("network %p state %s", network, |
| state_names[network->state]); |
| |
| if (network->state == NETWORK_STATE_CONNECTED) |
| return -EALREADY; |
| /* NB: we do not reject invalid state changes */ |
| |
| state_change(network, NETWORK_STATE_CONNECTED); |
| |
| if (network->registered == FALSE) { |
| _DBG_NETWORK("not registered; defer"); |
| /* XXX no signal sent, intentional? */ |
| g_idle_add(set_connected, network); |
| } else { |
| signal_state(network); |
| set_connected(network); |
| } |
| return 0; |
| } |
| |
| /** |
| * connman_network_set_connecting: |
| * @network: network structure |
| * |
| * Mark network connecting. |
| */ |
| int connman_network_set_connecting(struct connman_network *network) |
| { |
| struct connman_service *service; |
| |
| _DBG_NETWORK("network %p state %s", network, |
| state_names[network->state]); |
| |
| /* |
| * Even if the network state doesn't change, we may stiil |
| * need to change the service state below |
| */ |
| if (network->state != NETWORK_STATE_CONNECTING) |
| state_change(network, NETWORK_STATE_CONNECTING); |
| |
| /* NB: don't signal state change; let it happen later */ |
| |
| service = connman_service_lookup_from_network(network); |
| __connman_service_indicate_state(service, |
| CONNMAN_SERVICE_STATE_ASSOCIATION); |
| return 0; |
| } |
| |
| int connman_network_restart_connect_timer(struct connman_network *network) |
| { |
| struct connman_service *service; |
| |
| _DBG_NETWORK("network %s state %s", network->name, |
| state_names[network->state]); |
| |
| service = connman_service_lookup_from_network(network); |
| if (service != NULL) |
| __connman_service_restart_connect_timer(service); |
| return 0; |
| } |
| |
| /** |
| * __connman_network_connect: |
| * @network: network structure |
| * |
| * Connect network. |
| */ |
| int __connman_network_connect(struct connman_network *network) |
| { |
| int err; |
| |
| _DBG_NETWORK("network %p state %s", network, |
| state_names[network->state]); |
| |
| if (network->state == NETWORK_STATE_CONNECTED) |
| return -EISCONN; |
| if (network->state >= NETWORK_STATE_CONNECTING) |
| return -EALREADY; |
| if (network->driver == NULL) |
| return -EUNATCH; |
| if (network->driver->connect == NULL) |
| return -ENOSYS; |
| |
| /* NB: may be DISCONNECTING; driver must handle? */ |
| |
| state_change(network, NETWORK_STATE_CONNECTING); |
| |
| err = network->driver->connect(network); |
| if (err == 0) { |
| state_change(network, NETWORK_STATE_CONNECTED); |
| set_connected(network); |
| } else if (err != -EINPROGRESS) { |
| state_change(network, NETWORK_STATE_IDLE); |
| network->hidden = FALSE; /* XXX? */ |
| } |
| return err; |
| } |
| |
| /** |
| * connman_network_get_associating: |
| * @network: network structure |
| * |
| * Get network associating status |
| */ |
| connman_bool_t connman_network_get_associating(struct connman_network *network) |
| { |
| return (network->state == NETWORK_STATE_ASSOCIATING); |
| } |
| |
| /** |
| * connman_network_set_associating: |
| * @network: network structure |
| * |
| * Mark network in associating state. |
| */ |
| int connman_network_set_associating(struct connman_network *network) |
| { |
| struct connman_service *service; |
| |
| _DBG_NETWORK("network %p state %s", network, |
| state_names[network->state]); |
| |
| if (network->state == NETWORK_STATE_ASSOCIATING) |
| return -EALREADY; |
| /* NB: CONNECTED -> ASSOCIATING is valid */ |
| /* NB: IDLE -> ASSOCIATING is valid; fix supplicant? */ |
| if (network->state == NETWORK_STATE_DISCONNECTING) { |
| invalid_state(network, NETWORK_STATE_ASSOCIATING); |
| return -EINVAL; |
| } |
| |
| state_change(network, NETWORK_STATE_ASSOCIATING); |
| |
| service = connman_service_lookup_from_network(network); |
| __connman_service_indicate_state(service, |
| CONNMAN_SERVICE_STATE_ASSOCIATION); |
| return 0; |
| } |
| |
| /** |
| * connman_network_set_disconnecting: |
| * @network: network structure |
| * |
| * Mark network disconnecting. |
| */ |
| int connman_network_set_disconnecting(struct connman_network *network) |
| { |
| _DBG_NETWORK("network %p state %s", network, |
| state_names[network->state]); |
| |
| if (network->state <= NETWORK_STATE_DISCONNECTING) |
| return -EALREADY; |
| |
| state_change(network, NETWORK_STATE_DISCONNECTING); |
| return 0; |
| } |
| |
| /** |
| * connman_network_set_disconnected_only: |
| * @network: network structure |
| * |
| * Mark network (only) disconnected. |
| */ |
| int connman_network_set_disconnected_only(struct connman_network *network) |
| { |
| _DBG_NETWORK("network %p state %s%s", network, |
| state_names[network->state], |
| network->hidden == TRUE ? " (hidden)" : ""); |
| |
| if (network->state == NETWORK_STATE_IDLE) |
| return -EALREADY; |
| |
| state_change(network, NETWORK_STATE_IDLE); |
| |
| if (network->hidden == TRUE) { |
| /* |
| * Hack. A network is hidden when it is created without |
| * ever seeing it (e.g. due to a dbus request). These |
| * networks are useful only to get a connect request |
| * started after which they are relegated to the scrap |
| * heap when the real network is located or the request |
| * times out. They cannot be used again so when they are |
| * marked disconnected we remove it from the device which |
| * causes it to be reclaimed (and any associated service |
| * that would otherwise hang around solely attached to |
| * the network). |
| */ |
| network->hidden = FALSE; /* NB: no need but do it anyway */ |
| connman_device_remove_network(network->device, |
| network->identifier); |
| } |
| |
| if (network->registered == TRUE) |
| signal_state(network); |
| return 0; |
| } |
| |
| /** |
| * connman_network_set_disconnected: |
| * @network: network structure |
| * |
| * Mark network disconnected and update associated service and |
| * device to reflect network state change. |
| */ |
| int connman_network_set_disconnected(struct connman_network *network) |
| { |
| int err; |
| |
| _DBG_NETWORK("network %p state %s", network, |
| state_names[network->state]); |
| |
| /* NB: hold a ref as the network may be reclaimed */ |
| (void) connman_network_ref(network); |
| |
| err = connman_network_set_disconnected_only(network); |
| if (err == 0 && network->registered == TRUE) |
| set_connected(network); |
| |
| connman_network_unref(network); |
| |
| return err; |
| } |
| |
| /** |
| * __connman_network_disconnect: |
| * @network: network structure |
| * |
| * Disconnect network. |
| */ |
| int __connman_network_disconnect(struct connman_network *network) |
| { |
| int err; |
| |
| _DBG_NETWORK("network %p state %s", network, |
| state_names[network->state]); |
| |
| if (network->state == NETWORK_STATE_IDLE) |
| return -ENOTCONN; |
| if (network->state == NETWORK_STATE_DISCONNECTING) |
| return -EALREADY; |
| if (network->driver == NULL) |
| return -EUNATCH; |
| if (network->driver->disconnect == NULL) |
| return -ENOSYS; |
| |
| state_change(network, NETWORK_STATE_DISCONNECTING); |
| |
| err = network->driver->disconnect(network); |
| if (err == 0) |
| connman_network_set_disconnected(network); /* XXX? */ |
| return err; |
| } |
| |
| /** |
| * connman_network_get_in_use: |
| * @network: network structure |
| * |
| * Get whether network is connecting, associating, or connected. |
| */ |
| connman_bool_t connman_network_get_in_use(struct connman_network *network) |
| { |
| return (network->state >= NETWORK_STATE_CONNECTING); |
| } |
| |
| /** |
| * connman_network_get_previously_connected: |
| * @network: network structure |
| * |
| * Return whether there is an associated service that is marked as previously |
| * connected. |
| */ |
| connman_bool_t connman_network_get_previously_connected( |
| struct connman_network *network) |
| { |
| struct connman_service *service = |
| connman_service_lookup_from_network(network); |
| |
| if (service == NULL) |
| return FALSE; |
| return connman_service_get_previously_connected(service); |
| } |
| |
| /** |
| * connman_network_set_address: |
| * @network: network structure |
| * @address: binary address value |
| * @size: binary address length |
| * |
| * Set unique address value for network |
| */ |
| int connman_network_set_address(struct connman_network *network, |
| const void *address, unsigned int size) |
| { |
| const unsigned char *addr_octet = address; |
| char buf[20]; |
| |
| _DBG_NETWORK("network %p size %d", network, size); |
| |
| if (size != 6) { |
| connman_error("%s: bad size %d", __func__, size); |
| return -EINVAL; |
| } |
| |
| g_snprintf(buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X", |
| addr_octet[0], addr_octet[1], addr_octet[2], |
| addr_octet[3], addr_octet[4], addr_octet[5]); |
| return connman_network_set_string(network, "Address", buf); |
| } |
| |
| /** |
| * connman_network_set_name: |
| * @network: network structure |
| * @name: name value |
| * |
| * Set display name value for network |
| */ |
| int connman_network_set_name(struct connman_network *network, const char *name) |
| { |
| _DBG_NETWORK("network %p name %s", network, name); |
| |
| g_free(network->name); |
| network->name = g_strdup(name); |
| if (network->name == NULL) { |
| connman_error("%s: no memory", __func__); |
| return -ENOMEM; |
| } |
| return connman_element_set_string(&network->element, "Name", name); |
| } |
| |
| /** |
| * connman_network_get_strength: |
| * @network: network structure |
| * |
| * Return signal strength value for network |
| */ |
| connman_uint8_t connman_network_get_strength(struct connman_network *network) |
| { |
| return network->strength; |
| } |
| |
| /** |
| * connman_network_set_strength: |
| * @network: network structure |
| * @strength: strength value |
| * |
| * Set signal strength value for network |
| */ |
| int connman_network_set_strength(struct connman_network *network, |
| connman_uint8_t strength) |
| { |
| _DBG_NETWORK("network %p strength %d", network, strength); |
| |
| network->strength = strength; |
| __connman_service_update_strength(network); |
| |
| return 0; |
| } |
| |
| /** |
| * connman_network_set_string: |
| * @network: network structure |
| * @key: unique identifier |
| * @value: string value |
| * |
| * Set string value for specific key |
| */ |
| int connman_network_set_string(struct connman_network *network, |
| const char *key, const char *value) |
| { |
| _DBG_NETWORK("network %p key %s value %s", network, key, |
| connman_log_get_masked_value(key, value)); |
| |
| if (g_strcmp0(key, "Name") == 0) |
| return connman_network_set_name(network, value); |
| |
| if (g_str_equal(key, "Address") == TRUE) { |
| g_free(network->address); |
| network->address = g_strdup(value); |
| } else if (g_str_equal(key, "Node") == TRUE) { |
| g_free(network->node); |
| network->node = g_strdup(value); |
| } else if (g_str_equal(key, "WiFi.Mode") == TRUE) { |
| g_free(network->wifi.mode); |
| network->wifi.mode = g_strdup(value); |
| } else if (g_str_equal(key, "WiFi.Security") == TRUE) { |
| g_free(network->wifi.security); |
| network->wifi.security = g_strdup(value); |
| } else if (g_str_equal(key, CONNMAN_WIFI_AUTHMODE) == TRUE) { |
| g_free(network->wifi.authmode); |
| network->wifi.authmode = g_strdup(value); |
| } else if (g_str_equal(key, "WiFi.Passphrase") == TRUE) { |
| g_free(network->wifi.passphrase); |
| network->wifi.passphrase = g_strdup(value); |
| } |
| |
| return connman_element_set_string(&network->element, key, value); |
| } |
| |
| /** |
| * connman_network_get_string: |
| * @network: network structure |
| * @key: unique identifier |
| * |
| * Get string value for specific key |
| */ |
| const char *connman_network_get_string(struct connman_network *network, |
| const char *key) |
| { |
| _DBG_NETWORK("network %p key %s", network, key); |
| |
| if (g_str_equal(key, "Address") == TRUE) |
| return network->address; |
| else if (g_str_equal(key, "Name") == TRUE) |
| return network->name; |
| else if (g_str_equal(key, "Node") == TRUE) |
| return network->node; |
| else if (g_str_equal(key, "WiFi.Mode") == TRUE) |
| return network->wifi.mode; |
| else if (g_str_equal(key, "WiFi.Security") == TRUE) |
| return network->wifi.security; |
| else if (g_str_equal(key, CONNMAN_WIFI_AUTHMODE) == TRUE) |
| return network->wifi.authmode; |
| else if (g_str_equal(key, "WiFi.Passphrase") == TRUE) |
| return network->wifi.passphrase; |
| |
| return connman_element_get_string(&network->element, key); |
| } |
| |
| /** |
| * connman_network_set_uint8: |
| * @network: network structure |
| * @key: unique identifier |
| * @value: integer value |
| * |
| * Set integer value for specific key |
| */ |
| int connman_network_set_uint8(struct connman_network *network, |
| const char *key, connman_uint8_t value) |
| { |
| _DBG_NETWORK("network %p key %s value %d", network, key, value); |
| |
| if (g_str_equal(key, "WiFi.WEPKeyIndex") == TRUE) { |
| network->wifi.wep_key_idx = value; |
| return 0; |
| } |
| return connman_element_set_uint8(&network->element, key, value); |
| } |
| |
| /** |
| * connman_network_get_uint8: |
| * @network: network structure |
| * @key: unique identifier |
| * |
| * Get integer value for specific key |
| */ |
| connman_uint8_t connman_network_get_uint8(struct connman_network *network, |
| const char *key) |
| { |
| _DBG_NETWORK("network %p key %s", network, key); |
| |
| if (g_str_equal(key, "WiFi.WEPKeyIndex") == TRUE) |
| return network->wifi.wep_key_idx; |
| return connman_element_get_uint8(&network->element, key); |
| } |
| |
| /** |
| * connman_network_set_uint16: |
| * @network: network structure |
| * @key: unique identifier |
| * @value: integer value |
| * |
| * Set integer value for specific key |
| */ |
| int connman_network_set_uint16(struct connman_network *network, |
| const char *key, connman_uint16_t value) |
| { |
| _DBG_NETWORK("network %p key %s value %d", network, key, value); |
| |
| if (g_str_equal(key, "Frequency") == TRUE) { |
| network->frequency = value; |
| return 0; |
| } |
| if (g_str_equal(key, "WiFi.Channel") == TRUE) { |
| network->wifi.channel = value; |
| return 0; |
| } |
| return -EINVAL; |
| } |
| |
| /** |
| * connman_network_get_uint16: |
| * @network: network structure |
| * @key: unique identifier |
| * |
| * Get integer value for specific key |
| */ |
| connman_uint16_t connman_network_get_uint16(struct connman_network *network, |
| const char *key) |
| { |
| _DBG_NETWORK("network %p key %s", network, key); |
| |
| if (g_str_equal(key, "Frequency") == TRUE) |
| return network->frequency; |
| else if (g_str_equal(key, "WiFi.Channel") == TRUE) |
| return network->wifi.channel; |
| |
| return 0; |
| } |
| |
| /** |
| * connman_network_set_phymode: |
| * @network: network structure |
| * @phymode: WiFi phy mode |
| * |
| * Change WiFi phy mode number of network. |
| */ |
| void connman_network_set_phymode(struct connman_network *network, |
| enum connman_network_phymode phymode) |
| { |
| network->phymode = phymode; |
| } |
| |
| /** |
| * connman_network_get_phymode: |
| * @network: network structure |
| * |
| * Get WiFi phy mode |
| */ |
| enum connman_network_phymode connman_network_get_phymode(struct connman_network |
| *network) |
| { |
| return network->phymode; |
| } |
| |
| const char *connman_network_get_phymode_name(enum connman_network_phymode phymode) |
| { |
| static const char *phymodes[] = { |
| [CONNMAN_NETWORK_PHYMODE_UNDEF] = "Undef", |
| [CONNMAN_NETWORK_PHYMODE_11A] = "802.11a", |
| [CONNMAN_NETWORK_PHYMODE_11B] = "802.11b", |
| [CONNMAN_NETWORK_PHYMODE_11G] = "802.11g", |
| [CONNMAN_NETWORK_PHYMODE_11N] = "802.11n", |
| [CONNMAN_NETWORK_PHYMODE_HALF] = "PSB Half-width", |
| [CONNMAN_NETWORK_PHYMODE_QUARTER] = "PSB Quarter-width", |
| [CONNMAN_NETWORK_PHYMODE_TURBO] = "Atheros Turbo mode", |
| }; |
| return phymodes[phymode < CONNMAN_NETWORK_PHYMODE_MAX ? |
| phymode : CONNMAN_NETWORK_PHYMODE_UNDEF]; |
| } |
| |
| /** |
| * connman_network_set_scangen: |
| * @network: network structure |
| * @scangen: scan generation number |
| * |
| * Change scan generation number of network |
| */ |
| void connman_network_set_scangen(struct connman_network *network, int scangen) |
| { |
| network->scangen = scangen; |
| } |
| |
| /** |
| * connman_network_get_scangen: |
| * @network: network structure |
| * |
| * Get scan generation number of network |
| */ |
| int connman_network_get_scangen(struct connman_network *network) |
| { |
| return network->scangen; |
| } |
| |
| /** |
| * connman_network_set_blob: |
| * @network: network structure |
| * @key: unique identifier |
| * @data: blob data |
| * @size: blob size |
| * |
| * Set binary blob value for specific key |
| */ |
| int connman_network_set_blob(struct connman_network *network, |
| const char *key, const void *data, unsigned int size) |
| { |
| _DBG_NETWORK("network %p key %s size %d", network, key, size); |
| |
| if (g_strcmp0(key, "Address") == 0) |
| return connman_network_set_address(network, data, size); |
| |
| if (g_str_equal(key, "WiFi.SSID") == TRUE) { |
| g_free(network->wifi.ssid); |
| network->wifi.ssid = g_try_malloc(size); |
| if (network->wifi.ssid == NULL && size != 0) { |
| network->wifi.ssid_len = 0; |
| return -ENOMEM; |
| } |
| |
| memcpy(network->wifi.ssid, data, size); |
| network->wifi.ssid_len = size; |
| return 0; |
| } |
| |
| if (g_str_equal(key, "WiFi.WEPKey") == TRUE) { |
| if (size > MAX_WEP_KEYSPACE) |
| return -EINVAL; |
| memcpy(network->wifi.wep_key_matter, data, size); |
| network->wifi.wep_key_len = size; |
| return 0; |
| } |
| |
| return connman_element_set_blob(&network->element, key, data, size); |
| } |
| |
| /** |
| * connman_network_get_blob: |
| * @network: network structure |
| * @key: unique identifier |
| * @size: pointer to blob size |
| * |
| * Get binary blob value for specific key |
| */ |
| const void * connman_network_get_blob(struct connman_network *network, |
| const char *key, unsigned int *size) |
| { |
| _DBG_NETWORK("network %p key %s", network, key); |
| |
| if (g_str_equal(key, "WiFi.SSID") == TRUE) { |
| if (size != NULL) |
| *size = network->wifi.ssid_len; |
| return network->wifi.ssid; |
| } |
| |
| if (g_str_equal(key, "WiFi.WEPKey") == TRUE) { |
| *size = network->wifi.wep_key_len; |
| return network->wifi.wep_key_matter; |
| } |
| |
| return connman_element_get_blob(&network->element, key, size); |
| } |
| |
| /** |
| * connman_network_get_hex_ssid: |
| * @network: network structure |
| * |
| * Get hex SSID value for network or NULL if one does not exist, or is too |
| * long |
| */ |
| char *connman_network_get_hex_ssid(struct connman_network *network) |
| { |
| unsigned char *ssid = network->wifi.ssid; |
| int ssid_len = network->wifi.ssid_len; |
| char string_buf[MAX_SSID_LENGTH*2+1]; |
| char *string_ptr; |
| int i; |
| |
| if (ssid == NULL || ssid_len <= 0 || ssid_len > MAX_SSID_LENGTH || |
| ssid[0] == '\0') |
| return NULL; |
| |
| for (i = 0, string_ptr = string_buf; i < ssid_len; i++, string_ptr += 2) |
| snprintf(string_ptr, sizeof(string_buf)-(string_ptr-string_buf), |
| "%02x", ssid[i]); |
| |
| return g_strdup(string_buf); |
| } |
| |
| void __connman_network_set_device(struct connman_network *network, |
| struct connman_device *device) |
| { |
| network->device = device; |
| } |
| |
| /** |
| * connman_network_get_device: |
| * @network: network structure |
| * |
| * Get parent device of network |
| */ |
| struct connman_device *connman_network_get_device( |
| struct connman_network *network) |
| { |
| return network->device; |
| } |
| |
| /** |
| * connman_network_get_data: |
| * @network: network structure |
| * |
| * Get private network data pointer |
| */ |
| void *connman_network_get_data(struct connman_network *network) |
| { |
| return network->driver_data; |
| } |
| |
| /** |
| * connman_network_set_data: |
| * @network: network structure |
| * @data: data pointer |
| * |
| * Set private network data pointer |
| */ |
| void connman_network_set_data(struct connman_network *network, void *data) |
| { |
| network->driver_data = data; |
| } |
| |
| /** |
| * connman_network_set_operator: |
| * @network: network structure |
| * @op: network operator structire |
| * |
| * Set operator information (name, operator code, country, etc) for the network |
| */ |
| void connman_network_set_operator(struct connman_network *network, |
| struct connman_network_operator *op) |
| { |
| struct connman_service *service = |
| connman_service_lookup_from_network(network); |
| |
| if (service != NULL) |
| __connman_service_set_operator(service, op); |
| } |
| |
| /** |
| * connman_network_set_registration_info: |
| * @network: network structure |
| * @network_technology: e.g., "GPRS", "EDGE", "1xRTT", "EVDO", etc. |
| * @roaming_state: e.g., "home", "roaming", etc. |
| * |
| * Set network technology and roaming status information for the network. |
| */ |
| void connman_network_set_registration_info( |
| struct connman_network *network, |
| enum connman_network_cellular_technology network_technology, |
| enum connman_network_cellular_roaming_state roaming_state) |
| { |
| struct connman_service *service = |
| connman_service_lookup_from_network(network); |
| |
| if (service != NULL) |
| __connman_service_set_registration_info(service, |
| network_technology, |
| roaming_state); |
| } |
| |
| /** |
| * connman_network_set_activation_state: |
| * @network: network structure |
| * @activation state: control interface |
| * @err: if the activation failed a reason for the failure |
| * |
| * Set the activation state of the network |
| */ |
| void connman_network_set_activation_state(struct connman_network *network, |
| enum connman_network_activation_state activation_state, |
| enum connman_element_error err) |
| { |
| struct connman_service *service = |
| connman_service_lookup_from_network(network); |
| |
| if (service != NULL) |
| __connman_service_set_activation_state(service, |
| activation_state, err); |
| |
| } |
| |
| /** |
| * connman_network_set_olp_url: |
| * @network: network structure |
| * @olp_url: online payment url |
| * |
| * Set online payment url for the network |
| */ |
| void connman_network_set_olp_url(struct connman_network *network, |
| const char *olp_url, |
| const char *olp_method, |
| const char *olp_postdata) |
| { |
| struct connman_service *service = |
| connman_service_lookup_from_network(network); |
| |
| if (service != NULL) |
| __connman_service_set_olp_url(service, olp_url, olp_method, olp_postdata); |
| } |
| |
| /** |
| * connman_network_set_usage_url: |
| * @network: network structure |
| * @usage_url: data usage url |
| * |
| * Set data usage url for the network |
| */ |
| void connman_network_set_usage_url(struct connman_network *network, |
| const char *usage_url) |
| { |
| struct connman_service *service = |
| connman_service_lookup_from_network(network); |
| |
| if (service != NULL) |
| __connman_service_set_usage_url(service, usage_url); |
| } |
| |
| /** |
| * __connman_network_activate_cellular_modem |
| * @network: network structure |
| * @carrier: the carrier (CDMA) to activate |
| * |
| * Returns: -EINVAL if network type does not support activation |
| */ |
| int __connman_network_activate_cellular_modem( |
| struct connman_network *network, |
| const char *carrier |
| ) |
| { |
| if (network->driver->activate_cellular_network != NULL) |
| return network->driver->activate_cellular_network(network, |
| carrier); |
| else |
| return -EINVAL; |
| } |
| |
| const struct connman_network_apn *connman_network_get_apn( |
| struct connman_network *network) |
| { |
| struct connman_service *service = |
| connman_service_lookup_from_network(network); |
| |
| if (service != NULL) |
| return __connman_service_get_apn(service); |
| else { |
| connman_warn("In connman_network_get_apn: No service found for" |
| " network %s", network->identifier); |
| return NULL; |
| } |
| } |
| |
| void connman_network_save_last_good_apn(struct connman_network *network, |
| const struct connman_network_apn *apn) |
| { |
| struct connman_service *service = |
| connman_service_lookup_from_network(network); |
| |
| if (service != NULL) |
| __connman_service_save_last_good_apn(service, apn); |
| } |
| |
| const struct connman_network_apn *connman_network_get_last_good_apn( |
| struct connman_network *network) |
| { |
| struct connman_service *service = |
| connman_service_lookup_from_network(network); |
| |
| if (service == NULL) |
| return NULL; |
| return connman_service_get_last_good_apn(service); |
| } |
| |
| struct peer_count_state { |
| struct connman_network *exclude; |
| int peer_count; |
| }; |
| |
| static void match_peer_network(struct connman_element *element, |
| gpointer user_data) |
| { |
| struct connman_network *network = (struct connman_network *) element; |
| struct peer_count_state *state = user_data; |
| |
| if (network == state->exclude) |
| return; |
| if (g_strcmp0(state->exclude->group, network->group) != 0) |
| return; |
| |
| state->peer_count++; |
| } |
| |
| int connman_network_get_peer_count(struct connman_network *network) { |
| struct peer_count_state state; |
| |
| state.exclude = network; |
| state.peer_count = 0; |
| |
| __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_NETWORK, |
| match_peer_network, GUINT_TO_POINTER(&state)); |
| |
| return state.peer_count; |
| } |
| |
| enum connman_network_cellular_roaming_state |
| connman_network_get_roaming_state(struct connman_network *network) |
| { |
| struct connman_service *service = |
| connman_service_lookup_from_network(network); |
| |
| if (service == NULL) |
| return CONNMAN_NETWORK_ROAMING_STATE_UNKNOWN; |
| return __connman_service_get_roaming_state(service); |
| } |
| |
| struct match_state { |
| struct connman_network *exclude; |
| struct connman_network *result; |
| }; |
| |
| static void match_other_network(struct connman_element *element, |
| gpointer user_data) |
| { |
| struct connman_network *network = (struct connman_network *) element; |
| struct match_state *state = user_data; |
| |
| if (network == state->exclude) |
| return; |
| if (g_strcmp0(state->exclude->group, network->group) != 0) |
| return; |
| |
| if (state->result == NULL) { |
| state->result = network; |
| return; |
| } |
| |
| /* prefer network with highest signal strength */ |
| if (state->result->strength < network->strength) |
| state->result = network; |
| } |
| |
| /* |
| * Find the best network to replace the specified network by |
| * by matching the network group and selecting the network |
| * with the strongest signal. This is used to update the |
| * preferred network bound to a service when the current |
| * preferred network is removed (e.g. because it goes out of range). |
| */ |
| struct connman_network *__connman_network_find_other_network( |
| struct connman_network *network) |
| { |
| struct match_state state; |
| |
| state.exclude = network; |
| state.result = NULL; |
| |
| __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_NETWORK, |
| match_other_network, GUINT_TO_POINTER(&state)); |
| |
| return state.result; |
| } |
| |
| static gboolean 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 FALSE; |
| } |
| |
| static int network_probe(struct connman_element *element) |
| { |
| struct connman_network *network = element->network; |
| GSList *list; |
| int err; |
| |
| _DBG_NETWORK("element %p name %s", element, element->name); |
| |
| if (network == NULL) |
| return -ENODEV; |
| |
| for (list = driver_list; list; list = list->next) { |
| struct connman_network_driver *driver = list->data; |
| |
| if (match_driver(network, driver) == FALSE) |
| continue; |
| |
| _DBG_NETWORK("driver %p name %s", driver, driver->name); |
| |
| if (driver->probe(network) == 0) { |
| network->driver = driver; |
| break; |
| } |
| } |
| |
| if (network->driver == NULL) |
| return -ENODEV; |
| |
| err = register_interface(element); |
| if (err < 0) { |
| if (network->driver->remove) |
| network->driver->remove(network); |
| return err; |
| } |
| |
| switch (network->type) { |
| case CONNMAN_NETWORK_TYPE_UNKNOWN: |
| case CONNMAN_NETWORK_TYPE_VENDOR: |
| case CONNMAN_NETWORK_TYPE_CELLULAR: |
| /* |
| * CELLULAR has been added to the network types where |
| * we do not call __connman_profile_add_network, |
| * because otherwise the service object ends up with a |
| * reference count of two and never disappears even |
| * after unplugging the modem. |
| * __connman_profile_add_network was already called |
| * from connman_network_set_group. It is unclear why |
| * other NETWORK_TYPES do not need this fix. |
| */ |
| break; |
| case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN: |
| case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN: |
| case CONNMAN_NETWORK_TYPE_WIFI: |
| case CONNMAN_NETWORK_TYPE_WIMAX: |
| if (network->group != NULL) |
| __connman_profile_add_network(network); |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static void network_remove(struct connman_element *element) |
| { |
| struct connman_network *network = element->network; |
| |
| _DBG_NETWORK("element %p name %s", element, element->name); |
| |
| if (network == NULL) |
| return; |
| |
| if (network->driver == NULL) |
| return; |
| |
| switch (network->type) { |
| case CONNMAN_NETWORK_TYPE_UNKNOWN: |
| case CONNMAN_NETWORK_TYPE_VENDOR: |
| break; |
| 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_profile_remove_network(network); |
| |
| g_free(network->group); |
| network->group = NULL; |
| } |
| break; |
| } |
| |
| unregister_interface(element); |
| |
| if (network->driver->remove) |
| network->driver->remove(network); |
| } |
| |
| static void network_change(struct connman_element *element) |
| { |
| struct connman_network *network = element->network; |
| |
| _DBG_NETWORK("element %p name %s", element, element->name); |
| |
| if (element->state == CONNMAN_ELEMENT_STATE_ERROR && |
| element->error == CONNMAN_ELEMENT_ERROR_DHCP_FAILED && |
| network->state == NETWORK_STATE_CONNECTED) { |
| /* |
| * DHCP failed, tear down state. Clear out children |
| * (XXX connection?), mark device no longer connected |
| * (XXX wrong?) and disconnect network. |
| */ |
| connman_element_unregister_children(element); |
| /* XXX wrong for wifi, schedules scan for after disconnect */ |
| connman_device_set_connected(network->device, FALSE); |
| |
| __connman_network_disconnect(network); |
| } |
| } |
| |
| /** |
| * connman_network_get_name_from_id |
| * @id: character buffer containing id |
| * @id_len: length of ID |
| * @result: pointer to buf for result to be written, or NULL to allocate one |
| * @result_size: size of result |
| * |
| * Returns: Pointer to result buffer, which will be a null-terminated string, |
| * or NULL if result was passed in NULL and malloc fails, or if the |
| * passed in result_size is too small. |
| */ |
| char *connman_network_get_name_from_id(const unsigned char *id, |
| unsigned int id_len, |
| char *result, |
| unsigned int result_size) |
| { |
| unsigned int i; |
| |
| if (result == NULL) { |
| result = g_try_malloc(id_len+1); |
| if (result == NULL) |
| return NULL; |
| } else if (id_len >= result_size) { |
| connman_error("%s: bad result_size %d", __func__, result_size); |
| return NULL; |
| } |
| |
| /* |
| * TODO(pstew): We could use g_utf8_validate() to test whether |
| * the string is valid UTF-8, however nobody downstream (python |
| * utils or chrome) correctly handle UTF-8 strings yet. |
| */ |
| |
| for (i = 0; i < id_len; i++) { |
| /* |
| * NB: Filter using g_ascii_isprint so that all |
| * non-ASCII-printable characters are removed. This ensures |
| * that the resulting string will be clean as far as DBus |
| * strings are concerned, and current versions of downstream |
| * consumers such as Chrome won't do silly things with the |
| * results. |
| */ |
| if (g_ascii_isprint(id[i])) |
| result[i] = id[i]; |
| else |
| result[i] = '?'; |
| } |
| result[id_len] = '\0'; |
| |
| return result; |
| } |
| |
| |
| static struct connman_driver network_driver = { |
| .name = "network", |
| .type = CONNMAN_ELEMENT_TYPE_NETWORK, |
| .priority = CONNMAN_DRIVER_PRIORITY_LOW, |
| .probe = network_probe, |
| .remove = network_remove, |
| .change = network_change, |
| }; |
| |
| int __connman_network_init(void) |
| { |
| _DBG_NETWORK(""); |
| |
| connection = connman_dbus_get_connection(); |
| |
| return connman_driver_register(&network_driver); |
| } |
| |
| void __connman_network_cleanup(void) |
| { |
| _DBG_NETWORK(""); |
| |
| connman_driver_unregister(&network_driver); |
| |
| dbus_connection_unref(connection); |
| } |