diff --git a/connman/Makefile.am b/connman/Makefile.am index f84495346..4d15ed8f9 100644 --- a/connman/Makefile.am +++ b/connman/Makefile.am @@ -370,7 +370,7 @@ unit_test_service_SOURCES = $(backtrace_sources) $(gdhcp_sources) \ $(gweb_sources) $(shared_sources) \ unit/test-service.c \ src/connman.h src/log.c src/error.c src/task.c \ - src/device.c src/network.c src/connection.c \ + src/device.c src/network.c \ src/manager.c src/agent-connman.c src/clock.c \ src/timezone.c src/agent.c src/notifier.c \ src/resolver.c src/ipconfig.c src/detect.c \ diff --git a/connman/plugins/vpn.c b/connman/plugins/vpn.c index 5dd709a4e..960fd25b3 100644 --- a/connman/plugins/vpn.c +++ b/connman/plugins/vpn.c @@ -155,6 +155,8 @@ static const char *get_string(struct connman_provider *provider, return data->host_ip[0]; } else if (g_str_equal(key, "VPN.Domain")) return data->domain; + else if (g_str_equal(key, "Transport")) + return data->service_ident; return g_hash_table_lookup(data->setting_strings, key); } @@ -270,14 +272,12 @@ static bool provider_is_connected(struct connection_data *data) static void set_provider_state(struct connection_data *data) { enum connman_provider_state state = CONNMAN_PROVIDER_STATE_UNKNOWN; + bool connected; int err = 0; DBG("provider %p new state %s", data->provider, data->state); - if (!provider_is_connected(data)) { - g_free(data->service_ident); - data->service_ident = NULL; - } + connected = provider_is_connected(data); if (g_str_equal(data->state, "ready")) { state = CONNMAN_PROVIDER_STATE_READY; @@ -297,7 +297,7 @@ static void set_provider_state(struct connection_data *data) } connman_provider_set_state(data->provider, state); - return; + goto free; set: if (data->cb_data) @@ -308,6 +308,12 @@ static void set_provider_state(struct connection_data *data) free_config_cb_data(data->cb_data); data->cb_data = NULL; + +free: + if (!connected) { + g_free(data->service_ident); + data->service_ident = NULL; + } } static int create_provider(struct connection_data *data, void *user_data) @@ -1045,12 +1051,14 @@ static int disconnect_provider(struct connection_data *data) dbus_pending_call_set_notify(data->disconnect_call, disconnect_reply, data, NULL); - g_free(data->service_ident); - data->service_ident = NULL; data->default_route_set = false; connman_provider_set_state(data->provider, CONNMAN_PROVIDER_STATE_DISCONNECT); + + g_free(data->service_ident); + data->service_ident = NULL; + return -EINPROGRESS; } diff --git a/connman/src/connman.h b/connman/src/connman.h index 3d737e151..fb35b16d1 100644 --- a/connman/src/connman.h +++ b/connman/src/connman.h @@ -435,8 +435,11 @@ void __connman_ipconfig_set_ops(struct connman_ipconfig *ipconfig, const struct connman_ipconfig_ops *ops); int __connman_ipconfig_set_method(struct connman_ipconfig *ipconfig, enum connman_ipconfig_method method); -void __connman_ipconfig_disable_ipv6(struct connman_ipconfig *ipconfig); -void __connman_ipconfig_enable_ipv6(struct connman_ipconfig *ipconfig); +void __connman_ipconfig_disable_ipv6(struct connman_ipconfig *ipconfig, + bool forced); +int __connman_ipconfig_enable_ipv6(struct connman_ipconfig *ipconfig, + bool forced); +int __connman_ipconfig_set_ipv6_support(bool enable); int __connman_ipconfig_init(void); void __connman_ipconfig_cleanup(void); @@ -705,7 +708,8 @@ int __connman_network_disconnect(struct connman_network *network); int __connman_network_clear_ipconfig(struct connman_network *network, struct connman_ipconfig *ipconfig); int __connman_network_enable_ipconfig(struct connman_network *network, - struct connman_ipconfig *ipconfig); + struct connman_ipconfig *ipconfig, + bool force); const char *__connman_network_get_type(struct connman_network *network); const char *__connman_network_get_group(struct connman_network *network); @@ -761,6 +765,8 @@ void __connman_provider_list(DBusMessageIter *iter, void *user_data); bool __connman_provider_is_immutable(struct connman_provider *provider); int __connman_provider_create_and_connect(DBusMessage *msg); const char * __connman_provider_get_ident(struct connman_provider *provider); +const char * __connman_provider_get_transport_ident( + struct connman_provider *provider); int __connman_provider_indicate_state(struct connman_provider *provider, enum connman_provider_state state); int __connman_provider_indicate_error(struct connman_provider *provider, @@ -768,6 +774,8 @@ int __connman_provider_indicate_error(struct connman_provider *provider, int __connman_provider_connect(struct connman_provider *provider, const char *dbus_sender); int __connman_provider_remove_by_path(const char *path); +int __connman_provider_toggle_transport_ipv6(struct connman_provider *provider, + bool disable); void __connman_provider_cleanup(void); int __connman_provider_init(void); @@ -805,6 +813,8 @@ struct connman_ipconfig *__connman_service_get_ipconfig( struct connman_service *service, int family); void __connman_service_notify_ipv4_configuration( struct connman_service *service); +void __connman_service_notify_ipv6_configuration( + struct connman_service *service); bool __connman_service_is_connected_state(struct connman_service *service, enum connman_ipconfig_type type); const char *__connman_service_get_path(struct connman_service *service); diff --git a/connman/src/dnsproxy.c b/connman/src/dnsproxy.c index 9637f4c20..6f1fc48d2 100644 --- a/connman/src/dnsproxy.c +++ b/connman/src/dnsproxy.c @@ -2569,8 +2569,6 @@ static struct server_data *create_server(int index, { struct server_data *data; struct addrinfo hints, *rp; - struct connman_service *service; - GSList *vpn_index_list; int ret; DBG("index %d server %s", index, server); @@ -2645,9 +2643,6 @@ static struct server_data *create_server(int index, return NULL; } - service = connman_service_get_default(); - vpn_index_list = __connman_service_get_depending_vpn_index(service); - if (protocol == IPPROTO_UDP) { if (__connman_service_index_is_default(data->index) || __connman_service_index_is_split_routing( @@ -2656,19 +2651,11 @@ static struct server_data *create_server(int index, DBG("Adding DNS server %s", data->server); enable_fallback(false); - } else if (vpn_index_list && g_slist_index(vpn_index_list, - GINT_TO_POINTER(data->index)) != -1) { - data->enabled = true; - DBG("Adding DNS server of depending VPN %s", - data->server); } server_list = g_slist_append(server_list, data); } - if (vpn_index_list) - g_slist_free(vpn_index_list); - return data; } @@ -2896,7 +2883,7 @@ static void dnsproxy_default_changed(struct connman_service *service) bool server_enabled = false; GSList *list; int index; - GSList *vpn_index_list; + int vpn_index; DBG("service %p", service); @@ -2913,7 +2900,12 @@ static void dnsproxy_default_changed(struct connman_service *service) if (index < 0) return; - vpn_index_list = __connman_service_get_depending_vpn_index(service); + /* + * In case non-split-routed VPN is set as split routed the DNS servers + * the VPN must be enabled as well, when the transport becomes the + * default service. + */ + vpn_index = __connman_connection_get_vpn_index(index); for (list = server_list; list; list = list->next) { struct server_data *data = list->data; @@ -2922,10 +2914,8 @@ static void dnsproxy_default_changed(struct connman_service *service) DBG("Enabling DNS server %s", data->server); data->enabled = true; server_enabled = true; - } else if (vpn_index_list && g_slist_index(vpn_index_list, - GINT_TO_POINTER(data->index)) != -1) { - DBG("Enabling DNS server of depending VPN %s", - data->server); + } else if (data->index == vpn_index) { + DBG("Enabling DNS server of VPN %s", data->server); data->enabled = true; } else { DBG("Disabling DNS server %s", data->server); @@ -2933,9 +2923,6 @@ static void dnsproxy_default_changed(struct connman_service *service) } } - if (vpn_index_list) - g_slist_free(vpn_index_list); - if (!server_enabled) enable_fallback(true); @@ -4031,4 +4018,9 @@ void __connman_dnsproxy_cleanup(void) g_hash_table_destroy(listener_table); g_hash_table_destroy(partial_tcp_req_table); + + if (ipv4_resolve) + g_resolv_unref(ipv4_resolve); + if (ipv6_resolve) + g_resolv_unref(ipv6_resolve); } diff --git a/connman/src/ipconfig.c b/connman/src/ipconfig.c index bde20603a..39722a7e8 100644 --- a/connman/src/ipconfig.c +++ b/connman/src/ipconfig.c @@ -53,8 +53,12 @@ struct connman_ipconfig { struct connman_ipaddress *system; int ipv6_privacy_config; + int ipv6_accept_ra_config; + int ipv6_autoconf_config; char *last_dhcp_address; char **last_dhcpv6_prefixes; + + bool ipv6_force_disabled; }; struct connman_ipdevice { @@ -166,153 +170,215 @@ static const char *scope2str(unsigned char scope) return ""; } -static bool get_ipv6_state(gchar *ifname) +#define PROC_IPV4_CONF_PREFIX "/proc/sys/net/ipv4/conf" +#define PROC_IPV6_CONF_PREFIX "/proc/sys/net/ipv6/conf" + +static int read_conf_value(const char *prefix, const char *ifname, + const char *suffix, int *value) { - int disabled; gchar *path; FILE *f; - bool enabled = false; - - if (!ifname) - path = g_strdup("/proc/sys/net/ipv6/conf/all/disable_ipv6"); - else - path = g_strdup_printf( - "/proc/sys/net/ipv6/conf/%s/disable_ipv6", ifname); + int err; + path = g_build_filename(prefix, ifname ? ifname : "all", suffix, NULL); if (!path) - return enabled; + return -ENOMEM; + errno = 0; f = fopen(path, "r"); + if (!f) { + err = -errno; + } else { + errno = 0; /* Avoid stale errno values with fscanf */ - g_free(path); + err = fscanf(f, "%d", value); + if (err <= 0 && errno) + err = -errno; - if (f) { - if (fscanf(f, "%d", &disabled) > 0) - enabled = !disabled; fclose(f); } - return enabled; + if (err <= 0) + connman_error("failed to read %s", path); + + g_free(path); + + return err; +} + +static int read_ipv4_conf_value(const char *ifname, const char *suffix, + int *value) +{ + return read_conf_value(PROC_IPV4_CONF_PREFIX, ifname, suffix, value); } -static void set_ipv6_state(gchar *ifname, bool enable) +static int read_ipv6_conf_value(const char *ifname, const char *suffix, + int *value) { + return read_conf_value(PROC_IPV6_CONF_PREFIX, ifname, suffix, value); +} + +static int write_conf_value(const char *prefix, const char *ifname, + const char *suffix, int value) { gchar *path; FILE *f; + int rval; - if (!ifname) - path = g_strdup("/proc/sys/net/ipv6/conf/all/disable_ipv6"); - else - path = g_strdup_printf( - "/proc/sys/net/ipv6/conf/%s/disable_ipv6", ifname); - + path = g_build_filename(prefix, ifname ? ifname : "all", suffix, NULL); if (!path) - return; + return -ENOMEM; f = fopen(path, "r+"); + if (!f) { + rval = -errno; + } else { + rval = fprintf(f, "%d", value); + fclose(f); + } + + if (rval <= 0) + connman_error("failed to set %s value %d", path, value); g_free(path); - if (!f) - return; + return rval; +} - if (!enable) - fprintf(f, "1"); - else - fprintf(f, "0"); +static int write_ipv4_conf_value(const char *ifname, const char *suffix, + int value) +{ + return write_conf_value(PROC_IPV4_CONF_PREFIX, ifname, suffix, value); +} - fclose(f); +static int write_ipv6_conf_value(const char *ifname, const char *suffix, + int value) +{ + return write_conf_value(PROC_IPV6_CONF_PREFIX, ifname, suffix, value); } -static int get_ipv6_privacy(gchar *ifname) +static bool get_ipv6_state(gchar *ifname) { - gchar *path; - FILE *f; - int value; + int disabled; + bool enabled = false; - if (!ifname) - return 0; + if (read_ipv6_conf_value(ifname, "disable_ipv6", &disabled) > 0) + enabled = !disabled; - path = g_strdup_printf("/proc/sys/net/ipv6/conf/%s/use_tempaddr", - ifname); + return enabled; +} - if (!path) - return 0; +static int set_ipv6_state(gchar *ifname, bool enable) +{ + int disabled = enable ? 0 : 1; - f = fopen(path, "r"); + DBG("%s %d", ifname, disabled); - g_free(path); + return write_ipv6_conf_value(ifname, "disable_ipv6", disabled); +} - if (!f) +static int get_ipv6_privacy(gchar *ifname) +{ + int value; + + if (!ifname) return 0; - if (fscanf(f, "%d", &value) <= 0) + if (read_ipv6_conf_value(ifname, "use_tempaddr", &value) < 0) value = 0; - fclose(f); - return value; } /* Enable the IPv6 privacy extension for stateless address autoconfiguration. * The privacy extension is described in RFC 3041 and RFC 4941 */ -static void set_ipv6_privacy(gchar *ifname, int value) +static int set_ipv6_privacy(gchar *ifname, int value) { - gchar *path; - FILE *f; - if (!ifname) - return; + return -EINVAL; - path = g_strdup_printf("/proc/sys/net/ipv6/conf/%s/use_tempaddr", - ifname); + if (value < 0) + value = 0; - if (!path) - return; + return write_ipv6_conf_value(ifname, "use_tempaddr", value); +} - if (value < 0) +static bool get_ipv6_autoconf(gchar *ifname) +{ + int value; + + if (read_ipv6_conf_value(ifname, "autoconf", &value) < 0) value = 0; - f = fopen(path, "r+"); + return value == 1; +} - g_free(path); +static int set_ipv6_autoconf(gchar *ifname, bool enable) +{ + int value = enable ? 1 : 0; - if (!f) - return; + DBG("%s %d", ifname, enable); - fprintf(f, "%d", value); - fclose(f); + return write_ipv6_conf_value(ifname, "autoconf", value); } -static int get_rp_filter(void) +static int get_ipv6_accept_ra(gchar *ifname) { - FILE *f; - int value = -EINVAL, tmp; + int value; - f = fopen("/proc/sys/net/ipv4/conf/all/rp_filter", "r"); + if (read_ipv6_conf_value(ifname, "accept_ra", &value) < 0) + value = 0; - if (f) { - if (fscanf(f, "%d", &tmp) == 1) - value = tmp; - fclose(f); + return value; +} + +static int set_ipv6_accept_ra(gchar *ifname, int value) +{ + /* + * 0 = disabled, 1 = accept if forwarding is disabled, 2 = accept and + * overrule forwarding. + */ + switch (value) { + case -1: + value = 0; + /* fall through */ + case 0: + case 1: + case 2: + break; + default: + return -EINVAL; } - return value; + return write_ipv6_conf_value(ifname, "accept_ra", value); } -static void set_rp_filter(int value) +static int get_rp_filter(void) { - FILE *f; + int value; - f = fopen("/proc/sys/net/ipv4/conf/all/rp_filter", "r+"); + if (read_ipv4_conf_value(NULL, "rp_filter", &value) < 0) + value = -EINVAL; - if (!f) - return; + return value; +} - fprintf(f, "%d", value); +static int set_rp_filter(int value) +{ + /* 0 = no validation, 1 = strict mode, 2 = loose mode */ + switch (value) { + case -1: + value = 0; + /* fall through */ + case 0: + case 1: + case 2: + break; + default: + return -EINVAL; + } - fclose(f); + return write_ipv4_conf_value(NULL, "rp_filter", value); } int __connman_ipconfig_set_rp_filter() @@ -356,6 +422,13 @@ bool __connman_ipconfig_ipv6_is_enabled(struct connman_ipconfig *ipconfig) if (!ipconfig) return false; + /* + * Return forced value since kernel can enable LL address for IPv6 + * for handling ICMPv6. + */ + if (ipconfig->ipv6_force_disabled) + return false; + ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(ipconfig->index)); if (!ipdevice) @@ -1165,6 +1238,8 @@ static struct connman_ipconfig *create_ipv6config(int index) ipv6config->index = index; ipv6config->type = CONNMAN_IPCONFIG_TYPE_IPV6; + ipv6config->ipv6_autoconf_config = -1; + ipv6config->ipv6_accept_ra_config = -1; if (!is_ipv6_supported) ipv6config->method = CONNMAN_IPCONFIG_METHOD_OFF; @@ -1517,7 +1592,7 @@ char **__connman_ipconfig_get_dhcpv6_prefixes(struct connman_ipconfig *ipconfig) return ipconfig->last_dhcpv6_prefixes; } -static void disable_ipv6(struct connman_ipconfig *ipconfig) +static int disable_ipv6(struct connman_ipconfig *ipconfig, bool forced) { struct connman_ipdevice *ipdevice; char *ifname; @@ -1527,16 +1602,31 @@ static void disable_ipv6(struct connman_ipconfig *ipconfig) ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(ipconfig->index)); if (!ipdevice) - return; + return -EINVAL; + + DBG("%p forced %s force_disabled %s", ipconfig, forced ? "yes" : "no", + ipconfig->ipv6_force_disabled ? "yes" : "no"); ifname = connman_inet_ifname(ipconfig->index); set_ipv6_state(ifname, false); + if (forced) { + ipconfig->ipv6_autoconf_config = get_ipv6_autoconf(ifname); + set_ipv6_autoconf(ifname, false); + + ipconfig->ipv6_accept_ra_config = get_ipv6_accept_ra(ifname); + set_ipv6_accept_ra(ifname, 0); + + ipconfig->ipv6_force_disabled = true; + } + g_free(ifname); + + return 0; } -static void enable_ipv6(struct connman_ipconfig *ipconfig) +static int enable_ipv6(struct connman_ipconfig *ipconfig, bool forced) { struct connman_ipdevice *ipdevice; char *ifname; @@ -1546,7 +1636,15 @@ static void enable_ipv6(struct connman_ipconfig *ipconfig) ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(ipconfig->index)); if (!ipdevice) - return; + return -EINVAL; + + DBG("%p forced %s force_disabled %s", ipconfig, forced ? "yes" : "no", + ipconfig->ipv6_force_disabled ? "yes" : "no"); + + if (!is_ipv6_supported || (ipconfig->ipv6_force_disabled && !forced)) + return -EOPNOTSUPP; + + ipconfig->ipv6_force_disabled = false; ifname = connman_inet_ifname(ipconfig->index); @@ -1555,23 +1653,51 @@ static void enable_ipv6(struct connman_ipconfig *ipconfig) set_ipv6_state(ifname, true); + if (forced) { + /* Unset (-1) or previously on (1), (re-)enable autoconf. */ + if (ipconfig->ipv6_autoconf_config) + set_ipv6_autoconf(ifname, true); + + /* + * We're enabling IPv6 by force so RAs must be accepted at + * least by following the forwarding. + */ + set_ipv6_accept_ra(ifname, + ipconfig->ipv6_accept_ra_config > 0 ? + ipconfig->ipv6_accept_ra_config : 1); + } + g_free(ifname); + + return 0; } -void __connman_ipconfig_enable_ipv6(struct connman_ipconfig *ipconfig) +int __connman_ipconfig_enable_ipv6(struct connman_ipconfig *ipconfig, + bool forced) { if (!ipconfig || ipconfig->type != CONNMAN_IPCONFIG_TYPE_IPV6) - return; + return -EINVAL; - enable_ipv6(ipconfig); + return enable_ipv6(ipconfig, forced); } -void __connman_ipconfig_disable_ipv6(struct connman_ipconfig *ipconfig) +void __connman_ipconfig_disable_ipv6(struct connman_ipconfig *ipconfig, + bool forced) { if (!ipconfig || ipconfig->type != CONNMAN_IPCONFIG_TYPE_IPV6) return; - disable_ipv6(ipconfig); + disable_ipv6(ipconfig, forced); +} + +int __connman_ipconfig_set_ipv6_support(bool enable) +{ + if (set_ipv6_state(NULL, enable) != 1) + return -EIO; + + is_ipv6_supported = enable ? connman_inet_is_ipv6_supported() : false; + + return 0; } bool __connman_ipconfig_is_usable(struct connman_ipconfig *ipconfig) @@ -1600,6 +1726,7 @@ int __connman_ipconfig_enable(struct connman_ipconfig *ipconfig) bool lower_up = false, lower_down = false; enum connman_ipconfig_type type; char *ifname; + int err; DBG("ipconfig %p", ipconfig); @@ -1647,7 +1774,9 @@ int __connman_ipconfig_enable(struct connman_ipconfig *ipconfig) else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) { ipdevice->config_ipv6 = __connman_ipconfig_ref(ipconfig); - enable_ipv6(ipdevice->config_ipv6); + err = enable_ipv6(ipdevice->config_ipv6, false); + if (err) + return err; } ipconfig_list = g_list_append(ipconfig_list, ipconfig); @@ -1809,9 +1938,7 @@ int __connman_ipconfig_ipv6_set_privacy(struct connman_ipconfig *ipconfig, ipconfig->ipv6_privacy_config = privacy; - enable_ipv6(ipconfig); - - return 0; + return enable_ipv6(ipconfig, false); } void __connman_ipconfig_append_ipv4(struct connman_ipconfig *ipconfig, diff --git a/connman/src/network.c b/connman/src/network.c index 24792f6d4..96023251c 100644 --- a/connman/src/network.c +++ b/connman/src/network.c @@ -45,6 +45,13 @@ #define WIFI_BSSID_LEN 6 #define WIFI_BSSID_STR_LEN 18 +/* + * As per RFC 4861, a host should transmit up to MAX_RTR_SOLICITATIONS(3) + * Router Solicitation messages, each separated by at least + * RTR_SOLICITATION_INTERVAL(4) seconds to obtain RA for IPv6 auto-configuration. + */ +#define RTR_SOLICITATION_INTERVAL 4 + static GSList *network_list = NULL; static GSList *driver_list = NULL; @@ -275,7 +282,8 @@ static int set_connected_dhcp(struct connman_network *network) } static int manual_ipv6_set(struct connman_network *network, - struct connman_ipconfig *ipconfig_ipv6) + struct connman_ipconfig *ipconfig_ipv6, + bool force) { struct connman_service *service; int err; @@ -289,7 +297,9 @@ static int manual_ipv6_set(struct connman_network *network, if (!__connman_ipconfig_get_local(ipconfig_ipv6)) __connman_service_read_ip6config(service); - __connman_ipconfig_enable_ipv6(ipconfig_ipv6); + err = __connman_ipconfig_enable_ipv6(ipconfig_ipv6, force); + if (err) + return err; err = __connman_ipconfig_address_add(ipconfig_ipv6); if (err < 0) { @@ -367,7 +377,7 @@ static int dhcpv6_set_addresses(struct connman_network *network) return err; } -static void autoconf_ipv6_set(struct connman_network *network); +static void autoconf_ipv6_set(struct connman_network *network, bool force); static void dhcpv6_callback(struct connman_network *network, enum __connman_dhcpv6_status status, gpointer data); @@ -389,7 +399,7 @@ static void dhcpv6_renew_callback(struct connman_network *network, stop_dhcpv6(network); /* restart and do solicit again. */ - autoconf_ipv6_set(network); + autoconf_ipv6_set(network, false); break; } } @@ -415,7 +425,7 @@ static void dhcpv6_callback(struct connman_network *network, } else if (status == CONNMAN_DHCPV6_STATUS_RESTART) { stop_dhcpv6(network); - autoconf_ipv6_set(network); + autoconf_ipv6_set(network, false); } else stop_dhcpv6(network); } @@ -438,7 +448,7 @@ static void check_dhcpv6(struct nd_router_advert *reply, DBG("re-send router solicitation %d", network->router_solicit_count); network->router_solicit_count--; - __connman_inet_ipv6_send_rs(network->index, 1, + __connman_inet_ipv6_send_rs(network->index, RTR_SOLICITATION_INTERVAL, check_dhcpv6, network); return; } @@ -551,7 +561,7 @@ int __connman_network_refresh_rs_ipv6(struct connman_network *network, return ret; } -static void autoconf_ipv6_set(struct connman_network *network) +static void autoconf_ipv6_set(struct connman_network *network, bool force) { struct connman_service *service; struct connman_ipconfig *ipconfig; @@ -581,7 +591,8 @@ static void autoconf_ipv6_set(struct connman_network *network) __connman_ipconfig_enable(ipconfig); - __connman_ipconfig_enable_ipv6(ipconfig); + if (__connman_ipconfig_enable_ipv6(ipconfig, force)) + return; __connman_ipconfig_address_remove(ipconfig); @@ -591,7 +602,8 @@ static void autoconf_ipv6_set(struct connman_network *network) /* Try to get stateless DHCPv6 information, RFC 3736 */ network->router_solicit_count = 3; - __connman_inet_ipv6_send_rs(index, 1, check_dhcpv6, network); + __connman_inet_ipv6_send_rs(index, RTR_SOLICITATION_INTERVAL, + check_dhcpv6, network); } static void set_connected(struct connman_network *network) @@ -614,8 +626,8 @@ static void set_connected(struct connman_network *network) DBG("service %p ipv4 %p ipv6 %p", service, ipconfig_ipv4, ipconfig_ipv6); - __connman_network_enable_ipconfig(network, ipconfig_ipv4); - __connman_network_enable_ipconfig(network, ipconfig_ipv6); + __connman_network_enable_ipconfig(network, ipconfig_ipv4, false); + __connman_network_enable_ipconfig(network, ipconfig_ipv6, false); } static void set_disconnected(struct connman_network *network) @@ -722,8 +734,8 @@ static void set_disconnected(struct connman_network *network) * automagically. */ if (ipv6_method == CONNMAN_IPCONFIG_METHOD_AUTO) { - __connman_ipconfig_disable_ipv6(ipconfig_ipv6); - __connman_ipconfig_enable_ipv6(ipconfig_ipv6); + __connman_ipconfig_disable_ipv6(ipconfig_ipv6, false); + __connman_ipconfig_enable_ipv6(ipconfig_ipv6, false); } } @@ -1473,10 +1485,10 @@ int __connman_network_connect(struct connman_network *network) if (!network->device) return -ENODEV; - network->connecting = true; - __connman_device_disconnect(network->device); + network->connecting = true; + err = network->driver->connect(network); if (err < 0) { if (err == -EINPROGRESS) @@ -1567,7 +1579,7 @@ int __connman_network_clear_ipconfig(struct connman_network *network, } int __connman_network_enable_ipconfig(struct connman_network *network, - struct connman_ipconfig *ipconfig) + struct connman_ipconfig *ipconfig, bool force) { int r = 0; enum connman_ipconfig_type type; @@ -1595,19 +1607,20 @@ int __connman_network_enable_ipconfig(struct connman_network *network, break; case CONNMAN_IPCONFIG_METHOD_OFF: - __connman_ipconfig_disable_ipv6(ipconfig); + __connman_ipconfig_disable_ipv6(ipconfig, force); break; case CONNMAN_IPCONFIG_METHOD_AUTO: - autoconf_ipv6_set(network); + autoconf_ipv6_set(network, force); break; case CONNMAN_IPCONFIG_METHOD_FIXED: case CONNMAN_IPCONFIG_METHOD_MANUAL: - r = manual_ipv6_set(network, ipconfig); + r = manual_ipv6_set(network, ipconfig, force); break; case CONNMAN_IPCONFIG_METHOD_DHCP: + __connman_ipconfig_enable_ipv6(ipconfig, force); r = -ENOSYS; break; } diff --git a/connman/src/provider.c b/connman/src/provider.c index 7dcfae4d4..891b9bf0f 100644 --- a/connman/src/provider.c +++ b/connman/src/provider.c @@ -47,6 +47,7 @@ struct connman_provider { int family; struct connman_provider_driver *driver; void *driver_data; + enum connman_ipconfig_method transport_ipv6_method; }; void __connman_provider_append_properties(struct connman_provider *provider, @@ -121,11 +122,173 @@ void connman_provider_unref_debug(struct connman_provider *provider, provider_destruct(provider); } +int __connman_provider_toggle_transport_ipv6(struct connman_provider *provider, + bool disable) +{ + struct connman_service *service; + struct connman_service *transport; + struct connman_network *network; + struct connman_ipconfig *service_ipv6config; + struct connman_ipconfig *transport_ipv6config; + const char *transport_ident; + + if (!provider) + return -EINVAL; + + DBG("provider %p %s", provider, !disable ? "enable" : "disable"); + + service = provider->vpn_service; + if (!service) + return -EINVAL; + + /* + * If a VPN changes from non-split routed to split routed then IPv6 on + * the transport must be re-enabled. + */ + if (__connman_service_is_split_routing(service) && disable) + goto out; + + service_ipv6config = __connman_service_get_ip6config(service); + if (service_ipv6config && + __connman_ipconfig_ipv6_is_enabled(service_ipv6config)) + return 0; + + /* + * Set system IPv6 status if multiple connected technologies are used + * in order to avoid leaking DNS/data to any IPv6 network. That may + * happen when DNS server returns also AAAA record for a request. + */ + if (!connman_setting_get_bool("SingleConnectedTechnology")) { + if (!__connman_ipconfig_set_ipv6_support(!disable)) + connman_warn("cannot disable IPv6 support for " + "provider %p", provider); + } + + transport_ident = __connman_provider_get_transport_ident(provider); + transport = connman_service_lookup_from_identifier(transport_ident); + if (!transport) { + DBG("no transport for %p transport ident %s", service, + transport_ident); + return -ENODEV; + } + + DBG("transport %p", transport); + + network = __connman_service_get_network(transport); + if (!network) { + DBG("no network for transport %p", transport); + return -ENODEV; + } + + transport_ipv6config = __connman_service_get_ip6config(transport); + if (!transport_ipv6config) { + DBG("no ipv6config on transport %p", transport); + return 0; // Not enabled? + } + + if (disable) { + if (!__connman_ipconfig_ipv6_is_enabled(transport_ipv6config)) + return 0; + + provider->transport_ipv6_method = + __connman_ipconfig_get_method( + transport_ipv6config); + DBG("set IPv6 ipconfig %p off (old %d)", transport, + provider->transport_ipv6_method); + + __connman_network_clear_ipconfig(network, + transport_ipv6config); + __connman_ipconfig_gateway_remove(transport_ipv6config); + + __connman_service_ipconfig_indicate_state(transport, + CONNMAN_SERVICE_STATE_DISCONNECT, + CONNMAN_IPCONFIG_TYPE_IPV6); + __connman_ipconfig_address_unset(transport_ipv6config); + + /* + * Disables IPv6 on ipconfig and sets the force_disabled + * as true. + */ + connman_network_set_ipv6_method(network, + CONNMAN_IPCONFIG_METHOD_OFF); + __connman_network_enable_ipconfig(network, + transport_ipv6config, true); + + __connman_service_ipconfig_indicate_state(transport, + CONNMAN_SERVICE_STATE_IDLE, + CONNMAN_IPCONFIG_TYPE_IPV6); + } else { + if (__connman_ipconfig_ipv6_is_enabled(transport_ipv6config)) + return 0; + + switch (provider->transport_ipv6_method) { + case CONNMAN_IPCONFIG_METHOD_UNKNOWN: + case CONNMAN_IPCONFIG_METHOD_OFF: + DBG("IPv6 previously disabled on %p", transport); + goto out; + case CONNMAN_IPCONFIG_METHOD_MANUAL: + case CONNMAN_IPCONFIG_METHOD_AUTO: + case CONNMAN_IPCONFIG_METHOD_FIXED: + case CONNMAN_IPCONFIG_METHOD_DHCP: + break; + } + + DBG("set IPv6 method %d", provider->transport_ipv6_method); + + connman_network_set_ipv6_method(network, + provider->transport_ipv6_method); + __connman_network_enable_ipconfig(network, + transport_ipv6config, true); + } + + __connman_service_notify_ipv6_configuration(transport); + __connman_notifier_ipconfig_changed(transport, transport_ipv6config); + +out: + if (!disable) + provider->transport_ipv6_method = + CONNMAN_IPCONFIG_METHOD_UNKNOWN; + + return 0; +} + static int provider_indicate_state(struct connman_provider *provider, enum connman_service_state state) { + int err; + DBG("state %d", state); + switch (state) { + case CONNMAN_SERVICE_STATE_UNKNOWN: + case CONNMAN_SERVICE_STATE_IDLE: + case CONNMAN_SERVICE_STATE_ASSOCIATION: + case CONNMAN_SERVICE_STATE_CONFIGURATION: + break; + case CONNMAN_SERVICE_STATE_READY: + case CONNMAN_SERVICE_STATE_ONLINE: + if (provider->family != AF_INET) + break; + + err = __connman_provider_toggle_transport_ipv6(provider, true); + if (err) + DBG("cannot disable IPv6 on provider %p transport", + provider); + break; + case CONNMAN_SERVICE_STATE_DISCONNECT: + case CONNMAN_SERVICE_STATE_FAILURE: + if (provider->family != AF_INET) + break; + + err = __connman_provider_toggle_transport_ipv6(provider, + false); + if (err) + DBG("cannot enable IPv6 on provider %p transport", + provider); + + break; + } + __connman_service_ipconfig_indicate_state(provider->vpn_service, state, CONNMAN_IPCONFIG_TYPE_IPV4); @@ -248,6 +411,8 @@ static int set_connected(struct connman_provider *provider, struct connman_service *service = provider->vpn_service; struct connman_ipconfig *ipconfig; + DBG("provider %p", provider); + if (!service) return -ENODEV; @@ -446,6 +611,18 @@ const char *__connman_provider_get_ident(struct connman_provider *provider) return provider->identifier; } +const char * __connman_provider_get_transport_ident( + struct connman_provider *provider) +{ + if (!provider) + return NULL; + + if (provider->driver && provider->driver->get_property) + return provider->driver->get_property(provider, "Transport"); + + return NULL; +} + int connman_provider_set_string(struct connman_provider *provider, const char *key, const char *value) { diff --git a/connman/src/resolver.c b/connman/src/resolver.c index fbe4be731..51d6554a0 100644 --- a/connman/src/resolver.c +++ b/connman/src/resolver.c @@ -84,9 +84,22 @@ static void resolvfile_remove_entries(GList *entries) g_list_free(entries); } -static int resolvfile_export(void) +static bool already_exported(GList *export_list, const char *str) { GList *list; + + for (list = export_list; list; list = g_list_next(list)) { + const char *str0 = list->data; + if (g_strcmp0(str0, str) == 0) + return true; + } + + return false; +} + +static int resolvfile_export(void) +{ + GList *list, *export_list; GString *content; int fd, err; unsigned int count; @@ -100,36 +113,52 @@ static int resolvfile_export(void) * MAXDNSRCH/MAXNS entries are used. */ - for (count = 0, list = g_list_last(resolvfile_list); + export_list = NULL; + for (count = 0, list = g_list_first(resolvfile_list); list && (count < MAXDNSRCH); - list = g_list_previous(list)) { + list = g_list_next(list)) { struct resolvfile_entry *entry = list->data; if (!entry->domain) continue; + if (already_exported(export_list, entry->domain)) + continue; + if (count == 0) g_string_append_printf(content, "search "); g_string_append_printf(content, "%s ", entry->domain); + + export_list = g_list_append(export_list, entry->domain); + count++; } + g_list_free(export_list); + if (count) g_string_append_printf(content, "\n"); - for (count = 0, list = g_list_last(resolvfile_list); + export_list = NULL; + for (count = 0, list = g_list_first(resolvfile_list); list && (count < MAXNS); - list = g_list_previous(list)) { + list = g_list_next(list)) { struct resolvfile_entry *entry = list->data; if (!entry->server) continue; - g_string_append_printf(content, "nameserver %s\n", - entry->server); + if (already_exported(export_list, entry->server)) + continue; + + g_string_append_printf(content, "nameserver %s\n", entry->server); + + export_list = g_list_append(export_list, entry->server); + count++; } + g_list_free(export_list); old_umask = umask(022); @@ -173,7 +202,7 @@ int __connman_resolvfile_append(int index, const char *domain, { struct resolvfile_entry *entry; - DBG("index %d server %s", index, server); + DBG("index %d domain %s server %s", index, domain, server); if (index < 0) return -ENOENT; @@ -196,7 +225,7 @@ int __connman_resolvfile_remove(int index, const char *domain, { GList *list, *matches = NULL; - DBG("index %d server %s", index, server); + DBG("index %d domain %s server %s", index, domain, server); for (list = resolvfile_list; list; list = g_list_next(list)) { struct resolvfile_entry *entry = list->data; diff --git a/connman/src/service.c b/connman/src/service.c index 18d70aba3..6fe9ef3a2 100644 --- a/connman/src/service.c +++ b/connman/src/service.c @@ -322,6 +322,7 @@ struct connman_service { char **nameservers; char **nameservers_config; char **nameservers_auto; + int nameservers_timeout; char **domains; char *hostname; char *domainname; @@ -372,7 +373,6 @@ struct connman_service { GBytes *ssid; struct connman_access_service_policy *policy; char *access; - struct connman_service *depends_on; gboolean disabled; }; @@ -387,6 +387,7 @@ static struct connman_ipconfig *create_ip4config(struct connman_service *service int index, enum connman_ipconfig_method method); static struct connman_ipconfig *create_ip6config(struct connman_service *service, int index); +static void dns_changed(struct connman_service *service); static void vpn_auto_connect(void); static bool is_connecting(struct connman_service *service); @@ -723,79 +724,6 @@ static enum connman_service_proxy_method string2proxymethod(const char *method) return CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN; } -static void set_vpn_dependency(struct connman_service *vpn_service) -{ - struct connman_service *service = connman_service_get_default(); - - if (!vpn_service || !service) - return; - - DBG("vpn service %p default service %p", vpn_service, service); - - if (vpn_service->type != CONNMAN_SERVICE_TYPE_VPN) { - DBG("invalid service type %s", - __connman_service_type2string(vpn_service->type)); - return; - } - - if (vpn_service->depends_on && vpn_service->depends_on != service) { - DBG("dependency already set for %s (depends on %s)", - vpn_service->identifier, - vpn_service->depends_on->identifier); - - if (is_connected(vpn_service) || is_connecting(vpn_service)) { - DBG("disconnect connected or connecting vpn service %p", - vpn_service); - __connman_service_disconnect(vpn_service); - } - } - - switch (service->type) { - case CONNMAN_SERVICE_TYPE_VPN: - if (__connman_service_is_split_routing(service)) { - DBG("VPN as transport cannot be split routed"); - return; - } - - if (!__connman_service_is_split_routing(vpn_service)) { - DBG("non-split routed VPNs cannot depend on another"); - return; - } - case CONNMAN_SERVICE_TYPE_WIFI: - /* fall through */ - case CONNMAN_SERVICE_TYPE_CELLULAR: - /* fall through */ - case CONNMAN_SERVICE_TYPE_ETHERNET: - break; - default: - DBG("VPN cannot depend on service type %s", - __connman_service_type2string(service->type)); - return; - } - - vpn_service->depends_on = service; - - DBG("VPN %p identifier %s set to depend on %p identifier %s", - vpn_service, vpn_service->identifier, - service, service->identifier); -} - -static void unset_vpn_dependency(struct connman_service *vpn_service) -{ - if (!vpn_service || vpn_service->type != CONNMAN_SERVICE_TYPE_VPN) - return; - - DBG("%p", vpn_service); - - if (is_connected(vpn_service) || is_connecting(vpn_service)) { - DBG("cannot unset dependency for connected/connecting %s", - vpn_service->identifier); - return; - } - - vpn_service->depends_on = NULL; -} - void __connman_service_split_routing_changed(struct connman_service *service) { dbus_bool_t split_routing; @@ -817,9 +745,16 @@ void __connman_service_split_routing_changed(struct connman_service *service) void __connman_service_set_split_routing(struct connman_service *service, bool value) { + bool change; + if (service->type != CONNMAN_SERVICE_TYPE_VPN) return; + DBG("%p/%s value %s", service, service->identifier, + value ? "true" : "false"); + + change = service->do_split_routing != value; + service->do_split_routing = value; if (service->do_split_routing) @@ -827,6 +762,19 @@ void __connman_service_set_split_routing(struct connman_service *service, else service->order = 10; + /* + * Change IPv6 on the VPN transport when split routing value changes + * on a connected IPv4 VPN. + */ + if (connman_provider_get_family(service->provider) == AF_INET && + change && is_connected(service)) { + if (__connman_provider_toggle_transport_ipv6(service->provider, + !value)) + DBG("cannot toggle IPv6 on transport of service %p" + "provider %p", service, + service->provider); + } + /* * In order to make sure the value is propagated also when loading the * VPN service signal the value regardless of the value change. @@ -932,7 +880,6 @@ static void service_apply(struct connman_service *service, GKeyFile *keyfile) autoconnect = g_key_file_get_boolean(keyfile, service->identifier, "AutoConnect", &error); - unset_vpn_dependency(service); if (!error) connman_service_set_autoconnect(service, autoconnect); g_clear_error(&error); @@ -1458,8 +1405,6 @@ static bool is_connecting_state(struct connman_service *service, case CONNMAN_SERVICE_STATE_UNKNOWN: case CONNMAN_SERVICE_STATE_IDLE: case CONNMAN_SERVICE_STATE_FAILURE: - if (service->network) - return connman_network_get_connecting(service->network); case CONNMAN_SERVICE_STATE_DISCONNECT: case CONNMAN_SERVICE_STATE_READY: case CONNMAN_SERVICE_STATE_ONLINE: @@ -1485,12 +1430,25 @@ static bool is_connected_state(const struct connman_service *service, break; case CONNMAN_SERVICE_STATE_READY: case CONNMAN_SERVICE_STATE_ONLINE: - if (service->type == CONNMAN_SERVICE_TYPE_VPN && - (!service->depends_on || - !is_connected(service->depends_on))) - return false; + return true; + } + return false; +} + +static bool is_idle(struct connman_service *service) +{ + switch (service->state) { + case CONNMAN_SERVICE_STATE_IDLE: + case CONNMAN_SERVICE_STATE_DISCONNECT: + case CONNMAN_SERVICE_STATE_FAILURE: return true; + case CONNMAN_SERVICE_STATE_UNKNOWN: + case CONNMAN_SERVICE_STATE_ASSOCIATION: + case CONNMAN_SERVICE_STATE_CONFIGURATION: + case CONNMAN_SERVICE_STATE_READY: + case CONNMAN_SERVICE_STATE_ONLINE: + break; } return false; @@ -1506,6 +1464,29 @@ static bool is_connected(struct connman_service *service) return is_connected_state(service, service->state); } + +static int nameservers_changed_cb(void *user_data) +{ + struct connman_service *service = user_data; + + DBG("service %p", service); + + service->nameservers_timeout = 0; + if ((is_idle(service) && !service->nameservers) || + is_connected(service)) + dns_changed(service); + + return FALSE; +} + +static void nameservers_changed(struct connman_service *service) +{ + if (!service->nameservers_timeout) + service->nameservers_timeout = g_timeout_add_seconds(0, + nameservers_changed_cb, + service); +} + static bool nameserver_available(struct connman_service *service, enum connman_ipconfig_type type, const char *ns) @@ -1585,7 +1566,7 @@ static int nameserver_add(struct connman_service *service, enum connman_ipconfig_type type, const char *nameserver) { - int index; + int index, ret; if (!nameserver_available(service, type, nameserver)) return 0; @@ -1594,7 +1575,11 @@ static int nameserver_add(struct connman_service *service, if (index < 0) return -ENXIO; - return connman_resolver_append(index, NULL, nameserver); + ret = connman_resolver_append(index, NULL, nameserver); + if (ret >= 0) + nameservers_changed(service); + + return ret; } static int nameserver_add_all(struct connman_service *service, @@ -1628,7 +1613,7 @@ static int nameserver_remove(struct connman_service *service, enum connman_ipconfig_type type, const char *nameserver) { - int index; + int index, ret; if (!nameserver_available(service, type, nameserver)) return 0; @@ -1637,7 +1622,11 @@ static int nameserver_remove(struct connman_service *service, if (index < 0) return -ENXIO; - return connman_resolver_remove(index, NULL, nameserver); + ret = connman_resolver_remove(index, NULL, nameserver); + if (ret >= 0) + nameservers_changed(service); + + return ret; } static int nameserver_remove_all(struct connman_service *service, @@ -1713,6 +1702,8 @@ int __connman_service_nameserver_append(struct connman_service *service, nameserver_add(service, CONNMAN_IPCONFIG_TYPE_ALL, nameserver); } + nameservers_changed(service); + searchdomain_add_all(service); return 0; @@ -1738,7 +1729,7 @@ int __connman_service_nameserver_remove(struct connman_service *service, if (!nameservers) return 0; - for (i = 0; nameservers && nameservers[i]; i++) + for (i = 0; nameservers[i]; i++) if (g_strcmp0(nameservers[i], nameserver) == 0) { found = true; break; @@ -1904,6 +1895,18 @@ void __connman_service_nameserver_del_routes(struct connman_service *service, nameserver_del_routes(index, service->nameservers, type); } +static void address_updated(struct connman_service *service, + enum connman_ipconfig_type type) +{ + if (is_connected_state(service, service->state) && + service == connman_service_get_default()) { + nameserver_remove_all(service, type); + nameserver_add_all(service, type); + + __connman_timeserver_sync(service); + } +} + static struct connman_stats *stats_get_roaming(struct connman_service *service, gboolean create) { @@ -2568,11 +2571,13 @@ static void settings_changed(struct connman_service *service, { enum connman_ipconfig_type type; + type = __connman_ipconfig_get_config_type(ipconfig); + + __connman_notifier_ipconfig_changed(service, ipconfig); + if (!allow_property_changed(service)) return; - type = __connman_ipconfig_get_config_type(ipconfig); - if (type == CONNMAN_IPCONFIG_TYPE_IPV4) connman_dbus_property_changed_dict(service->path, CONNMAN_SERVICE_INTERFACE, "IPv4", @@ -2581,8 +2586,6 @@ static void settings_changed(struct connman_service *service, connman_dbus_property_changed_dict(service->path, CONNMAN_SERVICE_INTERFACE, "IPv6", append_ipv6, service); - - __connman_notifier_ipconfig_changed(service, ipconfig); } static void ipv4_configuration_changed(struct connman_service *service) @@ -2618,6 +2621,15 @@ static void ipv6_configuration_changed(struct connman_service *service) service); } +void __connman_service_notify_ipv6_configuration( + struct connman_service *service) +{ + if (!service) + return; + + ipv6_configuration_changed(service); +} + static void dns_changed(struct connman_service *service) { if (!allow_property_changed(service)) @@ -3530,49 +3542,6 @@ int __connman_service_get_index(struct connman_service *service) return -1; } -GSList *__connman_service_get_depending_vpn_index( - struct connman_service *service) -{ - int index = -1; - GSList *index_list = NULL; - GList *iter = NULL; - struct connman_service *vpn = NULL; - - if (!service || service->type == CONNMAN_SERVICE_TYPE_VPN) - goto out; - - for (iter = service_list ; iter ; iter = iter->next) { - - vpn = iter->data; - - /* - * Add the index of the VPN to the list of indexes if the VPN - * uses service as transport service and is not being split - * routed. This way dnsproxy can forward messages to the DNS - * servers reported by a split routed VPN for all traffic. - */ - if (vpn->type != CONNMAN_SERVICE_TYPE_VPN || - vpn->depends_on != service) - continue; - - if (!(is_connecting(service) || is_connected(service))) - continue; - - if (!__connman_service_is_split_routing(vpn)) - continue; - - index = connman_provider_get_index(vpn->provider); - if (index <= 0) - continue; - - index_list = g_slist_append(index_list, - GINT_TO_POINTER(index)); - } - -out: - return index_list; -} - void __connman_service_set_hidden(struct connman_service *service) { if (!service || service->hidden) @@ -3588,7 +3557,10 @@ void __connman_service_set_hostname(struct connman_service *service, return; g_free(service->hostname); - service->hostname = g_strdup(hostname); + service->hostname = NULL; + + if (hostname && g_str_is_ascii(hostname)) + service->hostname = g_strdup(hostname); } const char *__connman_service_get_hostname(struct connman_service *service) @@ -3606,7 +3578,10 @@ void __connman_service_set_domainname(struct connman_service *service, return; g_free(service->domainname); - service->domainname = g_strdup(domainname); + service->domainname = NULL; + + if (domainname && g_str_is_ascii(domainname)) + service->domainname = g_strdup(domainname); domain_changed(service); } @@ -4600,6 +4575,7 @@ int __connman_service_reset_ipconfig(struct connman_service *service, *new_state = service->state_ipv6; settings_changed(service, new_ipconfig); + address_updated(service, type); do_auto_connect(service, CONNMAN_SERVICE_CONNECT_REASON_AUTO); } @@ -4926,10 +4902,16 @@ static DBusMessage *set_property(DBusConnection *conn, if (err < 0) { if (is_connected_state(service, state) || is_connecting_state(service, state)) { - __connman_network_enable_ipconfig(service->network, - service->ipconfig_ipv4); - __connman_network_enable_ipconfig(service->network, - service->ipconfig_ipv6); + if (type == CONNMAN_IPCONFIG_TYPE_IPV4) + __connman_network_enable_ipconfig( + service->network, + service->ipconfig_ipv4, + false); + else + __connman_network_enable_ipconfig( + service->network, + service->ipconfig_ipv6, + false); } return __connman_error_failed(msg, -err); @@ -4941,10 +4923,16 @@ static DBusMessage *set_property(DBusConnection *conn, ipv6_configuration_changed(service); if (is_connecting(service) || is_connected(service)) { - __connman_network_enable_ipconfig(service->network, - service->ipconfig_ipv4); - __connman_network_enable_ipconfig(service->network, - service->ipconfig_ipv6); + if (type == CONNMAN_IPCONFIG_TYPE_IPV4) + __connman_network_enable_ipconfig( + service->network, + service->ipconfig_ipv4, + false); + else + __connman_network_enable_ipconfig( + service->network, + service->ipconfig_ipv6, + false); } service_save(service); @@ -5809,18 +5797,10 @@ static DBusMessage *connect_service(DBusConnection *conn, err = __connman_service_connect(service, CONNMAN_SERVICE_CONNECT_REASON_USER); - if (err == -EINPROGRESS) - return NULL; - - if (service->pending) { - dbus_message_unref(service->pending); - service->pending = NULL; - } - - if (err < 0) - return __connman_error_failed(msg, -err); + if (err != -EINPROGRESS) + reply_pending(service, -err); - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); + return NULL; } static DBusMessage *disconnect_service(DBusConnection *conn, @@ -6030,7 +6010,88 @@ static void switch_default_service(struct connman_service *default_service, downgrade_state(downgrade_service); } -static void service_schedule_changed(void); +static struct _services_notify { + int id; + GHashTable *add; + GHashTable *remove; +} *services_notify; + + +static void service_append_added_foreach(gpointer data, gpointer user_data) +{ + struct connman_service *service = data; + DBusMessageIter *iter = user_data; + + if (!service || !service->path) { + DBG("service %p or path is NULL", service); + return; + } + + if (g_hash_table_lookup(services_notify->add, service->path)) { + DBG("new %s", service->path); + + append_struct(service, iter); + g_hash_table_remove(services_notify->add, service->path); + } else { + DBG("changed %s", service->path); + + append_struct_service(iter, NULL, service); + } +} + +static void service_append_ordered(DBusMessageIter *iter, void *user_data) +{ + g_list_foreach(service_list, service_append_added_foreach, iter); +} + +static void append_removed(gpointer key, gpointer value, gpointer user_data) +{ + char *objpath = key; + DBusMessageIter *iter = user_data; + + DBG("removed %s", objpath); + dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &objpath); +} + +static void service_append_removed(DBusMessageIter *iter, void *user_data) +{ + g_hash_table_foreach(services_notify->remove, append_removed, iter); +} + +static gboolean service_send_changed(gpointer data) +{ + DBusMessage *signal; + + DBG(""); + + services_notify->id = 0; + + signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH, + CONNMAN_MANAGER_INTERFACE, "ServicesChanged"); + if (!signal) + return FALSE; + + __connman_dbus_append_objpath_dict_array(signal, + service_append_ordered, NULL); + __connman_dbus_append_objpath_array(signal, + service_append_removed, NULL); + + dbus_connection_send(connection, signal, NULL); + dbus_message_unref(signal); + + g_hash_table_remove_all(services_notify->remove); + g_hash_table_remove_all(services_notify->add); + + return FALSE; +} + +static void service_schedule_changed(void) +{ + if (services_notify->id != 0) + return; + + services_notify->id = g_timeout_add(100, service_send_changed, NULL); +} int __connman_service_move(struct connman_service *service, struct connman_service *target, bool before) @@ -6249,88 +6310,6 @@ static DBusMessage *check_access(DBusConnection *conn, return reply; } -static struct _services_notify { - int id; - GHashTable *add; - GHashTable *remove; -} *services_notify; - -static void service_append_added_foreach(gpointer data, gpointer user_data) -{ - struct connman_service *service = data; - DBusMessageIter *iter = user_data; - - if (!service || !service->path) { - DBG("service %p or path is NULL", service); - return; - } - - if (g_hash_table_lookup(services_notify->add, service->path)) { - DBG("new %s", service->path); - - append_struct(service, iter); - g_hash_table_remove(services_notify->add, service->path); - } else { - DBG("changed %s", service->path); - - append_struct_service(iter, NULL, service); - } -} - -static void service_append_ordered(DBusMessageIter *iter, void *user_data) -{ - g_list_foreach(service_list, service_append_added_foreach, iter); -} - -static void append_removed(gpointer key, gpointer value, gpointer user_data) -{ - char *objpath = key; - DBusMessageIter *iter = user_data; - - DBG("removed %s", objpath); - dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &objpath); -} - -static void service_append_removed(DBusMessageIter *iter, void *user_data) -{ - g_hash_table_foreach(services_notify->remove, append_removed, iter); -} - -static gboolean service_send_changed(gpointer data) -{ - DBusMessage *signal; - - DBG(""); - - services_notify->id = 0; - - signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH, - CONNMAN_MANAGER_INTERFACE, "ServicesChanged"); - if (!signal) - return FALSE; - - __connman_dbus_append_objpath_dict_array(signal, - service_append_ordered, NULL); - __connman_dbus_append_objpath_array(signal, - service_append_removed, NULL); - - dbus_connection_send(connection, signal, NULL); - dbus_message_unref(signal); - - g_hash_table_remove_all(services_notify->remove); - g_hash_table_remove_all(services_notify->add); - - return FALSE; -} - -static void service_schedule_changed(void) -{ - if (services_notify->id != 0) - return; - - services_notify->id = g_timeout_add(100, service_send_changed, NULL); -} - static void service_schedule_added(struct connman_service *service) { DBG("service %p", service); @@ -6427,6 +6406,11 @@ static void service_free(gpointer user_data) reply_pending(service, ENOENT); + if (service->nameservers_timeout) { + g_source_remove(service->nameservers_timeout); + dns_changed(service); + } + __connman_notifier_service_remove(service); /* In our fork, service_schedule_removed() is called by * service_removed() when the service is being removed @@ -6573,8 +6557,6 @@ static void service_initialize(struct connman_service *service) service->provider = NULL; service->wps = false; - - service->depends_on = NULL; } /** @@ -6693,6 +6675,8 @@ static int service_preferred_over(struct connman_service *a, /* Prefer a if connected */ if (is_connected(a)) position_a = 0; + else if (a->order > b->order) + position_a = 0; else position_b = 0; @@ -6703,6 +6687,8 @@ static int service_preferred_over(struct connman_service *a, /* Prefer b if connected */ if (is_connected(b)) position_b = 0; + else if (b->order > a->order) + position_b = 0; else position_a = 0; @@ -6732,6 +6718,40 @@ static int service_preferred_over(struct connman_service *a, return 0; } +static gint service_compare(gconstpointer a, gconstpointer b); + +static gint service_compare_vpn(struct connman_service *a, + struct connman_service *b) +{ + struct connman_provider *provider; + struct connman_service *service; + struct connman_service *transport; + const char *ident; + bool reverse; + + if (a->provider) { + provider = a->provider; + service = b; + reverse = false; + } else if (b->provider) { + provider = b->provider; + service = a; + reverse = true; + } else { + return 0; + } + + ident = __connman_provider_get_transport_ident(provider); + transport = connman_service_lookup_from_identifier(ident); + if (!transport) + return 0; + + if (reverse) + return service_compare(service, transport); + + return service_compare(transport, service); +} + static gint service_compare(gconstpointer a, gconstpointer b) { struct connman_service *service_a = (void *) a; @@ -6755,27 +6775,20 @@ static gint service_compare(gconstpointer a, gconstpointer b) b_connected = is_connected(service_b); if (a_connected && b_connected) { - int preference; - - if (service_a->type == CONNMAN_SERVICE_TYPE_VPN && - service_b->type != CONNMAN_SERVICE_TYPE_VPN && - service_a->depends_on && - service_a->depends_on != service_b && - is_connected(service_a->depends_on)) { - return service_compare(service_a->depends_on, - service_b); - } - - if (service_b->type == CONNMAN_SERVICE_TYPE_VPN && - service_a->type != CONNMAN_SERVICE_TYPE_VPN && - service_b->depends_on && - service_b->depends_on != service_a && - is_connected(service_b->depends_on)) { - return service_compare(service_a, - service_b->depends_on); + int rval; + + /* Compare the VPN transport and the service */ + if ((service_a->type == CONNMAN_SERVICE_TYPE_VPN || + service_b->type == CONNMAN_SERVICE_TYPE_VPN) && + service_b->type != service_a->type) { + rval = service_compare_vpn(service_a, service_b); + if (rval) + return rval; } if (state_a == state_b) { + int preference; + /* Return value only if preferred list is used. */ preference = service_preferred_over(service_a, service_b); @@ -6833,6 +6846,14 @@ static gint service_compare(gconstpointer a, gconstpointer b) return 1; if (service_a->type != service_b->type) { + if (state_a == state_b) { + int preference; + + preference = service_preferred_over(service_a, + service_b); + if (preference) + return preference; + } if (service_a->type == CONNMAN_SERVICE_TYPE_VPN) return -1; @@ -7480,19 +7501,12 @@ static int service_indicate_state(struct connman_service *service) case CONNMAN_SERVICE_STATE_IDLE: if (old_state != CONNMAN_SERVICE_STATE_DISCONNECT) __connman_service_disconnect(service); - - if (service->type == CONNMAN_SERVICE_TYPE_VPN) - unset_vpn_dependency(service); break; case CONNMAN_SERVICE_STATE_ASSOCIATION: - if (service->type == CONNMAN_SERVICE_TYPE_VPN) - set_vpn_dependency(service); break; case CONNMAN_SERVICE_STATE_CONFIGURATION: - if (service->type == CONNMAN_SERVICE_TYPE_VPN) - set_vpn_dependency(service); break; case CONNMAN_SERVICE_STATE_READY: @@ -7500,9 +7514,6 @@ static int service_indicate_state(struct connman_service *service) service_set_new_service(service, false); - if (service->type == CONNMAN_SERVICE_TYPE_VPN) - set_vpn_dependency(service); - default_changed(); def_service = connman_service_get_default(); @@ -7530,7 +7541,6 @@ static int service_indicate_state(struct connman_service *service) update_modified(service); service_save(service); - dns_changed(service); domain_changed(service); proxy_changed(service); @@ -7540,7 +7550,7 @@ static int service_indicate_state(struct connman_service *service) method = __connman_ipconfig_get_method(service->ipconfig_ipv6); if (method == CONNMAN_IPCONFIG_METHOD_OFF) __connman_ipconfig_disable_ipv6( - service->ipconfig_ipv6); + service->ipconfig_ipv6, false); if (connman_setting_get_bool("SingleConnectedTechnology")) single_connected_tech(service); @@ -7564,10 +7574,10 @@ static int service_indicate_state(struct connman_service *service) __connman_wpad_stop(service); - dns_changed(service); domain_changed(service); proxy_changed(service); + /* * Previous services which are connected and which states * are set to online should reset relevantly ipconfig_state @@ -7594,15 +7604,14 @@ static int service_indicate_state(struct connman_service *service) service_retry_connect, service); } - if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER && + if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER) { connman_agent_report_error(service, service->path, - error2string(service->error), - report_error_cb, - get_dbus_sender(service), - NULL) == -EINPROGRESS) - return 0; + error2string(service->error), + report_error_cb, + get_dbus_sender(service), + NULL); + } service_complete(service); - break; } @@ -8116,9 +8125,7 @@ static int service_connect(struct connman_service *service) case CONNMAN_SERVICE_TYPE_GADGET: case CONNMAN_SERVICE_TYPE_BLUETOOTH: case CONNMAN_SERVICE_TYPE_CELLULAR: - break; case CONNMAN_SERVICE_TYPE_VPN: - set_vpn_dependency(service); break; case CONNMAN_SERVICE_TYPE_WIFI: switch (service->security) { @@ -8247,10 +8254,8 @@ int __connman_service_connect(struct connman_service *service, case CONNMAN_SERVICE_TYPE_GADGET: case CONNMAN_SERVICE_TYPE_BLUETOOTH: case CONNMAN_SERVICE_TYPE_CELLULAR: - case CONNMAN_SERVICE_TYPE_WIFI: - break; case CONNMAN_SERVICE_TYPE_VPN: - set_vpn_dependency(service); + case CONNMAN_SERVICE_TYPE_WIFI: break; } @@ -8326,7 +8331,6 @@ int __connman_service_connect(struct connman_service *service, return err; } - reply_pending(service, -err); } return err; @@ -8341,8 +8345,6 @@ int __connman_service_disconnect(struct connman_service *service) service->connect_reason = CONNMAN_SERVICE_CONNECT_REASON_NONE; service->proxy = CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN; - unset_vpn_dependency(service); - __connman_wispr_stop(service); stop_recurring_online_check(service); @@ -8763,6 +8765,7 @@ static void service_ip_bound(struct connman_ipconfig *ipconfig, CONNMAN_IPCONFIG_TYPE_IPV6); settings_changed(service, ipconfig); + address_updated(service, type); } static void service_ip_release(struct connman_ipconfig *ipconfig, @@ -9246,7 +9249,6 @@ bool __connman_service_create_from_network(struct connman_network *network) service->favorite = true; break; case CONNMAN_SERVICE_TYPE_VPN: - unset_vpn_dependency(service); break; } @@ -9435,7 +9437,6 @@ __connman_service_create_from_provider(struct connman_provider *provider) } service->type = CONNMAN_SERVICE_TYPE_VPN; - unset_vpn_dependency(service); /* Try to load modifiable values from storage. If config does not * exist set current time as modify time if service is saved as is. diff --git a/connman/unit/test-service.c b/connman/unit/test-service.c index 90f6f2671..2a435c357 100644 --- a/connman/unit/test-service.c +++ b/connman/unit/test-service.c @@ -98,6 +98,8 @@ struct connman_provider { struct connman_service *vpn_service; int index; char *identifier; + int family; + const char *service_ident; }; int connman_provider_get_index(struct connman_provider *provider) @@ -105,6 +107,11 @@ int connman_provider_get_index(struct connman_provider *provider) return provider ? provider->index : -1; } +int connman_provider_get_family(struct connman_provider *provider) +{ + return provider ? provider->family : PF_UNSPEC; +} + int __connman_provider_create_and_connect(DBusMessage *msg) { return 0; } int connman_provider_disconnect(struct connman_provider *provider) { return 0; } @@ -142,13 +149,19 @@ const char *__connman_provider_get_ident(struct connman_provider *provider) return provider ? provider->identifier : NULL; } +const char * __connman_provider_get_transport_ident( + struct connman_provider *provider) +{ + return provider ? provider->service_ident : NULL; +} + void __connman_provider_append_properties(struct connman_provider *provider, DBusMessageIter *iter) { return; } -static int provider_count = 0; +static gint index_counter = 0; static struct connman_provider *provider_new(void) { @@ -160,13 +173,14 @@ static struct connman_provider *provider_new(void) return NULL; } - provider->index = provider_count; + provider->index = ++index_counter; provider->identifier = NULL; + provider->family = AF_INET; return provider; } - +static int provider_count = 0; struct connman_provider *connman_provider_get(const char *identifier) { @@ -194,6 +208,12 @@ const char *connman_provider_get_string(struct connman_provider *provider, return provider->name; } +int __connman_provider_toggle_transport_ipv6(struct connman_provider *provider, + bool disable) +{ + return 0; +} + //typedef struct connman_stats; int ptr2 = 0x87654321; @@ -248,10 +268,101 @@ int __connman_wispr_start(struct connman_service *service, void __connman_wispr_stop(struct connman_service *service) { return; } +static GHashTable *phy_vpn_table = NULL; + +int __connman_connection_get_vpn_index(int phy_index) +{ + GHashTableIter iter; + gpointer value, key; + + DBG("%d", phy_index); + + g_hash_table_iter_init(&iter, phy_vpn_table); + + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct connman_service *service = key; + struct connman_service *transport = value; + + if (__connman_service_get_index(transport) != phy_index) + continue; + + return __connman_service_get_index(service); + } + + return -1; +} + +int __connman_connection_get_vpn_phy_index(int vpn_index) +{ + GHashTableIter iter; + gpointer value, key; + + DBG("%d", vpn_index); + + g_hash_table_iter_init(&iter, phy_vpn_table); + + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct connman_service *service = key; + struct connman_service *transport = value; + + if (__connman_service_get_index(service) != vpn_index) + continue; + + return __connman_service_get_index(transport); + } + + return -1; +} +bool __connman_connection_update_gateway(void) +{ + return true; +} + +int __connman_connection_gateway_add(struct connman_service *service, + const char *gateway, + enum connman_ipconfig_type type, + const char *peer) +{ + return 0; +} + +void __connman_connection_gateway_remove(struct connman_service *service, + enum connman_ipconfig_type type) +{ + return; +} /* EOD - end of dummies */ +static void set_vpn_phy(struct connman_service *service) +{ + struct connman_service *transport; + + transport = get_connected_default_service(); + if (!transport) { + return; // May be unset, not connected + } + + DBG("VPN %p -> %p", service, transport); + + g_assert(phy_vpn_table); + + g_hash_table_replace(phy_vpn_table, service, transport); + + if (service->provider) + service->provider->service_ident = transport->identifier; +} + +static struct connman_service *get_vpn_transport( + struct connman_service *service) +{ + if (!phy_vpn_table) + return NULL; + + return g_hash_table_lookup(phy_vpn_table, service); +} + static gint ident_counter = 0; static char *create_ident(enum connman_service_type type) @@ -340,14 +451,19 @@ static void setup_network_or_provider(struct connman_service *service) ident = g_strdup_printf("%s_%s", prefixes[prefix_pos], service->identifier); - if (type != CONNMAN_NETWORK_TYPE_UNKNOWN) + if (type != CONNMAN_NETWORK_TYPE_UNKNOWN) { service->network = connman_network_create(ident, type); + connman_network_set_index(service->network, ++index_counter); + } if (service->type == CONNMAN_SERVICE_TYPE_VPN) { service->provider = connman_provider_get(ident); service->provider->vpn_service = service; } + connman_service_create_ip4config(service, index_counter); + connman_service_create_ip6config(service, index_counter); + g_free(ident); } @@ -368,13 +484,16 @@ static void add_service_type(enum connman_service_type type, if (type == CONNMAN_SERVICE_TYPE_VPN) { if (state == CONNMAN_SERVICE_STATE_READY) - service->depends_on = get_connected_default_service(); + set_vpn_phy(service); + /*service->vpn_transport = + get_connected_default_service();*/ __connman_service_set_split_routing(service, split_routing); } service_list = g_list_insert_sorted(service_list, service, service_compare); + g_hash_table_replace(service_hash, service->identifier, service); g_free(ident); } @@ -417,8 +536,9 @@ static void print_test_service(struct connman_service *service, void *user_data) if (!service) return; - printf("%p %-56s %-3d %-6s %-10s %-16s %-12s %u\n", - service, service->identifier, service->order, + printf("%p %2d %-56s %-3d %-6s %-10s %-16s %-12s %u\n", + service, __connman_service_get_index(service), + service->identifier, service->order, is_connected(service) ? "true" : "false", is_available(service) ? "available" : "non-avail", state2string(service->state), @@ -536,15 +656,21 @@ static void service_order_check(bool(*order_cb)(struct connman_service *a, * which need to be connected for state comparison to * to work. */ - if(a->depends_on && is_connected(a) && - is_connected(b)) { + if(is_connected(a) && is_connected(b)) { /* Both are VPNs */ - if (b->depends_on) - g_assert(a->depends_on->state >= - b->depends_on->state); + if (b->type == CONNMAN_SERVICE_TYPE_VPN) { + struct connman_service* a_transport; + struct connman_service* b_transport; + + a_transport = get_vpn_transport(a); + b_transport = get_vpn_transport(b); + + g_assert_cmpint(a_transport->state, >=, + b_transport->state); /* Check order only if b is transport */ - else + } else { g_assert_true(order_cb(a,b)); + } } continue; @@ -617,9 +743,26 @@ static void clean_service_list() service_list = NULL; } +static void test_init() +{ + service_hash = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, NULL); + ident_counter = index_counter = 0; + phy_vpn_table = g_hash_table_new(g_direct_hash, g_direct_equal); + g_assert(phy_vpn_table); +} + +static void test_cleanup() +{ + g_hash_table_destroy(service_hash); + g_hash_table_destroy(phy_vpn_table); + clean_service_list(); +} + static void test_service_sort_full_positive() { - ident_counter = 0; + test_init(); + add_services(); service_list = g_list_sort(service_list, service_compare); @@ -627,7 +770,7 @@ static void test_service_sort_full_positive() print_services(); service_order_check(check_type_order); - clean_service_list(); + test_cleanup(); } void test_service_sort_with_preferred_list1() @@ -639,7 +782,8 @@ void test_service_sort_with_preferred_list1() CONNMAN_SERVICE_TYPE_UNKNOWN, }; - ident_counter = 0; + test_init(); + preferred_list = list; add_service_type(CONNMAN_SERVICE_TYPE_WIFI, @@ -678,7 +822,8 @@ void test_service_sort_with_preferred_list2() CONNMAN_SERVICE_TYPE_UNKNOWN, }; - ident_counter = 0; + test_init(); + preferred_list = list; add_services(); @@ -705,12 +850,13 @@ void test_service_sort_with_preferred_list2() print_services(); service_order_check(check_preferred_type_order); - clean_service_list(); + test_cleanup(); } void test_service_sort_without_preferred_list() { - ident_counter = 0; + test_init(); + add_services(); add_service_type(CONNMAN_SERVICE_TYPE_WIFI, @@ -735,12 +881,12 @@ void test_service_sort_without_preferred_list() print_services(); service_order_check(check_type_order); - clean_service_list(); + test_cleanup(); } void test_service_sort_vpn_default() { - ident_counter = 0; + test_init(); add_service_type(CONNMAN_SERVICE_TYPE_WIFI, CONNMAN_SERVICE_STATE_ONLINE, false,50); @@ -764,7 +910,7 @@ void test_service_sort_vpn_default() print_services(); service_order_check(check_type_order); - clean_service_list(); + test_cleanup(); } void test_service_sort_vpn_default_preferred_list() @@ -777,7 +923,8 @@ void test_service_sort_vpn_default_preferred_list() CONNMAN_SERVICE_TYPE_UNKNOWN, }; - ident_counter = 0; + test_init(); + preferred_list = list; add_service_type(CONNMAN_SERVICE_TYPE_WIFI, @@ -804,12 +951,12 @@ void test_service_sort_vpn_default_preferred_list() print_services(); service_order_check(check_preferred_type_order); - clean_service_list(); + test_cleanup(); } void test_service_sort_vpn_split() { - ident_counter = 0; + test_init(); add_service_type(CONNMAN_SERVICE_TYPE_CELLULAR, CONNMAN_SERVICE_STATE_ONLINE, false, 0); @@ -829,7 +976,7 @@ void test_service_sort_vpn_split() print_services(); service_order_check(check_type_order); - clean_service_list(); + test_cleanup(); } void test_service_sort_vpn_split_preferred_list() @@ -842,7 +989,8 @@ void test_service_sort_vpn_split_preferred_list() CONNMAN_SERVICE_TYPE_UNKNOWN, }; - ident_counter = 0; + test_init(); + preferred_list = list; add_service_type(CONNMAN_SERVICE_TYPE_WIFI, @@ -867,16 +1015,17 @@ void test_service_sort_vpn_split_preferred_list() print_services(); service_order_check(check_preferred_type_order); - clean_service_list(); + test_cleanup(); } void test_service_sort_single_tech_types() { - ident_counter = 0; enum connman_service_type type; enum connman_service_state state; + test_init(); + for (type = CONNMAN_SERVICE_TYPE_UNKNOWN; type < MAX_CONNMAN_SERVICE_TYPES; type++) { for (state = CONNMAN_SERVICE_STATE_UNKNOWN + 1; @@ -896,6 +1045,8 @@ void test_service_sort_single_tech_types() clean_service_list(); } + + test_cleanup(); } int rmdir_r(const gchar* path) @@ -994,6 +1145,7 @@ int main(int argc, char **argv) g_assert_cmpint(__connman_storage_create_dir(VPN_STORAGEDIR, __connman_storage_dir_mode()), ==, 0); g_assert_cmpint(__connman_notifier_init(), ==, 0); + g_assert_cmpint(__connman_ipconfig_init(), ==, 0); connection = (DBusConnection*)&ptr; dbus_path_data = g_hash_table_new(g_str_hash, g_str_equal);