diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/agent-connman.c | 173 | ||||
-rw-r--r-- | src/agent.c | 21 | ||||
-rw-r--r-- | src/bridge.c | 2 | ||||
-rw-r--r-- | src/config.c | 146 | ||||
-rw-r--r-- | src/connection.c | 2 | ||||
-rw-r--r-- | src/connman.h | 54 | ||||
-rw-r--r-- | src/dbus.c | 32 | ||||
-rw-r--r-- | src/device.c | 6 | ||||
-rw-r--r-- | src/dhcp.c | 413 | ||||
-rw-r--r-- | src/dhcpv6.c | 197 | ||||
-rw-r--r-- | src/dnsproxy.c | 124 | ||||
-rw-r--r-- | src/inet.c | 63 | ||||
-rw-r--r-- | src/inotify.c | 2 | ||||
-rw-r--r-- | src/ipaddress.c | 84 | ||||
-rw-r--r-- | src/ipconfig.c | 39 | ||||
-rw-r--r-- | src/ippool.c | 2 | ||||
-rw-r--r-- | src/machine.c | 125 | ||||
-rw-r--r-- | src/main.c | 6 | ||||
-rw-r--r-- | src/manager.c | 127 | ||||
-rw-r--r-- | src/nat.c | 2 | ||||
-rw-r--r-- | src/net.connman.service.in | 2 | ||||
-rw-r--r-- | src/network.c | 60 | ||||
-rw-r--r-- | src/peer.c | 913 | ||||
-rw-r--r-- | src/peer_service.c | 429 | ||||
-rw-r--r-- | src/service.c | 417 | ||||
-rw-r--r-- | src/session.c | 2 | ||||
-rw-r--r-- | src/shared/netlink.c | 2 | ||||
-rw-r--r-- | src/stats.c | 2 | ||||
-rw-r--r-- | src/tethering.c | 7 | ||||
-rw-r--r-- | src/timeserver.c | 10 | ||||
-rw-r--r-- | src/wispr.c | 10 |
31 files changed, 2607 insertions, 867 deletions
diff --git a/src/agent-connman.c b/src/agent-connman.c index ab538f38..b2049a3d 100644 --- a/src/agent-connman.c +++ b/src/agent-connman.c @@ -56,7 +56,12 @@ static bool check_reply_has_dict(DBusMessage *reply) struct request_input_reply { struct connman_service *service; - authentication_cb_t callback; + struct connman_peer *peer; + union { + authentication_cb_t service_callback; + peer_wps_cb_t peer_callback; + }; + bool wps_requested; void *user_data; }; @@ -151,12 +156,10 @@ static void request_input_passphrase_reply(DBusMessage *reply, void *user_data) } done: - passphrase_reply->callback(passphrase_reply->service, values_received, - name, name_len, - identity, passphrase, - wps, wpspin, error, - passphrase_reply->user_data); - + passphrase_reply->service_callback(passphrase_reply->service, + values_received, name, name_len, + identity, passphrase, wps, wpspin, + error, passphrase_reply->user_data); out: g_free(passphrase_reply); } @@ -236,13 +239,21 @@ static void request_input_append_passphrase(DBusMessageIter *iter, } } +struct request_wps_data { + bool peer; +}; + static void request_input_append_wps(DBusMessageIter *iter, void *user_data) { + struct request_wps_data *wps = user_data; const char *str = "wpspin"; connman_dbus_dict_append_basic(iter, "Type", DBUS_TYPE_STRING, &str); - str = "alternate"; + if (wps && wps->peer) + str = "mandatory"; + else + str = "alternate"; connman_dbus_dict_append_basic(iter, "Requirement", DBUS_TYPE_STRING, &str); } @@ -399,12 +410,10 @@ static void request_input_login_reply(DBusMessage *reply, void *user_data) } done: - username_password_reply->callback(username_password_reply->service, - values_received, NULL, 0, - username, password, - FALSE, NULL, error, - username_password_reply->user_data); - + username_password_reply->service_callback( + username_password_reply->service, values_received, + NULL, 0, username, password, FALSE, NULL, error, + username_password_reply->user_data); out: g_free(username_password_reply); } @@ -477,7 +486,7 @@ int __connman_agent_request_passphrase_input(struct connman_service *service, } passphrase_reply->service = service; - passphrase_reply->callback = callback; + passphrase_reply->service_callback = callback; passphrase_reply->user_data = user_data; err = connman_agent_queue_message(service, message, @@ -542,7 +551,7 @@ int __connman_agent_request_login_input(struct connman_service *service, } username_password_reply->service = service; - username_password_reply->callback = callback; + username_password_reply->service_callback = callback; username_password_reply->user_data = user_data; err = connman_agent_queue_message(service, message, @@ -644,3 +653,135 @@ int __connman_agent_request_browser(struct connman_service *service, return -EINPROGRESS; } + +int __connman_agent_report_peer_error(struct connman_peer *peer, + const char *path, const char *error, + report_error_cb_t callback, + const char *dbus_sender, + void *user_data) +{ + return connman_agent_report_error_full(peer, path, "ReportPeerError", + error, callback, dbus_sender, user_data); +} + +static void request_peer_authorization_reply(DBusMessage *reply, + void *user_data) +{ + struct request_input_reply *auth_reply = user_data; + DBusMessageIter iter, dict; + const char *error = NULL; + bool choice_done = false; + char *wpspin = NULL; + char *key; + + if (!reply) + goto out; + + if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) { + error = dbus_message_get_error_name(reply); + goto done; + } + + if (!check_reply_has_dict(reply)) + goto done; + + dbus_message_iter_init(reply, &iter); + dbus_message_iter_recurse(&iter, &dict); + while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entry, value; + + dbus_message_iter_recurse(&dict, &entry); + if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) + break; + + dbus_message_iter_get_basic(&entry, &key); + + if (g_str_equal(key, "WPS")) { + choice_done = true; + + dbus_message_iter_next(&entry); + if (dbus_message_iter_get_arg_type(&entry) + != DBUS_TYPE_VARIANT) + break; + dbus_message_iter_recurse(&entry, &value); + dbus_message_iter_get_basic(&value, &wpspin); + break; + } + dbus_message_iter_next(&dict); + } + + if (!auth_reply->wps_requested) + choice_done = true; + +done: + auth_reply->peer_callback(auth_reply->peer, choice_done, wpspin, + error, auth_reply->user_data); +out: + g_free(auth_reply); +} + +int __connman_agent_request_peer_authorization(struct connman_peer *peer, + peer_wps_cb_t callback, + bool wps_requested, + const char *dbus_sender, + void *user_data) +{ + struct request_wps_data wps = { .peer = true }; + const char *path, *agent_sender, *agent_path; + struct request_input_reply *auth_reply; + DBusMessageIter dict, iter; + DBusMessage *message; + void *agent; + int err; + + agent = connman_agent_get_info(dbus_sender, &agent_sender, + &agent_path); + DBG("agent %p peer %p path %s", agent, peer, agent_path); + + if (!peer || !agent || !agent_path || !callback) + return -ESRCH; + + message = dbus_message_new_method_call(agent_sender, agent_path, + CONNMAN_AGENT_INTERFACE, "RequestPeerAuthorization"); + if (!message) + return -ENOMEM; + + dbus_message_iter_init_append(message, &iter); + + path = __connman_peer_get_path(peer); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path); + + connman_dbus_dict_open(&iter, &dict); + + if (wps_requested) + connman_dbus_dict_append_dict(&dict, "WPS", + request_input_append_wps, &wps); + + connman_dbus_dict_close(&iter, &dict); + + auth_reply = g_try_new0(struct request_input_reply, 1); + if (!auth_reply) { + dbus_message_unref(message); + return -ENOMEM; + } + + auth_reply->peer = peer; + auth_reply->peer_callback = callback; + auth_reply->wps_requested = wps_requested; + auth_reply->user_data = user_data; + + err = connman_agent_queue_message(peer, message, + connman_timeout_input_request(), + request_peer_authorization_reply, + auth_reply, agent); + if (err < 0 && err != -EBUSY) { + DBG("error %d sending agent message", err); + dbus_message_unref(message); + g_free(auth_reply); + return err; + } + + dbus_message_unref(message); + + return -EINPROGRESS; +} diff --git a/src/agent.c b/src/agent.c index 37cf5247..a3400262 100644 --- a/src/agent.c +++ b/src/agent.c @@ -350,6 +350,9 @@ static void report_error_reply(DBusMessage *reply, void *user_data) bool retry = false; const char *dbus_err; + if (!reply) + goto out; + if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) { dbus_err = dbus_message_get_error_name(reply); if (dbus_err && @@ -360,11 +363,12 @@ static void report_error_reply(DBusMessage *reply, void *user_data) report_error->callback(report_error->user_context, retry, report_error->user_data); +out: g_free(report_error); } -int connman_agent_report_error(void *user_context, const char *path, - const char *error, +int connman_agent_report_error_full(void *user_context, const char *path, + const char *method, const char *error, report_error_cb_t callback, const char *dbus_sender, void *user_data) { @@ -383,8 +387,7 @@ int connman_agent_report_error(void *user_context, const char *path, return -ESRCH; message = dbus_message_new_method_call(agent->owner, agent->path, - CONNMAN_AGENT_INTERFACE, - "ReportError"); + CONNMAN_AGENT_INTERFACE, method); if (!message) return -ENOMEM; @@ -421,6 +424,16 @@ int connman_agent_report_error(void *user_context, const char *path, return -EINPROGRESS; } +int connman_agent_report_error(void *user_context, const char *path, + const char *error, + report_error_cb_t callback, + const char *dbus_sender, void *user_data) +{ + return connman_agent_report_error_full(user_context, path, + "ReportError", error, callback, dbus_sender, + user_data); +} + static gint compare_priority(gconstpointer a, gconstpointer b) { const struct connman_agent_driver *driver1 = a; diff --git a/src/bridge.c b/src/bridge.c index 034fa139..ba200969 100644 --- a/src/bridge.c +++ b/src/bridge.c @@ -3,7 +3,7 @@ * Connection Manager * * Copyright (C) 2007-2013 Intel Corporation. All rights reserved. - * Copyright (C) 2012-2013 BMW Car IT GmbH. All rights reserved. + * Copyright (C) 2012-2014 BMW Car IT GmbH. * * 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 diff --git a/src/config.c b/src/config.c index 330ae817..93a788a1 100644 --- a/src/config.c +++ b/src/config.c @@ -52,6 +52,7 @@ struct connman_config_service { char *private_key_passphrase_type; char *phase2; char *passphrase; + enum connman_service_security security; GSList *service_identifiers; char *config_ident; /* file prefix */ char *config_entry; /* entry name */ @@ -99,6 +100,7 @@ static bool cleanup = false; #define SERVICE_KEY_IDENTITY "Identity" #define SERVICE_KEY_PHASE2 "Phase2" #define SERVICE_KEY_PASSPHRASE "Passphrase" +#define SERVICE_KEY_SECURITY "Security" #define SERVICE_KEY_HIDDEN "Hidden" #define SERVICE_KEY_IPv4 "IPv4" @@ -129,6 +131,7 @@ static const char *service_possible_keys[] = { SERVICE_KEY_IDENTITY, SERVICE_KEY_PHASE2, SERVICE_KEY_PASSPHRASE, + SERVICE_KEY_SECURITY, SERVICE_KEY_HIDDEN, SERVICE_KEY_IPv4, SERVICE_KEY_IPv6, @@ -399,7 +402,7 @@ static bool load_service_generic(GKeyFile *keyfile, char *ptr; long int value = strtol(mask, &ptr, 10); - if (ptr != mask && *ptr == '\0' && value <= 32) + if (ptr != mask && *ptr == '\0' && value && value <= 32) prefix_len = value; addr = 0xffffffff << (32 - prefix_len); @@ -513,6 +516,7 @@ static bool load_service(GKeyFile *keyfile, const char *group, struct connman_config_service *service; const char *ident; char *str, *hex_ssid; + enum connman_service_security security; bool service_created = false; /* Strip off "service_" prefix */ @@ -664,6 +668,38 @@ static bool load_service(GKeyFile *keyfile, const char *group, service->passphrase = str; } + str = __connman_config_get_string(keyfile, group, SERVICE_KEY_SECURITY, + NULL); + security = __connman_service_string2security(str); + + if (service->eap) { + + if (str && security != CONNMAN_SERVICE_SECURITY_8021X) + connman_info("Mismatch between EAP configuration and " + "setting %s = %s", + SERVICE_KEY_SECURITY, str); + + service->security = CONNMAN_SERVICE_SECURITY_8021X; + + } else if (service->passphrase) { + + if (str) { + if (security == CONNMAN_SERVICE_SECURITY_PSK || + security == CONNMAN_SERVICE_SECURITY_WEP) { + service->security = security; + } else { + connman_info("Mismatch with passphrase and " + "setting %s = %s", + SERVICE_KEY_SECURITY, str); + + service->security = + CONNMAN_SERVICE_SECURITY_PSK; + } + + } else + service->security = CONNMAN_SERVICE_SECURITY_PSK; + } + service->config_ident = g_strdup(config->ident); service->config_entry = g_strdup_printf("service_%s", service->ident); @@ -1062,22 +1098,7 @@ static int try_provision_service(struct connman_config_service *config, enum connman_service_type type; const void *ssid; unsigned int ssid_len; - - type = connman_service_get_type(service); - if (type == CONNMAN_SERVICE_TYPE_WIFI && - g_strcmp0(config->type, "wifi") != 0) - return -ENOENT; - - if (type == CONNMAN_SERVICE_TYPE_ETHERNET && - g_strcmp0(config->type, "ethernet") != 0) - return -ENOENT; - - if (type == CONNMAN_SERVICE_TYPE_GADGET && - g_strcmp0(config->type, "gadget") != 0) - return -ENOENT; - - DBG("service %p ident %s", service, - __connman_service_get_ident(service)); + const char *str; network = __connman_service_get_network(service); if (!network) { @@ -1088,6 +1109,54 @@ static int try_provision_service(struct connman_config_service *config, DBG("network %p ident %s", network, connman_network_get_identifier(network)); + type = connman_service_get_type(service); + + switch(type) { + case CONNMAN_SERVICE_TYPE_WIFI: + if (__connman_service_string2type(config->type) != type) + return -ENOENT; + + ssid = connman_network_get_blob(network, "WiFi.SSID", + &ssid_len); + if (!ssid) { + connman_error("Network SSID not set"); + return -EINVAL; + } + + if (!config->ssid || ssid_len != config->ssid_len) + return -ENOENT; + + if (memcmp(config->ssid, ssid, ssid_len)) + return -ENOENT; + + str = connman_network_get_string(network, "WiFi.Security"); + if (config->security != __connman_service_string2security(str)) + return -ENOENT; + + break; + + case CONNMAN_SERVICE_TYPE_ETHERNET: + case CONNMAN_SERVICE_TYPE_GADGET: + + if (__connman_service_string2type(config->type) != type) + return -ENOENT; + + break; + + case CONNMAN_SERVICE_TYPE_UNKNOWN: + case CONNMAN_SERVICE_TYPE_SYSTEM: + case CONNMAN_SERVICE_TYPE_BLUETOOTH: + case CONNMAN_SERVICE_TYPE_CELLULAR: + case CONNMAN_SERVICE_TYPE_GPS: + case CONNMAN_SERVICE_TYPE_VPN: + case CONNMAN_SERVICE_TYPE_P2P: + + return -ENOENT; + } + + DBG("service %p ident %s", service, + __connman_service_get_ident(service)); + if (config->mac) { struct connman_device *device; const char *device_addr; @@ -1106,22 +1175,6 @@ static int try_provision_service(struct connman_config_service *config, return -ENOENT; } - if (g_strcmp0(config->type, "wifi") == 0 && - type == CONNMAN_SERVICE_TYPE_WIFI) { - ssid = connman_network_get_blob(network, "WiFi.SSID", - &ssid_len); - if (!ssid) { - connman_error("Network SSID not set"); - return -EINVAL; - } - - if (!config->ssid || ssid_len != config->ssid_len) - return -ENOENT; - - if (memcmp(config->ssid, ssid, ssid_len) != 0) - return -ENOENT; - } - if (!config->ipv6_address) { connman_network_set_ipv6_method(network, CONNMAN_IPCONFIG_METHOD_AUTO); @@ -1210,9 +1263,6 @@ static int try_provision_service(struct connman_config_service *config, g_slist_prepend(config->service_identifiers, g_strdup(service_id)); - if (!config->virtual) - __connman_service_set_immutable(service, true); - __connman_service_set_favorite_delayed(service, true, true); __connman_service_set_config(service, config->config_ident, @@ -1240,13 +1290,10 @@ static int try_provision_service(struct connman_config_service *config, __connman_service_set_timeservers(service, config->timeservers); - if (g_strcmp0(config->type, "wifi") == 0 && - type == CONNMAN_SERVICE_TYPE_WIFI) { + if (type == CONNMAN_SERVICE_TYPE_WIFI) { provision_service_wifi(config, service, network, ssid, ssid_len); - } else - __connman_service_connect(service, - CONNMAN_SERVICE_CONNECT_REASON_AUTO); + } __connman_service_mark_dirty(); @@ -1260,8 +1307,21 @@ static int try_provision_service(struct connman_config_service *config, virtual->vfile = config->virtual_file; g_timeout_add(0, remove_virtual_config, virtual); - } else - __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO); + + return 0; + } + + __connman_service_set_immutable(service, true); + + if (type == CONNMAN_SERVICE_TYPE_ETHERNET || + type == CONNMAN_SERVICE_TYPE_GADGET) { + __connman_service_connect(service, + CONNMAN_SERVICE_CONNECT_REASON_AUTO); + + return 0; + } + + __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO); return 0; } diff --git a/src/connection.c b/src/connection.c index e98ccb5c..8fe97258 100644 --- a/src/connection.c +++ b/src/connection.c @@ -3,7 +3,7 @@ * Connection Manager * * Copyright (C) 2007-2013 Intel Corporation. All rights reserved. - * Copyright (C) 2011-2013 BMW Car IT GmbH. All rights reserved. + * Copyright (C) 2011-2014 BMW Car IT GmbH. * * 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 diff --git a/src/connman.h b/src/connman.h index 24db5f8d..da012152 100644 --- a/src/connman.h +++ b/src/connman.h @@ -88,12 +88,13 @@ int __connman_counter_unregister(const char *owner, const char *path); int __connman_counter_init(void); void __connman_counter_cleanup(void); +#include <connman/agent.h> + struct connman_service; +struct connman_peer; void __connman_agent_cancel(struct connman_service *service); -int __connman_service_add_passphrase(struct connman_service *service, - const gchar *passphrase); typedef void (* authentication_cb_t) (struct connman_service *service, bool values_received, const char *name, int name_len, @@ -103,6 +104,9 @@ typedef void (* authentication_cb_t) (struct connman_service *service, typedef void (* browser_authentication_cb_t) (struct connman_service *service, bool authentication_done, const char *error, void *user_data); +typedef void (* peer_wps_cb_t) (struct connman_peer *peer, bool choice_done, + const char *wpspin, const char *error, + void *user_data); int __connman_agent_request_passphrase_input(struct connman_service *service, authentication_cb_t callback, const char *dbus_sender, void *user_data); @@ -111,6 +115,16 @@ int __connman_agent_request_login_input(struct connman_service *service, int __connman_agent_request_browser(struct connman_service *service, browser_authentication_cb_t callback, const char *url, void *user_data); +int __connman_agent_report_peer_error(struct connman_peer *peer, + const char *path, const char *error, + report_error_cb_t callback, + const char *dbus_sender, + void *user_data); +int __connman_agent_request_peer_authorization(struct connman_peer *peer, + peer_wps_cb_t callback, + bool wps_requested, + const char *dbus_sender, + void *user_data); #include <connman/log.h> @@ -383,7 +397,6 @@ int __connman_ipconfig_address_remove(struct connman_ipconfig *ipconfig); int __connman_ipconfig_address_unset(struct connman_ipconfig *ipconfig); int __connman_ipconfig_gateway_add(struct connman_ipconfig *ipconfig); void __connman_ipconfig_gateway_remove(struct connman_ipconfig *ipconfig); -unsigned char __connman_ipaddress_netmask_prefix_len(const char *netmask); int __connman_ipconfig_set_proxy_autoconfig(struct connman_ipconfig *ipconfig, const char *url); @@ -400,6 +413,7 @@ int __connman_ipconfig_load(struct connman_ipconfig *ipconfig, int __connman_ipconfig_save(struct connman_ipconfig *ipconfig, GKeyFile *keyfile, const char *identifier, const char *prefix); bool __connman_ipconfig_ipv6_privacy_enabled(struct connman_ipconfig *ipconfig); +int __connman_ipconfig_ipv6_reset_privacy(struct connman_ipconfig *ipconfig); int __connman_ipconfig_ipv6_set_privacy(struct connman_ipconfig *ipconfig, const char *value); bool __connman_ipconfig_ipv6_is_enabled(struct connman_ipconfig *ipconfig); @@ -434,10 +448,13 @@ enum __connman_dhcpv6_status { typedef void (* dhcpv6_cb) (struct connman_network *network, enum __connman_dhcpv6_status status, gpointer data); -typedef void (* dhcp_cb) (struct connman_network *network, +typedef void (* dhcp_cb) (struct connman_ipconfig *ipconfig, + struct connman_network *opt_network, bool success, gpointer data); -int __connman_dhcp_start(struct connman_network *network, dhcp_cb callback); -void __connman_dhcp_stop(struct connman_network *network); +int __connman_dhcp_start(struct connman_ipconfig *ipconfig, + struct connman_network *network, dhcp_cb callback, + gpointer user_data); +void __connman_dhcp_stop(struct connman_ipconfig *ipconfig); int __connman_dhcp_init(void); void __connman_dhcp_cleanup(void); int __connman_dhcpv6_init(void); @@ -711,8 +728,6 @@ void __connman_service_set_hidden_data(struct connman_service *service, gpointer user_data); void __connman_service_return_error(struct connman_service *service, int error, gpointer user_data); -void __connman_service_reply_dbus_pending(DBusMessage *pending, int error, - const char *path); int __connman_service_provision_changed(const char *ident); void __connman_service_set_config(struct connman_service *service, @@ -720,6 +735,7 @@ void __connman_service_set_config(struct connman_service *service, const char *__connman_service_type2string(enum connman_service_type type); enum connman_service_type __connman_service_string2type(const char *str); +enum connman_service_security __connman_service_string2security(const char *str); int __connman_service_nameserver_append(struct connman_service *service, const char *nameserver, bool is_auto); @@ -780,6 +796,23 @@ int __connman_peer_init(void); void __connman_peer_cleanup(void); void __connman_peer_list_struct(DBusMessageIter *array); +const char *__connman_peer_get_path(struct connman_peer *peer); + +int __connman_peer_service_init(void); +void __connman_peer_service_cleanup(void); + +void __connman_peer_service_set_driver(struct connman_peer_driver *driver); +int __connman_peer_service_register(const char *owner, DBusMessage *msg, + const unsigned char *specification, + int specification_length, + const unsigned char *query, + int query_length, int version, + bool master); +int __connman_peer_service_unregister(const char *owner, + const unsigned char *specification, + int specification_length, + const unsigned char *query, + int query_length, int version); #include <connman/session.h> @@ -996,3 +1029,8 @@ int __connman_nfacct_disable(struct nfacct_context *ctx, void *user_data); void __connman_nfacct_cleanup(void); + +#include <connman/machine.h> + +int __connman_machine_init(void); +void __connman_machine_cleanup(void); @@ -653,6 +653,38 @@ err: return err; } +void connman_dbus_reply_pending(DBusMessage *pending, + int error, const char *path) +{ + if (pending) { + if (error > 0) { + DBusMessage *reply; + + reply = __connman_error_failed(pending, error); + if (reply) + g_dbus_send_message(connection, reply); + } else { + const char *sender; + + sender = dbus_message_get_interface(pending); + if (!path) + path = dbus_message_get_path(pending); + + DBG("sender %s path %s", sender, path); + + if (g_strcmp0(sender, CONNMAN_MANAGER_INTERFACE) == 0) + g_dbus_send_reply(connection, pending, + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID); + else + g_dbus_send_reply(connection, pending, + DBUS_TYPE_INVALID); + } + + dbus_message_unref(pending); + } +} + DBusConnection *connman_dbus_get_connection(void) { if (!connection) diff --git a/src/device.c b/src/device.c index a97d790e..c0683abd 100644 --- a/src/device.c +++ b/src/device.c @@ -185,6 +185,12 @@ int __connman_device_enable(struct connman_device *device) if (device->powered_pending == PENDING_NONE && device->powered) return -EALREADY; + if (device->index > 0) { + err = connman_inet_ifup(device->index); + if (err < 0 && err != -EALREADY) + return err; + } + device->powered_pending = PENDING_ENABLE; err = device->driver->enable(device); @@ -39,8 +39,10 @@ #define RATE_LIMIT_INTERVAL 60 /* delay between successive attempts */ struct connman_dhcp { + struct connman_ipconfig *ipconfig; struct connman_network *network; dhcp_cb callback; + gpointer user_data; char **nameservers; char **timeservers; @@ -54,7 +56,7 @@ struct connman_dhcp { char *dhcp_debug_prefix; }; -static GHashTable *network_table; +static GHashTable *ipconfig_table; static bool ipv4ll_running; static void dhcp_free(struct connman_dhcp *dhcp) @@ -70,41 +72,36 @@ static void dhcp_free(struct connman_dhcp *dhcp) g_free(dhcp); } -/** - * dhcp_invalidate: Invalidate an existing DHCP lease - * @dhcp: pointer to the DHCP lease to invalidate. - * @callback: flag indicating whether or not to invoke the client callback - * if present. - * - * Invalidates an existing DHCP lease, optionally invoking the client - * callback. The caller may wish to avoid the client callback invocation - * when the invocation of that callback might otherwise unnecessarily upset - * service state due to the IP configuration change implied by this - * invalidation. - */ -static void dhcp_invalidate(struct connman_dhcp *dhcp, bool callback) +static void ipv4ll_stop_client(struct connman_dhcp *dhcp) { - struct connman_service *service; - struct connman_ipconfig *ipconfig; - int i; + if (!dhcp->ipv4ll_client) + return; - DBG("dhcp %p callback %u", dhcp, callback); + g_dhcp_client_stop(dhcp->ipv4ll_client); + g_dhcp_client_unref(dhcp->ipv4ll_client); + dhcp->ipv4ll_client = NULL; + ipv4ll_running = false; - if (!dhcp) - return; + g_free(dhcp->ipv4ll_debug_prefix); + dhcp->ipv4ll_debug_prefix = NULL; +} - service = connman_service_lookup_from_network(dhcp->network); - if (!service) - return; +static bool apply_dhcp_invalidate_on_network(struct connman_dhcp *dhcp) +{ + struct connman_service *service; + int i; - ipconfig = __connman_service_get_ip4config(service); - if (!ipconfig) - return; + if (!dhcp->network) + return true; - __connman_6to4_remove(ipconfig); + service = connman_service_lookup_from_network(dhcp->network); + if (!service) { + connman_error("Can not lookup service"); + return false; + } __connman_service_set_domainname(service, NULL); - __connman_service_set_pac(service, NULL); + __connman_ipconfig_set_proxy_autoconfig(dhcp->ipconfig, NULL); if (dhcp->timeservers) { for (i = 0; dhcp->timeservers[i]; i++) { @@ -112,7 +109,6 @@ static void dhcp_invalidate(struct connman_dhcp *dhcp, bool callback) dhcp->timeservers[i]); } } - if (dhcp->nameservers) { for (i = 0; dhcp->nameservers[i]; i++) { __connman_service_nameserver_remove(service, @@ -120,25 +116,55 @@ static void dhcp_invalidate(struct connman_dhcp *dhcp, bool callback) } } - __connman_ipconfig_set_dhcp_address(ipconfig, - __connman_ipconfig_get_local(ipconfig)); - DBG("last address %s", __connman_ipconfig_get_dhcp_address(ipconfig)); + return true; +} - __connman_ipconfig_address_remove(ipconfig); +/** + * dhcp_invalidate: Invalidate an existing DHCP lease + * @dhcp: pointer to the DHCP lease to invalidate. + * @callback: flag indicating whether or not to invoke the client callback + * if present. + * + * Invalidates an existing DHCP lease, optionally invoking the client + * callback. The caller may wish to avoid the client callback invocation + * when the invocation of that callback might otherwise unnecessarily upset + * service state due to the IP configuration change implied by this + * invalidation. + */ +static void dhcp_invalidate(struct connman_dhcp *dhcp, bool callback) +{ + DBG("dhcp %p callback %u", dhcp, callback); + + if (!dhcp) + return; + + __connman_6to4_remove(dhcp->ipconfig); - __connman_ipconfig_set_local(ipconfig, NULL); - __connman_ipconfig_set_broadcast(ipconfig, NULL); - __connman_ipconfig_set_gateway(ipconfig, NULL); - __connman_ipconfig_set_prefixlen(ipconfig, 0); + if (!apply_dhcp_invalidate_on_network(dhcp)) + return; + + __connman_ipconfig_set_dhcp_address(dhcp->ipconfig, + __connman_ipconfig_get_local(dhcp->ipconfig)); + DBG("last address %s", + __connman_ipconfig_get_dhcp_address(dhcp->ipconfig)); + + __connman_ipconfig_address_remove(dhcp->ipconfig); + + __connman_ipconfig_set_local(dhcp->ipconfig, NULL); + __connman_ipconfig_set_broadcast(dhcp->ipconfig, NULL); + __connman_ipconfig_set_gateway(dhcp->ipconfig, NULL); + __connman_ipconfig_set_prefixlen(dhcp->ipconfig, 0); if (dhcp->callback && callback) - dhcp->callback(dhcp->network, false, NULL); + dhcp->callback(dhcp->ipconfig, dhcp->network, + false, dhcp->user_data); } static void dhcp_valid(struct connman_dhcp *dhcp) { if (dhcp->callback) - dhcp->callback(dhcp->network, true, NULL); + dhcp->callback(dhcp->ipconfig, dhcp->network, + true, dhcp->user_data); } static void dhcp_debug(const char *str, void *data) @@ -146,20 +172,6 @@ static void dhcp_debug(const char *str, void *data) connman_info("%s: %s", (const char *) data, str); } -static void ipv4ll_stop_client(struct connman_dhcp *dhcp) -{ - if (!dhcp->ipv4ll_client) - return; - - g_dhcp_client_stop(dhcp->ipv4ll_client); - g_dhcp_client_unref(dhcp->ipv4ll_client); - dhcp->ipv4ll_client = NULL; - ipv4ll_running = false; - - g_free(dhcp->ipv4ll_debug_prefix); - dhcp->ipv4ll_debug_prefix = NULL; -} - static void ipv4ll_lost_cb(GDHCPClient *dhcp_client, gpointer user_data); static void ipv4ll_available_cb(GDHCPClient *ipv4ll_client, gpointer user_data); @@ -174,7 +186,7 @@ static int ipv4ll_start_client(struct connman_dhcp *dhcp) if (dhcp->ipv4ll_client) return -EALREADY; - index = connman_network_get_index(dhcp->network); + index = __connman_ipconfig_get_index(dhcp->ipconfig); ipv4ll_client = g_dhcp_client_new(G_DHCP_IPV4LL, index, &error); if (error != G_DHCP_CLIENT_ERROR_NONE) @@ -189,10 +201,12 @@ static int ipv4ll_start_client(struct connman_dhcp *dhcp) g_dhcp_client_set_id(ipv4ll_client); - hostname = connman_utsname_get_hostname(); - if (hostname) - g_dhcp_client_set_send(ipv4ll_client, G_DHCP_HOST_NAME, - hostname); + if (dhcp->network) { + hostname = connman_utsname_get_hostname(); + if (hostname) + g_dhcp_client_set_send(ipv4ll_client, + G_DHCP_HOST_NAME, hostname); + } g_dhcp_client_register_event(ipv4ll_client, G_DHCP_CLIENT_EVENT_IPV4LL_LOST, ipv4ll_lost_cb, dhcp); @@ -216,16 +230,11 @@ static int ipv4ll_start_client(struct connman_dhcp *dhcp) static gboolean dhcp_retry_cb(gpointer user_data) { struct connman_dhcp *dhcp = user_data; - struct connman_service *service; - struct connman_ipconfig *ipconfig; dhcp->timeout = 0; - service = connman_service_lookup_from_network(dhcp->network); - ipconfig = __connman_service_get_ip4config(service); - g_dhcp_client_start(dhcp->dhcp_client, - __connman_ipconfig_get_dhcp_address(ipconfig)); + __connman_ipconfig_get_dhcp_address(dhcp->ipconfig)); return FALSE; } @@ -296,78 +305,33 @@ static bool compare_string_arrays(char **array_a, char **array_b) return true; } -static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data) +static bool apply_lease_available_on_network(GDHCPClient *dhcp_client, + struct connman_dhcp *dhcp) { - struct connman_dhcp *dhcp = user_data; - GList *list, *option = NULL; - char *address, *netmask = NULL, *gateway = NULL; - const char *c_address, *c_gateway; char **nameservers, **timeservers, *pac = NULL; - int ns_entries; - struct connman_ipconfig *ipconfig; struct connman_service *service; - unsigned char prefixlen, c_prefixlen; - bool ip_change; + GList *list, *option = NULL; + int ns_entries; int i; - DBG("Lease available"); - - if (dhcp->ipv4ll_client) { - ipv4ll_stop_client(dhcp); - dhcp_invalidate(dhcp, false); - } + if (!dhcp->network) + return true; service = connman_service_lookup_from_network(dhcp->network); if (!service) { connman_error("Can not lookup service"); - return; - } - - ipconfig = __connman_service_get_ip4config(service); - if (!ipconfig) { - connman_error("Could not lookup ipconfig"); - return; + return false; } - c_address = __connman_ipconfig_get_local(ipconfig); - c_gateway = __connman_ipconfig_get_gateway(ipconfig); - c_prefixlen = __connman_ipconfig_get_prefixlen(ipconfig); - - address = g_dhcp_client_get_address(dhcp_client); - - __connman_ipconfig_set_dhcp_address(ipconfig, address); - DBG("last address %s", address); - - option = g_dhcp_client_get_option(dhcp_client, G_DHCP_SUBNET); - if (option) - netmask = g_strdup(option->data); - - option = g_dhcp_client_get_option(dhcp_client, G_DHCP_ROUTER); + option = g_dhcp_client_get_option(dhcp_client, 252); if (option) - gateway = g_strdup(option->data); - - prefixlen = __connman_ipaddress_netmask_prefix_len(netmask); - if (prefixlen == 255) - connman_warn("netmask: %s is invalid", netmask); - - DBG("c_address %s", c_address); - - if (address && c_address && g_strcmp0(address, c_address) != 0) - ip_change = true; - else if (gateway && c_gateway && g_strcmp0(gateway, c_gateway) != 0) - ip_change = true; - else if (prefixlen != c_prefixlen) - ip_change = true; - else if (!c_address || !c_gateway) - ip_change = true; - else - ip_change = false; + pac = g_strdup(option->data); option = g_dhcp_client_get_option(dhcp_client, G_DHCP_DNS_SERVER); ns_entries = g_list_length(option); nameservers = g_try_new0(char *, ns_entries + 1); if (nameservers) { - for (i = 0, list = option; list; list = list->next, i++) + for (i = 0, list = option;list; list = list->next, i++) nameservers[i] = g_strdup(list->data); nameservers[ns_entries] = NULL; } @@ -389,18 +353,6 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data) timeservers[ns_entries] = NULL; } - option = g_dhcp_client_get_option(dhcp_client, 252); - if (option) - pac = g_strdup(option->data); - - __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_DHCP); - - if (ip_change) { - __connman_ipconfig_set_local(ipconfig, address); - __connman_ipconfig_set_prefixlen(ipconfig, prefixlen); - __connman_ipconfig_set_gateway(ipconfig, gateway); - } - if (!compare_string_arrays(nameservers, dhcp->nameservers)) { if (dhcp->nameservers) { for (i = 0; dhcp->nameservers[i]; i++) { @@ -412,8 +364,7 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data) dhcp->nameservers = nameservers; - for (i = 0; dhcp->nameservers && - dhcp->nameservers[i]; i++) { + for (i = 0; dhcp->nameservers && dhcp->nameservers[i]; i++) { __connman_service_nameserver_append(service, dhcp->nameservers[i], false); } @@ -432,8 +383,7 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data) dhcp->timeservers = timeservers; - for (i = 0; dhcp->timeservers && - dhcp->timeservers[i]; i++) { + for (i = 0; dhcp->timeservers && dhcp->timeservers[i]; i++) { __connman_service_timeserver_append(service, dhcp->timeservers[i]); } @@ -445,14 +395,77 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data) g_free(dhcp->pac); dhcp->pac = pac; - __connman_service_set_pac(service, dhcp->pac); + __connman_ipconfig_set_proxy_autoconfig(dhcp->ipconfig, + dhcp->pac); + } + + __connman_6to4_probe(service); + + return true; +} + +static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data) +{ + struct connman_dhcp *dhcp = user_data; + GList *option = NULL; + char *address, *netmask = NULL, *gateway = NULL; + const char *c_address, *c_gateway; + unsigned char prefixlen, c_prefixlen; + bool ip_change; + + DBG("Lease available"); + + if (dhcp->ipv4ll_client) { + ipv4ll_stop_client(dhcp); + dhcp_invalidate(dhcp, false); + } + + c_address = __connman_ipconfig_get_local(dhcp->ipconfig); + c_gateway = __connman_ipconfig_get_gateway(dhcp->ipconfig); + c_prefixlen = __connman_ipconfig_get_prefixlen(dhcp->ipconfig); + + address = g_dhcp_client_get_address(dhcp_client); + + __connman_ipconfig_set_dhcp_address(dhcp->ipconfig, address); + DBG("last address %s", address); + + option = g_dhcp_client_get_option(dhcp_client, G_DHCP_SUBNET); + if (option) + netmask = g_strdup(option->data); + + option = g_dhcp_client_get_option(dhcp_client, G_DHCP_ROUTER); + if (option) + gateway = g_strdup(option->data); + + prefixlen = connman_ipaddress_calc_netmask_len(netmask); + if (prefixlen == 255) + connman_warn("netmask: %s is invalid", netmask); + + DBG("c_address %s", c_address); + + if (g_strcmp0(address, c_address)) + ip_change = true; + else if (g_strcmp0(gateway, c_gateway)) + ip_change = true; + else if (prefixlen != c_prefixlen) + ip_change = true; + else + ip_change = false; + + __connman_ipconfig_set_method(dhcp->ipconfig, + CONNMAN_IPCONFIG_METHOD_DHCP); + if (ip_change) { + __connman_ipconfig_set_local(dhcp->ipconfig, address); + __connman_ipconfig_set_prefixlen(dhcp->ipconfig, prefixlen); + __connman_ipconfig_set_gateway(dhcp->ipconfig, gateway); } + if (!apply_lease_available_on_network(dhcp_client, dhcp)) + return; + if (ip_change) dhcp_valid(dhcp); - __connman_6to4_probe(service); - g_free(address); g_free(netmask); g_free(gateway); @@ -462,29 +475,20 @@ static void ipv4ll_available_cb(GDHCPClient *ipv4ll_client, gpointer user_data) { struct connman_dhcp *dhcp = user_data; char *address, *netmask; - struct connman_service *service; - struct connman_ipconfig *ipconfig; unsigned char prefixlen; DBG("IPV4LL available"); - service = connman_service_lookup_from_network(dhcp->network); - if (!service) - return; - - ipconfig = __connman_service_get_ip4config(service); - if (!ipconfig) - return; - address = g_dhcp_client_get_address(ipv4ll_client); netmask = g_dhcp_client_get_netmask(ipv4ll_client); - prefixlen = __connman_ipaddress_netmask_prefix_len(netmask); + prefixlen = connman_ipaddress_calc_netmask_len(netmask); - __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_DHCP); - __connman_ipconfig_set_local(ipconfig, address); - __connman_ipconfig_set_prefixlen(ipconfig, prefixlen); - __connman_ipconfig_set_gateway(ipconfig, NULL); + __connman_ipconfig_set_method(dhcp->ipconfig, + CONNMAN_IPCONFIG_METHOD_DHCP); + __connman_ipconfig_set_local(dhcp->ipconfig, address); + __connman_ipconfig_set_prefixlen(dhcp->ipconfig, prefixlen); + __connman_ipconfig_set_gateway(dhcp->ipconfig, NULL); dhcp_valid(dhcp); @@ -494,15 +498,13 @@ static void ipv4ll_available_cb(GDHCPClient *ipv4ll_client, gpointer user_data) static int dhcp_initialize(struct connman_dhcp *dhcp) { - struct connman_service *service; GDHCPClient *dhcp_client; GDHCPClientError error; - const char *hostname; int index; DBG("dhcp %p", dhcp); - index = connman_network_get_index(dhcp->network); + index = __connman_ipconfig_get_index(dhcp->ipconfig); dhcp_client = g_dhcp_client_new(G_DHCP_IPV4, index, &error); if (error != G_DHCP_CLIENT_ERROR_NONE) @@ -517,22 +519,29 @@ static int dhcp_initialize(struct connman_dhcp *dhcp) g_dhcp_client_set_id(dhcp_client); - service = connman_service_lookup_from_network(dhcp->network); + if (dhcp->network) { + struct connman_service *service; + const char *hostname; - hostname = __connman_service_get_hostname(service); - if (!hostname) - hostname = connman_utsname_get_hostname(); + service = connman_service_lookup_from_network(dhcp->network); + + hostname = __connman_service_get_hostname(service); + if (!hostname) + hostname = connman_utsname_get_hostname(); - if (hostname) - g_dhcp_client_set_send(dhcp_client, G_DHCP_HOST_NAME, hostname); + if (hostname) + g_dhcp_client_set_send(dhcp_client, + G_DHCP_HOST_NAME, hostname); + + g_dhcp_client_set_request(dhcp_client, G_DHCP_HOST_NAME); + g_dhcp_client_set_request(dhcp_client, G_DHCP_DNS_SERVER); + g_dhcp_client_set_request(dhcp_client, G_DHCP_DOMAIN_NAME); + g_dhcp_client_set_request(dhcp_client, G_DHCP_NTP_SERVER); + g_dhcp_client_set_request(dhcp_client, 252); + } - g_dhcp_client_set_request(dhcp_client, G_DHCP_HOST_NAME); g_dhcp_client_set_request(dhcp_client, G_DHCP_SUBNET); - g_dhcp_client_set_request(dhcp_client, G_DHCP_DNS_SERVER); - g_dhcp_client_set_request(dhcp_client, G_DHCP_DOMAIN_NAME); - g_dhcp_client_set_request(dhcp_client, G_DHCP_NTP_SERVER); g_dhcp_client_set_request(dhcp_client, G_DHCP_ROUTER); - g_dhcp_client_set_request(dhcp_client, 252); g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_LEASE_AVAILABLE, @@ -553,8 +562,10 @@ static int dhcp_release(struct connman_dhcp *dhcp) { DBG("dhcp %p", dhcp); - if (dhcp->timeout > 0) + if (dhcp->timeout > 0) { g_source_remove(dhcp->timeout); + dhcp->timeout = 0; + } if (dhcp->dhcp_client) { g_dhcp_client_stop(dhcp->dhcp_client); @@ -571,56 +582,74 @@ static int dhcp_release(struct connman_dhcp *dhcp) return 0; } -int __connman_dhcp_start(struct connman_network *network, dhcp_cb callback) +int __connman_dhcp_start(struct connman_ipconfig *ipconfig, + struct connman_network *network, dhcp_cb callback, + gpointer user_data) { - struct connman_service *service; - struct connman_ipconfig *ipconfig; const char *last_addr = NULL; struct connman_dhcp *dhcp; + int err; DBG(""); - service = connman_service_lookup_from_network(network); - if (!service) - return -EINVAL; + if (network) { + struct connman_service *service; - ipconfig = __connman_service_get_ip4config(service); - if (ipconfig) - last_addr = __connman_ipconfig_get_dhcp_address(ipconfig); + service = connman_service_lookup_from_network(network); + if (!service) + return -EINVAL; + } + + last_addr = __connman_ipconfig_get_dhcp_address(ipconfig); - dhcp = g_hash_table_lookup(network_table, network); + dhcp = g_hash_table_lookup(ipconfig_table, ipconfig); if (!dhcp) { dhcp = g_try_new0(struct connman_dhcp, 1); if (!dhcp) return -ENOMEM; - dhcp->network = network; - connman_network_ref(network); + dhcp->ipconfig = ipconfig; + __connman_ipconfig_ref(ipconfig); + + if (network) { + dhcp->network = network; + connman_network_ref(network); + } + + err = dhcp_initialize(dhcp); - g_hash_table_insert(network_table, network, dhcp); + if (err < 0) { + if (network) + connman_network_unref(network); + g_free(dhcp); + return err; + } - dhcp_initialize(dhcp); + g_hash_table_insert(ipconfig_table, ipconfig, dhcp); } dhcp->callback = callback; + dhcp->user_data = user_data; return g_dhcp_client_start(dhcp->dhcp_client, last_addr); } -void __connman_dhcp_stop(struct connman_network *network) +void __connman_dhcp_stop(struct connman_ipconfig *ipconfig) { struct connman_dhcp *dhcp; - DBG("network_table %p network %p", network_table, network); + DBG("ipconfig_table %p ipconfig %p", ipconfig_table, ipconfig); - if (!network_table) + if (!ipconfig_table) return; - dhcp = g_hash_table_lookup(network_table, network); + dhcp = g_hash_table_lookup(ipconfig_table, ipconfig); if (dhcp) { - g_hash_table_remove(network_table, network); - connman_network_unref(network); + g_hash_table_remove(ipconfig_table, ipconfig); + __connman_ipconfig_unref(ipconfig); + if (dhcp->network) + connman_network_unref(dhcp->network); dhcp_release(dhcp); dhcp_invalidate(dhcp, false); dhcp_free(dhcp); @@ -631,8 +660,8 @@ int __connman_dhcp_init(void) { DBG(""); - network_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, - NULL, NULL); + ipconfig_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, NULL); return 0; } @@ -641,6 +670,6 @@ void __connman_dhcp_cleanup(void) { DBG(""); - g_hash_table_destroy(network_table); - network_table = NULL; + g_hash_table_destroy(ipconfig_table); + ipconfig_table = NULL; } diff --git a/src/dhcpv6.c b/src/dhcpv6.c index 2ede854e..5f8029f1 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -631,6 +631,7 @@ static void set_address(int ifindex, struct connman_ipconfig *ipconfig, /* Is this prefix part of the subnet we are suppose to use? */ prefix_len = check_ipv6_addr_prefix(prefixes, address); + __connman_ipconfig_address_remove(ipconfig); __connman_ipconfig_set_local(ipconfig, address); __connman_ipconfig_set_prefixlen(ipconfig, prefix_len); @@ -804,7 +805,8 @@ static void dad_reply(struct nd_neighbor_advert *reply, service = __connman_service_lookup_from_index( data->ifindex); network = __connman_service_get_network(service); - data->callback(network, status, NULL); + if (network) + data->callback(network, status, NULL); } } @@ -1118,6 +1120,7 @@ static void rebind_cb(GDHCPClient *dhcp_client, gpointer user_data) { DBG(""); + g_dhcpv6_client_reset_request(dhcp_client); g_dhcpv6_client_clear_retransmit(dhcp_client); re_cb(REQ_REBIND, dhcp_client, user_data); @@ -1305,6 +1308,7 @@ static void renew_cb(GDHCPClient *dhcp_client, gpointer user_data) { DBG(""); + g_dhcpv6_client_reset_request(dhcp_client); g_dhcpv6_client_clear_retransmit(dhcp_client); re_cb(REQ_RENEW, dhcp_client, user_data); @@ -1398,7 +1402,7 @@ int __connman_dhcpv6_start_renew(struct connman_network *network, dhcpv6_cb callback) { struct connman_dhcpv6 *dhcp; - uint32_t T1, T2; + uint32_t T1, T2, delta; time_t started, current, expired; dhcp = g_hash_table_lookup(network_table, network); @@ -1421,11 +1425,13 @@ int __connman_dhcpv6_start_renew(struct connman_network *network, /* RFC 3315, 22.4 */ return 0; - if (T1 == 0) + if (T1 == 0) { /* RFC 3315, 22.4 * Client can choose the timeout. */ - T1 = 1800; + T1 = (expired - started) / 2; + T2 = (expired - started) / 10 * 8; + } dhcp->callback = callback; @@ -1436,22 +1442,23 @@ int __connman_dhcpv6_start_renew(struct connman_network *network, if (T2 != 0xffffffff && T2 > 0) { if ((unsigned)current >= (unsigned)started + T2) { /* RFC 3315, chapter 18.1.3, start rebind */ - DBG("rebind after %d secs", T2); + DBG("start rebind immediately"); - dhcp->timeout = g_timeout_add_seconds(T2, start_rebind, + dhcp->timeout = g_timeout_add_seconds(0, start_rebind, dhcp); } else if ((unsigned)current < (unsigned)started + T1) { - DBG("renew after %d secs", T1); + delta = started + T1 - current; + DBG("renew after %d secs", delta); - dhcp->timeout = g_timeout_add_seconds(T1, start_renew, - dhcp); + dhcp->timeout = g_timeout_add_seconds(delta, + start_renew, dhcp); } else { - DBG("rebind after %d secs", T2 - T1); + delta = started + T2 - current; + DBG("rebind after %d secs", delta); - dhcp->timeout = g_timeout_add_seconds(T2 - T1, - start_rebind, - dhcp); + dhcp->timeout = g_timeout_add_seconds(delta, + start_rebind, dhcp); } } @@ -1765,145 +1772,11 @@ static gboolean start_solicitation(gpointer user_data) return FALSE; } -static void confirm_cb(GDHCPClient *dhcp_client, gpointer user_data) -{ - struct connman_dhcpv6 *dhcp = user_data; - int status = g_dhcpv6_client_get_status(dhcp_client); - - DBG("dhcpv6 confirm msg %p status %d", dhcp, status); - - clear_timer(dhcp); - - g_dhcpv6_client_clear_retransmit(dhcp_client); - - /* - * If confirm fails, start from scratch. - */ - if (status != 0) { - g_dhcp_client_unref(dhcp->dhcp_client); - start_solicitation(dhcp); - } else { - do_dad(dhcp_client, dhcp); - } -} - -static int dhcpv6_confirm(struct connman_dhcpv6 *dhcp) -{ - GDHCPClient *dhcp_client; - GDHCPClientError error; - struct connman_service *service; - struct connman_ipconfig *ipconfig_ipv6; - int index, ret; - - DBG("dhcp %p", dhcp); - - index = connman_network_get_index(dhcp->network); - - dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error); - if (error != G_DHCP_CLIENT_ERROR_NONE) { - clear_timer(dhcp); - return -EINVAL; - } - - if (getenv("CONNMAN_DHCPV6_DEBUG")) - g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6"); - - service = connman_service_lookup_from_network(dhcp->network); - if (!service) { - clear_timer(dhcp); - g_dhcp_client_unref(dhcp_client); - return -EINVAL; - } - - ret = set_duid(service, dhcp->network, dhcp_client, index); - if (ret < 0) { - clear_timer(dhcp); - g_dhcp_client_unref(dhcp_client); - return ret; - } - - g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID); - g_dhcp_client_set_request(dhcp_client, G_DHCPV6_RAPID_COMMIT); - - ipconfig_ipv6 = __connman_service_get_ip6config(service); - dhcp->use_ta = __connman_ipconfig_ipv6_privacy_enabled(ipconfig_ipv6); - - g_dhcpv6_client_set_ia(dhcp_client, index, - dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA, - NULL, NULL, TRUE, - __connman_ipconfig_get_dhcp_address(ipconfig_ipv6)); - - clear_callbacks(dhcp_client); - - g_dhcp_client_register_event(dhcp_client, - G_DHCP_CLIENT_EVENT_CONFIRM, - confirm_cb, dhcp); - - dhcp->dhcp_client = dhcp_client; - - return g_dhcp_client_start(dhcp_client, NULL); -} - -static gboolean timeout_confirm(gpointer user_data) -{ - struct connman_dhcpv6 *dhcp = user_data; - - dhcp->RT = calc_delay(dhcp->RT, CNF_MAX_RT); - - DBG("confirm RT timeout %d msec", dhcp->RT); - - dhcp->timeout = g_timeout_add(dhcp->RT, timeout_confirm, dhcp); - - g_dhcpv6_client_set_retransmit(dhcp->dhcp_client); - - g_dhcp_client_start(dhcp->dhcp_client, NULL); - - return FALSE; -} - -static gboolean timeout_max_confirm(gpointer user_data) -{ - struct connman_dhcpv6 *dhcp = user_data; - - dhcp->MRD = 0; - - clear_timer(dhcp); - - DBG("confirm max retransmit duration timeout"); - - g_dhcpv6_client_clear_retransmit(dhcp->dhcp_client); - - if (dhcp->callback) - dhcp->callback(dhcp->network, CONNMAN_DHCPV6_STATUS_FAIL, - NULL); - - return FALSE; -} - -static gboolean start_confirm(gpointer user_data) -{ - struct connman_dhcpv6 *dhcp = user_data; - - /* Set the confirm timeout, RFC 3315 chapter 14 */ - dhcp->RT = CNF_TIMEOUT * (1 + get_random()); - - DBG("confirm initial RT timeout %d msec", dhcp->RT); - - dhcp->timeout = g_timeout_add(dhcp->RT, timeout_confirm, dhcp); - dhcp->MRD = g_timeout_add(CNF_MAX_RD, timeout_max_confirm, dhcp); - - dhcpv6_confirm(dhcp); - - return FALSE; -} - int __connman_dhcpv6_start(struct connman_network *network, GSList *prefixes, dhcpv6_cb callback) { struct connman_service *service; - struct connman_ipconfig *ipconfig_ipv6; struct connman_dhcpv6 *dhcp; - char *last_address; int delay; DBG(""); @@ -1936,24 +1809,18 @@ int __connman_dhcpv6_start(struct connman_network *network, /* Initial timeout, RFC 3315, 17.1.2 */ delay = rand() % 1000; - ipconfig_ipv6 = __connman_service_get_ip6config(service); - last_address = __connman_ipconfig_get_dhcp_address(ipconfig_ipv6); - - if (prefixes && last_address && - check_ipv6_addr_prefix(prefixes, - last_address) != 128) { - /* - * So we are in the same subnet - * RFC 3315, chapter 18.1.2 Confirm message - */ - dhcp->timeout = g_timeout_add(delay, start_confirm, dhcp); - } else { - /* - * Start from scratch. - * RFC 3315, chapter 17.1.2 Solicitation message - */ - dhcp->timeout = g_timeout_add(delay, start_solicitation, dhcp); - } + /* + * Start from scratch. + * RFC 3315, chapter 17.1.2 Solicitation message + * + * Note that we do not send CONFIRM message here as it does + * not make much sense because we do not save expiration time + * so we cannot really know how long the saved address is valid + * anyway. The reply to CONFIRM message does not send + * expiration times back to us. Because of this we need to + * start using SOLICITATION anyway. + */ + dhcp->timeout = g_timeout_add(delay, start_solicitation, dhcp); return 0; } diff --git a/src/dnsproxy.c b/src/dnsproxy.c index 7232b987..bdd7fd5c 100644 --- a/src/dnsproxy.c +++ b/src/dnsproxy.c @@ -356,8 +356,7 @@ static int dns_name_length(unsigned char *buf) static void update_cached_ttl(unsigned char *buf, int len, int new_ttl) { unsigned char *c; - uint32_t *i; - uint16_t *w; + uint16_t w; int l; /* skip the header */ @@ -387,17 +386,19 @@ static void update_cached_ttl(unsigned char *buf, int len, int new_ttl) break; /* now the 4 byte TTL field */ - i = (uint32_t *)c; - *i = htonl(new_ttl); + c[0] = new_ttl >> 24 & 0xff; + c[1] = new_ttl >> 16 & 0xff; + c[2] = new_ttl >> 8 & 0xff; + c[3] = new_ttl & 0xff; c += 4; len -= 4; if (len < 0) break; /* now the 2 byte rdlen field */ - w = (uint16_t *)c; - c += ntohs(*w) + 2; - len -= ntohs(*w) + 2; + w = c[0] << 8 | c[1]; + c += w + 2; + len -= w + 2; } } @@ -435,7 +436,7 @@ static void send_cached_response(int sk, unsigned char *buf, int len, hdr->id = id; hdr->qr = 1; - hdr->rcode = 0; + hdr->rcode = ns_r_noerror; hdr->ancount = htons(answers); hdr->nscount = 0; hdr->arcount = 0; @@ -482,7 +483,7 @@ static void send_response(int sk, unsigned char *buf, int len, DBG("id 0x%04x qr %d opcode %d", hdr->id, hdr->qr, hdr->opcode); hdr->qr = 1; - hdr->rcode = 2; + hdr->rcode = ns_r_servfail; hdr->ancount = 0; hdr->nscount = 0; @@ -1344,7 +1345,6 @@ static void cache_refresh(void) static int reply_query_type(unsigned char *msg, int len) { unsigned char *c; - uint16_t *w; int l; int type; @@ -1358,8 +1358,7 @@ static int reply_query_type(unsigned char *msg, int len) /* now the query, which is a name and 2 16 bit words */ l = dns_name_length(c) + 1; c += l; - w = (uint16_t *) c; - type = ntohs(*w); + type = c[0] << 8 | c[1]; return type; } @@ -1401,7 +1400,7 @@ static int cache_update(struct server_data *srv, unsigned char *msg, DBG("offset %d hdr %p msg %p rcode %d", offset, hdr, msg, hdr->rcode); /* Continue only if response code is 0 (=ok) */ - if (hdr->rcode != 0) + if (hdr->rcode != ns_r_noerror) return 0; if (!cache) @@ -1760,14 +1759,11 @@ static char *uncompress(int16_t field_count, char *start, char *end, int pos; /* position in compressed string */ char name[NS_MAXLABEL]; /* tmp label */ uint16_t dns_type, dns_class; + int comp_pos; - pos = dn_expand((const u_char *)start, (u_char *)end, - (u_char *)ptr, name, NS_MAXLABEL); - if (pos < 0) { - DBG("uncompress error [%d/%s]", errno, - strerror(errno)); + if (!convert_label(start, end, ptr, name, NS_MAXLABEL, + &pos, &comp_pos)) goto out; - } /* * Copy the uncompressed resource record, type, class and \0 to @@ -1775,7 +1771,6 @@ static char *uncompress(int16_t field_count, char *start, char *end, */ ulen = strlen(name); - *uptr++ = ulen; strncpy(uptr, name, uncomp_len - (uptr - uncompressed)); DBG("pos %d ulen %d left %d name %s", pos, ulen, @@ -1807,8 +1802,6 @@ static char *uncompress(int16_t field_count, char *start, char *end, * so we need to uncompress it also when necessary. */ if (dns_type == ns_t_cname) { - int comp_pos; - if (!convert_label(start, end, ptr, uptr, uncomp_len - (uptr - uncompressed), &pos, &comp_pos)) @@ -1833,7 +1826,6 @@ static char *uncompress(int16_t field_count, char *start, char *end, ptr += dlen; } else if (dns_type == ns_t_soa) { - int comp_pos; int total_len = 0; char *len_ptr; @@ -1884,6 +1876,45 @@ out: return NULL; } +static int strip_domains(char *name, char *answers, int maxlen) +{ + uint16_t data_len; + int name_len = strlen(name); + char *ptr, *start = answers, *end = answers + maxlen; + + while (maxlen > 0) { + ptr = strstr(answers, name); + if (ptr) { + char *domain = ptr + name_len; + + if (*domain) { + int domain_len = strlen(domain); + + memmove(answers + name_len, + domain + domain_len, + end - (domain + domain_len)); + + end -= domain_len; + maxlen -= domain_len; + } + } + + answers += strlen(answers) + 1; + answers += 2 + 2 + 4; /* skip type, class and ttl fields */ + + data_len = answers[0] << 8 | answers[1]; + answers += 2; /* skip the length field */ + + if (answers + data_len > end) + return -EINVAL; + + answers += data_len; + maxlen -= answers - ptr; + } + + return end - start; +} + static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol, struct server_data *data) { @@ -1911,7 +1942,7 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol, req->numresp++; - if (hdr->rcode == 0 || !req->resp) { + if (hdr->rcode == ns_r_noerror || !req->resp) { unsigned char *new_reply = NULL; /* @@ -1979,6 +2010,8 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol, */ if (domain_len > 0) { int len = host_len + 1; + int new_len, fixed_len; + char *answers; /* * First copy host (without domain name) into @@ -2001,6 +2034,8 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol, */ ptr += NS_QFIXEDSZ; uptr += NS_QFIXEDSZ; + answers = uptr; + fixed_len = answers - uncompressed; /* * We then uncompress the result to buffer @@ -2032,22 +2067,39 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol, goto out; /* + * The uncompressed buffer now contains almost + * valid response. Final step is to get rid of + * the domain name because at least glibc + * gethostbyname() implementation does extra + * checks and expects to find an answer without + * domain name if we asked a query without + * domain part. Note that glibc getaddrinfo() + * works differently and accepts FQDN in answer + */ + new_len = strip_domains(uncompressed, answers, + uptr - answers); + if (new_len < 0) { + DBG("Corrupted packet"); + return -EINVAL; + } + + /* * Because we have now uncompressed the answers - * we must create a bigger buffer to hold all - * that data. + * we might have to create a bigger buffer to + * hold all that data. */ - new_reply = g_try_malloc(header_len + - uptr - uncompressed); + reply_len = header_len + new_len + fixed_len; + + new_reply = g_try_malloc(reply_len); if (!new_reply) return -ENOMEM; memcpy(new_reply, reply, header_len); memcpy(new_reply + header_len, uncompressed, - uptr - uncompressed); + new_len + fixed_len); reply = new_reply; - reply_len = header_len + uptr - uncompressed; } } @@ -2068,8 +2120,13 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol, } out: - if (hdr->rcode > 0 && req->numresp < req->numserv) - return -EINVAL; + if (req->numresp < req->numserv) { + if (hdr->rcode > ns_r_noerror) { + return -EINVAL; + } else if (hdr->ancount == 0 && req->append_domain) { + return -EINVAL; + } + } request_list = g_slist_remove(request_list, req); @@ -2147,7 +2204,8 @@ static void destroy_server(struct server_data *server) * without any good reason. The small delay allows the new RDNSS to * create a new DNS server instance and the refcount does not go to 0. */ - g_timeout_add_seconds(3, try_remove_cache, NULL); + if (cache) + g_timeout_add_seconds(3, try_remove_cache, NULL); g_free(server); } @@ -240,36 +240,6 @@ char *connman_inet_ifname(int index) return g_strdup(ifr.ifr_name); } -short int connman_inet_ifflags(int index) -{ - struct ifreq ifr; - int sk, err; - - sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (sk < 0) - return -errno; - - memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_ifindex = index; - - if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) { - err = -errno; - goto done; - } - - if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) { - err = -errno; - goto done; - } - - err = ifr.ifr_flags; - -done: - close(sk); - - return err; -} - int connman_inet_ifup(int index) { struct ifreq ifr; @@ -360,36 +330,6 @@ done: return err; } -bool connman_inet_is_cfg80211(int index) -{ - bool result = false; - char phy80211_path[PATH_MAX]; - struct stat st; - struct ifreq ifr; - int sk; - - sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (sk < 0) - return false; - - memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_ifindex = index; - - if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) - goto done; - - snprintf(phy80211_path, PATH_MAX, - "/sys/class/net/%s/phy80211", ifr.ifr_name); - - if (stat(phy80211_path, &st) == 0 && (st.st_mode & S_IFDIR)) - result = true; - -done: - close(sk); - - return result; -} - struct in6_ifreq { struct in6_addr ifr6_addr; __u32 ifr6_prefixlen; @@ -480,7 +420,8 @@ int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress) broadcast = ipaddress->broadcast; peer = ipaddress->peer; - DBG("index %d address %s prefix_len %d", index, address, prefix_len); + DBG("index %d address %s prefix_len %d peer %s broadcast %s", index, + address, prefix_len, peer, broadcast); if (!address) return -EINVAL; diff --git a/src/inotify.c b/src/inotify.c index 72ba6f68..1ab3807c 100644 --- a/src/inotify.c +++ b/src/inotify.c @@ -3,7 +3,7 @@ * Connection Manager * * Copyright (C) 2007-2012 Intel Corporation. All rights reserved. - * Copyright (C) 2012-2013 BMW Car IT GmbH. All rights reserved. + * Copyright (C) 2012-2014 BMW Car IT GmbH. * * 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 diff --git a/src/ipaddress.c b/src/ipaddress.c index 57f9435d..d63d95c3 100644 --- a/src/ipaddress.c +++ b/src/ipaddress.c @@ -33,6 +33,29 @@ #include "connman.h" +unsigned char connman_ipaddress_calc_netmask_len(const char *netmask) +{ + unsigned char bits; + in_addr_t mask; + in_addr_t host; + + if (!netmask) + return 32; + + mask = inet_network(netmask); + host = ~mask; + + /* a valid netmask must be 2^n - 1 */ + if ((host & (host + 1)) != 0) + return -1; + + bits = 0; + for (; mask; mask <<= 1) + ++bits; + + return bits; +} + struct connman_ipaddress *connman_ipaddress_alloc(int family) { struct connman_ipaddress *ipaddress; @@ -63,29 +86,6 @@ void connman_ipaddress_free(struct connman_ipaddress *ipaddress) g_free(ipaddress); } -unsigned char __connman_ipaddress_netmask_prefix_len(const char *netmask) -{ - unsigned char bits; - in_addr_t mask; - in_addr_t host; - - if (!netmask) - return 32; - - mask = inet_network(netmask); - host = ~mask; - - /* a valid netmask must be 2^n - 1 */ - if ((host & (host + 1)) != 0) - return -1; - - bits = 0; - for (; mask; mask <<= 1) - ++bits; - - return bits; -} - static bool check_ipv6_address(const char *address) { unsigned char buf[sizeof(struct in6_addr)]; @@ -128,6 +128,19 @@ int connman_ipaddress_set_ipv6(struct connman_ipaddress *ipaddress, return 0; } +int connman_ipaddress_get_ip(struct connman_ipaddress *ipaddress, + const char **address, + unsigned char *netmask_prefix_length) +{ + if (!ipaddress) + return -EINVAL; + + *netmask_prefix_length = ipaddress->prefixlen; + *address = ipaddress->local; + + return 0; +} + int connman_ipaddress_set_ipv4(struct connman_ipaddress *ipaddress, const char *address, const char *netmask, const char *gateway) { @@ -136,7 +149,7 @@ int connman_ipaddress_set_ipv4(struct connman_ipaddress *ipaddress, ipaddress->family = AF_INET; - ipaddress->prefixlen = __connman_ipaddress_netmask_prefix_len(netmask); + ipaddress->prefixlen = connman_ipaddress_calc_netmask_len(netmask); g_free(ipaddress->local); ipaddress->local = g_strdup(address); @@ -179,8 +192,7 @@ void connman_ipaddress_clear(struct connman_ipaddress *ipaddress) /* * Note that this copy function only copies the actual address and - * prefixlen. If you need full copy of ipaddress struct, then you need - * to create a new function that does that. + * prefixlen. Use the other copy function to copy the whole struct. */ void connman_ipaddress_copy_address(struct connman_ipaddress *ipaddress, struct connman_ipaddress *source) @@ -194,3 +206,23 @@ void connman_ipaddress_copy_address(struct connman_ipaddress *ipaddress, g_free(ipaddress->local); ipaddress->local = g_strdup(source->local); } + +struct connman_ipaddress * +connman_ipaddress_copy(struct connman_ipaddress *ipaddress) +{ + struct connman_ipaddress *copy; + + if (!ipaddress) + return NULL; + + copy = g_new0(struct connman_ipaddress, 1); + + copy->family = ipaddress->family; + copy->prefixlen = ipaddress->prefixlen; + copy->local = g_strdup(ipaddress->local); + copy->peer = g_strdup(ipaddress->peer); + copy->broadcast = g_strdup(ipaddress->broadcast); + copy->gateway = g_strdup(ipaddress->gateway); + + return copy; +} diff --git a/src/ipconfig.c b/src/ipconfig.c index b23df160..ae70745f 100644 --- a/src/ipconfig.c +++ b/src/ipconfig.c @@ -1090,8 +1090,6 @@ int __connman_ipconfig_gateway_add(struct connman_ipconfig *ipconfig) if (!service) return -EINVAL; - __connman_connection_gateway_remove(service, ipconfig->type); - DBG("type %d gw %s peer %s", ipconfig->type, ipconfig->address->gateway, ipconfig->address->peer); @@ -1703,10 +1701,6 @@ int __connman_ipconfig_disable(struct connman_ipconfig *ipconfig) if (ipdevice->config_ipv6 == ipconfig) { ipconfig_list = g_list_remove(ipconfig_list, ipconfig); - if (ipdevice->config_ipv6->method == - CONNMAN_IPCONFIG_METHOD_AUTO) - disable_ipv6(ipdevice->config_ipv6); - connman_ipaddress_clear(ipdevice->config_ipv6->system); __connman_ipconfig_unref(ipdevice->config_ipv6); ipdevice->config_ipv6 = NULL; @@ -1776,6 +1770,25 @@ static int string2privacy(const char *privacy) return 0; } +int __connman_ipconfig_ipv6_reset_privacy(struct connman_ipconfig *ipconfig) +{ + struct connman_ipdevice *ipdevice; + int err; + + if (!ipconfig) + return -EINVAL; + + ipdevice = g_hash_table_lookup(ipdevice_hash, + GINT_TO_POINTER(ipconfig->index)); + if (!ipdevice) + return -ENODEV; + + err = __connman_ipconfig_ipv6_set_privacy(ipconfig, privacy2string( + ipdevice->ipv6_privacy)); + + return err; +} + int __connman_ipconfig_ipv6_set_privacy(struct connman_ipconfig *ipconfig, const char *value) { @@ -2093,8 +2106,7 @@ int __connman_ipconfig_set_config(struct connman_ipconfig *ipconfig, case CONNMAN_IPCONFIG_METHOD_OFF: ipconfig->method = method; - if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6) - disable_ipv6(ipconfig); + break; case CONNMAN_IPCONFIG_METHOD_AUTO: @@ -2104,7 +2116,7 @@ int __connman_ipconfig_set_config(struct connman_ipconfig *ipconfig, ipconfig->method = method; if (privacy_string) ipconfig->ipv6_privacy_config = privacy; - enable_ipv6(ipconfig); + break; case CONNMAN_IPCONFIG_METHOD_MANUAL: @@ -2139,6 +2151,7 @@ int __connman_ipconfig_set_config(struct connman_ipconfig *ipconfig, return connman_ipaddress_set_ipv6( ipconfig->address, address, prefix_length, gateway); + break; case CONNMAN_IPCONFIG_METHOD_DHCP: @@ -2168,9 +2181,11 @@ void __connman_ipconfig_append_ethernet(struct connman_ipconfig *ipconfig, if (ipconfig->index >= 0) { char *ifname = connman_inet_ifname(ipconfig->index); - connman_dbus_dict_append_basic(iter, "Interface", - DBUS_TYPE_STRING, &ifname); - g_free(ifname); + if (ifname) { + connman_dbus_dict_append_basic(iter, "Interface", + DBUS_TYPE_STRING, &ifname); + g_free(ifname); + } } if (ipdevice->address) diff --git a/src/ippool.c b/src/ippool.c index 558e9662..bb8568d9 100644 --- a/src/ippool.c +++ b/src/ippool.c @@ -3,7 +3,7 @@ * Connection Manager * * Copyright (C) 2007-2013 Intel Corporation. All rights reserved. - * Copyright (C) 2012-2013 BMW Car IT GmbH. All rights reserved. + * Copyright (C) 2012-2014 BMW Car IT GmbH. * * 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 diff --git a/src/machine.c b/src/machine.c new file mode 100644 index 00000000..14ea3667 --- /dev/null +++ b/src/machine.c @@ -0,0 +1,125 @@ +/* + * + * Connection Manager + * + * Copyright (C) 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 + * 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 <errno.h> +#include <gdbus.h> + +#include "connman.h" + +#define DEFAULT_MACHINE_TYPE "laptop" + +#define HOSTNAMED_SERVICE "org.freedesktop.hostname1" +#define HOSTNAMED_INTERFACE HOSTNAMED_SERVICE +#define HOSTNAMED_PATH "/org/freedesktop/hostname1" + +static GDBusClient *hostnamed_client = NULL; +static GDBusProxy *hostnamed_proxy = NULL; +static char *machine_type = NULL; + +const char *connman_machine_get_type(void) +{ + if (machine_type) + return machine_type; + + return DEFAULT_MACHINE_TYPE; +} + +static void machine_property_changed(GDBusProxy *proxy, const char *name, + DBusMessageIter *iter, void *user_data) +{ + DBG("Property %s", name); + + if (g_str_equal(name, "Chassis")) { + const char *str; + + if (!iter) { + g_dbus_proxy_refresh_property(proxy, name); + return; + } + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) + return; + + dbus_message_iter_get_basic(iter, &str); + g_free(machine_type); + machine_type = g_strdup(str); + + DBG("Machine type set to %s", machine_type); + } +} + +int __connman_machine_init(void) +{ + DBusConnection *connection; + int err = -EIO; + + DBG(""); + + connection = connman_dbus_get_connection(); + + hostnamed_client = g_dbus_client_new(connection, HOSTNAMED_SERVICE, + HOSTNAMED_PATH); + if (!hostnamed_client) + goto error; + + hostnamed_proxy = g_dbus_proxy_new(hostnamed_client, HOSTNAMED_PATH, + HOSTNAMED_INTERFACE); + if (!hostnamed_proxy) + goto error; + + g_dbus_proxy_set_property_watch(hostnamed_proxy, + machine_property_changed, NULL); + + dbus_connection_unref(connection); + + return 0; +error: + if (hostnamed_client) { + g_dbus_client_unref(hostnamed_client); + hostnamed_client = NULL; + } + + dbus_connection_unref(connection); + + return err; +} + +void __connman_machine_cleanup(void) +{ + DBG(""); + + if (hostnamed_proxy) { + g_dbus_proxy_unref(hostnamed_proxy); + hostnamed_proxy = NULL; + } + + if (hostnamed_client) { + g_dbus_client_unref(hostnamed_client); + hostnamed_client = NULL; + } + + g_free(machine_type); + machine_type = NULL; +} @@ -644,6 +644,7 @@ int main(int argc, char *argv[]) __connman_notifier_init(); __connman_agent_init(); __connman_service_init(); + __connman_peer_service_init(); __connman_peer_init(); __connman_provider_init(); __connman_network_init(); @@ -678,6 +679,7 @@ int main(int argc, char *argv[]) __connman_wpad_init(); __connman_wispr_init(); __connman_rfkill_init(); + __connman_machine_init(); g_free(option_config); g_free(option_device); @@ -689,6 +691,7 @@ int main(int argc, char *argv[]) g_source_remove(signal); + __connman_machine_cleanup(); __connman_rfkill_cleanup(); __connman_wispr_cleanup(); __connman_wpad_cleanup(); @@ -713,12 +716,13 @@ int main(int argc, char *argv[]) __connman_nat_cleanup(); __connman_firewall_cleanup(); __connman_iptables_cleanup(); + __connman_peer_service_cleanup(); + __connman_peer_cleanup(); __connman_ippool_cleanup(); __connman_device_cleanup(); __connman_network_cleanup(); __connman_dhcp_cleanup(); __connman_service_cleanup(); - __connman_peer_cleanup(); __connman_agent_cleanup(); __connman_ipconfig_cleanup(); __connman_notifier_cleanup(); diff --git a/src/manager.c b/src/manager.c index b31ab4c7..d15ce203 100644 --- a/src/manager.c +++ b/src/manager.c @@ -380,6 +380,126 @@ static DBusMessage *release_private_network(DBusConnection *conn, return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } +static int parse_peers_service_specs(DBusMessageIter *array, + const unsigned char **spec, int *spec_len, + const unsigned char **query, int *query_len, + int *version) +{ + *spec = *query = NULL; + *spec_len = *query_len = *version = 0; + + while (dbus_message_iter_get_arg_type(array) == + DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entry, inter, value; + const char *key; + + dbus_message_iter_recurse(array, &entry); + dbus_message_iter_get_basic(&entry, &key); + + dbus_message_iter_next(&entry); + + dbus_message_iter_recurse(&entry, &inter); + + if (!g_strcmp0(key, "BonjourResponse")) { + dbus_message_iter_recurse(&inter, &value); + dbus_message_iter_get_fixed_array(&value, + spec, spec_len); + } else if (!g_strcmp0(key, "BonjourQuery")) { + dbus_message_iter_recurse(&inter, &value); + dbus_message_iter_get_fixed_array(&value, + query, query_len); + } else if (!g_strcmp0(key, "UpnpService")) { + dbus_message_iter_get_basic(&inter, spec); + *spec_len = strlen((const char *)*spec)+1; + } else if (!g_strcmp0(key, "UpnpVersion")) { + dbus_message_iter_get_basic(&inter, version); + } else if (!g_strcmp0(key, "WiFiDisplayIEs")) { + if (*spec || *query) + return -EINVAL; + + dbus_message_iter_recurse(&inter, &value); + dbus_message_iter_get_fixed_array(&value, + spec, spec_len); + } else + return -EINVAL; + + dbus_message_iter_next(array); + } + + if ((*query && !*spec && !*version) || + (!*spec && !*query) || (!*spec && *version)) + return -EINVAL; + + return 0; +} + +static DBusMessage *register_peer_service(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + const unsigned char *spec, *query; + DBusMessageIter iter, array; + int spec_len, query_len; + dbus_bool_t master; + const char *owner; + int version; + int ret; + + DBG(""); + + owner = dbus_message_get_sender(msg); + + dbus_message_iter_init(msg, &iter); + dbus_message_iter_recurse(&iter, &array); + + ret = parse_peers_service_specs(&array, &spec, &spec_len, + &query, &query_len, &version); + if (ret) + goto error; + + dbus_message_iter_next(&iter); + dbus_message_iter_get_basic(&iter, &master); + + ret = __connman_peer_service_register(owner, msg, spec, spec_len, + query, query_len, version,master); + if (!ret) + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); + if (ret == -EINPROGRESS) + return NULL; +error: + return __connman_error_failed(msg, -ret); +} + +static DBusMessage *unregister_peer_service(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + const unsigned char *spec, *query; + DBusMessageIter iter, array; + int spec_len, query_len; + const char *owner; + int version; + int ret; + + DBG(""); + + owner = dbus_message_get_sender(msg); + + dbus_message_iter_init(msg, &iter); + dbus_message_iter_recurse(&iter, &array); + + ret = parse_peers_service_specs(&array, &spec, &spec_len, + &query, &query_len, &version); + if (ret) + goto error; + + ret = __connman_peer_service_unregister(owner, spec, spec_len, + query, query_len, version); + if (!ret) + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +error: + return __connman_error_failed(msg, -ret); + +} + static const GDBusMethodTable manager_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), @@ -432,6 +552,13 @@ static const GDBusMethodTable manager_methods[] = { { GDBUS_METHOD("ReleasePrivateNetwork", GDBUS_ARGS({ "path", "o" }), NULL, release_private_network) }, + { GDBUS_ASYNC_METHOD("RegisterPeerService", + GDBUS_ARGS({ "specification", "a{sv}" }, + { "master", "b" }), NULL, + register_peer_service) }, + { GDBUS_METHOD("UnregisterPeerService", + GDBUS_ARGS({ "specification", "a{sv}" }), NULL, + unregister_peer_service) }, { }, }; @@ -3,7 +3,7 @@ * Connection Manager * * Copyright (C) 2007-2012 Intel Corporation. All rights reserved. - * Copyright (C) 2012 BMW Car IT GmbH. All rights reserved. + * Copyright (C) 2012-2014 BMW Car IT GmbH. * * 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 diff --git a/src/net.connman.service.in b/src/net.connman.service.in index 0bb1e8b9..e76969bc 100644 --- a/src/net.connman.service.in +++ b/src/net.connman.service.in @@ -1,5 +1,5 @@ [D-BUS Service] Name=net.connman -Exec=@prefix@/sbin/connman -n +Exec=@prefix@/sbin/connmand -n User=root SystemdService=connman.service diff --git a/src/network.c b/src/network.c index 160bd061..b388995f 100644 --- a/src/network.c +++ b/src/network.c @@ -202,7 +202,8 @@ static void dhcp_failure(struct connman_network *network) __connman_ipconfig_gateway_remove(ipconfig_ipv4); } -static void dhcp_callback(struct connman_network *network, +static void dhcp_callback(struct connman_ipconfig *ipconfig, + struct connman_network *network, bool success, gpointer data) { if (success) @@ -285,13 +286,19 @@ err: static int set_connected_dhcp(struct connman_network *network) { + struct connman_service *service; + struct connman_ipconfig *ipconfig_ipv4; int err; DBG("network %p", network); set_configuration(network, CONNMAN_IPCONFIG_TYPE_IPV4); - err = __connman_dhcp_start(network, dhcp_callback); + service = connman_service_lookup_from_network(network); + ipconfig_ipv4 = __connman_service_get_ip4config(service); + + err = __connman_dhcp_start(ipconfig_ipv4, network, + dhcp_callback, NULL); if (err < 0) { connman_error("Can not request DHCP lease"); return err; @@ -457,6 +464,7 @@ 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("reply %p", reply); @@ -492,6 +500,23 @@ static void check_dhcpv6(struct nd_router_advert *reply, prefixes = __connman_inet_ipv6_get_prefixes(reply, length); /* + * 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); + + __connman_service_ipconfig_indicate_state(service, + CONNMAN_SERVICE_STATE_CONFIGURATION, + CONNMAN_IPCONFIG_TYPE_IPV6); + } + + /* * We do stateful/stateless DHCPv6 if router advertisement says so. */ if (reply->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) @@ -587,6 +612,8 @@ static void autoconf_ipv6_set(struct connman_network *network) if (!ipconfig) return; + __connman_ipconfig_address_remove(ipconfig); + index = __connman_ipconfig_get_index(ipconfig); connman_network_ref(network); @@ -717,7 +744,7 @@ static void set_disconnected(struct connman_network *network) case CONNMAN_IPCONFIG_METHOD_MANUAL: break; case CONNMAN_IPCONFIG_METHOD_DHCP: - __connman_dhcp_stop(network); + __connman_dhcp_stop(ipconfig_ipv4); break; } } @@ -1385,22 +1412,6 @@ void connman_network_set_error(struct connman_network *network, network_change(network); } -void connman_network_clear_error(struct connman_network *network) -{ - struct connman_service *service; - - DBG("network %p", network); - - if (!network) - return; - - if (network->connecting || network->associating) - return; - - service = connman_service_lookup_from_network(network); - __connman_service_clear_error(service); -} - /** * connman_network_set_connected: * @network: network structure @@ -1469,7 +1480,7 @@ void connman_network_clear_hidden(void *user_data) * error to the caller telling that we could not find * any network that we could connect to. */ - __connman_service_reply_dbus_pending(user_data, EIO, NULL); + connman_dbus_reply_pending(user_data, EIO, NULL); } int connman_network_connect_hidden(struct connman_network *network, @@ -1489,7 +1500,7 @@ int connman_network_connect_hidden(struct connman_network *network, __connman_service_set_agent_identity(service, identity); if (passphrase) - err = __connman_service_add_passphrase(service, passphrase); + err = __connman_service_set_passphrase(service, passphrase); if (err == -ENOKEY) { __connman_service_indicate_error(service, @@ -1607,6 +1618,7 @@ 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; @@ -1614,6 +1626,7 @@ int __connman_network_clear_ipconfig(struct connman_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); @@ -1629,7 +1642,7 @@ int __connman_network_clear_ipconfig(struct connman_network *network, __connman_ipconfig_address_remove(ipconfig); break; case CONNMAN_IPCONFIG_METHOD_DHCP: - __connman_dhcp_stop(network); + __connman_dhcp_stop(ipconfig_ipv4); break; } @@ -1691,7 +1704,8 @@ int __connman_network_set_ipconfig(struct connman_network *network, case CONNMAN_IPCONFIG_METHOD_MANUAL: return manual_ipv4_set(network, ipconfig_ipv4); case CONNMAN_IPCONFIG_METHOD_DHCP: - return __connman_dhcp_start(network, dhcp_callback); + return __connman_dhcp_start(ipconfig_ipv4, + network, dhcp_callback, NULL); } } @@ -24,7 +24,11 @@ #endif #include <errno.h> +#include <ctype.h> #include <gdbus.h> +#include <gdhcp/gdhcp.h> + +#include <connman/agent.h> #include "connman.h" @@ -32,21 +36,304 @@ static DBusConnection *connection = NULL; static GHashTable *peers_table = NULL; +static struct connman_peer_driver *peer_driver; + +struct _peers_notify { + int id; + GHashTable *add; + GHashTable *remove; +} *peers_notify; + +struct _peer_service { + enum connman_peer_service_type type; + unsigned char *data; + int length; +}; + struct connman_peer { + int refcount; + struct connman_device *device; + struct connman_device *sub_device; char *identifier; char *name; char *path; + enum connman_peer_state state; + struct connman_ipconfig *ipconfig; + DBusMessage *pending; + bool registered; + bool connection_master; + struct connman_ippool *ip_pool; + GDHCPServer *dhcp_server; + GSList *services; }; +static void stop_dhcp_server(struct connman_peer *peer) +{ + DBG(""); + + if (peer->dhcp_server) + g_dhcp_server_unref(peer->dhcp_server); + + peer->dhcp_server = NULL; + + if (peer->ip_pool) + __connman_ippool_unref(peer->ip_pool); + peer->ip_pool = NULL; +} + +static void dhcp_server_debug(const char *str, void *data) +{ + connman_info("%s: %s\n", (const char *) data, str); +} + +static gboolean dhcp_server_started(gpointer data) +{ + struct connman_peer *peer = data; + + connman_peer_set_state(peer, CONNMAN_PEER_STATE_READY); + connman_peer_unref(peer); + + return FALSE; +} + +static int start_dhcp_server(struct connman_peer *peer) +{ + const char *start_ip, *end_ip; + GDHCPServerError dhcp_error; + const char *broadcast; + const char *gateway; + const char *subnet; + int prefixlen; + int index; + int err; + + DBG(""); + + err = -ENOMEM; + + if (peer->sub_device) + index = connman_device_get_index(peer->sub_device); + else + index = connman_device_get_index(peer->device); + + peer->ip_pool = __connman_ippool_create(index, 2, 1, NULL, NULL); + if (!peer->ip_pool) + goto error; + + gateway = __connman_ippool_get_gateway(peer->ip_pool); + subnet = __connman_ippool_get_subnet_mask(peer->ip_pool); + broadcast = __connman_ippool_get_broadcast(peer->ip_pool); + start_ip = __connman_ippool_get_start_ip(peer->ip_pool); + end_ip = __connman_ippool_get_end_ip(peer->ip_pool); + + prefixlen = connman_ipaddress_calc_netmask_len(subnet); + + err = __connman_inet_modify_address(RTM_NEWADDR, + NLM_F_REPLACE | NLM_F_ACK, index, AF_INET, + gateway, NULL, prefixlen, broadcast); + if (err < 0) + goto error; + + peer->dhcp_server = g_dhcp_server_new(G_DHCP_IPV4, index, &dhcp_error); + if (!peer->dhcp_server) + goto error; + + g_dhcp_server_set_debug(peer->dhcp_server, + dhcp_server_debug, "Peer DHCP server"); + g_dhcp_server_set_lease_time(peer->dhcp_server, 3600); + g_dhcp_server_set_option(peer->dhcp_server, G_DHCP_SUBNET, subnet); + g_dhcp_server_set_option(peer->dhcp_server, G_DHCP_ROUTER, gateway); + g_dhcp_server_set_option(peer->dhcp_server, G_DHCP_DNS_SERVER, NULL); + g_dhcp_server_set_ip_range(peer->dhcp_server, start_ip, end_ip); + + err = g_dhcp_server_start(peer->dhcp_server); + if (err < 0) + goto error; + + g_timeout_add_seconds(0, dhcp_server_started, connman_peer_ref(peer)); + + return 0; + +error: + stop_dhcp_server(peer); + return err; +} + +static void reply_pending(struct connman_peer *peer, int error) +{ + if (!peer->pending) + return; + + connman_dbus_reply_pending(peer->pending, error, NULL); + peer->pending = NULL; +} + static void peer_free(gpointer data) { struct connman_peer *peer = data; - connman_peer_destroy(peer); + + reply_pending(peer, ENOENT); + + connman_peer_unregister(peer); + + if (peer->path) { + g_free(peer->path); + peer->path = NULL; + } + + if (peer->ipconfig) { + __connman_ipconfig_set_ops(peer->ipconfig, NULL); + __connman_ipconfig_set_data(peer->ipconfig, NULL); + __connman_ipconfig_unref(peer->ipconfig); + peer->ipconfig = NULL; + } + + stop_dhcp_server(peer); + + if (peer->device) { + connman_device_unref(peer->device); + peer->device = NULL; + } + + if (peer->services) + connman_peer_reset_services(peer); + + g_free(peer->identifier); + g_free(peer->name); + + g_free(peer); +} + +static const char *state2string(enum connman_peer_state state) +{ + switch (state) { + case CONNMAN_PEER_STATE_UNKNOWN: + break; + case CONNMAN_PEER_STATE_IDLE: + return "idle"; + case CONNMAN_PEER_STATE_ASSOCIATION: + return "association"; + case CONNMAN_PEER_STATE_CONFIGURATION: + return "configuration"; + case CONNMAN_PEER_STATE_READY: + return "ready"; + case CONNMAN_PEER_STATE_DISCONNECT: + return "disconnect"; + case CONNMAN_PEER_STATE_FAILURE: + return "failure"; + } + + return NULL; +} + +static bool is_connecting(struct connman_peer *peer) +{ + if (peer->state == CONNMAN_PEER_STATE_ASSOCIATION || + peer->state == CONNMAN_PEER_STATE_CONFIGURATION || + peer->pending) + return true; + + return false; +} + +static bool is_connected(struct connman_peer *peer) +{ + if (peer->state == CONNMAN_PEER_STATE_READY) + return true; + + return false; +} + +static bool allow_property_changed(struct connman_peer *peer) +{ + if (g_hash_table_lookup_extended(peers_notify->add, peer->path, + NULL, NULL)) + return false; + + return true; +} + +static void append_dhcp_server_ipv4(DBusMessageIter *iter, void *user_data) +{ + struct connman_peer *peer = user_data; + const char *str = "dhcp"; + const char *gateway; + const char *subnet; + + if (!peer->ip_pool) + return; + + gateway = __connman_ippool_get_gateway(peer->ip_pool); + subnet = __connman_ippool_get_subnet_mask(peer->ip_pool); + + connman_dbus_dict_append_basic(iter, "Method", DBUS_TYPE_STRING, &str); + connman_dbus_dict_append_basic(iter, "Address", + DBUS_TYPE_STRING, &gateway); + connman_dbus_dict_append_basic(iter, "Netmask", + DBUS_TYPE_STRING, &subnet); + connman_dbus_dict_append_basic(iter, "Gateway", + DBUS_TYPE_STRING, &gateway); +} + +static void append_ipv4(DBusMessageIter *iter, void *user_data) +{ + struct connman_peer *peer = user_data; + + if (!is_connected(peer)) + return; + + if (peer->connection_master) + append_dhcp_server_ipv4(iter, peer); + else if (peer->ipconfig) + __connman_ipconfig_append_ipv4(peer->ipconfig, iter); +} + +static void append_peer_service(DBusMessageIter *iter, + struct _peer_service *service) +{ + DBusMessageIter dict; + + connman_dbus_dict_open(iter, &dict); + + switch (service->type) { + case CONNMAN_PEER_SERVICE_UNKNOWN: + /* Should never happen */ + break; + case CONNMAN_PEER_SERVICE_WIFI_DISPLAY: + connman_dbus_dict_append_fixed_array(&dict, + "WiFiDisplayIEs", DBUS_TYPE_BYTE, + &service->data, service->length); + break; + } + + connman_dbus_dict_close(iter, &dict); +} + +static void append_peer_services(DBusMessageIter *iter, void *user_data) +{ + struct connman_peer *peer = user_data; + DBusMessageIter container; + GSList *list; + + dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, + NULL, &container); + + if (!peer->services) { + DBusMessageIter dict; + + connman_dbus_dict_open(&container, &dict); + connman_dbus_dict_close(&container, &dict); + } else { + for (list = peer->services; list; list = list->next) + append_peer_service(&container, list->data); + } + + dbus_message_iter_close_container(iter, &container); } static void append_properties(DBusMessageIter *iter, struct connman_peer *peer) { - const char *state = "disconnected"; + const char *state = state2string(peer->state); DBusMessageIter dict; connman_dbus_dict_open(iter, &dict); @@ -55,11 +342,23 @@ static void append_properties(DBusMessageIter *iter, struct connman_peer *peer) DBUS_TYPE_STRING, &state); connman_dbus_dict_append_basic(&dict, "Name", DBUS_TYPE_STRING, &peer->name); - connman_dbus_dict_append_dict(&dict, "IPv4", NULL, NULL); - + connman_dbus_dict_append_dict(&dict, "IPv4", append_ipv4, peer); + connman_dbus_dict_append_array(&dict, "Services", + DBUS_TYPE_DICT_ENTRY, + append_peer_services, peer); connman_dbus_dict_close(iter, &dict); } +static void settings_changed(struct connman_peer *peer) +{ + if (!allow_property_changed(peer)) + return; + + connman_dbus_property_changed_dict(peer->path, + CONNMAN_PEER_INTERFACE, "IPv4", + append_ipv4, peer); +} + static DBusMessage *get_peer_properties(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -92,31 +391,44 @@ static void append_peer_struct(gpointer key, gpointer value, dbus_message_iter_close_container(array, &entry); } -struct _peers_notify { - int id; - GHashTable *add; - GHashTable *remove; -} *peers_notify; +static void state_changed(struct connman_peer *peer) +{ + const char *state; + + state = state2string(peer->state); + if (!state || !allow_property_changed(peer)) + return; + + connman_dbus_property_changed_basic(peer->path, + CONNMAN_PEER_INTERFACE, "State", + DBUS_TYPE_STRING, &state); +} static void append_existing_and_new_peers(gpointer key, gpointer value, gpointer user_data) { struct connman_peer *peer = value; DBusMessageIter *iter = user_data; - DBusMessageIter entry; + DBusMessageIter entry, dict; + + if (!peer || !peer->registered) + return; if (g_hash_table_lookup(peers_notify->add, peer->path)) { DBG("new %s", peer->path); - append_peer_struct(key, value, user_data); + append_peer_struct(key, peer, iter); g_hash_table_remove(peers_notify->add, peer->path); - } else { + } else if (!g_hash_table_lookup(peers_notify->remove, peer->path)) { DBG("existing %s", peer->path); dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &peer->path); + connman_dbus_dict_open(&entry, &dict); + connman_dbus_dict_close(&entry, &dict); + dbus_message_iter_close_container(iter, &entry); } } @@ -195,32 +507,216 @@ static void peer_removed(struct connman_peer *peer) peer_schedule_changed(); } +static const char *get_dbus_sender(struct connman_peer *peer) +{ + if (!peer->pending) + return NULL; + + return dbus_message_get_sender(peer->pending); +} + +static enum connman_peer_wps_method check_wpspin(struct connman_peer *peer, + const char *wpspin) +{ + int len, i; + + if (!wpspin) + return CONNMAN_PEER_WPS_PBC; + + len = strlen(wpspin); + if (len == 0) + return CONNMAN_PEER_WPS_PBC; + + if (len != 8) + return CONNMAN_PEER_WPS_UNKNOWN; + for (i = 0; i < 8; i++) { + if (!isdigit((unsigned char) wpspin[i])) + return CONNMAN_PEER_WPS_UNKNOWN; + } + + return CONNMAN_PEER_WPS_PIN; +} + +static void request_authorization_cb(struct connman_peer *peer, + bool choice_done, const char *wpspin, + const char *error, void *user_data) +{ + enum connman_peer_wps_method wps_method; + int err; + + DBG("RequestInput return, %p", peer); + + if (error) { + if (g_strcmp0(error, + "net.connman.Agent.Error.Canceled") == 0 || + g_strcmp0(error, + "net.connman.Agent.Error.Rejected") == 0) { + err = -EINVAL; + goto out; + } + } + + if (!choice_done || !peer_driver->connect) { + err = -EINVAL; + goto out; + } + + wps_method = check_wpspin(peer, wpspin); + + err = peer_driver->connect(peer, wps_method, wpspin); + if (err == -EINPROGRESS) + return; + +out: + reply_pending(peer, EIO); + connman_peer_set_state(peer, CONNMAN_PEER_STATE_IDLE); +} + +static int peer_connect(struct connman_peer *peer) +{ + int err = -ENOTSUP; + + if (peer_driver->connect) + err = peer_driver->connect(peer, + CONNMAN_PEER_WPS_UNKNOWN, NULL); + + if (err == -ENOKEY) { + err = __connman_agent_request_peer_authorization(peer, + request_authorization_cb, true, + get_dbus_sender(peer), NULL); + } + + return err; +} + +static int peer_disconnect(struct connman_peer *peer) +{ + int err = -ENOTSUP; + + connman_agent_cancel(peer); + reply_pending(peer, ECONNABORTED); + + connman_peer_set_state(peer, CONNMAN_PEER_STATE_DISCONNECT); + + if (peer->connection_master) + stop_dhcp_server(peer); + else + __connman_dhcp_stop(peer->ipconfig); + + if (peer_driver->disconnect) + err = peer_driver->disconnect(peer); + + connman_peer_set_state(peer, CONNMAN_PEER_STATE_IDLE); + + return err; +} + +static DBusMessage *connect_peer(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + struct connman_peer *peer = user_data; + GList *list, *start; + int err; + + DBG("peer %p", peer); + + if (peer->pending) + return __connman_error_in_progress(msg); + + list = g_hash_table_get_values(peers_table); + start = list; + for (; list; list = list->next) { + struct connman_peer *temp = list->data; + + if (temp == peer || temp->device != peer->device) + continue; + + if (is_connecting(temp) || is_connected(temp)) { + if (peer_disconnect(temp) == -EINPROGRESS) { + g_list_free(start); + return __connman_error_in_progress(msg); + } + } + } + + g_list_free(start); + + peer->pending = dbus_message_ref(msg); + + err = peer_connect(peer); + if (err == -EINPROGRESS) + return NULL; + + if (err < 0) { + dbus_message_unref(peer->pending); + peer->pending = NULL; + + return __connman_error_failed(msg, -err); + } + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +static DBusMessage *disconnect_peer(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + struct connman_peer *peer = user_data; + int err; + + DBG("peer %p", peer); + + err = peer_disconnect(peer); + if (err < 0 && err != -EINPROGRESS) + return __connman_error_failed(msg, -err); + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + struct connman_peer *connman_peer_create(const char *identifier) { struct connman_peer *peer; peer = g_malloc0(sizeof(struct connman_peer)); - peer->identifier = g_strdup_printf("peer_%s", identifier); + peer->identifier = g_strdup(identifier); + peer->state = CONNMAN_PEER_STATE_IDLE; + + peer->refcount = 1; return peer; } -void connman_peer_destroy(struct connman_peer *peer) +struct connman_peer *connman_peer_ref_debug(struct connman_peer *peer, + const char *file, int line, const char *caller) { - if (!peer) + DBG("%p ref %d by %s:%d:%s()", peer, peer->refcount + 1, + file, line, caller); + + __sync_fetch_and_add(&peer->refcount, 1); + + return peer; +} + +void connman_peer_unref_debug(struct connman_peer *peer, + const char *file, int line, const char *caller) +{ + DBG("%p ref %d by %s:%d:%s()", peer, peer->refcount - 1, + file, line, caller); + + if (__sync_fetch_and_sub(&peer->refcount, 1) != 1) return; - if (peer->path) { - peer_removed(peer); - g_dbus_unregister_interface(connection, peer->path, - CONNMAN_PEER_INTERFACE); - g_free(peer->path); - } + if (!peer->registered && !peer->path) + return peer_free(peer); - g_free(peer->identifier); - g_free(peer->name); + g_hash_table_remove(peers_table, peer->path); +} - g_free(peer); +const char *connman_peer_get_identifier(struct connman_peer *peer) +{ + if (!peer) + return NULL; + + return peer->identifier; } void connman_peer_set_name(struct connman_peer *peer, const char *name) @@ -229,12 +725,304 @@ void connman_peer_set_name(struct connman_peer *peer, const char *name) peer->name = g_strdup(name); } +void connman_peer_set_device(struct connman_peer *peer, + struct connman_device *device) +{ + if (!peer || !device) + return; + + peer->device = device; + connman_device_ref(device); +} + +struct connman_device *connman_peer_get_device(struct connman_peer *peer) +{ + if (!peer) + return NULL; + + return peer->device; +} + +void connman_peer_set_sub_device(struct connman_peer *peer, + struct connman_device *device) +{ + if (!peer || !device || peer->sub_device) + return; + + peer->sub_device = device; +} + +void connman_peer_set_as_master(struct connman_peer *peer, bool master) +{ + if (!peer || !is_connecting(peer)) + return; + + peer->connection_master = master; +} + +static void dhcp_callback(struct connman_ipconfig *ipconfig, + struct connman_network *network, + bool success, gpointer data) +{ + struct connman_peer *peer = data; + int err; + + if (!success) + goto error; + + DBG("lease acquired for ipconfig %p", ipconfig); + + err = __connman_ipconfig_address_add(ipconfig); + if (err < 0) + goto error; + + return; + +error: + __connman_ipconfig_address_remove(ipconfig); + connman_peer_set_state(peer, CONNMAN_PEER_STATE_FAILURE); +} + +static int start_dhcp_client(struct connman_peer *peer) +{ + if (peer->sub_device) + __connman_ipconfig_set_index(peer->ipconfig, + connman_device_get_index(peer->sub_device)); + + __connman_ipconfig_enable(peer->ipconfig); + + return __connman_dhcp_start(peer->ipconfig, NULL, dhcp_callback, peer); +} + +static void report_error_cb(void *user_context, bool retry, void *user_data) +{ + struct connman_peer *peer = user_context; + + if (retry) { + int err; + err = peer_connect(peer); + + if (err == 0 || err == -EINPROGRESS) + return; + } + + reply_pending(peer, ENOTCONN); + + peer_disconnect(peer); + + if (!peer->connection_master) { + __connman_dhcp_stop(peer->ipconfig); + __connman_ipconfig_disable(peer->ipconfig); + } else + stop_dhcp_server(peer); + + peer->connection_master = false; + peer->sub_device = NULL; +} + +static int manage_peer_error(struct connman_peer *peer) +{ + int err; + + err = __connman_agent_report_peer_error(peer, peer->path, + "connect-failed", report_error_cb, + get_dbus_sender(peer), NULL); + if (err != -EINPROGRESS) { + report_error_cb(peer, false, NULL); + return err; + } + + return 0; +} + +int connman_peer_set_state(struct connman_peer *peer, + enum connman_peer_state new_state) +{ + enum connman_peer_state old_state = peer->state; + int err; + + DBG("peer (%s) old state %d new state %d", peer->name, + old_state, new_state); + + if (old_state == new_state) + return -EALREADY; + + switch (new_state) { + case CONNMAN_PEER_STATE_UNKNOWN: + return -EINVAL; + case CONNMAN_PEER_STATE_IDLE: + if (is_connecting(peer) || is_connected(peer)) + return peer_disconnect(peer); + peer->sub_device = NULL; + break; + case CONNMAN_PEER_STATE_ASSOCIATION: + break; + case CONNMAN_PEER_STATE_CONFIGURATION: + if (peer->connection_master) + err = start_dhcp_server(peer); + else + err = start_dhcp_client(peer); + if (err < 0) + return connman_peer_set_state(peer, + CONNMAN_PEER_STATE_FAILURE); + break; + case CONNMAN_PEER_STATE_READY: + reply_pending(peer, 0); + break; + case CONNMAN_PEER_STATE_DISCONNECT: + if (peer->connection_master) + stop_dhcp_server(peer); + peer->connection_master = false; + peer->sub_device = NULL; + + break; + case CONNMAN_PEER_STATE_FAILURE: + if (manage_peer_error(peer) == 0) + return 0; + break; + }; + + peer->state = new_state; + state_changed(peer); + + return 0; +} + +int connman_peer_request_connection(struct connman_peer *peer) +{ + return __connman_agent_request_peer_authorization(peer, + request_authorization_cb, false, + NULL, NULL); +} + +static void peer_service_free(gpointer data) +{ + struct _peer_service *service = data; + + if (!service) + return; + + g_free(service->data); + g_free(service); +} + +void connman_peer_reset_services(struct connman_peer *peer) +{ + if (!peer) + return; + + g_slist_free_full(peer->services, peer_service_free); + peer->services = NULL; +} + +void connman_peer_services_changed(struct connman_peer *peer) +{ + if (!peer || !peer->registered || !allow_property_changed(peer)) + return; + + connman_dbus_property_changed_array(peer->path, + CONNMAN_PEER_INTERFACE, "Services", + DBUS_TYPE_DICT_ENTRY, append_peer_services, peer); +} + +void connman_peer_add_service(struct connman_peer *peer, + enum connman_peer_service_type type, + const unsigned char *data, int data_length) +{ + struct _peer_service *service; + + if (!peer || !data || type == CONNMAN_PEER_SERVICE_UNKNOWN) + return; + + service = g_malloc0(sizeof(struct _peer_service)); + service->type = type; + service->data = g_memdup(data, data_length * sizeof(unsigned char)); + service->length = data_length; + + peer->services = g_slist_prepend(peer->services, service); +} + +static void peer_up(struct connman_ipconfig *ipconfig, const char *ifname) +{ + DBG("%s up", ifname); +} + +static void peer_down(struct connman_ipconfig *ipconfig, const char *ifname) +{ + DBG("%s down", ifname); +} + +static void peer_lower_up(struct connman_ipconfig *ipconfig, + const char *ifname) +{ + DBG("%s lower up", ifname); +} + +static void peer_lower_down(struct connman_ipconfig *ipconfig, + const char *ifname) +{ + struct connman_peer *peer = __connman_ipconfig_get_data(ipconfig); + + DBG("%s lower down", ifname); + + __connman_ipconfig_disable(ipconfig); + connman_peer_set_state(peer, CONNMAN_PEER_STATE_DISCONNECT); +} + +static void peer_ip_bound(struct connman_ipconfig *ipconfig, + const char *ifname) +{ + struct connman_peer *peer = __connman_ipconfig_get_data(ipconfig); + + DBG("%s ip bound", ifname); + + settings_changed(peer); + connman_peer_set_state(peer, CONNMAN_PEER_STATE_READY); +} + +static void peer_ip_release(struct connman_ipconfig *ipconfig, + const char *ifname) +{ + struct connman_peer *peer = __connman_ipconfig_get_data(ipconfig); + + DBG("%s ip release", ifname); + + settings_changed(peer); +} + +static const struct connman_ipconfig_ops peer_ip_ops = { + .up = peer_up, + .down = peer_down, + .lower_up = peer_lower_up, + .lower_down = peer_lower_down, + .ip_bound = peer_ip_bound, + .ip_release = peer_ip_release, + .route_set = NULL, + .route_unset = NULL, +}; + +static struct connman_ipconfig *create_ipconfig(int index, void *user_data) +{ + struct connman_ipconfig *ipconfig; + + ipconfig = __connman_ipconfig_create(index, + CONNMAN_IPCONFIG_TYPE_IPV4); + if (!ipconfig) + return NULL; + + __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_DHCP); + __connman_ipconfig_set_data(ipconfig, user_data); + __connman_ipconfig_set_ops(ipconfig, &peer_ip_ops); + + return ipconfig; +} + static const GDBusMethodTable peer_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), get_peer_properties) }, - { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, NULL) }, - { GDBUS_METHOD("Disconnect", NULL, NULL, NULL) }, + { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, connect_peer) }, + { GDBUS_METHOD("Disconnect", NULL, NULL, disconnect_peer) }, { }, }; @@ -244,23 +1032,37 @@ static const GDBusSignalTable peer_signals[] = { { }, }; +static char *get_peer_path(struct connman_device *device, + const char *identifier) +{ + return g_strdup_printf("%s/peer/peer_%s_%s", CONNMAN_PATH, + connman_device_get_ident(device), identifier); +} + int connman_peer_register(struct connman_peer *peer) { + int index; + DBG("peer %p", peer); - if (peer->path) + if (peer->path && peer->registered) return -EALREADY; - peer->path = g_strdup_printf("%s/peer/%s", CONNMAN_PATH, - peer->identifier); + index = connman_device_get_index(peer->device); + peer->ipconfig = create_ipconfig(index, peer); + if (!peer->ipconfig) + return -ENOMEM; + + peer->path = get_peer_path(peer->device, peer->identifier); DBG("path %s", peer->path); - g_hash_table_insert(peers_table, peer->identifier, peer); + g_hash_table_insert(peers_table, peer->path, peer); g_dbus_register_interface(connection, peer->path, CONNMAN_PEER_INTERFACE, peer_methods, peer_signals, NULL, peer, NULL); + peer->registered = true; peer_added(peer); return 0; @@ -270,15 +1072,22 @@ void connman_peer_unregister(struct connman_peer *peer) { DBG("peer %p", peer); - if (peer->path) - g_hash_table_remove(peers_table, peer->identifier); - else - connman_peer_destroy(peer); + if (!peer->path || !peer->registered) + return; + + connman_agent_cancel(peer); + reply_pending(peer, EIO); + + g_dbus_unregister_interface(connection, peer->path, + CONNMAN_PEER_INTERFACE); + peer->registered = false; + peer_removed(peer); } -struct connman_peer *connman_peer_get(const char *identifier) +struct connman_peer *connman_peer_get(struct connman_device *device, + const char *identifier) { - char *ident = g_strdup_printf("peer_%s", identifier); + char *ident = get_peer_path(device, identifier); struct connman_peer *peer; peer = g_hash_table_lookup(peers_table, ident); @@ -287,11 +1096,41 @@ struct connman_peer *connman_peer_get(const char *identifier) return peer; } +int connman_peer_driver_register(struct connman_peer_driver *driver) +{ + if (peer_driver && peer_driver != driver) + return -EINVAL; + + peer_driver = driver; + + __connman_peer_service_set_driver(driver); + + return 0; +} + +void connman_peer_driver_unregister(struct connman_peer_driver *driver) +{ + if (peer_driver != driver) + return; + + peer_driver = NULL; + + __connman_peer_service_set_driver(NULL); +} + void __connman_peer_list_struct(DBusMessageIter *array) { g_hash_table_foreach(peers_table, append_peer_struct, array); } +const char *__connman_peer_get_path(struct connman_peer *peer) +{ + if (!peer || !peer->registered) + return NULL; + + return peer->path; +} + int __connman_peer_init(void) { DBG(""); @@ -313,5 +1152,7 @@ void __connman_peer_cleanup(void) DBG(""); g_hash_table_destroy(peers_table); + peers_table = NULL; dbus_connection_unref(connection); + connection = NULL; } diff --git a/src/peer_service.c b/src/peer_service.c new file mode 100644 index 00000000..053672af --- /dev/null +++ b/src/peer_service.c @@ -0,0 +1,429 @@ +/* + * + * Connection Manager + * + * Copyright (C) 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 + * 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 <errno.h> + +#include <gdbus.h> + +#include "connman.h" + +static DBusConnection *connection; + +struct _peer_service { + bool registered; + const char *owner; + DBusMessage *pending; + + GBytes *specification; + GBytes *query; + int version; + + bool master; +}; + +struct _peer_service_owner { + char *owner; + guint watch; + GList *services; +}; + +static struct connman_peer_driver *peer_driver; + +static GHashTable *owners_map; +static GHashTable *services_map; +static int peer_master; + +static void reply_pending(struct _peer_service *service, int error) +{ + if (!service->pending) + return; + + connman_dbus_reply_pending(service->pending, error, NULL); + service->pending = NULL; +} + +static struct _peer_service *find_peer_service(GBytes *specification, + GBytes *query, int version, + const char *owner, bool remove) +{ + struct _peer_service *service = NULL; + struct _peer_service_owner *ps_owner; + GList *list; + + ps_owner = g_hash_table_lookup(services_map, specification); + if (!ps_owner) + return NULL; + + if (owner && g_strcmp0(owner, ps_owner->owner) != 0) + return NULL; + + for (list = ps_owner->services; list; list = list->next) { + service = list->data; + + if (service->specification == specification) + break; + + if (version) { + if (!service->version) + continue; + if (version != service->version) + continue; + } + + if (query) { + if (!service->query) + continue; + if (g_bytes_equal(service->query, query)) + continue; + } + + if (g_bytes_equal(service->specification, specification)) + break; + } + + if (!service) + return NULL; + + if (owner && remove) + ps_owner->services = g_list_delete_link(ps_owner->services, + list); + + return service; +} + +static void unregister_peer_service(struct _peer_service *service) +{ + gsize spec_length, query_length = 0; + const void *spec, *query = NULL; + + if (!peer_driver || !service->specification) + return; + + spec = g_bytes_get_data(service->specification, &spec_length); + if (service->query) + query = g_bytes_get_data(service->query, &query_length); + + peer_driver->unregister_service(spec, spec_length, query, + query_length, service->version); +} + +static void remove_peer_service(gpointer user_data) +{ + struct _peer_service *service = user_data; + + reply_pending(service, ECONNABORTED); + + if (service->registered) + unregister_peer_service(service); + + if (service->specification) { + if (service->owner) { + find_peer_service(service->specification, + service->query, service->version, + service->owner, true); + } + + g_hash_table_remove(services_map, service->specification); + g_bytes_unref(service->specification); + } + + if (service->query) + g_bytes_unref(service->query); + + if (service->master) + peer_master--; + + g_free(service); +} + +static void apply_peer_service_removal(gpointer user_data) +{ + struct _peer_service *service = user_data; + + service->owner = NULL; + remove_peer_service(user_data); +} + +static void remove_peer_service_owner(gpointer user_data) +{ + struct _peer_service_owner *ps_owner = user_data; + + DBG("owner %s", ps_owner->owner); + + if (ps_owner->watch > 0) + g_dbus_remove_watch(connection, ps_owner->watch); + + if (ps_owner->services) { + g_list_free_full(ps_owner->services, + apply_peer_service_removal); + } + + g_free(ps_owner->owner); + g_free(ps_owner); +} + +static void owner_disconnect(DBusConnection *conn, void *user_data) +{ + struct _peer_service_owner *ps_owner = user_data; + + ps_owner->watch = 0; + g_hash_table_remove(owners_map, ps_owner->owner); +} + +static void service_registration_result(int result, void *user_data) +{ + struct _peer_service *service = user_data; + + reply_pending(service, -result); + + if (service->registered) + return; + + if (result == 0) { + service->registered = true; + if (service->master) + peer_master++; + return; + } + + remove_peer_service(service); +} + +static int register_peer_service(struct _peer_service *service) +{ + gsize spec_length, query_length = 0; + const void *spec, *query = NULL; + + if (!peer_driver) + return 0; + + spec = g_bytes_get_data(service->specification, &spec_length); + if (service->query) + query = g_bytes_get_data(service->query, &query_length); + + return peer_driver->register_service(spec, spec_length, query, + query_length, service->version, + service_registration_result, service); +} + +static void register_all_services(gpointer key, gpointer value, + gpointer user_data) +{ + struct _peer_service_owner *ps_owner = value; + GList *list; + + for (list = ps_owner->services; list; list = list->next) { + struct _peer_service *service = list->data; + + if (service->registered) + register_peer_service(service); + } +} + +void __connman_peer_service_set_driver(struct connman_peer_driver *driver) +{ + peer_driver = driver; + if (!peer_driver) + return; + + g_hash_table_foreach(owners_map, register_all_services, NULL); +} + +int __connman_peer_service_register(const char *owner, DBusMessage *msg, + const unsigned char *specification, + int specification_length, + const unsigned char *query, + int query_length, int version, + bool master) +{ + struct _peer_service_owner *ps_owner; + GBytes *spec, *query_spec = NULL; + struct _peer_service *service; + bool new = false; + int ret = 0; + + DBG("owner %s - spec %p/length %d - query %p/length %d - version %d", + owner,specification, specification_length, + query, query_length, version); + + if (!specification || specification_length == 0) + return -EINVAL; + + ps_owner = g_hash_table_lookup(owners_map, owner); + if (!ps_owner) { + ps_owner = g_try_new0(struct _peer_service_owner, 1); + if (!ps_owner) + return -ENOMEM; + + ps_owner->owner = g_strdup(owner); + ps_owner->watch = g_dbus_add_disconnect_watch(connection, + owner, owner_disconnect, + ps_owner, NULL); + g_hash_table_insert(owners_map, ps_owner->owner, ps_owner); + new = true; + } + + spec = g_bytes_new(specification, specification_length); + if (query) + query_spec = g_bytes_new(query, query_length); + + service = find_peer_service(spec, query_spec, version, NULL, false); + if (service) { + DBG("Found one existing service %p", service); + + if (g_strcmp0(service->owner, owner)) + ret = -EBUSY; + + if (service->pending) + ret = -EINPROGRESS; + else + ret = -EEXIST; + + service = NULL; + goto error; + } + + service = g_try_new0(struct _peer_service, 1); + if (!service) { + ret = -ENOMEM; + goto error; + } + + service->owner = ps_owner->owner; + service->specification = spec; + service->query = query_spec; + service->version = version; + service->master = master; + + g_hash_table_insert(services_map, spec, ps_owner); + spec = query_spec = NULL; + + ret = register_peer_service(service); + if (ret != 0 && ret != -EINPROGRESS) + goto error; + else if (ret == -EINPROGRESS) + service->pending = dbus_message_ref(msg); + else { + service->registered = true; + if (master) + peer_master++; + } + + ps_owner->services = g_list_prepend(ps_owner->services, service); + + return ret; +error: + if (spec) + g_bytes_unref(spec); + if (query_spec) + g_bytes_unref(query_spec); + + if (service) + remove_peer_service(service); + + if (new) + g_hash_table_remove(owners_map, ps_owner->owner); + + return ret; +} + +int __connman_peer_service_unregister(const char *owner, + const unsigned char *specification, + int specification_length, + const unsigned char *query, + int query_length, int version) +{ + struct _peer_service_owner *ps_owner; + GBytes *spec, *query_spec = NULL; + struct _peer_service *service; + + DBG("owner %s - spec %p/length %d - query %p/length %d - version %d", + owner,specification, specification_length, + query, query_length, version); + + ps_owner = g_hash_table_lookup(owners_map, owner); + if (!ps_owner) + return -ESRCH; + + spec = g_bytes_new(specification, specification_length); + if (query) + query_spec = g_bytes_new(query, query_length); + + service = find_peer_service(spec, query_spec, version, owner, true); + + g_bytes_unref(spec); + g_bytes_unref(query_spec); + + if (!service) + return -ESRCH; + + remove_peer_service(service); + + if (!ps_owner->services) + g_hash_table_remove(owners_map, ps_owner->owner); + + return 0; +} + +bool connman_peer_service_is_master(void) +{ + if (!peer_master || !peer_driver) + return false; + + return true; +} + +int __connman_peer_service_init(void) +{ + DBG(""); + connection = connman_dbus_get_connection(); + if (!connection) + return -1; + + peer_driver = NULL; + + owners_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, + remove_peer_service_owner); + services_map = g_hash_table_new_full(g_bytes_hash, g_bytes_equal, + NULL, NULL); + peer_master = 0; + + return 0; +} + +void __connman_peer_service_cleanup(void) +{ + DBG(""); + + if (!connection) + return; + + g_hash_table_destroy(owners_map); + g_hash_table_destroy(services_map); + peer_master = 0; + + dbus_connection_unref(connection); + connection = NULL; +} diff --git a/src/service.c b/src/service.c index cbca669e..87a2f2cd 100644 --- a/src/service.c +++ b/src/service.c @@ -234,6 +234,23 @@ enum connman_service_type __connman_service_string2type(const char *str) return CONNMAN_SERVICE_TYPE_UNKNOWN; } +enum connman_service_security __connman_service_string2security(const char *str) +{ + if (!str) + return CONNMAN_SERVICE_SECURITY_UNKNOWN; + + if (!strcmp(str, "psk")) + return CONNMAN_SERVICE_SECURITY_PSK; + if (!strcmp(str, "ieee8021x")) + return CONNMAN_SERVICE_SECURITY_8021X; + if (!strcmp(str, "none")) + return CONNMAN_SERVICE_SECURITY_NONE; + if (!strcmp(str, "wep")) + return CONNMAN_SERVICE_SECURITY_WEP; + + return CONNMAN_SERVICE_SECURITY_UNKNOWN; +} + static const char *security2string(enum connman_service_security security) { switch (security) { @@ -302,18 +319,6 @@ static const char *error2string(enum connman_service_error error) return NULL; } -static enum connman_service_error string2error(const char *error) -{ - if (g_strcmp0(error, "dhcp-failed") == 0) - return CONNMAN_SERVICE_ERROR_DHCP_FAILED; - else if (g_strcmp0(error, "pin-missing") == 0) - return CONNMAN_SERVICE_ERROR_PIN_MISSING; - else if (g_strcmp0(error, "invalid-key") == 0) - return CONNMAN_SERVICE_ERROR_INVALID_KEY; - - return CONNMAN_SERVICE_ERROR_UNKNOWN; -} - static const char *proxymethod2string(enum connman_service_proxy_method method) { switch (method) { @@ -480,15 +485,6 @@ static int service_load(struct connman_service *service) service->favorite = g_key_file_get_boolean(keyfile, service->identifier, "Favorite", NULL); - str = g_key_file_get_string(keyfile, - service->identifier, "Failure", NULL); - if (str) { - if (!service->favorite) - service->state_ipv4 = service->state_ipv6 = - CONNMAN_SERVICE_STATE_FAILURE; - service->error = string2error(str); - g_free(str); - } /* fall through */ case CONNMAN_SERVICE_TYPE_ETHERNET: @@ -655,17 +651,9 @@ static int service_save(struct connman_service *service) g_key_file_set_boolean(keyfile, service->identifier, "Favorite", service->favorite); - if (service->state_ipv4 == CONNMAN_SERVICE_STATE_FAILURE || - service->state_ipv6 == CONNMAN_SERVICE_STATE_FAILURE) { - const char *failure = error2string(service->error); - if (failure) - g_key_file_set_string(keyfile, - service->identifier, - "Failure", failure); - } else { - g_key_file_remove_key(keyfile, service->identifier, - "Failure", NULL); - } + g_key_file_remove_key(keyfile, service->identifier, + "Failure", NULL); + /* fall through */ case CONNMAN_SERVICE_TYPE_ETHERNET: @@ -2804,30 +2792,29 @@ void __connman_service_set_agent_identity(struct connman_service *service, service->agent_identity); } -static int check_passphrase(struct connman_service *service, - enum connman_service_security security, - const char *passphrase) +static int check_passphrase(enum connman_service_security security, + const char *passphrase) { guint i; gsize length; - if (!passphrase) { - /* - * This will prevent __connman_service_set_passphrase() to - * wipe the passphrase out in case of -ENOKEY error for a - * favorite service. */ - if (service->favorite) - return 1; - else - return 0; - } + if (!passphrase) + return 0; length = strlen(passphrase); switch (security) { - case CONNMAN_SERVICE_SECURITY_PSK: + case CONNMAN_SERVICE_SECURITY_UNKNOWN: + case CONNMAN_SERVICE_SECURITY_NONE: case CONNMAN_SERVICE_SECURITY_WPA: case CONNMAN_SERVICE_SECURITY_RSN: + + DBG("service security '%s' (%d) not handled", + security2string(security), security); + + return -EOPNOTSUPP; + + case CONNMAN_SERVICE_SECURITY_PSK: /* A raw key is always 64 bytes length, * its content is in hex representation. * A PSK key must be between [8..63]. @@ -2852,8 +2839,7 @@ static int check_passphrase(struct connman_service *service, } else if (length != 5 && length != 13) return -ENOKEY; break; - case CONNMAN_SERVICE_SECURITY_UNKNOWN: - case CONNMAN_SERVICE_SECURITY_NONE: + case CONNMAN_SERVICE_SECURITY_8021X: break; } @@ -2864,25 +2850,29 @@ static int check_passphrase(struct connman_service *service, int __connman_service_set_passphrase(struct connman_service *service, const char *passphrase) { - int err = 0; + int err; - if (service->immutable || service->hidden) + if (service->hidden) + return -EINVAL; + + if (service->immutable && + service->security != CONNMAN_SERVICE_SECURITY_8021X) return -EINVAL; - err = check_passphrase(service, service->security, passphrase); + err = check_passphrase(service->security, passphrase); - if (err == 0) { - g_free(service->passphrase); - service->passphrase = g_strdup(passphrase); + if (err < 0) + return err; - if (service->network) - connman_network_set_string(service->network, - "WiFi.Passphrase", - service->passphrase); - service_save(service); - } + g_free(service->passphrase); + service->passphrase = g_strdup(passphrase); - return err; + if (service->network) + connman_network_set_string(service->network, "WiFi.Passphrase", + service->passphrase); + service_save(service); + + return 0; } const char *__connman_service_get_passphrase(struct connman_service *service) @@ -2893,6 +2883,16 @@ const char *__connman_service_get_passphrase(struct connman_service *service) return service->passphrase; } +static void clear_passphrase(struct connman_service *service) +{ + g_free(service->passphrase); + service->passphrase = NULL; + + if (service->network) + connman_network_set_string(service->network, "WiFi.Passphrase", + service->passphrase); +} + static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg, void *user_data) { @@ -3133,6 +3133,7 @@ int __connman_service_reset_ipconfig(struct connman_service *service, if (is_connecting_state(service, state) || is_connected_state(service, state)) __connman_network_clear_ipconfig(service->network, ipconfig); + __connman_ipconfig_unref(ipconfig); if (type == CONNMAN_IPCONFIG_TYPE_IPV4) @@ -3140,13 +3141,16 @@ int __connman_service_reset_ipconfig(struct connman_service *service, else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) service->ipconfig_ipv6 = new_ipconfig; - __connman_ipconfig_enable(new_ipconfig); + if (is_connecting_state(service, state) || + is_connected_state(service, state)) + __connman_ipconfig_enable(new_ipconfig); if (new_state && new_method != old_method) { if (type == CONNMAN_IPCONFIG_TYPE_IPV4) *new_state = service->state_ipv4; else *new_state = service->state_ipv6; + __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO); } @@ -3800,50 +3804,17 @@ static void remove_timeout(struct connman_service *service) } } -void __connman_service_reply_dbus_pending(DBusMessage *pending, int error, - const char *path) -{ - if (pending) { - if (error > 0) { - DBusMessage *reply; - - reply = __connman_error_failed(pending, error); - if (reply) - g_dbus_send_message(connection, reply); - } else { - const char *sender; - - sender = dbus_message_get_interface(pending); - if (!path) - path = dbus_message_get_path(pending); - - DBG("sender %s path %s", sender, path); - - if (g_strcmp0(sender, CONNMAN_MANAGER_INTERFACE) == 0) - g_dbus_send_reply(connection, pending, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID); - else - g_dbus_send_reply(connection, pending, - DBUS_TYPE_INVALID); - } - - dbus_message_unref(pending); - } -} - static void reply_pending(struct connman_service *service, int error) { remove_timeout(service); if (service->pending) { - __connman_service_reply_dbus_pending(service->pending, error, - NULL); + connman_dbus_reply_pending(service->pending, error, NULL); service->pending = NULL; } if (service->provider_pending) { - __connman_service_reply_dbus_pending(service->provider_pending, + connman_dbus_reply_pending(service->provider_pending, error, service->path); service->provider_pending = NULL; } @@ -3955,34 +3926,11 @@ static gboolean connect_timeout(gpointer user_data) return FALSE; } -static bool is_interface_available(struct connman_service *service, - struct connman_service *other_service) -{ - unsigned int index = 0, other_index = 0; - - if (service->ipconfig_ipv4) - index = __connman_ipconfig_get_index(service->ipconfig_ipv4); - else if (service->ipconfig_ipv6) - index = __connman_ipconfig_get_index(service->ipconfig_ipv6); - - if (other_service->ipconfig_ipv4) - other_index = __connman_ipconfig_get_index( - other_service->ipconfig_ipv4); - else if (other_service->ipconfig_ipv6) - other_index = __connman_ipconfig_get_index( - other_service->ipconfig_ipv6); - - if (index > 0 && other_index != index) - return true; - - return false; -} - static DBusMessage *connect_service(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct connman_service *service = user_data; - int err = 0; + int index, err = 0; GList *list; DBG("service %p", service); @@ -3990,27 +3938,27 @@ static DBusMessage *connect_service(DBusConnection *conn, if (service->pending) return __connman_error_in_progress(msg); + index = __connman_service_get_index(service); + for (list = service_list; list; list = list->next) { struct connman_service *temp = list->data; - /* - * We should allow connection if there are available - * interfaces for a given technology type (like having - * more than one wifi card). - */ if (!is_connecting(temp) && !is_connected(temp)) break; + if (service == temp) + continue; + if (service->type != temp->type) continue; - if(!is_interface_available(service, temp)) { - if (__connman_service_disconnect(temp) == -EINPROGRESS) - err = -EINPROGRESS; - } + if (__connman_service_get_index(temp) == index && + __connman_service_disconnect(temp) == -EINPROGRESS) + err = -EINPROGRESS; + } if (err == -EINPROGRESS) - return __connman_error_in_progress(msg); + return __connman_error_operation_timeout(msg); service->ignore = false; @@ -4022,8 +3970,10 @@ static DBusMessage *connect_service(DBusConnection *conn, if (err == -EINPROGRESS) return NULL; - dbus_message_unref(service->pending); - service->pending = NULL; + if (service->pending) { + dbus_message_unref(service->pending); + service->pending = NULL; + } if (err < 0) return __connman_error_failed(msg, -err); @@ -4042,10 +3992,8 @@ static DBusMessage *disconnect_service(DBusConnection *conn, service->ignore = true; err = __connman_service_disconnect(service); - if (err < 0) { - if (err != -EINPROGRESS) - return __connman_error_failed(msg, -err); - } + if (err < 0 && err != -EINPROGRESS) + return __connman_error_failed(msg, -err); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } @@ -4082,6 +4030,8 @@ bool __connman_service_remove(struct connman_service *service) __connman_service_set_favorite(service, false); + __connman_ipconfig_ipv6_reset_privacy(service->ipconfig_ipv6); + service_save(service); return true; @@ -4397,13 +4347,13 @@ static void service_schedule_added(struct connman_service *service) static void service_schedule_removed(struct connman_service *service) { - DBG("service %p %s", service, service->path); - if (!service || !service->path) { DBG("service %p or path is NULL", service); return; } + DBG("service %p %s", service, service->path); + g_hash_table_remove(services_notify->add, service->path); g_hash_table_replace(services_notify->remove, g_strdup(service->path), NULL); @@ -5003,10 +4953,8 @@ void __connman_service_set_string(struct connman_service *service, } else if (g_str_equal(key, "Phase2")) { g_free(service->phase2); service->phase2 = g_strdup(value); - } else if (g_str_equal(key, "Passphrase")) { - g_free(service->passphrase); - service->passphrase = g_strdup(value); - } + } else if (g_str_equal(key, "Passphrase")) + __connman_service_set_passphrase(service, value); } void __connman_service_set_search_domains(struct connman_service *service, @@ -5062,38 +5010,13 @@ static void report_error_cb(void *user_context, bool retry, else { /* It is not relevant to stay on Failure state * when failing is due to wrong user input */ - service->state = CONNMAN_SERVICE_STATE_IDLE; + __connman_service_clear_error(service); service_complete(service); __connman_connection_update_gateway(); } } -int __connman_service_add_passphrase(struct connman_service *service, - const gchar *passphrase) -{ - int err = 0; - - switch (service->security) { - case CONNMAN_SERVICE_SECURITY_WEP: - case CONNMAN_SERVICE_SECURITY_PSK: - case CONNMAN_SERVICE_SECURITY_8021X: - err = __connman_service_set_passphrase(service, passphrase); - break; - - case CONNMAN_SERVICE_SECURITY_UNKNOWN: - case CONNMAN_SERVICE_SECURITY_NONE: - case CONNMAN_SERVICE_SECURITY_WPA: - case CONNMAN_SERVICE_SECURITY_RSN: - DBG("service security '%s' (%d) not handled", - security2string(service->security), - service->security); - break; - } - - return err; -} - static int check_wpspin(struct connman_service *service, const char *wpspin) { int length; @@ -5187,7 +5110,7 @@ static void request_input_cb(struct connman_service *service, __connman_service_set_agent_identity(service, identity); if (passphrase) - err = __connman_service_add_passphrase(service, passphrase); + err = __connman_service_set_passphrase(service, passphrase); done: if (err >= 0) { @@ -5311,6 +5234,7 @@ static int service_indicate_state(struct connman_service *service) { enum connman_service_state old_state, new_state; struct connman_service *def_service; + enum connman_ipconfig_method method; int result; if (!service) @@ -5344,13 +5268,22 @@ static int service_indicate_state(struct connman_service *service) service->state = new_state; state_changed(service); - if (new_state == CONNMAN_SERVICE_STATE_IDLE && - old_state != CONNMAN_SERVICE_STATE_DISCONNECT) { + switch(new_state) { + case CONNMAN_SERVICE_STATE_UNKNOWN: - __connman_service_disconnect(service); - } + break; + + case CONNMAN_SERVICE_STATE_IDLE: + if (old_state != CONNMAN_SERVICE_STATE_DISCONNECT) + __connman_service_disconnect(service); + + break; + + case CONNMAN_SERVICE_STATE_ASSOCIATION: - if (new_state == CONNMAN_SERVICE_STATE_CONFIGURATION) { + break; + + case CONNMAN_SERVICE_STATE_CONFIGURATION: if (!service->new_service && __connman_stats_service_register(service) == 0) { /* @@ -5362,11 +5295,10 @@ static int service_indicate_state(struct connman_service *service) __connman_stats_get(service, true, &service->stats_roaming.data); } - } - if (new_state == CONNMAN_SERVICE_STATE_READY) { - enum connman_ipconfig_method method; + break; + case CONNMAN_SERVICE_STATE_READY: if (service->new_service && __connman_stats_service_register(service) == 0) { /* @@ -5426,7 +5358,16 @@ static int service_indicate_state(struct connman_service *service) else if (service->type != CONNMAN_SERVICE_TYPE_VPN) vpn_auto_connect(); - } else if (new_state == CONNMAN_SERVICE_STATE_DISCONNECT) { + break; + + case CONNMAN_SERVICE_STATE_ONLINE: + + break; + + case CONNMAN_SERVICE_STATE_DISCONNECT: + + reply_pending(service, ECONNABORTED); + def_service = __connman_service_get_default(); if (!__connman_notifier_is_connected() && @@ -5452,9 +5393,9 @@ static int service_indicate_state(struct connman_service *service) downgrade_connected_services(); __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO); - } + break; - if (new_state == CONNMAN_SERVICE_STATE_FAILURE) { + case CONNMAN_SERVICE_STATE_FAILURE: if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER && connman_agent_report_error(service, service->path, @@ -5464,7 +5405,11 @@ static int service_indicate_state(struct connman_service *service) NULL) == -EINPROGRESS) return 0; service_complete(service); - } else + + break; + } + + if (new_state != CONNMAN_SERVICE_STATE_FAILURE) set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN); service_list_sort(); @@ -5502,7 +5447,7 @@ int __connman_service_indicate_error(struct connman_service *service, */ if (service->error == CONNMAN_SERVICE_ERROR_INVALID_KEY || service->security == CONNMAN_SERVICE_SECURITY_8021X) - __connman_service_set_passphrase(service, NULL); + clear_passphrase(service); __connman_service_set_agent_identity(service, NULL); @@ -5517,6 +5462,8 @@ int __connman_service_indicate_error(struct connman_service *service, int __connman_service_clear_error(struct connman_service *service) { + DBusMessage *pending, *provider_pending; + DBG("service %p", service); if (!service) @@ -5525,25 +5472,23 @@ int __connman_service_clear_error(struct connman_service *service) if (service->state != CONNMAN_SERVICE_STATE_FAILURE) return -EINVAL; - service->state_ipv4 = service->state_ipv6 = - CONNMAN_SERVICE_STATE_UNKNOWN; - set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN); + pending = service->pending; + service->pending = NULL; + provider_pending = service->provider_pending; + service->provider_pending = NULL; __connman_service_ipconfig_indicate_state(service, - CONNMAN_SERVICE_STATE_IDLE, - CONNMAN_IPCONFIG_TYPE_IPV6); - - /* - * Toggling the IPv6 state to IDLE could trigger the auto connect - * machinery and consequently the IPv4 state. - */ - if (service->state_ipv4 != CONNMAN_SERVICE_STATE_UNKNOWN && - service->state_ipv4 != CONNMAN_SERVICE_STATE_FAILURE) - return 0; + CONNMAN_SERVICE_STATE_IDLE, + CONNMAN_IPCONFIG_TYPE_IPV6); - return __connman_service_ipconfig_indicate_state(service, + __connman_service_ipconfig_indicate_state(service, CONNMAN_SERVICE_STATE_IDLE, CONNMAN_IPCONFIG_TYPE_IPV4); + + service->pending = pending; + service->provider_pending = provider_pending; + + return 0; } int __connman_service_indicate_default(struct connman_service *service) @@ -5708,38 +5653,45 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service, enum connman_ipconfig_type type) { struct connman_ipconfig *ipconfig = NULL; - enum connman_service_state old_state; + enum connman_service_state *old_state; enum connman_ipconfig_method method; if (!service) return -EINVAL; - if (type == CONNMAN_IPCONFIG_TYPE_IPV4) { - old_state = service->state_ipv4; + switch (type) { + case CONNMAN_IPCONFIG_TYPE_UNKNOWN: + return -EINVAL; + + case CONNMAN_IPCONFIG_TYPE_IPV4: + old_state = &service->state_ipv4; ipconfig = service->ipconfig_ipv4; - } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) { - old_state = service->state_ipv6; + + break; + + case CONNMAN_IPCONFIG_TYPE_IPV6: + old_state = &service->state_ipv6; ipconfig = service->ipconfig_ipv6; + + break; } if (!ipconfig) return -EINVAL; /* Any change? */ - if (old_state == new_state) + if (*old_state == new_state) return -EALREADY; - DBG("service %p (%s) state %d (%s) type %d (%s)", + DBG("service %p (%s) old state %d (%s) new state %d (%s) type %d (%s)", service, service ? service->identifier : NULL, + *old_state, state2string(*old_state), new_state, state2string(new_state), type, __connman_ipconfig_type2string(type)); switch (new_state) { case CONNMAN_SERVICE_STATE_UNKNOWN: case CONNMAN_SERVICE_STATE_IDLE: - if (service->state == CONNMAN_SERVICE_STATE_FAILURE) - return -EINVAL; - break; case CONNMAN_SERVICE_STATE_ASSOCIATION: break; case CONNMAN_SERVICE_STATE_CONFIGURATION: @@ -5772,25 +5724,23 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service, the state to IDLE so that it will not affect the combined state in the future. */ - if (type == CONNMAN_IPCONFIG_TYPE_IPV4) { - method = __connman_ipconfig_get_method(service->ipconfig_ipv4); - - if (method == CONNMAN_IPCONFIG_METHOD_OFF || - method == CONNMAN_IPCONFIG_METHOD_UNKNOWN) - new_state = CONNMAN_SERVICE_STATE_IDLE; - - service->state_ipv4 = new_state; - - } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) { - method = __connman_ipconfig_get_method(service->ipconfig_ipv6); + method = __connman_ipconfig_get_method(ipconfig); + switch (method) { + case CONNMAN_IPCONFIG_METHOD_UNKNOWN: + case CONNMAN_IPCONFIG_METHOD_OFF: + new_state = CONNMAN_SERVICE_STATE_IDLE; + break; - if (method == CONNMAN_IPCONFIG_METHOD_OFF || - method == CONNMAN_IPCONFIG_METHOD_UNKNOWN) - new_state = CONNMAN_SERVICE_STATE_IDLE; + case CONNMAN_IPCONFIG_METHOD_FIXED: + case CONNMAN_IPCONFIG_METHOD_MANUAL: + case CONNMAN_IPCONFIG_METHOD_DHCP: + case CONNMAN_IPCONFIG_METHOD_AUTO: + break; - service->state_ipv6 = new_state; } + *old_state = new_state; + update_nameservers(service); return service_indicate_state(service); @@ -5896,10 +5846,9 @@ static int service_connect(struct connman_service *service) if (!service->wps || !connman_network_get_bool(service->network, "WiFi.UseWPS")) return -ENOKEY; - } else if (service->error == - CONNMAN_SERVICE_ERROR_INVALID_KEY) - return -ENOKEY; + } break; + case CONNMAN_SERVICE_SECURITY_8021X: if (!service->eap) return -EINVAL; @@ -5995,13 +5944,23 @@ int __connman_service_connect(struct connman_service *service, case CONNMAN_SERVICE_TYPE_GPS: case CONNMAN_SERVICE_TYPE_P2P: return -EINVAL; - default: - if (!is_ipconfig_usable(service)) - return -ENOLINK; - err = service_connect(service); + case CONNMAN_SERVICE_TYPE_ETHERNET: + case CONNMAN_SERVICE_TYPE_GADGET: + case CONNMAN_SERVICE_TYPE_BLUETOOTH: + case CONNMAN_SERVICE_TYPE_CELLULAR: + case CONNMAN_SERVICE_TYPE_VPN: + case CONNMAN_SERVICE_TYPE_WIFI: + break; } + if (!is_ipconfig_usable(service)) + return -ENOLINK; + + __connman_service_clear_error(service); + + err = service_connect(service); + service->connect_reason = reason; if (err >= 0) { set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN); diff --git a/src/session.c b/src/session.c index 00ef3696..08facc1d 100644 --- a/src/session.c +++ b/src/session.c @@ -3,7 +3,7 @@ * Connection Manager * * Copyright (C) 2007-2014 Intel Corporation. All rights reserved. - * Copyright (C) 2011-2014 BWM CarIT GmbH. All rights reserved. + * Copyright (C) 2011-2014 BMW Car IT GmbH. * * 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 diff --git a/src/shared/netlink.c b/src/shared/netlink.c index d72294cc..b32ab854 100644 --- a/src/shared/netlink.c +++ b/src/shared/netlink.c @@ -3,7 +3,7 @@ * Connection Manager * * Copyright (C) 2011-2012 Intel Corporation. All rights reserved. - * Copyright (C) 2013 BWM CarIT GmbH. + * Copyright (C) 2013-2014 BMW Car IT GmbH. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/stats.c b/src/stats.c index df5ab4eb..26343b13 100644 --- a/src/stats.c +++ b/src/stats.c @@ -2,7 +2,7 @@ * * Connection Manager * - * Copyright (C) 2010 BMW Car IT GmbH. All rights reserved. + * Copyright (C) 2010-2014 BMW Car IT GmbH. * * 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 diff --git a/src/tethering.c b/src/tethering.c index c7e17f5d..ceeec746 100644 --- a/src/tethering.c +++ b/src/tethering.c @@ -222,7 +222,7 @@ void __connman_tethering_set_enabled(void) end_ip = __connman_ippool_get_end_ip(dhcp_ippool); err = __connman_bridge_enable(BRIDGE_NAME, gateway, - __connman_ipaddress_netmask_prefix_len(subnet_mask), + connman_ipaddress_calc_netmask_len(subnet_mask), broadcast); if (err < 0 && err != -EALREADY) { __connman_ippool_unref(dhcp_ippool); @@ -267,7 +267,7 @@ void __connman_tethering_set_enabled(void) return; } - prefixlen = __connman_ipaddress_netmask_prefix_len(subnet_mask); + prefixlen = connman_ipaddress_calc_netmask_len(subnet_mask); err = __connman_nat_enable(BRIDGE_NAME, start_ip, prefixlen); if (err < 0) { connman_error("Cannot enable NAT %d/%s", err, strerror(-err)); @@ -340,8 +340,7 @@ static void setup_tun_interface(unsigned int flags, unsigned change, subnet_mask = __connman_ippool_get_subnet_mask(pn->pool); server_ip = __connman_ippool_get_start_ip(pn->pool); peer_ip = __connman_ippool_get_end_ip(pn->pool); - prefixlen = - __connman_ipaddress_netmask_prefix_len(subnet_mask); + prefixlen = connman_ipaddress_calc_netmask_len(subnet_mask); if ((__connman_inet_modify_address(RTM_NEWADDR, NLM_F_REPLACE | NLM_F_ACK, pn->index, AF_INET, diff --git a/src/timeserver.c b/src/timeserver.c index d41fa404..f0d33e5e 100644 --- a/src/timeserver.c +++ b/src/timeserver.c @@ -117,7 +117,7 @@ static void resolv_result(GResolvResultStatus status, char **results, * Once the timeserver list (ts_list) is created, we start querying the * servers one by one. If resolving fails on one of them, we move to the * next one. The user can enter either an IP address or a URL for the - * timeserver. We only resolve the urls. Once we have a IP for the NTP + * timeserver. We only resolve the URLs. Once we have an IP for the NTP * server, we start querying it for time corrections. */ void __connman_timeserver_sync_next() @@ -137,7 +137,7 @@ void __connman_timeserver_sync_next() ts_list = g_slist_delete_link(ts_list, ts_list); - /* if its a IP , directly query it. */ + /* if it's an IP, directly query it. */ if (connman_inet_check_ipaddress(ts_current) > 0) { DBG("Using timeserver %s", ts_current); @@ -146,7 +146,7 @@ void __connman_timeserver_sync_next() return; } - DBG("Resolving server %s", ts_current); + DBG("Resolving timeserver %s", ts_current); resolv_id = g_resolv_lookup_hostname(resolv, ts_current, resolv_result, NULL); @@ -200,7 +200,7 @@ GSList *__connman_timeserver_get_all(struct connman_service *service) service_ts = connman_service_get_timeservers(service); - /* First add Service Timeservers via DHCP to the list */ + /* Then add Service Timeservers via DHCP to the list */ for (i = 0; service_ts && service_ts[i]; i++) list = __connman_timeserver_add_list(list, service_ts[i]); @@ -286,7 +286,7 @@ static void ts_recheck_enable(void) /* * This function must be called everytime the default service changes, the - * service timeserver(s) or gatway changes or the global timeserver(s) changes. + * service timeserver(s) or gateway changes or the global timeserver(s) changes. */ int __connman_timeserver_sync(struct connman_service *default_service) { diff --git a/src/wispr.c b/src/wispr.c index dcce93cc..c4fcd60b 100644 --- a/src/wispr.c +++ b/src/wispr.c @@ -711,6 +711,11 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data) DBG("status: %03u", status); switch (status) { + case 000: + __connman_agent_request_browser(wp_context->service, + wispr_portal_browser_reply_cb, + wp_context->status_url, wp_context); + break; case 200: if (wp_context->wispr_msg.message_type >= 0) break; @@ -755,6 +760,11 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data) } break; + case 505: + __connman_agent_request_browser(wp_context->service, + wispr_portal_browser_reply_cb, + wp_context->status_url, wp_context); + break; default: break; } |