diff options
author | Seonah Moon <seonah1.moon@samsung.com> | 2018-01-23 14:50:50 +0900 |
---|---|---|
committer | Seonah Moon <seonah1.moon@samsung.com> | 2018-01-23 14:50:55 +0900 |
commit | 30602f521a85820a9f6b7ac04876400e00c68b15 (patch) | |
tree | f035a4fcc014a034f3b492886d1e8395f327fd25 /src | |
parent | a079cfe6f815f8c69055de834d1ccbdf1fd94ba7 (diff) | |
parent | 9362752a471a5c892d679548fbf2828d5fc5684b (diff) | |
download | connman-30602f521a85820a9f6b7ac04876400e00c68b15.tar.gz connman-30602f521a85820a9f6b7ac04876400e00c68b15.tar.bz2 connman-30602f521a85820a9f6b7ac04876400e00c68b15.zip |
Updated connman to version 1.35
Change-Id: I13526fbf80296a79be15548fc226a308941ac9ec
Signed-off-by: Taesub Kim <taesub.kim@samsung.com>
Diffstat (limited to 'src')
-rwxr-xr-x | src/6to4.c | 2 | ||||
-rwxr-xr-x | src/agent-connman.c | 114 | ||||
-rwxr-xr-x | src/agent.c | 17 | ||||
-rwxr-xr-x | src/bridge.c | 2 | ||||
-rwxr-xr-x | src/config.c | 148 | ||||
-rwxr-xr-x | src/connection.c | 93 | ||||
-rwxr-xr-x | src/connman.h | 54 | ||||
-rw-r--r-- | src/connmand-wait-online.c | 461 | ||||
-rwxr-xr-x | src/device.c | 48 | ||||
-rwxr-xr-x | src/dhcp.c | 95 | ||||
-rwxr-xr-x | src/dhcpv6.c | 17 | ||||
-rwxr-xr-x | src/dnsproxy.c | 230 | ||||
-rw-r--r--[-rwxr-xr-x] | src/firewall-iptables.c (renamed from src/firewall.c) | 265 | ||||
-rw-r--r-- | src/firewall-nftables.c | 1133 | ||||
-rwxr-xr-x | src/inet.c | 357 | ||||
-rwxr-xr-x | src/ipconfig.c | 105 | ||||
-rwxr-xr-x | src/ippool.c | 45 | ||||
-rwxr-xr-x | src/iptables.c | 89 | ||||
-rwxr-xr-x | src/log.c | 2 | ||||
-rwxr-xr-x | src/main.c | 87 | ||||
-rwxr-xr-x | src/main.conf | 20 | ||||
-rwxr-xr-x | src/nat.c | 64 | ||||
-rwxr-xr-x | src/network.c | 188 | ||||
-rwxr-xr-x | src/ntp.c | 205 | ||||
-rwxr-xr-x | src/peer.c | 9 | ||||
-rwxr-xr-x | src/peer_service.c | 3 | ||||
-rwxr-xr-x | src/provider.c | 15 | ||||
-rwxr-xr-x | src/proxy.c | 2 | ||||
-rwxr-xr-x | src/resolver.c | 78 | ||||
-rwxr-xr-x | src/rfkill.c | 2 | ||||
-rwxr-xr-x | src/rtnl.c | 16 | ||||
-rwxr-xr-x | src/service.c | 1086 | ||||
-rwxr-xr-x | src/session.c | 427 | ||||
-rwxr-xr-x | src/stats.c | 50 | ||||
-rwxr-xr-x | src/storage.c | 2 | ||||
-rwxr-xr-x | src/task.c | 3 | ||||
-rwxr-xr-x | src/technology.c | 97 | ||||
-rwxr-xr-x | src/tethering.c | 18 | ||||
-rwxr-xr-x | src/timeserver.c | 13 | ||||
-rwxr-xr-x | src/util.c | 15 | ||||
-rwxr-xr-x | src/wispr.c | 7 | ||||
-rwxr-xr-x | src/wpad.c | 9 |
42 files changed, 4353 insertions, 1340 deletions
@@ -63,7 +63,7 @@ static int tunnel_create(struct in_addr *addr) { struct ip_tunnel_parm p; struct ifreq ifr; - int fd = -1; + int fd; int ret; /* ip tunnel add tun6to4 mode sit remote any local 1.2.3.4 ttl 64 */ diff --git a/src/agent-connman.c b/src/agent-connman.c index 177cbe0a..e4850a8f 100755 --- a/src/agent-connman.c +++ b/src/agent-connman.c @@ -100,73 +100,101 @@ static void request_input_passphrase_reply(DBusMessage *reply, void *user_data) DBusMessageIter entry, value; dbus_message_iter_recurse(&dict, &entry); - if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) + if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) { + error = CONNMAN_ERROR_INTERFACE ".InvalidArguments"; break; + } dbus_message_iter_get_basic(&entry, &key); if (g_str_equal(key, "Identity")) { dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) - != DBUS_TYPE_VARIANT) + != DBUS_TYPE_VARIANT) { + error = CONNMAN_ERROR_INTERFACE ".InvalidArguments"; break; + } + dbus_message_iter_recurse(&entry, &value); + if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_STRING) { + error = CONNMAN_ERROR_INTERFACE ".InvalidArguments"; + break; + } + dbus_message_iter_get_basic(&value, &identity); } else if (g_str_equal(key, "Passphrase")) { dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) - != DBUS_TYPE_VARIANT) + != DBUS_TYPE_VARIANT) { + error = CONNMAN_ERROR_INTERFACE ".InvalidArguments"; break; + } + dbus_message_iter_recurse(&entry, &value); + if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_STRING) { + error = CONNMAN_ERROR_INTERFACE ".InvalidArguments"; + break; + } + dbus_message_iter_get_basic(&value, &passphrase); } else if (g_str_equal(key, "WPS")) { - wps = true; dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) - != DBUS_TYPE_VARIANT) + != DBUS_TYPE_VARIANT) { + error = CONNMAN_ERROR_INTERFACE ".InvalidArguments"; break; + } + dbus_message_iter_recurse(&entry, &value); + if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_STRING) { + error = CONNMAN_ERROR_INTERFACE ".InvalidArguments"; + break; + } + + wps = true; dbus_message_iter_get_basic(&value, &wpspin); break; } else if (g_str_equal(key, "Name")) { dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) - != DBUS_TYPE_VARIANT) + != DBUS_TYPE_VARIANT) { + error = CONNMAN_ERROR_INTERFACE ".InvalidArguments"; break; + } + dbus_message_iter_recurse(&entry, &value); + if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_STRING) { + error = CONNMAN_ERROR_INTERFACE ".InvalidArguments"; + break; + } + dbus_message_iter_get_basic(&value, &name); name_len = strlen(name); } else if (g_str_equal(key, "SSID")) { -#if defined TIZEN_EXT - DBusMessageIter array; -#endif + DBusMessageIter array_iter; + dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) - != DBUS_TYPE_VARIANT) - break; -#if defined TIZEN_EXT - dbus_message_iter_recurse(&entry, &array); - if (dbus_message_iter_get_arg_type(&array) - != DBUS_TYPE_ARRAY) - break; - dbus_message_iter_recurse(&array, &value); - if (dbus_message_iter_get_arg_type(&value) - != DBUS_TYPE_BYTE) + != DBUS_TYPE_VARIANT) { + error = CONNMAN_ERROR_INTERFACE ".InvalidArguments"; break; -#else - + } dbus_message_iter_recurse(&entry, &value); if (dbus_message_iter_get_arg_type(&value) - != DBUS_TYPE_VARIANT) + != DBUS_TYPE_ARRAY) { + error = CONNMAN_ERROR_INTERFACE ".InvalidArguments"; break; - if (dbus_message_iter_get_element_type(&value) - != DBUS_TYPE_VARIANT) + } + dbus_message_iter_recurse(&value, &array_iter); + if (dbus_message_iter_get_arg_type(&array_iter) + != DBUS_TYPE_BYTE) { + error = CONNMAN_ERROR_INTERFACE ".InvalidArguments"; break; -#endif - dbus_message_iter_get_fixed_array(&value, &name, + } + dbus_message_iter_get_fixed_array(&array_iter, &name, &name_len); } dbus_message_iter_next(&dict); @@ -417,17 +445,33 @@ static void request_input_login_reply(DBusMessage *reply, void *user_data) if (g_str_equal(key, "Username")) { dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) - != DBUS_TYPE_VARIANT) + != DBUS_TYPE_VARIANT) { + error = CONNMAN_ERROR_INTERFACE ".InvalidArguments"; break; + } + dbus_message_iter_recurse(&entry, &value); + if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_STRING) { + error = CONNMAN_ERROR_INTERFACE ".InvalidArguments"; + break; + } + dbus_message_iter_get_basic(&value, &username); } else if (g_str_equal(key, "Password")) { dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) != - DBUS_TYPE_VARIANT) + DBUS_TYPE_VARIANT) { + error = CONNMAN_ERROR_INTERFACE ".InvalidArguments"; break; + } + dbus_message_iter_recurse(&entry, &value); + if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_STRING) { + error = CONNMAN_ERROR_INTERFACE ".InvalidArguments"; + break; + } + dbus_message_iter_get_basic(&value, &password); } @@ -723,8 +767,10 @@ static void request_peer_authorization_reply(DBusMessage *reply, DBusMessageIter entry, value; dbus_message_iter_recurse(&dict, &entry); - if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) + if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) { + error = CONNMAN_ERROR_INTERFACE ".InvalidArguments"; break; + } dbus_message_iter_get_basic(&entry, &key); @@ -733,9 +779,17 @@ static void request_peer_authorization_reply(DBusMessage *reply, dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) - != DBUS_TYPE_VARIANT) + != DBUS_TYPE_VARIANT) { + error = CONNMAN_ERROR_INTERFACE ".InvalidArguments"; break; + } + dbus_message_iter_recurse(&entry, &value); + if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_STRING) { + error = CONNMAN_ERROR_INTERFACE ".InvalidArguments"; + break; + } + dbus_message_iter_get_basic(&value, &wpspin); break; } diff --git a/src/agent.c b/src/agent.c index bdeb0e71..8f7b19ba 100755 --- a/src/agent.c +++ b/src/agent.c @@ -165,12 +165,17 @@ static int send_cancel_request(struct connman_agent *agent, struct connman_agent_request *request) { DBusMessage *message; + const char *interface = NULL; - DBG("send cancel req to %s %s", agent->owner, agent->path); + if (request && request->driver) + interface = request->driver->interface; + + DBG("send cancel req to %s %s iface %s", agent->owner, agent->path, + interface); message = dbus_message_new_method_call(agent->owner, agent->path, - request->driver->interface, + interface, "Cancel"); if (!message) { connman_error("Couldn't allocate D-Bus message"); @@ -519,12 +524,12 @@ void connman_agent_cancel(void *user_context) user_context) { DBG("cancel pending %p", request); + agent->queue = g_list_delete_link(agent->queue, + list); + request->callback(NULL, request->user_data); agent_request_free(request); - - agent->queue = g_list_delete_link(agent->queue, - list); } list = next; @@ -581,7 +586,7 @@ static void agent_release(struct connman_agent *agent, const char *interface) message = dbus_message_new_method_call(agent->owner, agent->path, interface, "Release"); - if (message == NULL) { + if (!message) { connman_error("Couldn't allocate D-Bus message"); return; } diff --git a/src/bridge.c b/src/bridge.c index ba200969..cd2d9cee 100755 --- a/src/bridge.c +++ b/src/bridge.c @@ -56,7 +56,7 @@ static int set_forward_delay(const char *name, unsigned int delay) if (!f) return -errno; - fprintf(f, "%d", delay); + fprintf(f, "%u", delay); fclose(f); diff --git a/src/config.c b/src/config.c index 25dd1748..75cd717f 100755 --- a/src/config.c +++ b/src/config.c @@ -45,7 +45,12 @@ struct connman_config_service { unsigned int ssid_len; char *eap; char *identity; + char *anonymous_identity; char *ca_cert_file; + char *subject_match; + char *altsubject_match; + char *domain_suffix_match; + char *domain_match; char *client_cert_file; char *private_key_file; char *private_key_passphrase; @@ -98,6 +103,11 @@ static bool cleanup = false; #define SERVICE_KEY_PRV_KEY_PASS "PrivateKeyPassphrase" #define SERVICE_KEY_PRV_KEY_PASS_TYPE "PrivateKeyPassphraseType" #define SERVICE_KEY_IDENTITY "Identity" +#define SERVICE_KEY_ANONYMOUS_IDENTITY "AnonymousIdentity" +#define SERVICE_KEY_SUBJECT_MATCH "SubjectMatch" +#define SERVICE_KEY_ALT_SUBJECT_MATCH "AltSubjectMatch" +#define SERVICE_KEY_DOMAIN_SUFF_MATCH "DomainSuffixMatch" +#define SERVICE_KEY_DOMAIN_MATCH "DomainMatch" #define SERVICE_KEY_PHASE2 "Phase2" #define SERVICE_KEY_PASSPHRASE "Passphrase" #define SERVICE_KEY_SECURITY "Security" @@ -129,6 +139,11 @@ static const char *service_possible_keys[] = { SERVICE_KEY_PRV_KEY_PASS, SERVICE_KEY_PRV_KEY_PASS_TYPE, SERVICE_KEY_IDENTITY, + SERVICE_KEY_ANONYMOUS_IDENTITY, + SERVICE_KEY_SUBJECT_MATCH, + SERVICE_KEY_ALT_SUBJECT_MATCH, + SERVICE_KEY_DOMAIN_SUFF_MATCH, + SERVICE_KEY_DOMAIN_MATCH, SERVICE_KEY_PHASE2, SERVICE_KEY_PASSPHRASE, SERVICE_KEY_SECURITY, @@ -220,7 +235,12 @@ free_only: g_free(config_service->ssid); g_free(config_service->eap); g_free(config_service->identity); + g_free(config_service->anonymous_identity); g_free(config_service->ca_cert_file); + g_free(config_service->subject_match); + g_free(config_service->altsubject_match); + g_free(config_service->domain_suffix_match); + g_free(config_service->domain_match); g_free(config_service->client_cert_file); g_free(config_service->private_key_file); g_free(config_service->private_key_passphrase); @@ -655,6 +675,41 @@ static bool load_service(GKeyFile *keyfile, const char *group, service->identity = str; } + str = __connman_config_get_string(keyfile, group, + SERVICE_KEY_ANONYMOUS_IDENTITY, NULL); + if (str) { + g_free(service->anonymous_identity); + service->anonymous_identity = str; + } + + str = __connman_config_get_string(keyfile, group, + SERVICE_KEY_SUBJECT_MATCH, NULL); + if (str) { + g_free(service->subject_match); + service->subject_match = str; + } + + str = __connman_config_get_string(keyfile, group, + SERVICE_KEY_ALT_SUBJECT_MATCH, NULL); + if (str) { + g_free(service->altsubject_match); + service->altsubject_match = str; + } + + str = __connman_config_get_string(keyfile, group, + SERVICE_KEY_DOMAIN_SUFF_MATCH, NULL); + if (str) { + g_free(service->domain_suffix_match); + service->domain_suffix_match = str; + } + + str = __connman_config_get_string(keyfile, group, + SERVICE_KEY_DOMAIN_MATCH, NULL); + if (str) { + g_free(service->domain_match); + service->domain_match = str; + } + str = __connman_config_get_string(keyfile, group, SERVICE_KEY_PHASE2, NULL); if (str) { g_free(service->phase2); @@ -701,7 +756,18 @@ static bool load_service(GKeyFile *keyfile, const char *group, } else service->security = CONNMAN_SERVICE_SECURITY_PSK; - } + } else if (str) { + + if (security != CONNMAN_SERVICE_SECURITY_NONE) { + connman_info("Mismatch no security and " + "setting %s = %s", + SERVICE_KEY_SECURITY, str); + } + service->security = CONNMAN_SERVICE_SECURITY_NONE; + } else + service->security = CONNMAN_SERVICE_SECURITY_NONE; + + g_free(str); service->config_ident = g_strdup(config->ident); service->config_entry = g_strdup_printf("service_%s", service->ident); @@ -894,10 +960,10 @@ static void config_notify_handler(struct inotify_event *event, return; } - if (event->mask & IN_CREATE || event->mask & IN_MOVED_TO) + if (event->mask & (IN_CREATE | IN_MOVED_TO)) create_config(ident); - if (event->mask & IN_MODIFY) { + if (event->mask & (IN_MODIFY | IN_MOVED_TO)) { struct connman_config *config; config = g_hash_table_lookup(config_table, ident); @@ -919,7 +985,7 @@ static void config_notify_handler(struct inotify_event *event, } } - if (event->mask & IN_DELETE) + if (event->mask & (IN_DELETE | IN_MOVED_FROM)) g_hash_table_remove(config_table, ident); } @@ -956,6 +1022,11 @@ char *__connman_config_get_string(GKeyFile *key_file, if (!str) return NULL; + /* passphrases can have spaces in the end */ + if (!g_strcmp0(key, SERVICE_KEY_PASSPHRASE) || + !g_strcmp0(key, SERVICE_KEY_PRV_KEY_PASS)) + return str; + return g_strchomp(str); } @@ -1028,10 +1099,30 @@ static void provision_service_wifi(struct connman_config_service *config, __connman_service_set_string(service, "Identity", config->identity); + if (config->anonymous_identity) + __connman_service_set_string(service, "AnonymousIdentity", + config->anonymous_identity); + if (config->ca_cert_file) __connman_service_set_string(service, "CACertFile", config->ca_cert_file); + if (config->subject_match) + __connman_service_set_string(service, "SubjectMatch", + config->subject_match); + + if (config->altsubject_match) + __connman_service_set_string(service, "AltSubjectMatch", + config->altsubject_match); + + if (config->domain_suffix_match) + __connman_service_set_string(service, "DomainSuffixMatch", + config->domain_suffix_match); + + if (config->domain_match) + __connman_service_set_string(service, "DomainMatch", + config->domain_match); + if (config->client_cert_file) __connman_service_set_string(service, "ClientCertFile", config->client_cert_file); @@ -1333,7 +1424,7 @@ static int try_provision_service(struct connman_config_service *config, virtual->service = service; virtual->vfile = config->virtual_file; - g_timeout_add(0, remove_virtual_config, virtual); + g_idle_add(remove_virtual_config, virtual); return 0; } @@ -1353,22 +1444,35 @@ static int try_provision_service(struct connman_config_service *config, return 0; } +static int +find_and_provision_service_from_config(struct connman_service *service, + struct connman_config *config) +{ + GHashTableIter iter; + gpointer value, key; + + g_hash_table_iter_init(&iter, config->service_table); + while (g_hash_table_iter_next(&iter, &key, + &value)) { + if (!try_provision_service(value, service)) + return 0; + } + + return -ENOENT; +} + static int find_and_provision_service(struct connman_service *service) { - GHashTableIter iter, iter_service; - gpointer value, key, value_service, key_service; + GHashTableIter iter; + gpointer value, key; g_hash_table_iter_init(&iter, config_table); while (g_hash_table_iter_next(&iter, &key, &value)) { struct connman_config *config = value; - g_hash_table_iter_init(&iter_service, config->service_table); - while (g_hash_table_iter_next(&iter_service, &key_service, - &value_service)) { - if (!try_provision_service(value_service, service)) - return 0; - } + if (!find_and_provision_service_from_config(service, config)) + return 0; } return -ENOENT; @@ -1459,7 +1563,7 @@ int __connman_config_provision_service_ident(struct connman_service *service, } } - find_and_provision_service(service); + find_and_provision_service_from_config(service, config); } return ret; @@ -1489,7 +1593,7 @@ int connman_config_provision_mutable_service(GKeyFile *keyfile) { struct connman_config_service *service_config; struct connman_config *config; - char *vfile, *group; + char *vfile, *group = NULL; char rstr[11]; DBG(""); @@ -1525,13 +1629,14 @@ int connman_config_provision_mutable_service(GKeyFile *keyfile) if (g_strcmp0(service_config->type, "wifi") == 0) __connman_device_request_scan(CONNMAN_SERVICE_TYPE_WIFI); + g_free(group); return 0; error: DBG("Could not proceed"); g_hash_table_remove(config_table, vfile); g_free(vfile); - + g_free(group); return -EINVAL; } @@ -1545,13 +1650,16 @@ struct connman_config_entry **connman_config_get_entries(const char *type) g_hash_table_iter_init(&iter_file, config_table); while (g_hash_table_iter_next(&iter_file, &key, &value)) { struct connman_config *config_file = value; + struct connman_config_entry **tmp_entries = entries; count = g_hash_table_size(config_file->service_table); entries = g_try_realloc(entries, (i + count + 1) * sizeof(struct connman_config_entry *)); - if (!entries) + if (!entries) { + g_free(tmp_entries); return NULL; + } g_hash_table_iter_init(&iter_config, config_file->service_table); @@ -1584,10 +1692,14 @@ struct connman_config_entry **connman_config_get_entries(const char *type) } if (entries) { + struct connman_config_entry **tmp_entries = entries; + entries = g_try_realloc(entries, (i + 1) * sizeof(struct connman_config_entry *)); - if (!entries) + if (!entries) { + g_free(tmp_entries); return NULL; + } entries[i] = NULL; diff --git a/src/connection.c b/src/connection.c index 4389d587..64d48b7d 100755 --- a/src/connection.c +++ b/src/connection.c @@ -46,7 +46,6 @@ struct gateway_config { struct gateway_data { int index; struct connman_service *service; - unsigned int order; struct gateway_config *ipv4_gateway; struct gateway_config *ipv6_gateway; bool default_checked; @@ -381,8 +380,6 @@ static struct gateway_data *add_gateway(struct connman_service *service, data->service = service; - data->order = __connman_service_get_order(service); - /* * If the service is already in the hash, then we * must not replace it blindly but disable the gateway @@ -558,25 +555,13 @@ static void unset_default_gateway(struct gateway_data *data, static struct gateway_data *find_default_gateway(void) { - struct gateway_data *found = NULL; - unsigned int order = 0; - GHashTableIter iter; - gpointer value, key; - - g_hash_table_iter_init(&iter, gateway_hash); - - while (g_hash_table_iter_next(&iter, &key, &value)) { - struct gateway_data *data = value; - - if (!found || data->order > order) { - found = data; - order = data->order; + struct connman_service *service; - DBG("default %p order %d", found, order); - } - } + service = __connman_service_get_default(); + if (!service) + return NULL; - return found; + return g_hash_table_lookup(gateway_hash, service); } static bool choose_default_gateway(struct gateway_data *data, @@ -589,37 +574,35 @@ static bool choose_default_gateway(struct gateway_data *data, * this one as default. If the other one is already active * we mark this one as non default. */ - if (data->ipv4_gateway) { - if (candidate->ipv4_gateway && - !candidate->ipv4_gateway->active) { + if (data->ipv4_gateway && candidate->ipv4_gateway) { + + if (!candidate->ipv4_gateway->active) { DBG("ipv4 downgrading %p", candidate); unset_default_gateway(candidate, CONNMAN_IPCONFIG_TYPE_IPV4); } - if (candidate->ipv4_gateway && - candidate->ipv4_gateway->active && - candidate->order > data->order) { + + if (candidate->ipv4_gateway->active && + __connman_service_compare(candidate->service, + data->service) < 0) { DBG("ipv4 downgrading this %p", data); - unset_default_gateway(data, - CONNMAN_IPCONFIG_TYPE_IPV4); + unset_default_gateway(data, CONNMAN_IPCONFIG_TYPE_IPV4); downgraded = true; } } - if (data->ipv6_gateway) { - if (candidate->ipv6_gateway && - !candidate->ipv6_gateway->active) { + if (data->ipv6_gateway && candidate->ipv6_gateway) { + if (!candidate->ipv6_gateway->active) { DBG("ipv6 downgrading %p", candidate); unset_default_gateway(candidate, CONNMAN_IPCONFIG_TYPE_IPV6); } - if (candidate->ipv6_gateway && - candidate->ipv6_gateway->active && - candidate->order > data->order) { + if (candidate->ipv6_gateway->active && + __connman_service_compare(candidate->service, + data->service) < 0) { DBG("ipv6 downgrading this %p", data); - unset_default_gateway(data, - CONNMAN_IPCONFIG_TYPE_IPV6); + unset_default_gateway(data, CONNMAN_IPCONFIG_TYPE_IPV6); downgraded = true; } } @@ -762,40 +745,6 @@ static struct gateway_data *find_active_gateway(void) return NULL; } -static void update_order(void) -{ - GHashTableIter iter; - gpointer value, key; - - DBG(""); - - g_hash_table_iter_init(&iter, gateway_hash); - - while (g_hash_table_iter_next(&iter, &key, &value)) { - struct gateway_data *data = value; - - data->order = __connman_service_get_order(data->service); - } -} - -void __connman_connection_gateway_activate(struct connman_service *service, - enum connman_ipconfig_type type) -{ - struct gateway_data *data = NULL; - - data = g_hash_table_lookup(gateway_hash, service); - if (!data) - return; - - DBG("gateway %p/%p type %d", data->ipv4_gateway, - data->ipv6_gateway, type); - - if (type == CONNMAN_IPCONFIG_TYPE_IPV4) - data->ipv4_gateway->active = true; - else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) - data->ipv6_gateway->active = true; -} - static void add_host_route(int family, int index, const char *gateway, enum connman_service_type service_type) { @@ -1090,12 +1039,8 @@ bool __connman_connection_update_gateway(void) if (!gateway_hash) return updated; - update_order(); - default_gateway = find_default_gateway(); - __connman_service_update_ordering(); - DBG("default %p", default_gateway); /* diff --git a/src/connman.h b/src/connman.h index 57cfc872..4125463b 100755 --- a/src/connman.h +++ b/src/connman.h @@ -244,10 +244,14 @@ int __connman_inet_del_default_from_table(uint32_t table_id, int ifindex, const int __connman_inet_get_address_netmask(int ifindex, struct sockaddr_in *address, struct sockaddr_in *netmask); +bool __connman_inet_isrootnfs_device(const char *devname); +char **__connman_inet_get_pnp_nameservers(const char *pnp_file); + #include <connman/resolver.h> int __connman_resolver_init(gboolean dnsproxy); void __connman_resolver_cleanup(void); +void __connman_resolver_append_fallback_nameservers(void); int __connman_resolvfile_append(int index, const char *domain, const char *server); int __connman_resolvfile_remove(int index, const char *domain, const char *server); int __connman_resolver_redo_servers(int index); @@ -511,8 +515,6 @@ void __connman_connection_gateway_remove(struct connman_service *service, int __connman_connection_get_vpn_index(int phy_index); bool __connman_connection_update_gateway(void); -void __connman_connection_gateway_activate(struct connman_service *service, - enum connman_ipconfig_type type); int __connman_ntp_start(char *server); void __connman_ntp_stop(); @@ -648,7 +650,7 @@ int __connman_tethering_init(void); void __connman_tethering_cleanup(void); const char *__connman_tethering_get_bridge(void); -void __connman_tethering_set_enabled(void); +int __connman_tethering_set_enabled(void); void __connman_tethering_set_disabled(void); int __connman_private_network_request(DBusMessage *msg, const char *owner); @@ -671,7 +673,8 @@ int __connman_provider_indicate_state(struct connman_provider *provider, enum connman_provider_state state); int __connman_provider_indicate_error(struct connman_provider *provider, enum connman_provider_error error); -int __connman_provider_connect(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); void __connman_provider_cleanup(void); int __connman_provider_init(void); @@ -693,6 +696,9 @@ enum connman_dnsconfig_method { }; #endif +int __connman_service_compare(const struct connman_service *a, + const struct connman_service *b); + struct connman_service *__connman_service_lookup_from_index(int index); struct connman_service *__connman_service_lookup_from_ident(const char *identifier); struct connman_service *__connman_service_create_from_network(struct connman_network *network); @@ -710,6 +716,8 @@ struct connman_ipconfig *__connman_service_get_ip6config( struct connman_service *service); struct connman_ipconfig *__connman_service_get_ipconfig( struct connman_service *service, int family); +void __connman_service_notify_ipv4_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_ident(struct connman_service *service); @@ -717,7 +725,6 @@ const char *__connman_service_get_path(struct connman_service *service); const char *__connman_service_get_name(struct connman_service *service); unsigned int __connman_service_get_order(struct connman_service *service); enum connman_service_state __connman_service_get_state(struct connman_service *service); -void __connman_service_update_ordering(void); struct connman_network *__connman_service_get_network(struct connman_service *service); enum connman_service_security __connman_service_get_security(struct connman_service *service); const char *__connman_service_get_phase2(struct connman_service *service); @@ -842,11 +849,23 @@ void __connman_service_set_proxy_autoconfig(struct connman_service *service, void __connman_service_set_identity(struct connman_service *service, const char *identity); +void __connman_service_set_anonymous_identity(struct connman_service *service, + const char *anonymous_identity); +void __connman_service_set_subject_match(struct connman_service *service, + const char *subject_match); +void __connman_service_set_altsubject_match(struct connman_service *service, + const char *altsubject_match); +void __connman_service_set_domain_suffix_match(struct connman_service *service, + const char *domain_suffix_match); +void __connman_service_set_domain_match(struct connman_service *service, + const char *domain_match); void __connman_service_set_agent_identity(struct connman_service *service, const char *agent_identity); int __connman_service_set_passphrase(struct connman_service *service, const char *passphrase); const char *__connman_service_get_passphrase(struct connman_service *service); +int __connman_service_check_passphrase(enum connman_service_security security, + const char *passphrase); int __connman_service_reset_ipconfig(struct connman_service *service, enum connman_ipconfig_type type, DBusMessageIter *array, enum connman_service_state *new_state); @@ -939,6 +958,11 @@ unsigned int __connman_rtnl_update_interval_remove(unsigned int interval); int __connman_rtnl_request_update(void); int __connman_rtnl_send(const void *buf, size_t len); +#if defined TIZEN_EXT +void rtnl_nameserver_add_all(struct connman_service *service, + enum connman_ipconfig_type type); +#endif + bool __connman_session_policy_autoconnect(enum connman_service_connect_reason reason); int __connman_session_create(DBusMessage *msg); @@ -1063,13 +1087,19 @@ struct firewall_context; struct firewall_context *__connman_firewall_create(void); void __connman_firewall_destroy(struct firewall_context *ctx); -int __connman_firewall_add_rule(struct firewall_context *ctx, - const char *table, - const char *chain, - const char *rule_fmt, ...); -int __connman_firewall_enable(struct firewall_context *ctx); -int __connman_firewall_disable(struct firewall_context *ctx); -bool __connman_firewall_is_up(void); +int __connman_firewall_enable_nat(struct firewall_context *ctx, + char *address, unsigned char prefixlen, + char *interface); +int __connman_firewall_disable_nat(struct firewall_context *ctx); +int __connman_firewall_enable_snat(struct firewall_context *ctx, + int index, const char *ifname, + const char *addr); +int __connman_firewall_disable_snat(struct firewall_context *ctx); +int __connman_firewall_enable_marking(struct firewall_context *ctx, + enum connman_session_id_type id_type, + char *id, const char *src_ip, + uint32_t mark); +int __connman_firewall_disable_marking(struct firewall_context *ctx); int __connman_firewall_init(void); void __connman_firewall_cleanup(void); diff --git a/src/connmand-wait-online.c b/src/connmand-wait-online.c new file mode 100644 index 00000000..2711d56f --- /dev/null +++ b/src/connmand-wait-online.c @@ -0,0 +1,461 @@ +/* + * + * Connection Manager + * + * Copyright (C) 2015 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <glib.h> +#include <errno.h> +#include <stdbool.h> +#include <dbus/dbus.h> + +#include <gdbus.h> +#include <connman/dbus.h> + +static DBusConnection *connection; +static GMainLoop *main_loop; +static int timeout = 0; +static int exit_value = 0; + +static gboolean option_version = FALSE; +static gchar *option_interface = NULL; +static gchar *option_ignore = NULL; +static gint option_timeout = 120; + +struct devices { + char **interface; + char **ignore; +}; + +static GOptionEntry options[] = { + { "interface", 'i', 0, G_OPTION_ARG_STRING, &option_interface, + "Specify networking device or interface", "DEV" }, + { "ignore", 'I', 0, G_OPTION_ARG_STRING, &option_ignore, + "Specify networking device or interface to ignore", "DEV" }, + { "timeout", 0, 0, G_OPTION_ARG_INT, &option_timeout, + "Time to wait for network going online. Default is 120 seconds.", + "seconds" }, + { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, + "Show version information and exit" }, + { NULL }, +}; + +static bool compare_interface(const char *interface, struct devices *devices) +{ + int i; + + if (!interface || !devices) + return false; + + for (i = 0; devices->ignore && devices->ignore[i]; i++) + if (!strcmp(interface, devices->ignore[i])) + return false; + + if (!devices->interface) + return true; + + for (i = 0; devices->interface[i]; i++) + if (!strcmp(interface, devices->interface[i])) + return true; + + return false; +} + +static bool state_online(DBusMessageIter *iter) +{ + char *str; + DBusMessageIter variant; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) + return false; + + dbus_message_iter_get_basic(iter, &str); + if (strcmp(str, "State")) + return false; + + dbus_message_iter_next(iter); + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT) + return false; + + dbus_message_iter_recurse(iter, &variant); + + if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_STRING) + return false; + + dbus_message_iter_get_basic(&variant, &str); + if (strcmp(str, "ready") && strcmp(str, "online")) + return false; + + return true; +} + +static bool service_properties_online(DBusMessageIter *array_entry, + struct devices *devices) +{ + bool interface = !devices; + bool state = false; + DBusMessageIter dict, dict_entry, variant, eth_array, eth_dict, + eth_variant; + char *str; + + for (dbus_message_iter_recurse(array_entry, &dict); + dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY; + dbus_message_iter_next(&dict)) { + + dbus_message_iter_recurse(&dict, &dict_entry); + if (dbus_message_iter_get_arg_type(&dict_entry) + != DBUS_TYPE_STRING) + continue; + + if (state_online(&dict_entry)) { + state = true; + continue; + } + + dbus_message_iter_recurse(&dict, &dict_entry); + + dbus_message_iter_get_basic(&dict_entry, &str); + + if (devices && !strcmp(str, "Ethernet")) { + dbus_message_iter_next(&dict_entry); + + if (dbus_message_iter_get_arg_type(&dict_entry) + != DBUS_TYPE_VARIANT) + break; + + dbus_message_iter_recurse(&dict_entry, &variant); + if (dbus_message_iter_get_arg_type(&variant) + != DBUS_TYPE_ARRAY) + break; + + for (dbus_message_iter_recurse(&variant, ð_array); + dbus_message_iter_get_arg_type(ð_array) + == DBUS_TYPE_DICT_ENTRY; + dbus_message_iter_next(ð_array)) { + + dbus_message_iter_recurse(ð_array, ð_dict); + + if (dbus_message_iter_get_arg_type(ð_dict) + != DBUS_TYPE_STRING) + continue; + + dbus_message_iter_get_basic(ð_dict, &str); + if (!strcmp(str, "Interface")) { + + dbus_message_iter_next(ð_dict); + if (dbus_message_iter_get_arg_type(ð_dict) + != DBUS_TYPE_VARIANT) + break; + + dbus_message_iter_recurse(ð_dict, + ð_variant); + if (dbus_message_iter_get_arg_type(ð_variant) + != DBUS_TYPE_STRING) + break; + + dbus_message_iter_get_basic(ð_variant, + &str); + interface = compare_interface(str, + devices); + + break; + } + } + } + + if (state && interface) { + g_main_loop_quit(main_loop); + return true; + } + } + + return false; +} + +static void services_dict_online(DBusMessageIter *iter, struct devices *devices) +{ + DBusMessageIter array, array_entry; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return; + + for (dbus_message_iter_recurse(iter, &array); + dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT; + dbus_message_iter_next(&array)) { + + dbus_message_iter_recurse(&array, &array_entry); + + if (dbus_message_iter_get_arg_type(&array_entry) != + DBUS_TYPE_OBJECT_PATH) + break; + + dbus_message_iter_next(&array_entry); + + if (dbus_message_iter_get_arg_type(&array_entry) != + DBUS_TYPE_ARRAY) + continue; + + if (service_properties_online(&array_entry, devices)) + break; + } +} + +static void manager_get_services_return(DBusPendingCall *call, + void *user_data) +{ + struct devices *devices = user_data; + DBusMessage *reply; + DBusMessageIter iter; + + reply = dbus_pending_call_steal_reply(call); + if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) + goto fail; + + if (!dbus_message_iter_init(reply, &iter)) + goto fail; + + services_dict_online(&iter, devices); + +fail: + dbus_message_unref(reply); + dbus_pending_call_unref(call); +} + +static void manager_get_services(struct devices *devices) +{ + DBusMessage *message; + DBusPendingCall *call; + + message = dbus_message_new_method_call(CONNMAN_SERVICE, + CONNMAN_MANAGER_PATH, + CONNMAN_MANAGER_INTERFACE, + "GetServices"); + if (!message) + return; + + if (!dbus_connection_send_with_reply(connection, message, &call, -1)) + goto fail; + + if (!call) + goto fail; + + dbus_pending_call_set_notify(call, manager_get_services_return, + devices, NULL); + +fail: + dbus_message_unref(message); +} + +static void manager_properties_online(DBusMessageIter *iter, + struct devices *devices) +{ + DBusMessageIter array, dict_entry; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return; + + for (dbus_message_iter_recurse(iter, &array); + dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY; + dbus_message_iter_next(&array)) { + + dbus_message_iter_recurse(&array, &dict_entry); + + if (state_online(&dict_entry)) { + if (devices) + manager_get_services(devices); + else + g_main_loop_quit(main_loop); + + break; + } + } +} + +static void manager_get_properties_return(DBusPendingCall *call, void *user_data) +{ + struct devices *devices = user_data; + DBusMessage *reply; + DBusMessageIter iter; + + reply = dbus_pending_call_steal_reply(call); + if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) + goto fail; + + if (!dbus_message_iter_init(reply, &iter)) + goto fail; + + manager_properties_online(&iter, devices); + +fail: + dbus_message_unref(reply); + dbus_pending_call_unref(call); +} + +static void manager_get_properties(struct devices *devices) +{ + DBusMessage *message; + DBusPendingCall *call; + + message = dbus_message_new_method_call(CONNMAN_SERVICE, + CONNMAN_MANAGER_PATH, + CONNMAN_MANAGER_INTERFACE, + "GetProperties"); + if (!message) + return; + + if (!dbus_connection_send_with_reply(connection, message, &call, -1)) + goto fail; + + if (!call) + goto fail; + + dbus_pending_call_set_notify(call, manager_get_properties_return, + devices, NULL); + +fail: + dbus_message_unref(message); +} + +static DBusHandlerResult manager_property_changed(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct devices *devices = user_data; + DBusMessageIter iter; + + if (dbus_message_is_signal(message, CONNMAN_MANAGER_INTERFACE, + "PropertyChanged")) { + dbus_message_iter_init(message, &iter); + + if (state_online(&iter)) { + if (devices) + manager_get_services(devices); + else + g_main_loop_quit(main_loop); + } + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static gboolean timeout_triggered(gpointer user_data) +{ + exit_value = -ETIMEDOUT; + g_main_loop_quit(main_loop); + timeout = 0; + + return FALSE; +} + +int main(int argc, char *argv[]) +{ + const char *filter = "type='signal',interface='" + CONNMAN_MANAGER_INTERFACE "'"; + int err = 0; + GError *g_err = NULL; + struct devices devices = { NULL, NULL }; + DBusError dbus_err; + GOptionContext *context; + + context = g_option_context_new(NULL); + g_option_context_add_main_entries(context, options, NULL); + + if (!g_option_context_parse(context, &argc, &argv, &g_err)) { + if (g_err) { + fprintf(stderr, "%s\n", g_err->message); + g_error_free(g_err); + } else + fprintf(stderr, "An unknown error occurred\n"); + + return EOPNOTSUPP; + } + + g_option_context_free(context); + + if (option_interface) { + devices.interface = g_strsplit(option_interface, ",", -1); + g_free(option_interface); + } + + if (option_ignore) { + devices.ignore = g_strsplit(option_ignore, ",", -1); + g_free(option_ignore); + } + + if (option_version) { + fprintf(stdout, "%s\n", VERSION); + goto free; + } + + dbus_error_init(&dbus_err); + connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, &dbus_err); + + if (dbus_error_is_set(&dbus_err)) { + fprintf(stderr, "Error: %s\n", dbus_err.message); + + err = -ENOPROTOOPT; + goto fail; + } + + main_loop = g_main_loop_new(NULL, FALSE); + + dbus_connection_add_filter(connection, manager_property_changed, + &devices, NULL); + + dbus_bus_add_match(connection, filter, &dbus_err); + + if (dbus_error_is_set(&dbus_err)) { + fprintf(stderr, "Error: %s\n", dbus_err.message); + + err = -ENOPROTOOPT; + goto cleanup; + } + + if (option_timeout) + timeout = g_timeout_add_seconds(option_timeout, + timeout_triggered, NULL); + + manager_get_properties(&devices); + + g_main_loop_run(main_loop); + err = exit_value; + +cleanup: + dbus_bus_remove_match(connection, filter, NULL); + dbus_connection_remove_filter(connection, manager_property_changed, + &devices); + + dbus_connection_unref(connection); + g_main_loop_unref(main_loop); + +fail: + dbus_error_free(&dbus_err); +free: + g_strfreev(devices.interface); + g_strfreev(devices.ignore); + if (timeout) + g_source_remove(timeout); + + return -err; +} diff --git a/src/device.c b/src/device.c index b5855cc2..3ec8f715 100755 --- a/src/device.c +++ b/src/device.c @@ -53,7 +53,6 @@ struct connman_device { */ bool powered; bool scanning; - bool disconnected; char *name; char *node; char *address; @@ -385,6 +384,9 @@ static void device_destruct(struct connman_device *device) clear_pending_trigger(device); + g_hash_table_destroy(device->networks); + device->networks = NULL; + g_free(device->ident); g_free(device->node); g_free(device->name); @@ -394,9 +396,6 @@ static void device_destruct(struct connman_device *device) g_free(device->last_network); - g_hash_table_destroy(device->networks); - device->networks = NULL; - g_free(device); } @@ -568,7 +567,7 @@ int connman_device_set_powered(struct connman_device *device, { enum connman_service_type type; - DBG("driver %p powered %d", device, powered); + DBG("device %p powered %d", device, powered); if (device->powered == powered) return -EALREADY; @@ -588,7 +587,6 @@ int connman_device_set_powered(struct connman_device *device, __connman_technology_enabled(type); - connman_device_set_disconnected(device, false); device->scanning = false; if (device->driver && device->driver->scan) @@ -623,8 +621,6 @@ int __connman_device_disconnect(struct connman_device *device) DBG("device %p", device); - connman_device_set_disconnected(device, true); - g_hash_table_iter_init(&iter, device->networks); while (g_hash_table_iter_next(&iter, &key, &value)) { @@ -751,37 +747,6 @@ int connman_device_set_scanning(struct connman_device *device, } /** - * connman_device_set_disconnected: - * @device: device structure - * @disconnected: disconnected state - * - * Change disconnected state of device (only for device with networks) - */ -int connman_device_set_disconnected(struct connman_device *device, - bool disconnected) -{ - DBG("device %p disconnected %d", device, disconnected); - - if (device->disconnected == disconnected) - return -EALREADY; - - device->disconnected = disconnected; - - return 0; -} - -/** - * connman_device_get_disconnected: - * @device: device structure - * - * Get device disconnected state - */ -bool connman_device_get_disconnected(struct connman_device *device) -{ - return device->disconnected; -} - -/** * connman_device_set_string: * @device: device structure * @key: unique identifier @@ -1435,6 +1400,11 @@ nodevice: } list: + if (__connman_inet_isrootnfs_device(devname)) { + DBG("ignoring device %s (rootnfs)", devname); + return true; + } + blacklisted_interfaces = connman_setting_get_string_list("NetworkInterfaceBlacklist"); if (!blacklisted_interfaces) @@ -26,6 +26,11 @@ #include <errno.h> #include <string.h> #include <stdlib.h> +#include <net/ethernet.h> + +#ifndef IPV6_MIN_MTU +#define IPV6_MIN_MTU 1280 +#endif #include <connman/ipconfig.h> #include <include/setting.h> @@ -54,10 +59,11 @@ struct connman_dhcp { GDHCPClient *dhcp_client; char *ipv4ll_debug_prefix; char *dhcp_debug_prefix; + + bool ipv4ll_running; }; static GHashTable *ipconfig_table; -static bool ipv4ll_running; static void dhcp_free(struct connman_dhcp *dhcp) { @@ -89,7 +95,7 @@ static void ipv4ll_stop_client(struct connman_dhcp *dhcp) g_dhcp_client_stop(dhcp->ipv4ll_client); g_dhcp_client_unref(dhcp->ipv4ll_client); dhcp->ipv4ll_client = NULL; - ipv4ll_running = false; + dhcp->ipv4ll_running = false; g_free(dhcp->ipv4ll_debug_prefix); dhcp->ipv4ll_debug_prefix = NULL; @@ -117,18 +123,22 @@ static bool apply_dhcp_invalidate_on_network(struct connman_dhcp *dhcp) __connman_service_timeserver_remove(service, dhcp->timeservers[i]); } + g_strfreev(dhcp->timeservers); + dhcp->timeservers = NULL; } if (dhcp->nameservers) { for (i = 0; dhcp->nameservers[i]; i++) { #if defined TIZEN_EXT __connman_service_nameserver_remove(service, - dhcp->nameservers[i], false, - CONNMAN_IPCONFIG_TYPE_IPV4); + dhcp->nameservers[i], false, + CONNMAN_IPCONFIG_TYPE_IPV4); #else __connman_service_nameserver_remove(service, - dhcp->nameservers[i], false); + dhcp->nameservers[i], false); #endif } + g_strfreev(dhcp->nameservers); + dhcp->nameservers = NULL; } return true; @@ -246,7 +256,7 @@ static int ipv4ll_start_client(struct connman_dhcp *dhcp) return err; } - ipv4ll_running = true; + dhcp->ipv4ll_running = true; return 0; } @@ -270,16 +280,16 @@ static void no_lease_cb(GDHCPClient *dhcp_client, gpointer user_data) struct connman_dhcp *dhcp = user_data; int err; - DBG("No lease available ipv4ll %d client %p", ipv4ll_running, + DBG("No lease available ipv4ll %d client %p", dhcp->ipv4ll_running, dhcp->ipv4ll_client); if (dhcp->timeout > 0) g_source_remove(dhcp->timeout); dhcp->timeout = g_timeout_add_seconds(RATE_LIMIT_INTERVAL, - dhcp_retry_cb, - dhcp); - if (ipv4ll_running) + dhcp_retry_cb, + dhcp); + if (dhcp->ipv4ll_running) return; err = ipv4ll_start_client(dhcp); @@ -287,7 +297,7 @@ static void no_lease_cb(GDHCPClient *dhcp_client, gpointer user_data) DBG("Cannot start ipv4ll client (%d/%s)", err, strerror(-err)); /* Only notify upper layer if we have a problem */ - dhcp_invalidate(dhcp, !ipv4ll_running); + dhcp_invalidate(dhcp, !dhcp->ipv4ll_running); } static void lease_lost_cb(GDHCPClient *dhcp_client, gpointer user_data) @@ -352,6 +362,20 @@ static bool apply_lease_available_on_network(GDHCPClient *dhcp_client, return false; } + option = g_dhcp_client_get_option(dhcp_client, G_DHCP_MTU); + if (option && option->data) { + int mtu, index, err; + + mtu = atoi(option->data); + + if (mtu >= IPV6_MIN_MTU && mtu <= ETH_DATA_LEN) { + index = __connman_ipconfig_get_index(dhcp->ipconfig); + err = connman_inet_set_mtu(index, mtu); + + DBG("MTU %d index %d err %d", mtu, index, err); + } + } + option = g_dhcp_client_get_option(dhcp_client, 252); if (option) pac = g_strdup(option->data); @@ -452,6 +476,7 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data) { struct connman_dhcp *dhcp = user_data; GList *option = NULL; + enum connman_ipconfig_method old_method; char *address, *netmask = NULL, *gateway = NULL; const char *c_address, *c_gateway; unsigned char prefixlen, c_prefixlen; @@ -507,6 +532,7 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data) } else if (prefixlen != c_prefixlen) ip_change = true; + old_method = __connman_ipconfig_get_method(dhcp->ipconfig); __connman_ipconfig_set_method(dhcp->ipconfig, CONNMAN_IPCONFIG_METHOD_DHCP); @@ -514,6 +540,20 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data) __connman_ipconfig_set_dhcp_lease_duration(dhcp->ipconfig, dhcp_lease_duration); #endif + /* + * Notify IPv4.Configuration's method moved back to DHCP. + * + * This is the case ConnMan initially set an address by using + * IPv4LL because DHCP failed but now we got an address from DHCP. + */ + if (old_method == CONNMAN_IPCONFIG_METHOD_AUTO) { + struct connman_service *service = + connman_service_lookup_from_network(dhcp->network); + + if (service) + __connman_service_notify_ipv4_configuration(service); + } + if (ip_change) { __connman_ipconfig_set_local(dhcp->ipconfig, address); __connman_ipconfig_set_prefixlen(dhcp->ipconfig, prefixlen); @@ -535,8 +575,9 @@ done: static void ipv4ll_available_cb(GDHCPClient *ipv4ll_client, gpointer user_data) { struct connman_dhcp *dhcp = user_data; - char *address, *netmask; - unsigned char prefixlen; + enum connman_ipconfig_method old_method; + char *address, *netmask; + unsigned char prefixlen; DBG("IPV4LL available"); @@ -545,8 +586,25 @@ static void ipv4ll_available_cb(GDHCPClient *ipv4ll_client, gpointer user_data) prefixlen = connman_ipaddress_calc_netmask_len(netmask); + old_method = __connman_ipconfig_get_method(dhcp->ipconfig); __connman_ipconfig_set_method(dhcp->ipconfig, - CONNMAN_IPCONFIG_METHOD_DHCP); + CONNMAN_IPCONFIG_METHOD_AUTO); + + /* + * Notify IPv4.Configuration's method is AUTO now. + * + * This is the case DHCP failed thus ConnMan used IPv4LL to get an + * address. Set IPv4.Configuration method to AUTO allows user to + * ask for a DHCP address by setting the method again to DHCP. + */ + if (old_method == CONNMAN_IPCONFIG_METHOD_DHCP) { + struct connman_service *service = + connman_service_lookup_from_network(dhcp->network); + + if (service) + __connman_service_notify_ipv4_configuration(service); + } + __connman_ipconfig_set_local(dhcp->ipconfig, address); __connman_ipconfig_set_prefixlen(dhcp->ipconfig, prefixlen); __connman_ipconfig_set_gateway(dhcp->ipconfig, NULL); @@ -562,6 +620,7 @@ static int dhcp_initialize(struct connman_dhcp *dhcp) GDHCPClient *dhcp_client; GDHCPClientError error; int index; + const char *vendor_class_id; DBG("dhcp %p", dhcp); @@ -610,10 +669,16 @@ static int dhcp_initialize(struct connman_dhcp *dhcp) 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_MTU); } - g_dhcp_client_set_request(dhcp_client, G_DHCP_SUBNET); g_dhcp_client_set_request(dhcp_client, G_DHCP_ROUTER); + g_dhcp_client_set_request(dhcp_client, G_DHCP_SUBNET); + + vendor_class_id = connman_option_get_string("VendorClassID"); + if (vendor_class_id) + g_dhcp_client_set_send(dhcp_client, G_DHCP_VENDOR_CLASS_ID, + vendor_class_id); g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_LEASE_AVAILABLE, diff --git a/src/dhcpv6.c b/src/dhcpv6.c index adf88273..c624cb00 100755 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -105,20 +105,14 @@ static void clear_timer(struct connman_dhcpv6 *dhcp) } } -static inline guint get_random(void) +static guint compute_random(guint val) { - uint64_t val; - - __connman_util_get_random(&val); + uint64_t rand; - /* Make sure the value is always positive so strip MSB */ - return ((uint32_t)val) >> 1; -} + __connman_util_get_random(&rand); -static guint compute_random(guint val) -{ return val - val / 10 + - (get_random() % (2 * 1000)) * val / 10 / 1000; + ((guint) rand % (2 * 1000)) * val / 10 / 1000; } /* Calculate a random delay, RFC 3315 chapter 14 */ @@ -253,6 +247,7 @@ static int set_duid(struct connman_service *service, hex_duid = convert_to_hex(duid, duid_len); if (!hex_duid) { + g_free(duid); g_key_file_free(keyfile); return -ENOMEM; } @@ -481,7 +476,6 @@ static int check_ipv6_addr_prefix(GSList *prefixes, char *address) if (!slash) continue; - prefix = g_strndup(prefix, slash - prefix); len = strtol(slash + 1, NULL, 10); if (len < 3 || len > 128) break; @@ -492,6 +486,7 @@ static int check_ipv6_addr_prefix(GSList *prefixes, char *address) left = plen % 8; i = 16 - count; + prefix = g_strndup(prefix, slash - prefix); inet_pton(AF_INET6, prefix, &addr_prefix); inet_pton(AF_INET6, address, &addr); diff --git a/src/dnsproxy.c b/src/dnsproxy.c index 55ba69a6..3fa7bf46 100755 --- a/src/dnsproxy.c +++ b/src/dnsproxy.c @@ -46,6 +46,8 @@ #include <systemd/sd-daemon.h> #endif +#define debug(fmt...) do { } while (0) + #if __BYTE_ORDER == __LITTLE_ENDIAN struct domain_hdr { uint16_t id; @@ -301,7 +303,7 @@ static struct server_data *find_server(int index, { GSList *list; - DBG("index %d server %s proto %d", index, server, protocol); + debug("index %d server %s proto %d", index, server, protocol); for (list = server_list; list; list = list->next) { struct server_data *data = list->data; @@ -352,14 +354,14 @@ static void refresh_dns_entry(struct cache_entry *entry, char *name) } if (!entry->ipv4) { - DBG("Refresing A record for %s", name); + debug("Refreshing A record for %s", name); g_resolv_lookup_hostname(ipv4_resolve, name, dummy_resolve_func, NULL); age = 4; } if (!entry->ipv6) { - DBG("Refresing AAAA record for %s", name); + debug("Refreshing AAAA record for %s", name); g_resolv_lookup_hostname(ipv6_resolve, name, dummy_resolve_func, NULL); age = 4; @@ -374,7 +376,7 @@ static int dns_name_length(unsigned char *buf) { if ((buf[0] & NS_CMPRSFLGS) == NS_CMPRSFLGS) /* compressed name */ return 2; - return strlen((char *)buf); + return strlen((char *)buf) + 1; } static void update_cached_ttl(unsigned char *buf, int len, int new_ttl) @@ -471,7 +473,7 @@ static void send_cached_response(int sk, unsigned char *buf, int len, else update_cached_ttl((unsigned char *)hdr, adj_len, ttl); - DBG("sk %d id 0x%04x answers %d ptr %p length %d dns %d", + debug("sk %d id 0x%04x answers %d ptr %p length %d dns %d", sk, hdr->id, answers, ptr, len, dns_len); err = sendto(sk, ptr, len, MSG_NOSIGNAL, to, tolen); @@ -483,7 +485,7 @@ static void send_cached_response(int sk, unsigned char *buf, int len, if (err != len || (dns_len != (len - 2) && protocol == IPPROTO_TCP) || (dns_len != len && protocol == IPPROTO_UDP)) - DBG("Packet length mismatch, sent %d wanted %d dns %d", + debug("Packet length mismatch, sent %d wanted %d dns %d", err, len, dns_len); } @@ -494,7 +496,7 @@ static void send_response(int sk, unsigned char *buf, int len, struct domain_hdr *hdr; int err, offset = protocol_offset(protocol); - DBG("sk %d", sk); + debug("sk %d", sk); if (offset < 0) return; @@ -504,7 +506,7 @@ static void send_response(int sk, unsigned char *buf, int len, hdr = (void *) (buf + offset); - DBG("id 0x%04x qr %d opcode %d", hdr->id, hdr->qr, hdr->opcode); + debug("id 0x%04x qr %d opcode %d", hdr->id, hdr->qr, hdr->opcode); hdr->qr = 1; hdr->rcode = ns_r_servfail; @@ -556,7 +558,7 @@ static gboolean request_timeout(gpointer user_data) if (!req) return FALSE; - DBG("id 0x%04x", req->srcid); + debug("id 0x%04x", req->srcid); request_list = g_slist_remove(request_list, req); @@ -598,7 +600,7 @@ static gboolean request_timeout(gpointer user_data) * if we get a request timeout from server. */ if (req->protocol == IPPROTO_TCP) { - DBG("client %d removed", req->client_sk); + debug("client %d removed", req->client_sk); g_hash_table_remove(partial_tcp_req_table, GINT_TO_POINTER(req->client_sk)); } @@ -616,7 +618,7 @@ static int append_query(unsigned char *buf, unsigned int size, unsigned char *ptr = buf; int len; - DBG("query %s domain %s", query, domain); + debug("query %s domain %s", query, domain); while (query) { const char *tmp; @@ -686,7 +688,7 @@ static void cache_enforce_validity(struct cache_entry *entry) if (!cache_check_is_valid(entry->ipv4, current_time) && entry->ipv4) { - DBG("cache timeout \"%s\" type A", entry->key); + debug("cache timeout \"%s\" type A", entry->key); g_free(entry->ipv4->data); g_free(entry->ipv4); entry->ipv4 = NULL; @@ -695,7 +697,7 @@ static void cache_enforce_validity(struct cache_entry *entry) if (!cache_check_is_valid(entry->ipv6, current_time) && entry->ipv6) { - DBG("cache timeout \"%s\" type AAAA", entry->key); + debug("cache timeout \"%s\" type AAAA", entry->key); g_free(entry->ipv6->data); g_free(entry->ipv6); entry->ipv6 = NULL; @@ -720,7 +722,7 @@ static uint16_t cache_check_validity(char *question, uint16_t type, switch (type) { case 1: /* IPv4 */ if (!cache_check_is_valid(entry->ipv4, current_time)) { - DBG("cache %s \"%s\" type A", entry->ipv4 ? + debug("cache %s \"%s\" type A", entry->ipv4 ? "timeout" : "entry missing", question); if (want_refresh) @@ -739,7 +741,7 @@ static uint16_t cache_check_validity(char *question, uint16_t type, case 28: /* IPv6 */ if (!cache_check_is_valid(entry->ipv6, current_time)) { - DBG("cache %s \"%s\" type AAAA", entry->ipv6 ? + debug("cache %s \"%s\" type AAAA", entry->ipv6 ? "timeout" : "entry missing", question); if (want_refresh) @@ -785,7 +787,7 @@ static gboolean try_remove_cache(gpointer user_data) cache_timer = 0; if (__sync_fetch_and_sub(&cache_refcount, 1) == 1) { - DBG("No cache users, removing it."); + debug("No cache users, removing it."); g_hash_table_destroy(cache); cache = NULL; @@ -1002,7 +1004,7 @@ static int parse_response(unsigned char *buf, int buflen, if (buflen < 12) return -EINVAL; - DBG("qr %d qdcount %d", hdr->qr, qdcount); + debug("qr %d qdcount %d", hdr->qr, qdcount); /* We currently only cache responses where question count is 1 */ if (hdr->qr != 1 || qdcount != 1) @@ -1248,7 +1250,7 @@ static void cache_cleanup(void) count = g_hash_table_foreach_remove(cache, cache_check_entry, &data); } - DBG("removed %d in the first pass", count); + debug("removed %d in the first pass", count); /* * In the second pass, if the first pass turned up blank, @@ -1312,7 +1314,7 @@ static gboolean cache_invalidate_entry(gpointer key, gpointer value, */ static void cache_invalidate(void) { - DBG("Invalidating the DNS cache %p", cache); + debug("Invalidating the DNS cache %p", cache); if (!cache) return; @@ -1344,7 +1346,7 @@ static void cache_refresh_entry(struct cache_entry *entry) *c = '.'; c += jump + 1; } - DBG("Refreshing %s\n", dns_name); + debug("Refreshing %s\n", dns_name); /* then refresh the hostname */ refresh_dns_entry(entry, &dns_name[1]); } @@ -1380,7 +1382,7 @@ static int reply_query_type(unsigned char *msg, int len) return 0; /* now the query, which is a name and 2 16 bit words */ - l = dns_name_length(c) + 1; + l = dns_name_length(c); c += l; type = c[0] << 8 | c[1]; @@ -1421,7 +1423,7 @@ static int cache_update(struct server_data *srv, unsigned char *msg, if (offset < 0) return 0; - DBG("offset %d hdr %p msg %p rcode %d", offset, hdr, msg, hdr->rcode); + debug("offset %d hdr %p msg %p rcode %d", offset, hdr, msg, hdr->rcode); /* Continue only if response code is 0 (=ok) */ if (hdr->rcode != ns_r_noerror) @@ -1601,7 +1603,7 @@ static int cache_update(struct server_data *srv, unsigned char *msg, cache_size++; } - DBG("cache %d %squestion \"%s\" type %d ttl %d size %zd packet %u " + debug("cache %d %squestion \"%s\" type %d ttl %d size %zd packet %u " "dns len %u", cache_size, new_entry ? "new " : "old ", question, type, ttl, @@ -1627,7 +1629,7 @@ static int ns_resolv(struct server_data *server, struct request_data *req, int ttl_left = 0; struct cache_data *data; - DBG("cache hit %s type %s", lookup, type == 1 ? "A" : "AAAA"); + debug("cache hit %s type %s", lookup, type == 1 ? "A" : "AAAA"); if (type == 1) data = entry->ipv4; else @@ -1689,7 +1691,7 @@ static int ns_resolv(struct server_data *server, struct request_data *req, err = sendto(sk, request, req->request_len, MSG_NOSIGNAL, server->server_addr, server->server_addr_len); if (err < 0) { - DBG("Cannot send message to server %s sock %d " + debug("Cannot send message to server %s sock %d " "protocol %d (%s/%d)", server->server, sk, server->protocol, strerror(errno), errno); @@ -1749,7 +1751,7 @@ static int ns_resolv(struct server_data *server, struct request_data *req, alt[1] = req_len & 0xff; } - DBG("req %p dstid 0x%04x altid 0x%04x", req, req->dstid, + debug("req %p dstid 0x%04x altid 0x%04x", req, req->dstid, req->altid); err = send(sk, alt, req->request_len + domlen, MSG_NOSIGNAL); @@ -1771,7 +1773,7 @@ static char *convert_label(char *start, char *end, char *ptr, char *uptr, pos = dn_expand((u_char *)start, (u_char *)end, (u_char *)ptr, name, NS_MAXLABEL); if (pos < 0) { - DBG("uncompress error [%d/%s]", errno, strerror(errno)); + debug("uncompress error [%d/%s]", errno, strerror(errno)); goto out; } @@ -1781,7 +1783,7 @@ static char *convert_label(char *start, char *end, char *ptr, char *uptr, */ comp_pos = dn_comp(name, (u_char *)uptr, remaining_len, NULL, NULL); if (comp_pos < 0) { - DBG("compress error [%d/%s]", errno, strerror(errno)); + debug("compress error [%d/%s]", errno, strerror(errno)); goto out; } @@ -1800,7 +1802,7 @@ static char *uncompress(int16_t field_count, char *start, char *end, { char *uptr = *uncompressed_ptr; /* position in result buffer */ - DBG("count %d ptr %p end %p uptr %p", field_count, ptr, end, uptr); + debug("count %d ptr %p end %p uptr %p", field_count, ptr, end, uptr); while (field_count-- > 0 && ptr < end) { int dlen; /* data field length */ @@ -1822,7 +1824,7 @@ static char *uncompress(int16_t field_count, char *start, char *end, ulen = strlen(name); strncpy(uptr, name, uncomp_len - (uptr - uncompressed)); - DBG("pos %d ulen %d left %d name %s", pos, ulen, + debug("pos %d ulen %d left %d name %s", pos, ulen, (int)(uncomp_len - (uptr - uncompressed)), uptr); uptr += ulen; @@ -1866,7 +1868,7 @@ static char *uncompress(int16_t field_count, char *start, char *end, dlen = uptr[-2] << 8 | uptr[-1]; if (ptr + dlen > end) { - DBG("data len %d too long", dlen); + debug("data len %d too long", dlen); goto out; } @@ -1977,13 +1979,13 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol, hdr = (void *)(reply + offset); dns_id = reply[offset] | reply[offset + 1] << 8; - DBG("Received %d bytes (id 0x%04x)", reply_len, dns_id); + debug("Received %d bytes (id 0x%04x)", reply_len, dns_id); req = find_request(dns_id); if (!req) return -EINVAL; - DBG("req %p dstid 0x%04x altid 0x%04x rcode %d", + debug("req %p dstid 0x%04x altid 0x%04x rcode %d", req, req->dstid, req->altid, hdr->rcode); reply[offset] = req->srcid & 0xff; @@ -2039,7 +2041,7 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol, ptr[dns_type_pos + 3]; if (dns_type != ns_t_a && dns_type != ns_t_aaaa && dns_class != ns_c_in) { - DBG("Pass msg dns type %d class %d", + debug("Pass msg dns type %d class %d", dns_type, dns_class); goto pass; } @@ -2098,21 +2100,21 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol, (char *)reply + offset, eom, ptr, uncompressed, NS_MAXDNAME, &uptr); - if (ptr == NULL) + if (!ptr) goto out; ptr = uncompress(ntohs(hdr->nscount), (char *)reply + offset, eom, ptr, uncompressed, NS_MAXDNAME, &uptr); - if (ptr == NULL) + if (!ptr) goto out; ptr = uncompress(ntohs(hdr->arcount), (char *)reply + offset, eom, ptr, uncompressed, NS_MAXDNAME, &uptr); - if (ptr == NULL) + if (!ptr) goto out; /* @@ -2128,7 +2130,7 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol, new_len = strip_domains(uncompressed, answers, uptr - answers); if (new_len < 0) { - DBG("Corrupted packet"); + debug("Corrupted packet"); return -EINVAL; } @@ -2193,10 +2195,10 @@ out: } if (err < 0) - DBG("Cannot send msg, sk %d proto %d errno %d/%s", sk, + debug("Cannot send msg, sk %d proto %d errno %d/%s", sk, protocol, errno, strerror(errno)); else - DBG("proto %d sent %d bytes to %d", protocol, err, sk); + debug("proto %d sent %d bytes to %d", protocol, err, sk); destroy_request_data(req); @@ -2205,7 +2207,7 @@ out: static void server_destroy_socket(struct server_data *data) { - DBG("index %d server %s proto %d", data->index, + debug("index %d server %s proto %d", data->index, data->server, data->protocol); if (data->watch > 0) { @@ -2230,7 +2232,7 @@ static void server_destroy_socket(struct server_data *data) static void destroy_server(struct server_data *server) { - DBG("index %d server %s sock %d", server->index, server->server, + debug("index %d server %s sock %d", server->index, server->server, server->channel ? g_io_channel_unix_get_fd(server->channel): -1); @@ -2238,7 +2240,7 @@ static void destroy_server(struct server_data *server) server_destroy_socket(server); if (server->protocol == IPPROTO_UDP && server->enabled) - DBG("Removing DNS server %s", server->server); + debug("Removing DNS server %s", server->server); g_free(server->server); g_list_free_full(server->domains, g_free); @@ -2311,7 +2313,7 @@ static gboolean tcp_server_event(GIOChannel *channel, GIOCondition condition, if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { GSList *list; hangup: - DBG("TCP server channel closed, sk %d", sk); + debug("TCP server channel closed, sk %d", sk); /* * Discard any partial response which is buffered; better @@ -2320,9 +2322,11 @@ hangup: g_free(server->incoming_reply); server->incoming_reply = NULL; - for (list = request_list; list; list = list->next) { + list = request_list; + while (list) { struct request_data *req = list->data; struct domain_hdr *hdr; + list = list->next; if (req->protocol == IPPROTO_UDP) continue; @@ -2364,7 +2368,7 @@ hangup: domains = domains->next) { char *dom = domains->data; - DBG("Adding domain %s to %s", + debug("Adding domain %s to %s", dom, server->server); server->domains = g_list_append(server->domains, @@ -2389,7 +2393,7 @@ hangup: continue; } - DBG("Sending req %s over TCP", (char *)req->name); + debug("Sending req %s over TCP", (char *)req->name); status = ns_resolv(server, req, req->request, req->name); @@ -2448,7 +2452,7 @@ hangup: reply_len = reply_len_buf[1] | reply_len_buf[0] << 8; reply_len += 2; - DBG("TCP reply %d bytes from %d", reply_len, sk); + debug("TCP reply %d bytes from %d", reply_len, sk); reply = g_try_malloc(sizeof(*reply) + reply_len + 2); if (!reply) @@ -2495,7 +2499,7 @@ static gboolean tcp_idle_timeout(gpointer user_data) { struct server_data *server = user_data; - DBG(""); + debug(""); if (!server) return FALSE; @@ -2510,7 +2514,7 @@ static int server_create_socket(struct server_data *data) int sk, err; char *interface; - DBG("index %d server %s proto %d", data->index, + debug("index %d server %s proto %d", data->index, data->server, data->protocol); sk = socket(data->server_addr->sa_family, @@ -2524,7 +2528,7 @@ static int server_create_socket(struct server_data *data) return -err; } - DBG("sk %d", sk); + debug("sk %d", sk); interface = connman_inet_ifname(data->index); if (interface) { @@ -2584,6 +2588,25 @@ static int server_create_socket(struct server_data *data) return 0; } +static void enable_fallback(bool enable) +{ + GSList *list; + + for (list = server_list; list; list = list->next) { + struct server_data *data = list->data; + + if (data->index != -1) + continue; + + if (enable) + DBG("Enabling fallback DNS server %s", data->server); + else + DBG("Disabling fallback DNS server %s", data->server); + + data->enabled = enable; + } +} + #if defined TIZEN_EXT static void destroy_server_sec(struct server_data *server) @@ -2841,6 +2864,8 @@ static struct server_data *create_server(int index, data->index)) { data->enabled = true; DBG("Adding DNS server %s", data->server); + + enable_fallback(false); } server_list = g_slist_append(server_list, data); @@ -2862,7 +2887,7 @@ static bool resolv(struct request_data *req, continue; } - DBG("server %s enabled %d", data->server, data->enabled); + debug("server %s enabled %d", data->server, data->enabled); if (!data->enabled) continue; @@ -2881,7 +2906,7 @@ static bool resolv(struct request_data *req, return false; } -static void append_domain(int index, const char *domain) +static void update_domain(int index, const char *domain, bool append) { GSList *list; @@ -2912,13 +2937,27 @@ static void append_domain(int index, const char *domain) } } - if (!dom_found) { + if (!dom_found && append) { data->domains = g_list_append(data->domains, g_strdup(domain)); + } else if (dom_found && !append) { + data->domains = + g_list_remove(data->domains, dom); + g_free(dom); } } } +static void append_domain(int index, const char *domain) +{ + update_domain(index, domain, true); +} + +static void remove_domain(int index, const char *domain) +{ + update_domain(index, domain, false); +} + static void flush_requests(struct server_data *server) { GSList *list; @@ -2988,12 +3027,22 @@ static void remove_server(int index, const char *domain, const char *server, int protocol) { struct server_data *data; + GSList *list; data = find_server(index, server, protocol); if (!data) return; destroy_server(data); + + for (list = server_list; list; list = list->next) { + struct server_data *data = list->data; + + if (data->index != -1 && data->enabled == true) + return; + } + + enable_fallback(true); } int __connman_dnsproxy_remove(int index, const char *domain, @@ -3001,9 +3050,15 @@ int __connman_dnsproxy_remove(int index, const char *domain, { DBG("index %d server %s", index, server); - if (!server) + if (!server && !domain) return -EINVAL; + if (!server) { + remove_domain(index, domain); + + return 0; + } + if (g_str_equal(server, "127.0.0.1")) return -ENODEV; @@ -3044,6 +3099,7 @@ static void dnsproxy_offline_mode(bool enabled) static void dnsproxy_default_changed(struct connman_service *service) { + bool server_enabled = false; GSList *list; int index; @@ -3068,12 +3124,16 @@ static void dnsproxy_default_changed(struct connman_service *service) if (data->index == index) { DBG("Enabling DNS server %s", data->server); data->enabled = true; + server_enabled = true; } else { DBG("Disabling DNS server %s", data->server); data->enabled = false; } } + if (!server_enabled) + enable_fallback(true); + cache_refresh(); } @@ -3098,7 +3158,7 @@ static int parse_request(unsigned char *buf, int len, if (len < 12) return -EINVAL; - DBG("id 0x%04x qr %d opcode %d qdcount %d arcount %d", + debug("id 0x%04x qr %d opcode %d qdcount %d arcount %d", hdr->id, hdr->qr, hdr->opcode, qdcount, arcount); @@ -3136,7 +3196,7 @@ static int parse_request(unsigned char *buf, int len, edns0_bufsize = last_label[7] << 8 | last_label[8]; - DBG("EDNS0 buffer size %u", edns0_bufsize); + debug("EDNS0 buffer size %u", edns0_bufsize); /* This is an evil hack until full TCP support has been * implemented. @@ -3152,7 +3212,7 @@ static int parse_request(unsigned char *buf, int len, } } - DBG("query %s", name); + debug("query %s", name); return 0; } @@ -3163,7 +3223,7 @@ static void client_reset(struct tcp_partial_client_data *client) return; if (client->channel) { - DBG("client %d closing", + debug("client %d closing", g_io_channel_unix_get_fd(client->channel)); g_io_channel_unref(client->channel); @@ -3207,14 +3267,14 @@ static bool read_tcp_data(struct tcp_partial_client_data *client, client_sk = g_io_channel_unix_get_fd(client->channel); if (read_len == 0) { - DBG("client %d closed, pending %d bytes", + debug("client %d closed, pending %d bytes", client_sk, client->buf_end); g_hash_table_remove(partial_tcp_req_table, GINT_TO_POINTER(client_sk)); return false; } - DBG("client %d received %d bytes", client_sk, read_len); + debug("client %d received %d bytes", client_sk, read_len); client->buf_end += read_len; @@ -3223,24 +3283,24 @@ static bool read_tcp_data(struct tcp_partial_client_data *client, msg_len = get_msg_len(client->buf); if (msg_len > TCP_MAX_BUF_LEN) { - DBG("client %d sent too much data %d", client_sk, msg_len); + debug("client %d sent too much data %d", client_sk, msg_len); g_hash_table_remove(partial_tcp_req_table, GINT_TO_POINTER(client_sk)); return false; } read_another: - DBG("client %d msg len %d end %d past end %d", client_sk, msg_len, + debug("client %d msg len %d end %d past end %d", client_sk, msg_len, client->buf_end, client->buf_end - (msg_len + 2)); if (client->buf_end < (msg_len + 2)) { - DBG("client %d still missing %d bytes", + debug("client %d still missing %d bytes", client_sk, msg_len + 2 - client->buf_end); return true; } - DBG("client %d all data %d received", client_sk, msg_len); + debug("client %d all data %d received", client_sk, msg_len); err = parse_request(client->buf + 2, msg_len, query, sizeof(query)); @@ -3281,7 +3341,7 @@ read_another: int ttl_left = 0; struct cache_data *data; - DBG("cache hit %s type %s", query, qtype == 1 ? "A" : "AAAA"); + debug("cache hit %s type %s", query, qtype == 1 ? "A" : "AAAA"); if (qtype == 1) data = entry->ipv4; else @@ -3298,7 +3358,7 @@ read_another: g_free(req); goto out; } else - DBG("data missing, ignoring cache for this query"); + debug("data missing, ignoring cache for this query"); } for (list = server_list; list; list = list->next) { @@ -3353,7 +3413,7 @@ read_another: out: if (client->buf_end > (msg_len + 2)) { - DBG("client %d buf %p -> %p end %d len %d new %d", + debug("client %d buf %p -> %p end %d len %d new %d", client_sk, client->buf + msg_len + 2, client->buf, client->buf_end, @@ -3369,12 +3429,12 @@ out: */ msg_len = get_msg_len(client->buf); if ((msg_len + 2) == client->buf_end) { - DBG("client %d reading another %d bytes", client_sk, + debug("client %d reading another %d bytes", client_sk, msg_len + 2); goto read_another; } } else { - DBG("client %d clearing reading buffer", client_sk); + debug("client %d clearing reading buffer", client_sk); client->buf_end = 0; memset(client->buf, 0, TCP_MAX_BUF_LEN); @@ -3436,7 +3496,7 @@ static gboolean tcp_client_event(GIOChannel *channel, GIOCondition condition, if (errno == EAGAIN || errno == EWOULDBLOCK) return TRUE; - DBG("client %d cannot read errno %d/%s", client_sk, -errno, + debug("client %d cannot read errno %d/%s", client_sk, -errno, strerror(errno)); g_hash_table_remove(partial_tcp_req_table, GINT_TO_POINTER(client_sk)); @@ -3453,7 +3513,7 @@ static gboolean client_timeout(gpointer user_data) sock = g_io_channel_unix_get_fd(client->channel); - DBG("client %d timeout pending %d bytes", sock, client->buf_end); + debug("client %d timeout pending %d bytes", sock, client->buf_end); g_hash_table_remove(partial_tcp_req_table, GINT_TO_POINTER(sock)); @@ -3493,7 +3553,7 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition, struct timeval tv; fd_set readfds; - DBG("condition 0x%02x channel %p ifdata %p family %d", + debug("condition 0x%02x channel %p ifdata %p family %d", condition, channel, ifdata, family); if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { @@ -3529,9 +3589,9 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition, select(sk + 1, &readfds, NULL, NULL, &tv); if (FD_ISSET(sk, &readfds)) { client_sk = accept(sk, client_addr, client_addr_len); - DBG("client %d accepted", client_sk); + debug("client %d accepted", client_sk); } else { - DBG("No data to read from master %d, waiting.", sk); + debug("No data to read from master %d, waiting.", sk); return true; } @@ -3565,9 +3625,9 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition, client->ifdata = ifdata; - DBG("client %d created %p", client_sk, client); + debug("client %d created %p", client_sk, client); } else { - DBG("client %d already exists %p", client_sk, client); + debug("client %d already exists %p", client_sk, client); } if (!client->buf) { @@ -3591,11 +3651,11 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition, len = recv(client_sk, client->buf, TCP_MAX_BUF_LEN, 0); if (len < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { - DBG("client %d no data to read, waiting", client_sk); + debug("client %d no data to read, waiting", client_sk); return true; } - DBG("client %d cannot read errno %d/%s", client_sk, -errno, + debug("client %d cannot read errno %d/%s", client_sk, -errno, strerror(errno)); g_hash_table_remove(partial_tcp_req_table, GINT_TO_POINTER(client_sk)); @@ -3603,14 +3663,14 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition, } if (len < 2) { - DBG("client %d not enough data to read, waiting", client_sk); + debug("client %d not enough data to read, waiting", client_sk); client->buf_end += len; return true; } msg_len = get_msg_len(client->buf); if (msg_len > TCP_MAX_BUF_LEN) { - DBG("client %d invalid message length %u ignoring packet", + debug("client %d invalid message length %u ignoring packet", client_sk, msg_len); g_hash_table_remove(partial_tcp_req_table, GINT_TO_POINTER(client_sk)); @@ -3626,7 +3686,7 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition, #else if (msg_len != (unsigned int)(len - 2)) { #endif - DBG("client %d sent %d bytes but expecting %u pending %d", + debug("client %d sent %d bytes but expecting %u pending %d", client_sk, len, msg_len + 2, msg_len + 2 - len); client->buf_end += len; @@ -3696,7 +3756,7 @@ static bool udp_listener_event(GIOChannel *channel, GIOCondition condition, if (len < 2) return true; - DBG("Received %d bytes (id 0x%04x)", len, buf[0] | buf[1] << 8); + debug("Received %d bytes (id 0x%04x)", len, buf[0] | buf[1] << 8); err = parse_request(buf, len, query, sizeof(query)); if (err < 0 || (g_slist_length(server_list) == 0)) { @@ -3788,7 +3848,7 @@ static GIOChannel *get_listener(int family, int protocol, int index) int is_socket_inet = 0; #endif - DBG("family %d protocol %d index %d", family, protocol, index); + debug("family %d protocol %d index %d", family, protocol, index); switch (protocol) { case IPPROTO_UDP: @@ -4087,7 +4147,7 @@ static void destroy_listener(struct listener_data *ifdata) for (list = request_list; list; list = list->next) { struct request_data *req = list->data; - DBG("Dropping request (id 0x%04x -> 0x%04x)", + debug("Dropping request (id 0x%04x -> 0x%04x)", req->srcid, req->dstid); destroy_request_data(req); list->data = NULL; diff --git a/src/firewall.c b/src/firewall-iptables.c index 90c3d3c1..45943a82 100755..100644 --- a/src/firewall.c +++ b/src/firewall-iptables.c @@ -2,7 +2,7 @@ * * Connection Manager * - * Copyright (C) 2013 BMW Car IT GmbH. + * Copyright (C) 2013,2015 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 @@ -46,6 +46,7 @@ struct connman_managed_table { }; struct fw_rule { + bool enabled; char *table; char *chain; char *rule_spec; @@ -56,8 +57,8 @@ struct firewall_context { }; static GSList *managed_tables; - -static bool firewall_is_up; +static struct firewall_context *connmark_ctx; +static unsigned int connmark_ref; static int chain_to_index(const char *chain_name) { @@ -267,7 +268,55 @@ void __connman_firewall_destroy(struct firewall_context *ctx) g_free(ctx); } -int __connman_firewall_add_rule(struct firewall_context *ctx, +static int enable_rule(struct fw_rule *rule) +{ + int err; + + if (rule->enabled) + return -EALREADY; + + DBG("%s %s %s", rule->table, rule->chain, rule->rule_spec); + + err = insert_managed_rule(rule->table, rule->chain, rule->rule_spec); + if (err < 0) + return err; + + err = __connman_iptables_commit(rule->table); + if (err < 0) + return err; + + rule->enabled = true; + + return 0; +} + +static int disable_rule(struct fw_rule *rule) +{ + int err; + + if (!rule->enabled) + return -EALREADY; + + err = delete_managed_rule(rule->table, rule->chain, rule->rule_spec); + if (err < 0) { + connman_error("Cannot remove previously installed " + "iptables rules: %s", strerror(-err)); + return err; + } + + err = __connman_iptables_commit(rule->table); + if (err < 0) { + connman_error("Cannot remove previously installed " + "iptables rules: %s", strerror(-err)); + return err; + } + + rule->enabled = false; + + return 0; +} + +static void firewall_add_rule(struct firewall_context *ctx, const char *table, const char *chain, const char *rule_fmt, ...) @@ -284,85 +333,208 @@ int __connman_firewall_add_rule(struct firewall_context *ctx, rule = g_new0(struct fw_rule, 1); + rule->enabled = false; rule->table = g_strdup(table); rule->chain = g_strdup(chain); rule->rule_spec = rule_spec; ctx->rules = g_list_append(ctx->rules, rule); - - return 0; } -static int firewall_disable(GList *rules) +static void firewall_remove_rules(struct firewall_context *ctx) { struct fw_rule *rule; GList *list; - int err; - for (list = rules; list; list = g_list_previous(list)) { + for (list = g_list_last(ctx->rules); list; + list = g_list_previous(list)) { rule = list->data; - err = delete_managed_rule(rule->table, - rule->chain, rule->rule_spec); - if (err < 0) { - connman_error("Cannot remove previously installed " - "iptables rules: %s", strerror(-err)); - return err; - } + ctx->rules = g_list_remove(ctx->rules, rule); + cleanup_fw_rule(rule); + } +} - err = __connman_iptables_commit(rule->table); - if (err < 0) { - connman_error("Cannot remove previously installed " - "iptables rules: %s", strerror(-err)); - return err; - } +static int firewall_enable_rules(struct firewall_context *ctx) +{ + struct fw_rule *rule; + GList *list; + int err = -ENOENT; + + for (list = g_list_first(ctx->rules); list; list = g_list_next(list)) { + rule = list->data; + + err = enable_rule(rule); + if (err < 0) + break; } - return 0; + return err; } -int __connman_firewall_enable(struct firewall_context *ctx) +static int firewall_disable_rules(struct firewall_context *ctx) { struct fw_rule *rule; GList *list; - int err; + int e; + int err = -ENOENT; - for (list = g_list_first(ctx->rules); list; - list = g_list_next(list)) { + for (list = g_list_last(ctx->rules); list; + list = g_list_previous(list)) { rule = list->data; - DBG("%s %s %s", rule->table, rule->chain, rule->rule_spec); + e = disable_rule(rule); - err = insert_managed_rule(rule->table, - rule->chain, rule->rule_spec); - if (err < 0) - goto err; - - err = __connman_iptables_commit(rule->table); - if (err < 0) - goto err; + /* Report last error back */ + if (e == 0 && err == -ENOENT) + err = 0; + else if (e < 0) + err = e; } - firewall_is_up = true; + return err; +} + +int __connman_firewall_enable_nat(struct firewall_context *ctx, + char *address, unsigned char prefixlen, + char *interface) +{ + char *cmd; + int err; + cmd = g_strdup_printf("-s %s/%d -o %s -j MASQUERADE", + address, prefixlen, interface); + + firewall_add_rule(ctx, "nat", "POSTROUTING", cmd); + g_free(cmd); + err = firewall_enable_rules(ctx); + if (err) + firewall_remove_rules(ctx); + return err; +} + +int __connman_firewall_disable_nat(struct firewall_context *ctx) +{ + int err; + + err = firewall_disable_rules(ctx); + if (err < 0) { + DBG("could not disable NAT rule"); + return err; + } + + firewall_remove_rules(ctx); return 0; +} -err: - connman_warn("Failed to install iptables rules: %s", strerror(-err)); +int __connman_firewall_enable_snat(struct firewall_context *ctx, + int index, const char *ifname, + const char *addr) +{ + int err; - firewall_disable(g_list_previous(list)); + firewall_add_rule(ctx, "nat", "POSTROUTING", + "-o %s -j SNAT --to-source %s", + ifname, addr); + err = firewall_enable_rules(ctx); + if (err) + firewall_remove_rules(ctx); return err; } -int __connman_firewall_disable(struct firewall_context *ctx) +int __connman_firewall_disable_snat(struct firewall_context *ctx) +{ + int err; + + err = firewall_disable_rules(ctx); + if (err < 0) { + DBG("could not disable SNAT rule"); + return err; + } + + firewall_remove_rules(ctx); + return 0; +} + +static int firewall_enable_connmark(void) { - return firewall_disable(g_list_last(ctx->rules)); + int err; + + if (connmark_ref > 0) { + connmark_ref++; + return 0; + } + + connmark_ctx = __connman_firewall_create(); + + firewall_add_rule(connmark_ctx, "mangle", "INPUT", + "-j CONNMARK --restore-mark"); + firewall_add_rule(connmark_ctx, "mangle", "POSTROUTING", + "-j CONNMARK --save-mark"); + err = firewall_enable_rules(connmark_ctx); + if (err) { + __connman_firewall_destroy(connmark_ctx); + connmark_ctx = NULL; + return err; + } + connmark_ref++; + return 0; } -bool __connman_firewall_is_up(void) +static void firewall_disable_connmark(void) { - return firewall_is_up; + connmark_ref--; + if (connmark_ref > 0) + return; + + firewall_disable_rules(connmark_ctx); + __connman_firewall_destroy(connmark_ctx); + connmark_ctx = NULL; +} + +int __connman_firewall_enable_marking(struct firewall_context *ctx, + enum connman_session_id_type id_type, + char *id, const char *src_ip, + uint32_t mark) +{ + int err; + + err = firewall_enable_connmark(); + if (err) + return err; + + switch (id_type) { + case CONNMAN_SESSION_ID_TYPE_UID: + firewall_add_rule(ctx, "mangle", "OUTPUT", + "-m owner --uid-owner %s -j MARK --set-mark %d", + id, mark); + break; + case CONNMAN_SESSION_ID_TYPE_GID: + firewall_add_rule(ctx, "mangle", "OUTPUT", + "-m owner --gid-owner %s -j MARK --set-mark %d", + id, mark); + break; + case CONNMAN_SESSION_ID_TYPE_UNKNOWN: + break; + case CONNMAN_SESSION_ID_TYPE_LSM: + default: + return -EINVAL; + } + + if (src_ip) { + firewall_add_rule(ctx, "mangle", "OUTPUT", + "-s %s -j MARK --set-mark %d", + src_ip, mark); + } + + return firewall_enable_rules(ctx); +} + +int __connman_firewall_disable_marking(struct firewall_context *ctx) +{ + firewall_disable_connmark(); + return firewall_disable_rules(ctx); } static void iterate_chains_cb(const char *chain_name, void *user_data) @@ -432,12 +604,9 @@ static void flush_all_tables(void) if (!g_file_test("/proc/net/ip_tables_names", G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) { - firewall_is_up = false; return; } - firewall_is_up = true; - flush_table("filter"); flush_table("mangle"); flush_table("nat"); @@ -447,6 +616,7 @@ int __connman_firewall_init(void) { DBG(""); + __connman_iptables_init(); flush_all_tables(); return 0; @@ -457,4 +627,5 @@ void __connman_firewall_cleanup(void) DBG(""); g_slist_free_full(managed_tables, cleanup_managed_table); + __connman_iptables_cleanup(); } diff --git a/src/firewall-nftables.c b/src/firewall-nftables.c new file mode 100644 index 00000000..1febce44 --- /dev/null +++ b/src/firewall-nftables.c @@ -0,0 +1,1133 @@ +/* + * + * Connection Manager + * + * Copyright (C) 2016 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 + * 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 + * + */ + +/* + * This file is based on the libnftnl examples: + * https://git.netfilter.org/libnftnl/tree/examples + * by Pablo Neira Ayuso. and inspiration from systemd nft implemention + * https://github.com/zonque/systemd/blob/rfc-nftnl/src/shared/firewall-util.c + * by Daniel Mack. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <time.h> +#include <string.h> +#include <errno.h> +#include <stdint.h> +#include <alloca.h> +#include <netinet/in.h> +#include <netinet/ip.h> + +#include <sys/types.h> +#include <pwd.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <linux/netfilter.h> +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter/nf_nat.h> +#include <linux/netfilter/nf_tables.h> + +#include <libmnl/libmnl.h> +#include <libnftnl/table.h> +#include <libnftnl/chain.h> +#include <libnftnl/rule.h> +#include <libnftnl/expr.h> + +#include <glib.h> + +#include "connman.h" + +#define CONNMAN_TABLE "connman" +#define CONNMAN_CHAIN_NAT_PRE "nat-prerouting" +#define CONNMAN_CHAIN_NAT_POST "nat-postrouting" +#define CONNMAN_CHAIN_ROUTE_OUTPUT "route-output" + +static bool debug_enabled = true; + +struct firewall_handle { + uint64_t handle; + const char *chain; +}; + +struct firewall_context { + struct firewall_handle rule; +}; + +struct nftables_info { + struct firewall_handle ct; +}; + +static struct nftables_info *nft_info; + +enum callback_return_type { + CALLBACK_RETURN_NONE = 0, + CALLBACK_RETURN_HANDLE, + CALLBACK_RETURN_BYTE_COUNTER, + _CALLBACK_RETURN_MAX, +}; + +struct callback_data { + enum callback_return_type type; + uint64_t value; + bool success; +}; + +static void debug_netlink_dump_rule(struct nftnl_rule *nlr) +{ + char buf[4096]; + + if (!debug_enabled) + return; + + nftnl_rule_snprintf(buf, sizeof(buf), nlr, 0, 0); + fprintf(stdout, "%s\n", buf); +} + +static void debug_mnl_dump_rule(const void *req, size_t req_size) +{ + if (!debug_enabled) + return; + + mnl_nlmsg_fprintf(stdout, req, req_size, 0); + printf("\n"); +} + +static int rule_expr_cb(struct nftnl_expr *expr, void *data) { + + struct callback_data *cb = data; + const char *name; + + name = nftnl_expr_get_str(expr, NFTNL_EXPR_NAME); + + if (strcmp(name, "counter")) { + cb->value = nftnl_expr_get_u64(expr, NFTNL_EXPR_CTR_BYTES); + cb->success = true; + } + + return 0; +} + +static int rule_cb(const struct nlmsghdr *nlh, int event, + struct callback_data *cb) +{ + struct nftnl_rule *rule; + + rule = nftnl_rule_alloc(); + if (!rule) + return MNL_CB_OK; + + if (nftnl_rule_nlmsg_parse(nlh, rule) < 0) + goto out; + + switch (cb->type) { + case CALLBACK_RETURN_HANDLE: + cb->value = nftnl_rule_get_u64(rule, NFTNL_RULE_HANDLE); + cb->success = true; + break; + + case CALLBACK_RETURN_BYTE_COUNTER: + nftnl_expr_foreach(rule, rule_expr_cb, cb); + break; + + default: + DBG("unhandled callback type %d\n", cb->type); + break; + } + +out: + nftnl_rule_free(rule); + return MNL_CB_STOP; +} + +static int chain_cb(const struct nlmsghdr *nlh, int event, + struct callback_data *cb) +{ + struct nftnl_chain *chain; + + chain = nftnl_chain_alloc(); + if (!chain) + return MNL_CB_OK; + + if (nftnl_chain_nlmsg_parse(nlh, chain) < 0) + goto out; + + switch (cb->type) { + case CALLBACK_RETURN_HANDLE: + cb->value = nftnl_chain_get_u64(chain, NFTNL_CHAIN_HANDLE); + cb->success = true; + break; + + default: + DBG("unhandled callback type %d\n", cb->type); + break; + } + +out: + nftnl_chain_free(chain); + return MNL_CB_OK; +} + +static const char *event_to_str(enum nf_tables_msg_types type) +{ + const char *table[] = { + "NFT_MSG_NEWTABLE", + "NFT_MSG_GETTABLE", + "NFT_MSG_DELTABLE", + "NFT_MSG_NEWCHAIN", + "NFT_MSG_GETCHAIN", + "NFT_MSG_DELCHAIN", + "NFT_MSG_NEWRULE", + "NFT_MSG_GETRULE", + "NFT_MSG_DELRULE", + "NFT_MSG_NEWSET", + "NFT_MSG_GETSET", + "NFT_MSG_DELSET", + "NFT_MSG_NEWSETELEM", + "NFT_MSG_GETSETELEM", + "NFT_MSG_DELSETELEM", + "NFT_MSG_NEWGEN", + "NFT_MSG_GETGEN", + "NFT_MSG_TRACE" + }; + + if (type < sizeof(table)/sizeof(table[0])) + return table[type]; + + return "unknown"; +} + +static int events_cb(const struct nlmsghdr *nlh, void *data) +{ + int event = NFNL_MSG_TYPE(nlh->nlmsg_type); + struct callback_data *cb = data; + int err = MNL_CB_OK; + + if (!cb || cb->type == CALLBACK_RETURN_NONE) + return err; + + DBG("handle event %s", event_to_str(event)); + + switch(event) { + case NFT_MSG_NEWCHAIN: + err = chain_cb(nlh, event, cb); + break; + + case NFT_MSG_NEWRULE: + err = rule_cb(nlh, event, cb); + break; + default: + DBG("unhandled event type %s", event_to_str(event)); + break; + } + + return err; +} + +static int socket_open_and_bind(struct mnl_socket **n) +{ + + struct mnl_socket *nl = NULL; + int err; + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (!nl) + return -errno; + + err = mnl_socket_bind(nl, 1 << (NFNLGRP_NFTABLES-1), + MNL_SOCKET_AUTOPID); + if (err < 0) { + err = errno; + mnl_socket_close(nl); + return -err; + } + + *n = nl; + return 0; +} + +static int send_and_dispatch(struct mnl_socket *nl, const void *req, + size_t req_size, enum callback_return_type callback_type, + uint64_t *callback_value) +{ + struct callback_data cb = {}; + uint32_t portid; + int err; + + debug_mnl_dump_rule(req, req_size); + + err = mnl_socket_sendto(nl, req, req_size); + if (err < 0) + return -errno; + + portid = mnl_socket_get_portid(nl); + cb.type = callback_type; + + for (;;) { + char buf[MNL_SOCKET_BUFFER_SIZE]; + + err = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (err <= 0) + break; + + err = mnl_cb_run(buf, err, 0, portid, events_cb, &cb); + if (err <= 0) + break; + } + + if (err < 0) + return -errno; + + if (callback_type == CALLBACK_RETURN_NONE) + return 0; + + if (cb.success) { + if (callback_value) + *callback_value = cb.value; + + return 0; + } + + return -ENOENT; +} + +static void put_batch_headers(char *buf, uint16_t type, uint32_t seq) +{ + + struct nlmsghdr *nlh; + struct nfgenmsg *nfg; + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = type; + nlh->nlmsg_flags = NLM_F_REQUEST; + nlh->nlmsg_seq = seq; + + nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_INET; + nfg->version = NFNETLINK_V0; + nfg->res_id = NFNL_SUBSYS_NFTABLES; +} + +static int add_payload(struct nftnl_rule *rule, uint32_t base, + uint32_t dreg, uint32_t offset, uint32_t len) +{ + struct nftnl_expr *expr; + + expr = nftnl_expr_alloc("payload"); + if (!expr) + return -ENOMEM; + + nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_BASE, base); + nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_DREG, dreg); + nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_OFFSET, offset); + nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_LEN, len); + + nftnl_rule_add_expr(rule, expr); + + return 0; +} + +static int add_bitwise(struct nftnl_rule *rule, int reg, const void *mask, + size_t len) +{ + struct nftnl_expr *expr; + uint8_t *xor; + + expr = nftnl_expr_alloc("bitwise"); + if (!expr) + return -ENOMEM; + + xor = alloca(len); + memset(xor, 0, len); + + nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_SREG, reg); + nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_DREG, reg); + nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_LEN, len); + nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_MASK, mask, len); + nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_XOR, xor, len); + + nftnl_rule_add_expr(rule, expr); + + return 0; +} + +static int add_cmp(struct nftnl_rule *rule, uint32_t sreg, uint32_t op, + const void *data, uint32_t data_len) +{ + struct nftnl_expr *expr; + + expr = nftnl_expr_alloc("cmp"); + if (!expr) + return -ENOMEM; + + nftnl_expr_set_u32(expr, NFTNL_EXPR_CMP_SREG, sreg); + nftnl_expr_set_u32(expr, NFTNL_EXPR_CMP_OP, op); + nftnl_expr_set(expr, NFTNL_EXPR_CMP_DATA, data, data_len); + + nftnl_rule_add_expr(rule, expr); + + return 0; +} + +static int table_cmd(struct mnl_socket *nl, struct nftnl_table *t, + uint16_t cmd, uint16_t family, uint16_t type) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct mnl_nlmsg_batch *batch; + struct nlmsghdr *nlh; + uint32_t seq = 0; + int err; + + batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), + cmd, family, type, seq++); + nftnl_table_nlmsg_build_payload(nlh, t); + nftnl_table_free(t); + mnl_nlmsg_batch_next(batch); + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + /* The current table commands do not support any callback returns. */ + err = send_and_dispatch(nl, mnl_nlmsg_batch_head(batch), + mnl_nlmsg_batch_size(batch), 0, NULL); + + mnl_nlmsg_batch_stop(batch); + return err; +} + +static int chain_cmd(struct mnl_socket *nl, struct nftnl_chain *chain, + uint16_t cmd, int family, uint16_t type, + enum callback_return_type cb_type, uint64_t *cb_val) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct mnl_nlmsg_batch *batch; + struct nlmsghdr *nlh; + uint32_t seq = 0; + int err; + + batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), + cmd, family, type, seq++); + nftnl_chain_nlmsg_build_payload(nlh, chain); + nftnl_chain_free(chain); + mnl_nlmsg_batch_next(batch); + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + err = send_and_dispatch(nl, mnl_nlmsg_batch_head(batch), + mnl_nlmsg_batch_size(batch), cb_type, cb_val); + + mnl_nlmsg_batch_stop(batch); + return err; +} + +static int rule_cmd(struct mnl_socket *nl, struct nftnl_rule *rule, + uint16_t cmd, uint16_t family, uint16_t type, + enum callback_return_type callback_type, + uint64_t *callback_value) +{ + + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct mnl_nlmsg_batch *batch; + struct nlmsghdr *nlh; + uint32_t seq = 0; + int err; + + debug_netlink_dump_rule(rule); + + batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); + put_batch_headers(mnl_nlmsg_batch_current(batch), + NFNL_MSG_BATCH_BEGIN, seq++); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), + cmd, family, type, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule); + mnl_nlmsg_batch_next(batch); + + put_batch_headers(mnl_nlmsg_batch_current(batch), + NFNL_MSG_BATCH_END, seq++); + mnl_nlmsg_batch_next(batch); + + err = send_and_dispatch(nl, mnl_nlmsg_batch_head(batch), + mnl_nlmsg_batch_size(batch), + callback_type, callback_value); + mnl_nlmsg_batch_stop(batch); + + return err; +} + +static int rule_delete(struct firewall_handle *handle) +{ + struct nftnl_rule *rule; + struct mnl_socket *nl; + int err; + + DBG(""); + + rule = nftnl_rule_alloc(); + if (!rule) + return -ENOMEM; + + nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE); + nftnl_rule_set(rule, NFTNL_RULE_CHAIN, handle->chain); + nftnl_rule_set_u64(rule, NFTNL_RULE_HANDLE, handle->handle); + + err = socket_open_and_bind(&nl); + if (err < 0) { + nftnl_rule_free(rule); + return err; + } + + err = rule_cmd(nl, rule, NFT_MSG_DELRULE, NFPROTO_IPV4, + NLM_F_ACK, 0, NULL); + nftnl_rule_free(rule); + mnl_socket_close(nl); + + return err; +} + +struct firewall_context *__connman_firewall_create(void) +{ + struct firewall_context *ctx; + + DBG(""); + + ctx = g_new0(struct firewall_context, 1); + + return ctx; +} + +void __connman_firewall_destroy(struct firewall_context *ctx) +{ + DBG(""); + + g_free(ctx); +} + +static int build_rule_nat(const char *address, unsigned char prefixlen, + const char *interface, struct nftnl_rule **res) +{ + struct nftnl_rule *rule; + struct in_addr ipv4_addr, ipv4_mask; + struct nftnl_expr *expr; + int err; + + /* + * # nft --debug netlink add rule connman nat-postrouting \ + * oifname eth0 ip saddr 10.10.0.0/24 masquerade + * + * ip connman nat-postrouting + * [ meta load oifname => reg 1 ] + * [ cmp eq reg 1 0x30687465 0x00000000 0x00000000 0x00000000 ] + * [ payload load 4b @ network header + 12 => reg 1 ] + * [ bitwise reg 1 = (reg=1 & 0x00ffffff ) ^ 0x00000000 ] + * [ cmp eq reg 1 0x00000a0a ] + * [ masq ] + */ + + rule = nftnl_rule_alloc(); + if (!rule) + return -ENOMEM; + + nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE); + nftnl_rule_set(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_NAT_POST); + + /* family ipv4 */ + nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, NFPROTO_IPV4); + + /* oifname */ + expr = nftnl_expr_alloc("meta"); + if (!expr) + goto err; + nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, NFT_META_OIFNAME); + nftnl_expr_set_u32(expr, NFTNL_EXPR_META_DREG, NFT_REG_1); + nftnl_rule_add_expr(rule, expr); + err = add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, interface, + strlen(interface) + 1); + if (err < 0) + goto err; + + /* source */ + ipv4_mask.s_addr = htonl((0xffffffff << (32 - prefixlen)) & 0xffffffff); + ipv4_addr.s_addr = inet_addr(address); + ipv4_addr.s_addr &= ipv4_mask.s_addr; + + err = add_payload(rule, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, + offsetof(struct iphdr, saddr), sizeof(struct in_addr)); + if (err < 0) + goto err; + err = add_bitwise(rule, NFT_REG_1, &ipv4_mask.s_addr, + sizeof(struct in_addr)); + if (err < 0) + goto err; + err = add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, &ipv4_addr.s_addr, + sizeof(struct in_addr)); + if (err < 0) + goto err; + + /* masquerade */ + expr = nftnl_expr_alloc("masq"); + if (!expr) + goto err; + nftnl_rule_add_expr(rule, expr); + + *res = rule; + return 0; + +err: + nftnl_rule_free(rule); + return -ENOMEM; +} + +int __connman_firewall_enable_nat(struct firewall_context *ctx, + char *address, unsigned char prefixlen, + char *interface) +{ + struct mnl_socket *nl; + struct nftnl_rule *rule; + int err; + + DBG("address %s/%d interface %s", address, (int)prefixlen, interface); + + err = socket_open_and_bind(&nl); + if (err < 0) + return err; + + err = build_rule_nat(address, prefixlen, interface, &rule); + if (err) + goto out; + + ctx->rule.chain = CONNMAN_CHAIN_NAT_POST; + err = rule_cmd(nl, rule, NFT_MSG_NEWRULE, NFPROTO_IPV4, + NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, + CALLBACK_RETURN_HANDLE, &ctx->rule.handle); + nftnl_rule_free(rule); +out: + mnl_socket_close(nl); + return err; +} + +int __connman_firewall_disable_nat(struct firewall_context *ctx) +{ + return rule_delete(&ctx->rule); +} + +static int build_rule_snat(int index, const char *address, + struct nftnl_rule **res) +{ + struct nftnl_rule *rule; + struct nftnl_expr *expr; + uint32_t snat; + int err; + + /* + * # nft --debug netlink add rule connman nat-postrouting \ + * oif eth0 snat 1.2.3.4 + * ip connman nat-postrouting + * [ meta load oif => reg 1 ] + * [ cmp eq reg 1 0x0000000b ] + * [ immediate reg 1 0x04030201 ] + * [ nat snat ip addr_min reg 1 addr_max reg 0 ] + */ + + rule = nftnl_rule_alloc(); + if (!rule) + return -ENOMEM; + + nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE); + nftnl_rule_set(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_NAT_POST); + + /* IOF */ + expr = nftnl_expr_alloc("meta"); + if (!expr) + goto err; + nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, NFT_META_OIF); + nftnl_expr_set_u32(expr, NFTNL_EXPR_META_DREG, NFT_REG_1); + nftnl_rule_add_expr(rule, expr); + err = add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, &index, sizeof(index)); + if (err < 0) + goto err; + + /* snat */ + expr = nftnl_expr_alloc("immediate"); + if (!expr) + goto err; + snat = inet_addr(address); + nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG_1); + nftnl_expr_set(expr, NFTNL_EXPR_IMM_DATA, &snat, sizeof(snat)); + nftnl_rule_add_expr(rule, expr); + + expr = nftnl_expr_alloc("nat"); + if (!expr) + goto err; + nftnl_expr_set_u32(expr, NFTNL_EXPR_NAT_TYPE, NFT_NAT_SNAT); + nftnl_expr_set_u32(expr, NFTNL_EXPR_NAT_FAMILY, NFPROTO_IPV4); + nftnl_expr_set_u32(expr, NFTNL_EXPR_NAT_REG_ADDR_MIN, NFT_REG_1); + nftnl_rule_add_expr(rule, expr); + + *res = rule; + return 0; + +err: + nftnl_rule_free(rule); + return -ENOMEM; +} + +int __connman_firewall_enable_snat(struct firewall_context *ctx, + int index, const char *ifname, const char *addr) +{ + struct nftnl_rule *rule; + struct mnl_socket *nl; + int err; + + DBG(""); + + err = socket_open_and_bind(&nl); + if (err < 0) + return err; + + err = build_rule_snat(index, addr, &rule); + if (err) + goto out; + + ctx->rule.chain = CONNMAN_CHAIN_NAT_POST; + err = rule_cmd(nl, rule, NFT_MSG_NEWRULE, NFPROTO_IPV4, + NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, + CALLBACK_RETURN_HANDLE, &ctx->rule.handle); + nftnl_rule_free(rule); +out: + mnl_socket_close(nl); + return err; +} + +int __connman_firewall_disable_snat(struct firewall_context *ctx) +{ + DBG(""); + + return rule_delete(&ctx->rule); +} + +static int build_rule_marking(uid_t uid, uint32_t mark, struct nftnl_rule **res) +{ + struct nftnl_rule *rule; + struct nftnl_expr *expr; + int err; + + /* + * http://wiki.nftables.org/wiki-nftables/index.php/Setting_packet_metainformation + * http://wiki.nftables.org/wiki-nftables/index.php/Matching_packet_metainformation + * + * # nft --debug netlink add rule connman route-output \ + * meta skuid wagi mark set 1234 + * + * ip connman route-output + * [ meta load skuid => reg 1 ] + * [ cmp eq reg 1 0x000003e8 ] + * [ immediate reg 1 0x000004d2 ] + * [ meta set mark with reg 1 ] + */ + + rule = nftnl_rule_alloc(); + if (!rule) + return -ENOMEM; + + nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE); + nftnl_rule_set(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_ROUTE_OUTPUT); + + expr = nftnl_expr_alloc("meta"); + if (!expr) + goto err; + nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, NFT_META_SKUID); + nftnl_expr_set_u32(expr, NFTNL_EXPR_META_DREG, NFT_REG_1); + nftnl_rule_add_expr(rule, expr); + err = add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, &uid, sizeof(uid)); + if (err < 0) + goto err; + + expr = nftnl_expr_alloc("immediate"); + if (!expr) + goto err; + nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG_1); + nftnl_expr_set(expr, NFTNL_EXPR_IMM_DATA, &mark, sizeof(mark)); + nftnl_rule_add_expr(rule, expr); + + expr = nftnl_expr_alloc("meta"); + if (!expr) + goto err; + nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, NFT_META_MARK); + nftnl_expr_set_u32(expr, NFTNL_EXPR_META_SREG, NFT_REG_1); + nftnl_rule_add_expr(rule, expr); + + *res = rule; + return 0; + +err: + return -ENOMEM; +} + +static int build_rule_src_ip(const char *src_ip, uint32_t mark, struct nftnl_rule **res) +{ + struct nftnl_rule *rule; + struct nftnl_expr *expr; + int err; + in_addr_t s_addr; + + /* + * # nft --debug netlink add rule connman route-output \ + * ip saddr 192.168.10.31 mark set 1234 + * + * ip connman route-output + * [ payload load 4b @ network header + 12 => reg 1 ] + * [ cmp eq reg 1 0x1f0aa8c0 ] + * [ immediate reg 1 0x000004d2 ] + * [ meta set mark with reg 1 ] + */ + + rule = nftnl_rule_alloc(); + if (!rule) + return -ENOMEM; + + nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE); + nftnl_rule_set(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_ROUTE_OUTPUT); + + /* family ipv4 */ + nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, NFPROTO_IPV4); + + /* source IP */ + err = add_payload(rule, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, + offsetof(struct iphdr, saddr), sizeof(struct in_addr)); + if (err < 0) + goto err; + + s_addr = inet_addr(src_ip); + err = add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, &s_addr, sizeof(s_addr)); + if (err < 0) + goto err; + + expr = nftnl_expr_alloc("immediate"); + if (!expr) + goto err; + nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG_1); + nftnl_expr_set(expr, NFTNL_EXPR_IMM_DATA, &mark, sizeof(mark)); + nftnl_rule_add_expr(rule, expr); + + expr = nftnl_expr_alloc("meta"); + if (!expr) + goto err; + nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, NFT_META_MARK); + nftnl_expr_set_u32(expr, NFTNL_EXPR_META_SREG, NFT_REG_1); + nftnl_rule_add_expr(rule, expr); + + *res = rule; + return 0; + +err: + return -ENOMEM; +} + +int __connman_firewall_enable_marking(struct firewall_context *ctx, + enum connman_session_id_type id_type, + char *id, const char *src_ip, + uint32_t mark) +{ + struct nftnl_rule *rule; + struct mnl_socket *nl; + struct passwd *pw; + uid_t uid; + int err; + + DBG(""); + + if (id_type == CONNMAN_SESSION_ID_TYPE_UID) { + pw = getpwnam(id); + if (!pw) + return -EINVAL; + uid = pw->pw_uid; + } + else if (!src_ip) + return -ENOTSUP; + + err = socket_open_and_bind(&nl); + if (err < 0) + return err; + + if (id_type == CONNMAN_SESSION_ID_TYPE_UID) { + err = build_rule_marking(uid, mark, &rule); + if (err < 0) + goto out; + + ctx->rule.chain = CONNMAN_CHAIN_ROUTE_OUTPUT; + err = rule_cmd(nl, rule, NFT_MSG_NEWRULE, NFPROTO_IPV4, + NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, + CALLBACK_RETURN_HANDLE, &ctx->rule.handle); + + nftnl_rule_free(rule); + } + + if (src_ip) { + err = build_rule_src_ip(src_ip, mark, &rule); + if (err < 0) + goto out; + + ctx->rule.chain = CONNMAN_CHAIN_ROUTE_OUTPUT; + err = rule_cmd(nl, rule, NFT_MSG_NEWRULE, NFPROTO_IPV4, + NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, + CALLBACK_RETURN_HANDLE, &ctx->rule.handle); + + nftnl_rule_free(rule); + } +out: + mnl_socket_close(nl); + return err; +} + +int __connman_firewall_disable_marking(struct firewall_context *ctx) +{ + int err; + + DBG(""); + + err = rule_delete(&ctx->rule); + return err; +} + +static struct nftnl_table *build_table(const char *name, uint16_t family) +{ + struct nftnl_table *table; + + table = nftnl_table_alloc(); + if (!table) + return NULL; + + nftnl_table_set_u32(table, NFTNL_TABLE_FAMILY, family); + nftnl_table_set_str(table, NFTNL_TABLE_NAME, name); + + return table; +} + + +static struct nftnl_chain *build_chain(const char *name, const char *table, + const char *type, int hooknum, int prio) +{ + struct nftnl_chain *chain; + + chain = nftnl_chain_alloc(); + if (!chain) + return NULL; + + nftnl_chain_set(chain, NFTNL_CHAIN_TABLE, table); + nftnl_chain_set(chain, NFTNL_CHAIN_NAME, name); + + if (type) + nftnl_chain_set_str(chain, NFTNL_CHAIN_TYPE, type); + + if (hooknum >= 0) + nftnl_chain_set_u32(chain, NFTNL_CHAIN_HOOKNUM, hooknum); + + if (prio >= 0) + nftnl_chain_set_u32(chain, NFTNL_CHAIN_PRIO, prio); + + return chain; +} + +static int create_table_and_chains(struct nftables_info *nft_info) +{ + struct mnl_socket *nl; + struct nftnl_table *table; + struct nftnl_chain *chain; + int err; + + + DBG(""); + + err = socket_open_and_bind(&nl); + if (err < 0) + return err; + + /* + * Add table + * http://wiki.nftables.org/wiki-nftables/index.php/Configuring_tables + */ + + /* + * # nft add table connman + */ + table = build_table(CONNMAN_TABLE, NFPROTO_IPV4); + if (!table) { + err = -ENOMEM; + goto out; + } + + err = table_cmd(nl, table, NFT_MSG_NEWTABLE, NFPROTO_IPV4, + NLM_F_CREATE|NLM_F_ACK); + if (err < 0) + goto out; + + /* + * Add basic chains + * http://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains + */ + + /* + * # nft add chain connman nat-prerouting \ + * { type nat hook prerouting priortiy 0 ; } + */ + chain = build_chain(CONNMAN_CHAIN_NAT_PRE, CONNMAN_TABLE, + "nat", NF_INET_PRE_ROUTING, 0); + if (!chain) { + err = -ENOMEM; + goto out; + } + + err = chain_cmd(nl, chain, NFT_MSG_NEWCHAIN, + NFPROTO_IPV4, NLM_F_CREATE | NLM_F_ACK, + CALLBACK_RETURN_NONE, NULL); + if (err < 0) + goto out; + + /* + * # nft add chain connman nat-postrouting \ + * { type nat hook postrouting priortiy 0 ; } + */ + chain = build_chain(CONNMAN_CHAIN_NAT_POST, CONNMAN_TABLE, + "nat", NF_INET_POST_ROUTING, 0); + if (!chain) { + err = -ENOMEM; + goto out; + } + + err = chain_cmd(nl, chain, NFT_MSG_NEWCHAIN, + NFPROTO_IPV4, NLM_F_CREATE | NLM_F_ACK, + CALLBACK_RETURN_NONE, NULL); + if (err < 0) + goto out; + + /* + * # nft add chain connman route-output \ + * { type route hook output priority 0 ; } + */ + chain = build_chain(CONNMAN_CHAIN_ROUTE_OUTPUT, CONNMAN_TABLE, + "route", NF_INET_LOCAL_OUT, 0); + if (!chain) { + err = -ENOMEM; + goto out; + } + + err = chain_cmd(nl, chain, NFT_MSG_NEWCHAIN, + NFPROTO_IPV4, NLM_F_CREATE | NLM_F_ACK, + CALLBACK_RETURN_NONE, NULL); + if (err < 0) + goto out; + +out: + if (err) + connman_warn("Failed to create basic chains: %s", + strerror(-err)); + mnl_socket_close(nl); + return err; +} + +static int cleanup_table_and_chains(void) +{ + struct nftnl_table *table; + struct mnl_socket *nl; + int err; + + DBG(""); + + err = socket_open_and_bind(&nl); + if (err < 0) + return -ENOMEM; + + /* + * Cleanup everythying in one go. There is little point in + * step-by-step removal of rules and chains if you can get it + * as simple as this. + */ + /* + * # nft delete table connman + */ + table = build_table(CONNMAN_TABLE, NFPROTO_IPV4); + err = table_cmd(nl, table, NFT_MSG_DELTABLE, NFPROTO_IPV4, NLM_F_ACK); + + mnl_socket_close(nl); + return err; +} + +int __connman_firewall_init(void) +{ + int err; + + DBG(""); + + if (getenv("CONNMAN_NFTABLES_DEBUG")) + debug_enabled = true; + + /* + * EAFNOSUPPORT is return whenever the nf_tables_ipv4 hasn't been + * loaded yet. ENOENT is return in case the table is missing. + */ + err = cleanup_table_and_chains(); + if (err < 0 && (err != EAFNOSUPPORT && err != -ENOENT)) { + connman_warn("initializing nftable failed with '%s' %d. Check if kernel module nf_tables_ipv4 is missing\n", + strerror(-err), err); + return err; + } + + nft_info = g_new0(struct nftables_info, 1); + err = create_table_and_chains(nft_info); + if (err) { + g_free(nft_info); + nft_info = NULL; + } + + return err; +} + +void __connman_firewall_cleanup(void) +{ + int err; + + DBG(""); + + err = cleanup_table_and_chains(); + if (err < 0) + connman_warn("cleanup table and chains failed with '%s' %d\n", + strerror(-err), err); + + g_free(nft_info); + nft_info = NULL; +} @@ -1307,13 +1307,12 @@ static gboolean rs_timeout_cb(gpointer user_data) return FALSE; } -static int icmpv6_recv(int fd, gpointer user_data) +static int icmpv6_recv(int fd, struct xs_cb_data *data) { struct msghdr mhdr; struct iovec iov; unsigned char chdr[CMSG_BUF_LEN]; unsigned char buf[1540]; - struct xs_cb_data *data = user_data; struct nd_router_advert *hdr; struct sockaddr_in6 saddr; ssize_t len; @@ -1335,7 +1334,6 @@ static int icmpv6_recv(int fd, gpointer user_data) len = recvmsg(fd, &mhdr, 0); if (len < 0) { cb(NULL, 0, data->user_data); - xs_cleanup(data); return -errno; } @@ -1377,7 +1375,6 @@ static int icmpv6_recv(int fd, gpointer user_data) return 0; cb(hdr, len, data->user_data); - xs_cleanup(data); return len; } @@ -1385,18 +1382,21 @@ static int icmpv6_recv(int fd, gpointer user_data) static gboolean icmpv6_event(GIOChannel *chan, GIOCondition cond, gpointer data) { int fd, ret; + struct xs_cb_data *xs_data = data; DBG(""); if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) - return FALSE; + goto cleanup; fd = g_io_channel_unix_get_fd(chan); - ret = icmpv6_recv(fd, data); + ret = icmpv6_recv(fd, xs_data); if (ret == 0) return TRUE; - return FALSE; +cleanup: + xs_cleanup(xs_data); + return TRUE; } /* Adapted from RFC 1071 "C" Implementation Example */ @@ -1759,13 +1759,12 @@ void __connman_inet_ipv6_stop_recv_rs(void *context) xs_cleanup(context); } -static int icmpv6_rs_recv(int fd, gpointer user_data) +static int icmpv6_rs_recv(int fd, struct xs_cb_data *data) { struct msghdr mhdr; struct iovec iov; unsigned char chdr[CMSG_BUF_LEN]; unsigned char buf[1540]; - struct xs_cb_data *data = user_data; struct nd_router_solicit *hdr; struct sockaddr_in6 saddr; ssize_t len; @@ -1804,17 +1803,20 @@ static gboolean icmpv6_rs_event(GIOChannel *chan, GIOCondition cond, gpointer data) { int fd, ret; + struct xs_cb_data *xs_data = data; DBG(""); if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) - return FALSE; + goto cleanup; fd = g_io_channel_unix_get_fd(chan); - ret = icmpv6_rs_recv(fd, data); + ret = icmpv6_rs_recv(fd, xs_data); if (ret == 0) return TRUE; +cleanup: + xs_data->watch_id = 0; return FALSE; } @@ -1889,13 +1891,12 @@ static gboolean ns_timeout_cb(gpointer user_data) return FALSE; } -static int icmpv6_nd_recv(int fd, gpointer user_data) +static int icmpv6_nd_recv(int fd, struct xs_cb_data *data) { struct msghdr mhdr; struct iovec iov; unsigned char chdr[CMSG_BUF_LEN]; unsigned char buf[1540]; - struct xs_cb_data *data = user_data; struct nd_neighbor_advert *hdr; struct sockaddr_in6 saddr; ssize_t len; @@ -1917,7 +1918,6 @@ static int icmpv6_nd_recv(int fd, gpointer user_data) len = recvmsg(fd, &mhdr, 0); if (len < 0) { cb(NULL, 0, &data->addr.sin6_addr, data->user_data); - xs_cleanup(data); return -errno; } @@ -1936,7 +1936,6 @@ static int icmpv6_nd_recv(int fd, gpointer user_data) return 0; cb(hdr, len, &data->addr.sin6_addr, data->user_data); - xs_cleanup(data); return len; } @@ -1945,18 +1944,21 @@ static gboolean icmpv6_nd_event(GIOChannel *chan, GIOCondition cond, gpointer data) { int fd, ret; + struct xs_cb_data *xs_data = data; DBG(""); if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) - return FALSE; + goto cleanup; fd = g_io_channel_unix_get_fd(chan); - ret = icmpv6_nd_recv(fd, data); + ret = icmpv6_nd_recv(fd, xs_data); if (ret == 0) return TRUE; - return FALSE; +cleanup: + xs_cleanup(xs_data); + return TRUE; } int __connman_inet_ipv6_do_dad(int index, int timeout_ms, @@ -2280,9 +2282,8 @@ static gboolean inet_rtnl_timeout_cb(gpointer user_data) return FALSE; } -static int inet_rtnl_recv(GIOChannel *chan, gpointer user_data) +static int inet_rtnl_recv(GIOChannel *chan, struct inet_rtnl_cb_data *rtnl_data) { - struct inet_rtnl_cb_data *rtnl_data = user_data; struct __connman_inet_rtnl_handle *rth = rtnl_data->rtnl; struct nlmsghdr *h = NULL; struct sockaddr_nl nladdr; @@ -2364,17 +2365,20 @@ static gboolean inet_rtnl_event(GIOChannel *chan, GIOCondition cond, gpointer user_data) { int ret; + struct inet_rtnl_cb_data *rtnl_data = user_data; DBG(""); if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) - return FALSE; + goto cleanup; - ret = inet_rtnl_recv(chan, user_data); - if (ret != 0) + ret = inet_rtnl_recv(chan, rtnl_data); + if (ret == 0) return TRUE; - return FALSE; +cleanup: + inet_rtnl_cleanup(rtnl_data); + return TRUE; } int __connman_inet_rtnl_talk(struct __connman_inet_rtnl_handle *rtnl, @@ -2383,13 +2387,12 @@ int __connman_inet_rtnl_talk(struct __connman_inet_rtnl_handle *rtnl, { struct sockaddr_nl nladdr; struct inet_rtnl_cb_data *data; - unsigned seq; int err; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; - n->nlmsg_seq = seq = ++rtnl->seq; + n->nlmsg_seq = ++rtnl->seq; if (callback) { data = g_try_malloc0(sizeof(struct inet_rtnl_cb_data)); @@ -2403,7 +2406,6 @@ int __connman_inet_rtnl_talk(struct __connman_inet_rtnl_handle *rtnl, inet_rtnl_timeout_cb, data); data->channel = g_io_channel_unix_new(rtnl->fd); - g_io_channel_set_close_on_unref(data->channel, TRUE); g_io_channel_set_encoding(data->channel, NULL, NULL); g_io_channel_set_buffered(data->channel, FALSE); @@ -2747,7 +2749,7 @@ char **__connman_inet_get_running_interfaces(void) result = g_try_realloc(result, (count + 1) * sizeof(char *)); if (!result) { g_free(prev_result); - goto error; + return NULL; } } @@ -3058,3 +3060,302 @@ out: close(sk); return ret; } + +static int get_nfs_server_ip(const char *cmdline_file, const char *pnp_file, + struct in_addr *addr) +{ + char *s, *nfsargs; + size_t len; + char addrstr[INET_ADDRSTRLEN]; + struct in_addr taddr; + GError *error = NULL; + char *cmdline = NULL; + char *pnp = NULL; + char **args = NULL; + char **pnpent = NULL; + char **pp = NULL; + int err = -1; + + if (!cmdline_file) + cmdline_file = "/proc/cmdline"; + if (!pnp_file) + pnp_file = "/proc/net/pnp"; + if (!addr) + addr = &taddr; + addr->s_addr = INADDR_NONE; + + if (!g_file_get_contents(cmdline_file, &cmdline, NULL, &error)) { + connman_error("%s: Cannot read %s %s\n", __func__, + cmdline_file, error->message); + goto out; + } + + if (g_file_test(pnp_file, G_FILE_TEST_EXISTS)) { + if (!g_file_get_contents(pnp_file, &pnp, NULL, &error)) { + connman_error("%s: Cannot read %s %s\n", __func__, + pnp_file, error->message); + goto out; + } + } else { + connman_error("%s: File %s doesn't exist\n", __func__, pnp_file); + goto out; + } + + len = strlen(cmdline); + if (len <= 1) { + /* too short */ + goto out; + } + /* remove newline */ + if (cmdline[len - 1] == '\n') + cmdline[--len] = '\0'; + + /* split in arguments (seperated by space) */ + args = g_strsplit(cmdline, " ", 0); + if (!args) { + connman_error("%s: Cannot split cmdline \"%s\"\n", __func__, + cmdline); + goto out; + } + + /* split in entries (by newlines) */ + pnpent = g_strsplit(pnp, "\n", 0); + if (!pnpent) { + connman_error("%s: Cannot split pnp at file \"%s\"\n", __func__, + pnp_file); + goto out; + } + + /* first find root argument */ + for (pp = args; *pp; pp++) { + if (!strcmp(*pp, "root=/dev/nfs")) + break; + } + /* no rootnfs found */ + if (!*pp) + goto out; + + /* locate nfsroot argument */ + for (pp = args; *pp; pp++) { + if (!strncmp(*pp, "nfsroot=", strlen("nfsroot="))) + break; + } + /* no nfsroot argument found */ + if (!*pp) + goto out; + + /* determine if nfsroot server is provided */ + nfsargs = strchr(*pp, '='); + if (!nfsargs) + goto out; + nfsargs++; + + /* find whether serverip is present */ + s = strchr(nfsargs, ':'); + if (s) { + len = s - nfsargs; + s = nfsargs; + } else { + /* no serverip, use bootserver */ + for (pp = pnpent; *pp; pp++) { + if (!strncmp(*pp, "bootserver ", strlen("bootserver "))) + break; + } + /* no bootserver found */ + if (!*pp) + goto out; + s = *pp + strlen("bootserver "); + len = strlen(s); + } + + /* copy to addr string buffer */ + if (len >= sizeof(addrstr)) { + connman_error("%s: Bad server\n", __func__); + goto out; + } + memcpy(addrstr, s, len); + addrstr[len] = '\0'; + + err = inet_pton(AF_INET, addrstr, addr); + if (err <= 0) { + connman_error("%s: Cannot convert to numeric addr \"%s\"\n", + __func__, addrstr); + err = -1; + goto out; + } + + /* all done */ + err = 0; +out: + g_strfreev(pnpent); + g_strfreev(args); + if (error) + g_error_free(error); + g_free(pnp); + g_free(cmdline); + + return err; +} + +/* get interface out of which peer is reachable (IPv4 only) */ +static int get_peer_iface(struct in_addr *addr, char *ifname) +{ + struct ifaddrs *ifaddr, *ifa; + struct sockaddr_in saddr, *ifsaddr; + socklen_t socklen; + int s; + int err = -1; + + /* Obtain address(es) matching host/port */ + err = getifaddrs(&ifaddr); + if (err < 0) { + connman_error("%s: getifaddrs() failed %d (%s)\n", + __func__, errno, strerror(errno)); + return -1; + } + + s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s < 0) { + connman_error("%s: socket() failed %d (%s)\n", + __func__, errno, strerror(errno)); + return -1; + } + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = 0; /* any port */ + saddr.sin_addr = *addr; + + /* no need to bind, connect will select iface */ + err = connect(s, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)); + if (err < 0) { + connman_error("%s: connect() failed: %d (%s)\n", + __func__, errno, strerror(errno)); + goto out; + } + + socklen = sizeof(saddr); + err = getsockname(s, (struct sockaddr *)&saddr, &socklen); + if (err < 0) { + connman_error("%s: getsockname() failed: %d (%s)\n", + __func__, errno, strerror(errno)); + goto out; + } + + err = -1; + for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) + continue; + + /* only IPv4 address */ + if (ifa->ifa_addr->sa_family != AF_INET) + continue; + + ifsaddr = (struct sockaddr_in *)ifa->ifa_addr; + + /* match address? */ + if (ifsaddr->sin_addr.s_addr == saddr.sin_addr.s_addr) + break; + } + + if (ifa) { + err = 0; + if (ifname) + strcpy(ifname, ifa->ifa_name); + } + +out: + close(s); + + freeifaddrs(ifaddr); + + return err; +} + +bool __connman_inet_isrootnfs_device(const char *devname) +{ + struct in_addr addr; + char ifname[IFNAMSIZ]; + + return get_nfs_server_ip(NULL, NULL, &addr) == 0 && + get_peer_iface(&addr, ifname) == 0 && + strcmp(devname, ifname) == 0; +} + +char **__connman_inet_get_pnp_nameservers(const char *pnp_file) +{ + char **pp; + char *s; + int pass, count; + GError *error = NULL; + char *pnp = NULL; + char **pnpent = NULL; + char **nameservers = NULL; + + if (!pnp_file) + pnp_file = "/proc/net/pnp"; + + if (!g_file_get_contents(pnp_file, &pnp, NULL, &error)) { + connman_error("%s: Cannot read %s %s\n", __func__, + pnp_file, error->message); + goto out; + } + + /* split in entries (by newlines) */ + pnpent = g_strsplit(pnp, "\n", 0); + if (!pnpent) { + connman_error("%s: Cannot split pnp \"%s\"\n", __func__, + pnp_file); + goto out; + } + + /* + * Perform two passes to retreive a char ** array of + * nameservers that are not 0.0.0.0 + * + * The first pass counts them, the second fills in the + * array. + */ + count = 0; + nameservers = NULL; + for (pass = 1; pass <= 2; pass++) { + + /* at the start of the second pass allocate */ + if (pass == 2) + nameservers = g_new(char *, count + 1); + + count = 0; + for (pp = pnpent; *pp; pp++) { + /* match 'nameserver ' at the start of each line */ + if (strncmp(*pp, "nameserver ", strlen("nameserver "))) + continue; + + /* compare it against 0.0.0.0 */ + s = *pp + strlen("nameserver "); + if (!strcmp(s, "0.0.0.0")) + continue; + + /* on second pass fill in array */ + if (pass == 2) + nameservers[count] = g_strdup(s); + count++; + } + + /* no nameservers? */ + if (count == 0) + goto out; + + /* and terminate char ** array with NULL */ + if (pass == 2) + nameservers[count] = NULL; + + } + +out: + g_strfreev(pnpent); + g_free(pnp); + if (error) + g_error_free(error); + + return nameservers; +} diff --git a/src/ipconfig.c b/src/ipconfig.c index ff1909d3..d94b8734 100755 --- a/src/ipconfig.c +++ b/src/ipconfig.c @@ -48,7 +48,6 @@ struct connman_ipconfig { const struct connman_ipconfig_ops *ops; void *ops_data; - bool enabled; enum connman_ipconfig_method method; struct connman_ipaddress *address; struct connman_ipaddress *system; @@ -413,40 +412,6 @@ static void free_ipdevice(gpointer data) g_free(ipdevice); } -static void __connman_ipconfig_lower_up(struct connman_ipdevice *ipdevice) -{ - DBG("ipconfig ipv4 %p ipv6 %p", ipdevice->config_ipv4, - ipdevice->config_ipv6); -#if defined TIZEN_EXT - if (ipdevice->config_ipv6 != NULL && - ipdevice->config_ipv6->enabled == TRUE) - return; - - char *ifname = connman_inet_ifname(ipdevice->index); - - if (__connman_device_isfiltered(ifname) == FALSE) { - ipdevice->ipv6_enabled = get_ipv6_state(ifname); - set_ipv6_state(ifname, FALSE); - } - g_free(ifname); -#endif -} - -static void __connman_ipconfig_lower_down(struct connman_ipdevice *ipdevice) -{ - DBG("ipconfig ipv4 %p ipv6 %p", ipdevice->config_ipv4, - ipdevice->config_ipv6); - - if (ipdevice->config_ipv4) - connman_inet_clear_address(ipdevice->index, - ipdevice->config_ipv4->address); - - if (ipdevice->config_ipv6) - connman_inet_clear_ipv6_address(ipdevice->index, - ipdevice->config_ipv6->address->local, - ipdevice->config_ipv6->address->prefixlen); -} - static void update_stats(struct connman_ipdevice *ipdevice, const char *ifname, struct rtnl_link_stats *stats) { @@ -606,11 +571,6 @@ update: g_list_free(ipconfig_copy); - if (lower_up) - __connman_ipconfig_lower_up(ipdevice); - if (lower_down) - __connman_ipconfig_lower_down(ipdevice); - out: g_free(ifname); } @@ -651,8 +611,6 @@ void __connman_ipconfig_dellink(int index, struct rtnl_link_stats *stats) g_free(ifname); - __connman_ipconfig_lower_down(ipdevice); - g_hash_table_remove(ipdevice_hash, GINT_TO_POINTER(index)); } @@ -1178,8 +1136,6 @@ static struct connman_ipconfig *create_ipv6config(int index) struct connman_ipconfig *ipv6config; struct connman_ipdevice *ipdevice; - DBG("index %d", index); - ipv6config = g_try_new0(struct connman_ipconfig, 1); if (!ipv6config) return NULL; @@ -1187,7 +1143,6 @@ static struct connman_ipconfig *create_ipv6config(int index) ipv6config->refcount = 1; ipv6config->index = index; - ipv6config->enabled = false; ipv6config->type = CONNMAN_IPCONFIG_TYPE_IPV6; if (!is_ipv6_supported) @@ -1210,7 +1165,7 @@ static struct connman_ipconfig *create_ipv6config(int index) ipv6config->system = connman_ipaddress_alloc(AF_INET6); - DBG("ipconfig %p method %s", ipv6config, + DBG("ipconfig %p index %d method %s", ipv6config, index, __connman_ipconfig_method2string(ipv6config->method)); return ipv6config; @@ -1231,8 +1186,6 @@ struct connman_ipconfig *__connman_ipconfig_create(int index, if (type == CONNMAN_IPCONFIG_TYPE_IPV6) return create_ipv6config(index); - DBG("index %d", index); - ipconfig = g_try_new0(struct connman_ipconfig, 1); if (!ipconfig) return NULL; @@ -1240,7 +1193,6 @@ struct connman_ipconfig *__connman_ipconfig_create(int index, ipconfig->refcount = 1; ipconfig->index = index; - ipconfig->enabled = false; ipconfig->type = CONNMAN_IPCONFIG_TYPE_IPV4; ipconfig->address = connman_ipaddress_alloc(AF_INET); @@ -1251,7 +1203,7 @@ struct connman_ipconfig *__connman_ipconfig_create(int index, ipconfig->system = connman_ipaddress_alloc(AF_INET); - DBG("ipconfig %p", ipconfig); + DBG("ipconfig %p index %d", ipconfig, index); return ipconfig; } @@ -1384,8 +1336,6 @@ enum connman_ipconfig_method __connman_ipconfig_get_method( int __connman_ipconfig_address_add(struct connman_ipconfig *ipconfig) { - DBG(""); - switch (ipconfig->method) { case CONNMAN_IPCONFIG_METHOD_UNKNOWN: case CONNMAN_IPCONFIG_METHOD_OFF: @@ -1409,13 +1359,9 @@ int __connman_ipconfig_address_remove(struct connman_ipconfig *ipconfig) { int err; - DBG(""); - if (!ipconfig) return 0; - DBG("method %d", ipconfig->method); - switch (ipconfig->method) { case CONNMAN_IPCONFIG_METHOD_UNKNOWN: case CONNMAN_IPCONFIG_METHOD_OFF: @@ -1437,8 +1383,6 @@ int __connman_ipconfig_address_unset(struct connman_ipconfig *ipconfig) { int err; - DBG(""); - if (!ipconfig) return 0; @@ -1478,8 +1422,6 @@ int __connman_ipconfig_set_proxy_autoconfig(struct connman_ipconfig *ipconfig, { struct connman_ipdevice *ipdevice; - DBG("ipconfig %p", ipconfig); - if (!ipconfig || ipconfig->index < 0) return -ENODEV; @@ -1498,8 +1440,6 @@ const char *__connman_ipconfig_get_proxy_autoconfig(struct connman_ipconfig *ipc { struct connman_ipdevice *ipdevice; - DBG("ipconfig %p", ipconfig); - if (!ipconfig || ipconfig->index < 0) return NULL; @@ -1652,8 +1592,6 @@ int __connman_ipconfig_enable(struct connman_ipconfig *ipconfig) } else return -EINVAL; - ipconfig->enabled = true; - if (type == CONNMAN_IPCONFIG_TYPE_IPV4 && ipdevice->config_ipv4) { ipconfig_list = g_list_remove(ipconfig_list, @@ -1728,8 +1666,6 @@ int __connman_ipconfig_disable(struct connman_ipconfig *ipconfig) if (!ipdevice->config_ipv4 && !ipdevice->config_ipv6) return -EINVAL; - ipconfig->enabled = false; - if (ipdevice->config_ipv4 == ipconfig) { ipconfig_list = g_list_remove(ipconfig_list, ipconfig); @@ -1843,8 +1779,6 @@ int __connman_ipconfig_ipv6_set_privacy(struct connman_ipconfig *ipconfig, if (!ipconfig) return -EINVAL; - DBG("ipconfig %p privacy %s", ipconfig, value); - privacy = string2privacy(value); ipconfig->ipv6_privacy_config = privacy; @@ -1859,9 +1793,6 @@ void __connman_ipconfig_append_ipv4(struct connman_ipconfig *ipconfig, { struct connman_ipaddress *append_addr = NULL; const char *str; -#if defined TIZEN_EXT - DBG(""); -#endif if (ipconfig->type != CONNMAN_IPCONFIG_TYPE_IPV4) return; @@ -1875,13 +1806,13 @@ void __connman_ipconfig_append_ipv4(struct connman_ipconfig *ipconfig, switch (ipconfig->method) { case CONNMAN_IPCONFIG_METHOD_UNKNOWN: case CONNMAN_IPCONFIG_METHOD_OFF: - case CONNMAN_IPCONFIG_METHOD_AUTO: return; case CONNMAN_IPCONFIG_METHOD_FIXED: append_addr = ipconfig->address; break; + case CONNMAN_IPCONFIG_METHOD_AUTO: case CONNMAN_IPCONFIG_METHOD_MANUAL: case CONNMAN_IPCONFIG_METHOD_DHCP: append_addr = ipconfig->system; @@ -1936,9 +1867,6 @@ void __connman_ipconfig_append_ipv6(struct connman_ipconfig *ipconfig, { struct connman_ipaddress *append_addr = NULL; const char *str, *privacy; -#if defined TIZEN_EXT - DBG(""); -#endif if (ipconfig->type != CONNMAN_IPCONFIG_TYPE_IPV6) return; @@ -2000,9 +1928,6 @@ void __connman_ipconfig_append_ipv6config(struct connman_ipconfig *ipconfig, DBusMessageIter *iter) { const char *str, *privacy; -#if !defined TIZEN_EXT - DBG(""); -#endif str = __connman_ipconfig_method2string(ipconfig->method); if (!str) @@ -2045,9 +1970,6 @@ void __connman_ipconfig_append_ipv4config(struct connman_ipconfig *ipconfig, DBusMessageIter *iter) { const char *str; -#if !defined TIZEN_EXT - DBG(""); -#endif str = __connman_ipconfig_method2string(ipconfig->method); if (!str) @@ -2099,8 +2021,6 @@ int __connman_ipconfig_set_config(struct connman_ipconfig *ipconfig, DBusMessageIter dict; int type = -1; - DBG("ipconfig %p", ipconfig); - if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY) return -EINVAL; @@ -2375,6 +2295,20 @@ int __connman_ipconfig_load(struct connman_ipconfig *ipconfig, g_free(key); break; + case CONNMAN_IPCONFIG_METHOD_AUTO: + + if (ipconfig->type != CONNMAN_IPCONFIG_TYPE_IPV4) + break; + + /* + * If the last used method for IPv4 was AUTO then we + * try first DHCP. We will try also to use the last + * used DHCP address, if exits. + */ + __connman_ipconfig_set_method(ipconfig, + CONNMAN_IPCONFIG_METHOD_DHCP); + /* fall through */ + case CONNMAN_IPCONFIG_METHOD_DHCP: key = g_strdup_printf("%sDHCP.LastAddress", prefix); @@ -2386,9 +2320,6 @@ int __connman_ipconfig_load(struct connman_ipconfig *ipconfig, g_free(key); break; - - case CONNMAN_IPCONFIG_METHOD_AUTO: - break; } return 0; @@ -2488,6 +2419,8 @@ int __connman_ipconfig_save(struct connman_ipconfig *ipconfig, if (ipconfig->address->gateway) g_key_file_set_string(keyfile, identifier, key, ipconfig->address->gateway); + else + g_key_file_remove_key(keyfile, identifier, key, NULL); g_free(key); return 0; diff --git a/src/ippool.c b/src/ippool.c index bb8568d9..cea1dccd 100755 --- a/src/ippool.c +++ b/src/ippool.c @@ -58,7 +58,6 @@ struct connman_ippool { }; GSList *allocated_blocks; -GHashTable *pool_hash; static uint32_t last_block; static uint32_t block_16_bits; @@ -90,7 +89,18 @@ void __connman_ippool_unref_debug(struct connman_ippool *pool, if (__sync_fetch_and_sub(&pool->refcount, 1) != 1) return; - g_hash_table_remove(pool_hash, pool); + if (pool->info) { + allocated_blocks = g_slist_remove(allocated_blocks, pool->info); + g_free(pool->info); + } + + g_free(pool->gateway); + g_free(pool->broadcast); + g_free(pool->start_ip); + g_free(pool->end_ip); + g_free(pool->subnet_mask); + + g_free(pool); } static char *get_ip(uint32_t ip) @@ -181,10 +191,10 @@ static uint32_t get_free_block(unsigned int size) * To only thing we have to make sure is that we terminated if * there is no block left. */ - if (last_block == 0) - block = block_16_bits; + if (last_block) + block = last_block; else - block = next_block(last_block); + block = block_16_bits; do { collision = false; @@ -393,7 +403,6 @@ struct connman_ippool *__connman_ippool_create(int index, pool->end_ip = get_ip(block + start + range); allocated_blocks = g_slist_prepend(allocated_blocks, info); - g_hash_table_insert(pool_hash, pool, pool); return pool; } @@ -423,24 +432,6 @@ const char *__connman_ippool_get_subnet_mask(struct connman_ippool *pool) return pool->subnet_mask; } -static void pool_free(gpointer data) -{ - struct connman_ippool *pool = data; - - if (pool->info) { - allocated_blocks = g_slist_remove(allocated_blocks, pool->info); - g_free(pool->info); - } - - g_free(pool->gateway); - g_free(pool->broadcast); - g_free(pool->start_ip); - g_free(pool->end_ip); - g_free(pool->subnet_mask); - - g_free(pool); -} - int __connman_ippool_init(void) { DBG(""); @@ -450,9 +441,6 @@ int __connman_ippool_init(void) block_24_bits = ntohl(inet_addr("10.0.0.0")); subnet_mask_24 = ntohl(inet_addr("255.255.255.0")); - pool_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, - pool_free); - return 0; } @@ -460,9 +448,6 @@ void __connman_ippool_cleanup(void) { DBG(""); - g_hash_table_destroy(pool_hash); - pool_hash = NULL; - g_slist_free_full(allocated_blocks, g_free); last_block = 0; allocated_blocks = NULL; diff --git a/src/iptables.c b/src/iptables.c index a5774ada..aaddf9d6 100755 --- a/src/iptables.c +++ b/src/iptables.c @@ -31,6 +31,7 @@ #include <sys/errno.h> #include <sys/socket.h> #include <xtables.h> +#include <inttypes.h> #include <linux/netfilter_ipv4/ip_tables.h> @@ -154,6 +155,7 @@ struct error_target { struct connman_iptables_entry { int offset; int builtin; + int counter_idx; struct ipt_entry *entry; }; @@ -251,8 +253,9 @@ static int print_entry(struct ipt_entry *entry, int builtin, unsigned int hook, { iterate_entries_cb_t cb = user_data; - DBG("entry %p hook %d offset %d size %d", entry, hook, - offset, entry->next_offset); + DBG("entry %p hook %u offset %u size %u packets %"PRIu64" bytes %"PRIu64, + entry, hook, offset, (unsigned int) entry->next_offset, + (uint64_t) entry->counters.pcnt, (uint64_t) entry->counters.bcnt); return cb(entry, builtin, hook, size, offset, NULL); } @@ -460,7 +463,7 @@ static void update_targets_reference(struct connman_iptables *table, static int iptables_add_entry(struct connman_iptables *table, struct ipt_entry *entry, GList *before, - int builtin) + int builtin, int counter_idx) { struct connman_iptables_entry *e, *entry_before; @@ -473,6 +476,7 @@ static int iptables_add_entry(struct connman_iptables *table, e->entry = entry; e->builtin = builtin; + e->counter_idx = counter_idx; table->entries = g_list_insert_before(table->entries, before, e); table->num_entries++; @@ -620,7 +624,7 @@ static int iptables_add_chain(struct connman_iptables *table, error->t.u.user.target_size = ALIGN(sizeof(struct error_target)); g_stpcpy(error->error, name); - if (iptables_add_entry(table, entry_head, last, -1) < 0) + if (iptables_add_entry(table, entry_head, last, -1, -1) < 0) goto err_head; /* tail entry */ @@ -638,7 +642,7 @@ static int iptables_add_chain(struct connman_iptables *table, ALIGN(sizeof(struct ipt_standard_target)); standard->verdict = XT_RETURN; - if (iptables_add_entry(table, entry_return, last, -1) < 0) + if (iptables_add_entry(table, entry_return, last, -1, -1) < 0) goto err; return 0; @@ -826,7 +830,7 @@ static int iptables_append_rule(struct connman_iptables *table, if (!new_entry) return -EINVAL; - ret = iptables_add_entry(table, new_entry, chain_tail->prev, builtin); + ret = iptables_add_entry(table, new_entry, chain_tail->prev, builtin, -1); if (ret < 0) g_free(new_entry); @@ -857,7 +861,7 @@ static int iptables_insert_rule(struct connman_iptables *table, if (builtin == -1) chain_head = chain_head->next; - ret = iptables_add_entry(table, new_entry, chain_head, builtin); + ret = iptables_add_entry(table, new_entry, chain_head, builtin, -1); if (ret < 0) g_free(new_entry); @@ -1128,6 +1132,8 @@ static int iptables_change_policy(struct connman_iptables *table, target = ipt_get_target(entry->entry); t = (struct xt_standard_target *)target; + if (t->verdict != verdict) + entry->counter_idx = -1; t->verdict = verdict; return 0; @@ -1405,6 +1411,19 @@ static int iptables_replace(struct connman_iptables *table, return 0; } +static int iptables_add_counters(struct connman_iptables *table, + struct xt_counters_info *c) +{ + int err; + + err = setsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_SET_ADD_COUNTERS, c, + sizeof(*c) + sizeof(struct xt_counters) * c->num_counters); + if (err < 0) + return -errno; + + return 0; +} + static int add_entry(struct ipt_entry *entry, int builtin, unsigned int hook, size_t size, unsigned offset, void *user_data) { @@ -1417,7 +1436,8 @@ static int add_entry(struct ipt_entry *entry, int builtin, unsigned int hook, memcpy(new_entry, entry, entry->next_offset); - return iptables_add_entry(table, new_entry, NULL, builtin); + return iptables_add_entry(table, new_entry, NULL, builtin, + table->num_entries); } static void table_cleanup(struct connman_iptables *table) @@ -1748,6 +1768,7 @@ struct parse_context { struct xtables_target *xt_t; GList *xt_m; struct xtables_rule_match *xt_rm; + int proto; }; static int prepare_getopt_args(const char *str, struct parse_context *ctx) @@ -1787,6 +1808,14 @@ static int parse_xt_modules(int c, bool invert, { struct xtables_match *m; struct xtables_rule_match *rm; + struct ipt_entry fw; + + memset(&fw, 0, sizeof(fw)); + + /* The SNAT parser wants to know the protocol. */ + if (ctx->proto == 0) + ctx->proto = IPPROTO_IP; + fw.ip.proto = ctx->proto; for (rm = ctx->xt_rm; rm; rm = rm->next) { if (rm->completed != 0) @@ -1802,7 +1831,7 @@ static int parse_xt_modules(int c, bool invert, + XT_OPTION_OFFSET_SCALE) continue; - xtables_option_mpcall(c, ctx->argv, invert, m, NULL); + xtables_option_mpcall(c, ctx->argv, invert, m, &fw); } if (!ctx->xt_t) @@ -1816,7 +1845,7 @@ static int parse_xt_modules(int c, bool invert, + XT_OPTION_OFFSET_SCALE) return 0; - xtables_option_tpcall(c, ctx->argv, invert, ctx->xt_t, NULL); + xtables_option_tpcall(c, ctx->argv, invert, ctx->xt_t, &fw); return 0; } @@ -1991,6 +2020,9 @@ static int parse_rule_spec(struct connman_iptables *table, ctx->xt_m = g_list_append(ctx->xt_m, xt_m); break; + case 'p': + ctx->proto = xtables_parse_protocol(optarg); + break; case 'j': /* Target */ ctx->xt_t = prepare_target(table, optarg); @@ -2306,6 +2338,10 @@ int __connman_iptables_commit(const char *table_name) struct connman_iptables *table; struct ipt_replace *repl; int err; + struct xt_counters_info *counters; + struct connman_iptables_entry *e; + GList *list; + unsigned int cnt; DBG("%s", table_name); @@ -2314,25 +2350,44 @@ int __connman_iptables_commit(const char *table_name) return -EINVAL; repl = iptables_blob(table); -#if defined TIZEN_EXT if(!repl) return -ENOMEM; -#endif if (debug_enabled) dump_ipt_replace(repl); err = iptables_replace(table, repl); - g_free(repl->counters); - g_free(repl); + if (err < 0) + goto out_free; + + counters = g_try_malloc0(sizeof(*counters) + + sizeof(struct xt_counters) * table->num_entries); + if (!counters) { + err = -ENOMEM; + goto out_hash_remove; + } + g_stpcpy(counters->name, table->info->name); + counters->num_counters = table->num_entries; + for (list = table->entries, cnt = 0; list; list = list->next, cnt++) { + e = list->data; + if (e->counter_idx >= 0) + counters->counters[cnt] = repl->counters[e->counter_idx]; + } + err = iptables_add_counters(table, counters); + g_free(counters); if (err < 0) - return err; + goto out_hash_remove; - g_hash_table_remove(table_hash, table_name); + err = 0; - return 0; +out_hash_remove: + g_hash_table_remove(table_hash, table_name); +out_free: + g_free(repl->counters); + g_free(repl); + return err; } static void remove_table(gpointer user_data) @@ -216,6 +216,7 @@ void connman_error(const char *format, ...) vsyslog(LOG_ERR, format, ap); va_end(ap); + fflush(log_file); } /** @@ -234,6 +235,7 @@ void connman_debug(const char *format, ...) vsyslog(LOG_DEBUG, format, ap); va_end(ap); + fflush(log_file); } static void print_backtrace(unsigned int offset) @@ -59,6 +59,8 @@ static char *default_blacklist[] = { "vboxnet", "virbr", "ifb", + "ve-", + "vb-", NULL }; @@ -67,6 +69,7 @@ static struct { char **pref_timeservers; unsigned int *auto_connect; unsigned int *preferred_techs; + unsigned int *always_connected_techs; char **fallback_nameservers; unsigned int timeout_inputreq; unsigned int timeout_browserlaunch; @@ -76,6 +79,8 @@ static struct { char **tethering_technologies; bool persistent_tethering_mode; bool enable_6to4; + char *vendor_class_id; + bool enable_online_check; #if defined TIZEN_EXT char **cellular_interfaces; bool tizen_tv_extension; @@ -85,6 +90,7 @@ static struct { .pref_timeservers = NULL, .auto_connect = NULL, .preferred_techs = NULL, + .always_connected_techs = NULL, .fallback_nameservers = NULL, .timeout_inputreq = DEFAULT_INPUT_REQUEST_TIMEOUT, .timeout_browserlaunch = DEFAULT_BROWSER_LAUNCH_TIMEOUT, @@ -94,6 +100,8 @@ static struct { .tethering_technologies = NULL, .persistent_tethering_mode = false, .enable_6to4 = false, + .vendor_class_id = NULL, + .enable_online_check = true, #if defined TIZEN_EXT .cellular_interfaces = NULL, .tizen_tv_extension = false, @@ -103,6 +111,7 @@ static struct { #define CONF_BG_SCAN "BackgroundScanning" #define CONF_PREF_TIMESERVERS "FallbackTimeservers" #define CONF_AUTO_CONNECT "DefaultAutoConnectTechnologies" +#define CONF_ALWAYS_CONNECTED_TECHS "AlwaysConnectedTechnologies" #define CONF_PREFERRED_TECHS "PreferredTechnologies" #define CONF_FALLBACK_NAMESERVERS "FallbackNameservers" #define CONF_TIMEOUT_INPUTREQ "InputRequestTimeout" @@ -113,6 +122,8 @@ static struct { #define CONF_TETHERING_TECHNOLOGIES "TetheringTechnologies" #define CONF_PERSISTENT_TETHERING_MODE "PersistentTetheringMode" #define CONF_ENABLE_6TO4 "Enable6to4" +#define CONF_VENDOR_CLASS_ID "VendorClassID" +#define CONF_ENABLE_ONLINE_CHECK "EnableOnlineCheck" #if defined TIZEN_EXT #define CONF_CELLULAR_INTERFACE "NetworkCellularInterfaceList" #define CONF_TIZEN_TV_EXT "TizenTVExtension" @@ -122,6 +133,7 @@ static const char *supported_options[] = { CONF_BG_SCAN, CONF_PREF_TIMESERVERS, CONF_AUTO_CONNECT, + CONF_ALWAYS_CONNECTED_TECHS, CONF_PREFERRED_TECHS, CONF_FALLBACK_NAMESERVERS, CONF_TIMEOUT_INPUTREQ, @@ -132,6 +144,7 @@ static const char *supported_options[] = { CONF_TETHERING_TECHNOLOGIES, CONF_PERSISTENT_TETHERING_MODE, CONF_ENABLE_6TO4, + CONF_ENABLE_ONLINE_CHECK, #if defined TIZEN_EXT CONF_CELLULAR_INTERFACE, CONF_TIZEN_TV_EXT, @@ -298,6 +311,7 @@ static void parse_config(GKeyFile *config) char **interfaces; char **str_list; char **tethering; + char *vendor_class_id; gsize len; int timeout; @@ -351,6 +365,17 @@ static void parse_config(GKeyFile *config) g_clear_error(&error); str_list = __connman_config_get_string_list(config, "General", + CONF_ALWAYS_CONNECTED_TECHS, &len, &error); + + if (!error) + connman_settings.always_connected_techs = + parse_service_types(str_list, len); + + g_strfreev(str_list); + + g_clear_error(&error); + + str_list = __connman_config_get_string_list(config, "General", CONF_FALLBACK_NAMESERVERS, &len, &error); if (!error) @@ -424,6 +449,23 @@ static void parse_config(GKeyFile *config) g_clear_error(&error); + vendor_class_id = __connman_config_get_string(config, "General", + CONF_VENDOR_CLASS_ID, &error); + if (!error) + connman_settings.vendor_class_id = vendor_class_id; + + g_clear_error(&error); + + boolean = __connman_config_get_bool(config, "General", + CONF_ENABLE_ONLINE_CHECK, &error); + if (!error) { + connman_settings.enable_online_check = boolean; + if (!boolean) + connman_info("Online check disabled by main config."); + } + + g_clear_error(&error); + #if defined TIZEN_EXT check_Tizen_configuration(config); #endif @@ -539,10 +581,34 @@ static gboolean option_version = FALSE; static bool parse_debug(const char *key, const char *value, gpointer user_data, GError **error) { - if (value) - option_debug = g_strdup(value); - else + if (value) { + if (option_debug) { + char *prev = option_debug; + + option_debug = g_strconcat(prev, ",", value, NULL); + g_free(prev); + } else { + option_debug = g_strdup(value); + } + } else { + g_free(option_debug); option_debug = g_strdup("*"); + } + + return true; +} + +static bool parse_noplugin(const char *key, const char *value, + gpointer user_data, GError **error) +{ + if (option_noplugin) { + char *prev = option_noplugin; + + option_noplugin = g_strconcat(prev, ",", value, NULL); + g_free(prev); + } else { + option_noplugin = g_strdup(value); + } return true; } @@ -560,7 +626,7 @@ static GOptionEntry options[] = { "Specify networking interface to ignore", "DEV" }, { "plugin", 'p', 0, G_OPTION_ARG_STRING, &option_plugin, "Specify plugins to load", "NAME,..." }, - { "noplugin", 'P', 0, G_OPTION_ARG_STRING, &option_noplugin, + { "noplugin", 'P', 0, G_OPTION_ARG_CALLBACK, &parse_noplugin, "Specify plugins not to load", "NAME,..." }, { "wifi", 'W', 0, G_OPTION_ARG_STRING, &option_wifi, "Specify driver for WiFi/Supplicant", "NAME" }, @@ -580,6 +646,9 @@ static GOptionEntry options[] = { const char *connman_option_get_string(const char *key) { + if (g_str_equal(key, CONF_VENDOR_CLASS_ID)) + return connman_settings.vendor_class_id; + if (g_strcmp0(key, "wifi") == 0) { if (!option_wifi) return "nl80211,wext"; @@ -607,6 +676,9 @@ bool connman_setting_get_bool(const char *key) if (g_str_equal(key, CONF_ENABLE_6TO4)) return connman_settings.enable_6to4; + if (g_str_equal(key, CONF_ENABLE_ONLINE_CHECK)) + return connman_settings.enable_online_check; + return false; } @@ -640,6 +712,9 @@ unsigned int *connman_setting_get_uint_list(const char *key) if (g_str_equal(key, CONF_PREFERRED_TECHS)) return connman_settings.preferred_techs; + if (g_str_equal(key, CONF_ALWAYS_CONNECTED_TECHS)) + return connman_settings.always_connected_techs; + return NULL; } @@ -737,7 +812,6 @@ int main(int argc, char *argv[]) __connman_device_init(option_device, option_nodevice); __connman_ippool_init(); - __connman_iptables_init(); __connman_firewall_init(); __connman_nat_init(); __connman_tethering_init(); @@ -746,7 +820,6 @@ int main(int argc, char *argv[]) __connman_stats_init(); __connman_clock_init(); - __connman_resolver_init(option_dnsproxy); __connman_ipconfig_init(); __connman_rtnl_init(); __connman_task_init(); @@ -758,6 +831,7 @@ int main(int argc, char *argv[]) __connman_plugin_init(option_plugin, option_noplugin); + __connman_resolver_init(option_dnsproxy); __connman_rtnl_start(); __connman_dhcp_init(); __connman_dhcpv6_init(); @@ -804,7 +878,6 @@ int main(int argc, char *argv[]) __connman_tethering_cleanup(); __connman_nat_cleanup(); __connman_firewall_cleanup(); - __connman_iptables_cleanup(); __connman_peer_service_cleanup(); __connman_peer_cleanup(); __connman_ippool_cleanup(); diff --git a/src/main.conf b/src/main.conf index d0edbdf7..a2cc1e20 100755 --- a/src/main.conf +++ b/src/main.conf @@ -60,8 +60,8 @@ PreferredTechnologies = wifi, ethernet # Found interfaces will be compared to the list and will # not be handled by connman, if their first characters # match any of the list entries. Default value is -# vmnet,vboxnet,virbr,ifb. -# NetworkInterfaceBlacklist = vmnet,vboxnet,virbr,ifb +# vmnet,vboxnet,virbr,ifb,ve-,vb-. +# NetworkInterfaceBlacklist = vmnet,vboxnet,virbr,ifb,ve-,vb- NetworkInterfaceBlacklist = veth, vmnet,vboxnet,virbr,usb,rndis,rmnet,rev_rmnet,dummy,seth_td,seth_w # Allow connman to change the system hostname. This can @@ -107,4 +107,20 @@ SingleConnectedTechnology = true # section 4.1). # Enable6to4 = false +# Enable use of http get as on online status check. +# When a service is in a READY state, and is selected as default, +# ConnMan will issue an HTTP GET request to verify that end-to-end +# connectivity is successful. Only then the service will be +# transitioned to ONLINE state. +# If this setting is false, the default service will remain in READY state. +# Default value is true. +# EnableOnlineCheck = false + +# List of technologies with AutoConnect = true which are always connected +# regardless of PreferredTechnologies setting. Default value is empty and +# will connect a technology only if it is at a higher preference than any +# other which is already connected. +# This setting has no effect if SingleConnectedTechnologies is enabled. +# AlwaysConnectedTechnologies = + NetworkCellularInterfaceList = pdp,rmnet,seth_td,seth_w @@ -25,7 +25,10 @@ #endif #include <errno.h> -#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> #include "connman.h" @@ -42,46 +45,52 @@ struct connman_nat { static int enable_ip_forward(bool enable) { - FILE *f; + static char value = 0; + int f, err = 0; - f = fopen("/proc/sys/net/ipv4/ip_forward", "r+"); - if (!f) + if ((f = open("/proc/sys/net/ipv4/ip_forward", O_CLOEXEC | O_RDWR)) < 0) return -errno; - if (enable) - fprintf(f, "1"); - else - fprintf(f, "0"); + if (!value) { + if (read(f, &value, sizeof(value)) < 0) + value = 0; - fclose(f); + if (lseek(f, 0, SEEK_SET) < 0) + return -errno; + } - return 0; + if (enable) { + char allow = '1'; + + if (write (f, &allow, sizeof(allow)) < 0) + err = -errno; + } else { + char deny = '0'; + + if (value) + deny = value; + + if (write(f, &deny, sizeof(deny)) < 0) + err = -errno; + + value = 0; + } + + close(f); + + return err; } static int enable_nat(struct connman_nat *nat) { - char *cmd; - int err; - g_free(nat->interface); nat->interface = g_strdup(default_interface); if (!nat->interface) return 0; - /* Enable masquerading */ - cmd = g_strdup_printf("-s %s/%d -o %s -j MASQUERADE", - nat->address, - nat->prefixlen, - nat->interface); - - err = __connman_firewall_add_rule(nat->fw, "nat", - "POSTROUTING", cmd); - g_free(cmd); - if (err < 0) - return err; - - return __connman_firewall_enable(nat->fw); + return __connman_firewall_enable_nat(nat->fw, nat->address, + nat->prefixlen, nat->interface); } static void disable_nat(struct connman_nat *nat) @@ -89,8 +98,7 @@ static void disable_nat(struct connman_nat *nat) if (!nat->interface) return; - /* Disable masquerading */ - __connman_firewall_disable(nat->fw); + __connman_firewall_disable_nat(nat->fw); } int __connman_nat_enable(const char *name, const char *address, diff --git a/src/network.c b/src/network.c index f7a9925a..d38fc0af 100755 --- a/src/network.c +++ b/src/network.c @@ -90,8 +90,13 @@ struct connman_network { char *passphrase; char *eap; char *identity; + char *anonymous_identity; char *agent_identity; char *ca_cert_path; + char *subject_match; + char *altsubject_match; + char *domain_suffix_match; + char *domain_match; char *client_cert_path; char *private_key_path; char *private_key_passphrase; @@ -109,7 +114,8 @@ struct connman_network { bool rsn_mode; int disconnect_reason; int assoc_status_code; - GSList *vsie_list; + void *wifi_vsie; + unsigned int wifi_vsie_len; #endif } wifi; @@ -164,8 +170,6 @@ static void set_configuration(struct connman_network *network, __connman_device_set_network(network->device, network); - connman_device_set_disconnected(network->device, false); - service = connman_service_lookup_from_network(network); __connman_service_ipconfig_indicate_state(service, CONNMAN_SERVICE_STATE_CONFIGURATION, @@ -201,6 +205,8 @@ static void dhcp_success(struct connman_network *network) if (err < 0) goto err; + __connman_service_save(service); + return; err: @@ -251,8 +257,8 @@ static int set_connected_manual(struct connman_network *network) network->connecting = false; service = connman_service_lookup_from_network(network); - ipconfig = __connman_service_get_ip4config(service); + __connman_ipconfig_enable(ipconfig); if (!__connman_ipconfig_get_local(ipconfig)) __connman_service_read_ip4config(service); @@ -283,6 +289,7 @@ static int set_connected_dhcp(struct connman_network *network) service = connman_service_lookup_from_network(network); ipconfig_ipv4 = __connman_service_get_ip4config(service); + __connman_ipconfig_enable(ipconfig_ipv4); err = __connman_dhcp_start(ipconfig_ipv4, network, dhcp_callback, NULL); @@ -326,13 +333,8 @@ static int manual_ipv6_set(struct connman_network *network, if (err < 0) return err; - __connman_connection_gateway_activate(service, - CONNMAN_IPCONFIG_TYPE_IPV6); - __connman_device_set_network(network->device, network); - connman_device_set_disconnected(network->device, false); - connman_network_set_associating(network, false); network->connecting = false; @@ -613,8 +615,6 @@ static void autoconf_ipv6_set(struct connman_network *network) __connman_device_set_network(network->device, network); - connman_device_set_disconnected(network->device, false); - #if defined TIZEN_EXT if(network->type == CONNMAN_NETWORK_TYPE_CELLULAR) return; @@ -628,6 +628,8 @@ static void autoconf_ipv6_set(struct connman_network *network) if (!ipconfig) return; + __connman_ipconfig_enable(ipconfig); + __connman_ipconfig_enable_ipv6(ipconfig); __connman_ipconfig_address_remove(ipconfig); @@ -710,10 +712,20 @@ static void set_disconnected(struct connman_network *network) switch (ipv4_method) { case CONNMAN_IPCONFIG_METHOD_UNKNOWN: case CONNMAN_IPCONFIG_METHOD_OFF: - case CONNMAN_IPCONFIG_METHOD_AUTO: case CONNMAN_IPCONFIG_METHOD_FIXED: case CONNMAN_IPCONFIG_METHOD_MANUAL: break; + case CONNMAN_IPCONFIG_METHOD_AUTO: + /* + * If the current method is AUTO then next time we + * try first DHCP. DHCP also needs to be stopped + * in this case because if we fell in AUTO means + * that DHCP was launched for IPv4 but it failed. + */ + __connman_ipconfig_set_method(ipconfig_ipv4, + CONNMAN_IPCONFIG_METHOD_DHCP); + __connman_service_notify_ipv4_configuration(service); + /* fall through */ case CONNMAN_IPCONFIG_METHOD_DHCP: __connman_dhcp_stop(ipconfig_ipv4); break; @@ -895,20 +907,6 @@ static void probe_driver(struct connman_network_driver *driver) } } -static void remove_driver(struct connman_network_driver *driver) -{ - GSList *list; - - DBG("driver %p name %s", driver, driver->name); - - for (list = network_list; list; list = list->next) { - struct connman_network *network = list->data; - - if (network->driver == driver) - network_remove(network); - } -} - static gint compare_priority(gconstpointer a, gconstpointer b) { const struct connman_network_driver *driver1 = a; @@ -945,11 +943,18 @@ int connman_network_driver_register(struct connman_network_driver *driver) */ void connman_network_driver_unregister(struct connman_network_driver *driver) { + GSList *list; + DBG("driver %p name %s", driver, driver->name); driver_list = g_slist_remove(driver_list, driver); - remove_driver(driver); + for (list = network_list; list; list = list->next) { + struct connman_network *network = list->data; + + if (network->driver == driver) + network_remove(network); + } } static void network_destruct(struct connman_network *network) @@ -962,15 +967,20 @@ static void network_destruct(struct connman_network *network) g_free(network->wifi.passphrase); g_free(network->wifi.eap); g_free(network->wifi.identity); + g_free(network->wifi.anonymous_identity); g_free(network->wifi.agent_identity); g_free(network->wifi.ca_cert_path); + g_free(network->wifi.subject_match); + g_free(network->wifi.altsubject_match); + g_free(network->wifi.domain_suffix_match); + g_free(network->wifi.domain_match); g_free(network->wifi.client_cert_path); g_free(network->wifi.private_key_path); g_free(network->wifi.private_key_passphrase); g_free(network->wifi.phase2_auth); g_free(network->wifi.pin_wps); #if defined TIZEN_EXT - g_slist_free_full(network->wifi.vsie_list, g_free); + g_free(network->wifi.wifi_vsie); #endif g_free(network->path); g_free(network->group); @@ -997,14 +1007,10 @@ struct connman_network *connman_network_create(const char *identifier, struct connman_network *network; char *ident; - DBG("identifier %s type %d", identifier, type); - network = g_try_new0(struct connman_network, 1); if (!network) return NULL; - DBG("network %p", network); - network->refcount = 1; ident = g_strdup(identifier); @@ -1019,6 +1025,8 @@ struct connman_network *connman_network_create(const char *identifier, network_list = g_slist_prepend(network_list, network); + DBG("network %p identifier %s type %s", network, identifier, + type2string(type)); return network; } @@ -2058,18 +2066,6 @@ int connman_network_get_disconnect_reason(struct connman_network *network) return network->wifi.disconnect_reason; } - -int connman_network_set_assoc_status_code(struct connman_network *network, - int assoc_status_code) -{ - - if (network == NULL) - return 0; - - network->wifi.assoc_status_code = assoc_status_code; - return 0; -} - int connman_network_get_assoc_status_code(struct connman_network *network) { if (network == NULL) @@ -2077,7 +2073,6 @@ int connman_network_get_assoc_status_code(struct connman_network *network) return network->wifi.assoc_status_code; } - #endif int connman_network_set_nameservers(struct connman_network *network, @@ -2161,10 +2156,6 @@ int connman_network_set_name(struct connman_network *network, int connman_network_set_strength(struct connman_network *network, uint8_t strength) { -#if !defined TIZEN_EXT - DBG("network %p strengh %d", network, strength); -#endif - network->strength = strength; return 0; @@ -2178,10 +2169,6 @@ uint8_t connman_network_get_strength(struct connman_network *network) int connman_network_set_frequency(struct connman_network *network, uint16_t frequency) { -#if !defined TIZEN_EXT - DBG("network %p frequency %d", network, frequency); -#endif - network->frequency = frequency; return 0; @@ -2195,8 +2182,6 @@ uint16_t connman_network_get_frequency(struct connman_network *network) int connman_network_set_wifi_channel(struct connman_network *network, uint16_t channel) { - DBG("network %p wifi channel %d", network, channel); - network->wifi.channel = channel; return 0; @@ -2218,10 +2203,6 @@ uint16_t connman_network_get_wifi_channel(struct connman_network *network) int connman_network_set_string(struct connman_network *network, const char *key, const char *value) { -#if !defined TIZEN_EXT - DBG("network %p key %s value %s", network, key, value); -#endif - if (g_strcmp0(key, "Name") == 0) return connman_network_set_name(network, value); @@ -2249,12 +2230,27 @@ int connman_network_set_string(struct connman_network *network, } else if (g_str_equal(key, "WiFi.Identity")) { g_free(network->wifi.identity); network->wifi.identity = g_strdup(value); + } else if (g_str_equal(key, "WiFi.AnonymousIdentity")) { + g_free(network->wifi.anonymous_identity); + network->wifi.anonymous_identity = g_strdup(value); } else if (g_str_equal(key, "WiFi.AgentIdentity")) { g_free(network->wifi.agent_identity); network->wifi.agent_identity = g_strdup(value); } else if (g_str_equal(key, "WiFi.CACertFile")) { g_free(network->wifi.ca_cert_path); network->wifi.ca_cert_path = g_strdup(value); + } else if (g_str_equal(key, "WiFi.SubjectMatch")) { + g_free(network->wifi.subject_match); + network->wifi.subject_match = g_strdup(value); + } else if (g_str_equal(key, "WiFi.AltSubjectMatch")) { + g_free(network->wifi.altsubject_match); + network->wifi.altsubject_match = g_strdup(value); + } else if (g_str_equal(key, "WiFi.DomainSuffixMatch")) { + g_free(network->wifi.domain_suffix_match); + network->wifi.domain_suffix_match = g_strdup(value); + } else if (g_str_equal(key, "WiFi.DomainMatch")) { + g_free(network->wifi.domain_match); + network->wifi.domain_match = g_strdup(value); } else if (g_str_equal(key, "WiFi.ClientCertFile")) { g_free(network->wifi.client_cert_path); network->wifi.client_cert_path = g_strdup(value); @@ -2287,10 +2283,6 @@ int connman_network_set_string(struct connman_network *network, const char *connman_network_get_string(struct connman_network *network, const char *key) { -#if !defined TIZEN_EXT - DBG("network %p key %s", network, key); -#endif - if (g_str_equal(key, "Path")) return network->path; else if (g_str_equal(key, "Name")) @@ -2315,10 +2307,20 @@ const char *connman_network_get_string(struct connman_network *network, return network->wifi.eap; else if (g_str_equal(key, "WiFi.Identity")) return network->wifi.identity; + else if (g_str_equal(key, "WiFi.AnonymousIdentity")) + return network->wifi.anonymous_identity; else if (g_str_equal(key, "WiFi.AgentIdentity")) return network->wifi.agent_identity; else if (g_str_equal(key, "WiFi.CACertFile")) return network->wifi.ca_cert_path; + else if (g_str_equal(key, "WiFi.SubjectMatch")) + return network->wifi.subject_match; + else if (g_str_equal(key, "WiFi.AltSubjectMatch")) + return network->wifi.altsubject_match; + else if (g_str_equal(key, "WiFi.DomainSuffixMatch")) + return network->wifi.domain_suffix_match; + else if (g_str_equal(key, "WiFi.DomainMatch")) + return network->wifi.domain_match; else if (g_str_equal(key, "WiFi.ClientCertFile")) return network->wifi.client_cert_path; else if (g_str_equal(key, "WiFi.PrivateKeyFile")) @@ -2344,10 +2346,6 @@ const char *connman_network_get_string(struct connman_network *network, int connman_network_set_bool(struct connman_network *network, const char *key, bool value) { -#if !defined TIZEN_EXT - DBG("network %p key %s value %d", network, key, value); -#endif - if (g_strcmp0(key, "Roaming") == 0) network->roaming = value; else if (g_strcmp0(key, "WiFi.WPS") == 0) @@ -2374,10 +2372,6 @@ int connman_network_set_bool(struct connman_network *network, bool connman_network_get_bool(struct connman_network *network, const char *key) { -#if !defined TIZEN_EXT - DBG("network %p key %s", network, key); -#endif - if (g_str_equal(key, "Roaming")) return network->roaming; else if (g_str_equal(key, "WiFi.WPS")) @@ -2394,31 +2388,6 @@ bool connman_network_get_bool(struct connman_network *network, return false; } -#if defined TIZEN_EXT -/** - * connman_network_set_vsie_list: - * @network: network structure - * @vsie_list: GSList pointer - * - * Set vendor specific list pointer - */ -void connman_network_set_vsie_list(struct connman_network *network, GSList *vsie_list) -{ - network->wifi.vsie_list = vsie_list; -} - -/** - * connman_network_get_vsie_list: - * @network: network structure - * - * Get vendor specific list pointer - */ -void *connman_network_get_vsie_list(struct connman_network *network) -{ - return network->wifi.vsie_list; -} -#endif - /** * connman_network_set_blob: * @network: network structure @@ -2431,10 +2400,6 @@ void *connman_network_get_vsie_list(struct connman_network *network) int connman_network_set_blob(struct connman_network *network, const char *key, const void *data, unsigned int size) { -#if !defined TIZEN_EXT - DBG("network %p key %s size %d", network, key, size); -#endif - if (g_str_equal(key, "WiFi.SSID")) { g_free(network->wifi.ssid); network->wifi.ssid = g_try_malloc(size); @@ -2443,6 +2408,16 @@ int connman_network_set_blob(struct connman_network *network, network->wifi.ssid_len = size; } else network->wifi.ssid_len = 0; +#if defined TIZEN_EXT + } else if (g_str_equal(key, "WiFi.Vsie")){ + g_free(network->wifi.wifi_vsie); + network->wifi.wifi_vsie = g_try_malloc(size); + if (network->wifi.wifi_vsie) { + memcpy(network->wifi.wifi_vsie, data, size); + network->wifi.wifi_vsie_len = size; + } else + network->wifi.wifi_vsie_len = 0; +#endif } else { return -EINVAL; } @@ -2461,16 +2436,21 @@ int connman_network_set_blob(struct connman_network *network, const void *connman_network_get_blob(struct connman_network *network, const char *key, unsigned int *size) { -#if !defined TIZEN_EXT - DBG("network %p key %s", network, key); -#endif - if (g_str_equal(key, "WiFi.SSID")) { if (size) *size = network->wifi.ssid_len; return network->wifi.ssid; } +#if defined TIZEN_EXT + if (g_str_equal(key, "WiFi.Vsie")) { + if (size) + *size = network->wifi.wifi_vsie_len; + + return network->wifi.wifi_vsie; + } +#endif + return NULL; } @@ -30,10 +30,12 @@ #include <stdlib.h> #include <string.h> #include <sys/time.h> +#include <sys/timex.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <arpa/inet.h> +#include <netdb.h> #include <glib.h> @@ -65,9 +67,13 @@ struct ntp_msg { #define OFFSET_1900_1970 2208988800UL /* 1970 - 1900 in seconds */ -#define STEPTIME_MIN_OFFSET 0.128 +#define STEPTIME_MIN_OFFSET 0.4 #define LOGTOD(a) ((a) < 0 ? 1. / (1L << -(a)) : 1L << (int)(a)) +#define NSEC_PER_SEC ((uint64_t)1000000000ULL) +#ifndef ADJ_SETOFFSET +#define ADJ_SETOFFSET 0x0100 /* add 'time' to current time */ +#endif #define NTP_SEND_TIMEOUT 2 #define NTP_SEND_RETRIES 3 @@ -117,12 +123,12 @@ static struct timespec mtx_time; static int transmit_fd = 0; static char *timeserver = NULL; -static struct sockaddr_in timeserver_addr; +static struct sockaddr_in6 timeserver_addr; static gint poll_id = 0; static gint timeout_id = 0; static guint retries = 0; -static void send_packet(int fd, const char *server, uint32_t timeout); +static void send_packet(int fd, struct sockaddr *server, uint32_t timeout); static void next_server(void) { @@ -143,17 +149,19 @@ static gboolean send_timeout(gpointer user_data) if (retries++ == NTP_SEND_RETRIES) next_server(); else - send_packet(transmit_fd, timeserver, timeout << 1); + send_packet(transmit_fd, (struct sockaddr *)×erver_addr, timeout << 1); return FALSE; } -static void send_packet(int fd, const char *server, uint32_t timeout) +static void send_packet(int fd, struct sockaddr *server, uint32_t timeout) { struct ntp_msg msg; - struct sockaddr_in addr; struct timeval transmit_timeval; ssize_t len; + void * addr; + int size; + char ipaddrstring[INET6_ADDRSTRLEN + 1]; /* * At some point, we could specify the actual system precision with: @@ -164,14 +172,19 @@ static void send_packet(int fd, const char *server, uint32_t timeout) memset(&msg, 0, sizeof(msg)); msg.flags = NTP_FLAGS_ENCODE(NTP_FLAG_LI_NOTINSYNC, NTP_FLAG_VN_VER4, NTP_FLAG_MD_CLIENT); - msg.poll = 4; // min - msg.poll = 10; // max + msg.poll = 10; msg.precision = NTP_PRECISION_S; - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(123); - addr.sin_addr.s_addr = inet_addr(server); + if (server->sa_family == AF_INET) { + size = sizeof(struct sockaddr_in); + addr = (void *)&(((struct sockaddr_in *)×erver_addr)->sin_addr); + } else if (server->sa_family == AF_INET6) { + size = sizeof(struct sockaddr_in6); + addr = (void *)×erver_addr.sin6_addr; + } else { + connman_error("Family is neither ipv4 nor ipv6"); + return; + } gettimeofday(&transmit_timeval, NULL); clock_gettime(CLOCK_MONOTONIC, &mtx_time); @@ -180,10 +193,12 @@ static void send_packet(int fd, const char *server, uint32_t timeout) msg.xmttime.fraction = htonl(transmit_timeval.tv_usec * 1000); len = sendto(fd, &msg, sizeof(msg), MSG_DONTWAIT, - &addr, sizeof(addr)); + server, size); + if (len < 0) { connman_error("Time request for server %s failed (%d/%s)", - server, errno, strerror(errno)); + inet_ntop(server->sa_family, addr, ipaddrstring, sizeof(ipaddrstring)), + errno, strerror(errno)); if (errno == ENETUNREACH) __connman_timeserver_sync_next(); @@ -192,7 +207,8 @@ static void send_packet(int fd, const char *server, uint32_t timeout) } if (len != sizeof(msg)) { - connman_error("Broken time request for server %s", server); + connman_error("Broken time request for server %s", + inet_ntop(server->sa_family, addr, ipaddrstring, sizeof(ipaddrstring))); return; } @@ -213,7 +229,7 @@ static gboolean next_poll(gpointer user_data) if (!timeserver || transmit_fd == 0) return FALSE; - send_packet(transmit_fd, timeserver, NTP_SEND_TIMEOUT); + send_packet(transmit_fd, (struct sockaddr *)×erver_addr, NTP_SEND_TIMEOUT); return FALSE; } @@ -235,7 +251,9 @@ static void decode_msg(void *base, size_t len, struct timeval *tv, double m_delta, org, rec, xmt, dst; double delay, offset; static guint transmit_delay; - +#if !defined TIZEN_EXT + struct timex tmx = {}; +#endif if (len < sizeof(*msg)) { connman_error("Invalid response from time server"); return; @@ -324,8 +342,6 @@ static void decode_msg(void *base, size_t len, struct timeval *tv, poll_id = g_timeout_add_seconds(transmit_delay, next_poll, NULL); - connman_info("ntp: time slew %+.6f s", offset); - #if defined TIZEN_EXT //send the dbus message to alram-manager { @@ -387,37 +403,44 @@ static void decode_msg(void *base, size_t len, struct timeval *tv, } #else if (offset < STEPTIME_MIN_OFFSET && offset > -STEPTIME_MIN_OFFSET) { - struct timeval adj; - - adj.tv_sec = (long) offset; - adj.tv_usec = (offset - adj.tv_sec) * 1000000; - - DBG("adjusting time"); - - if (adjtime(&adj, &adj) < 0) { - connman_error("Failed to adjust time"); - return; - } - - DBG("%lu seconds, %lu msecs", adj.tv_sec, adj.tv_usec); + tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_OFFSET | ADJ_TIMECONST | ADJ_MAXERROR | ADJ_ESTERROR; + tmx.status = STA_PLL; + tmx.offset = offset * NSEC_PER_SEC; + tmx.constant = msg->poll - 4; + tmx.maxerror = 0; + tmx.esterror = 0; + + connman_info("ntp: adjust (slew): %+.6f sec", offset); } else { - struct timeval cur; - double dtime; - - gettimeofday(&cur, NULL); - dtime = offset + cur.tv_sec + 1.0e-6 * cur.tv_usec; - cur.tv_sec = (long) dtime; - cur.tv_usec = (dtime - cur.tv_sec) * 1000000; + tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_SETOFFSET | ADJ_MAXERROR | ADJ_ESTERROR; + + /* ADJ_NANO uses nanoseconds in the microseconds field */ + tmx.time.tv_sec = (long)offset; + tmx.time.tv_usec = (offset - tmx.time.tv_sec) * NSEC_PER_SEC; + tmx.maxerror = 0; + tmx.esterror = 0; + + /* the kernel expects -0.3s as {-1, 7000.000.000} */ + if (tmx.time.tv_usec < 0) { + tmx.time.tv_sec -= 1; + tmx.time.tv_usec += NSEC_PER_SEC; + } - DBG("setting time"); + connman_info("ntp: adjust (jump): %+.6f sec", offset); + } - if (settimeofday(&cur, NULL) < 0) { - connman_error("Failed to set time"); - return; - } + if (NTP_FLAGS_LI_DECODE(msg->flags) & NTP_FLAG_LI_ADDSECOND) + tmx.status |= STA_INS; + else if (NTP_FLAGS_LI_DECODE(msg->flags) & NTP_FLAG_LI_DELSECOND) + tmx.status |= STA_DEL; - DBG("%lu seconds, %lu msecs", cur.tv_sec, cur.tv_usec); + if (adjtimex(&tmx) < 0) { + connman_error("Failed to adjust time"); + return; } + + DBG("interval/delta/delay/drift %fs/%+.3fs/%.3fs/%+ldppm", + LOGTOD(msg->poll), offset, delay, tmx.freq / 65536); #endif } @@ -425,7 +448,7 @@ static gboolean received_data(GIOChannel *channel, GIOCondition condition, gpointer user_data) { unsigned char buf[128]; - struct sockaddr_in sender_addr; + struct sockaddr_in6 sender_addr; struct msghdr msg; struct iovec iov; struct cmsghdr *cmsg; @@ -434,6 +457,9 @@ static gboolean received_data(GIOChannel *channel, GIOCondition condition, char aux[128]; ssize_t len; int fd; + int size; + void * addr_ptr; + void * src_ptr; if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { connman_error("Problem with timer server channel"); @@ -458,8 +484,20 @@ static gboolean received_data(GIOChannel *channel, GIOCondition condition, if (len < 0) return TRUE; - if (timeserver_addr.sin_addr.s_addr != sender_addr.sin_addr.s_addr) - /* only accept messages from the timeserver */ + if (sender_addr.sin6_family == AF_INET) { + size = 4; + addr_ptr = &((struct sockaddr_in *)×erver_addr)->sin_addr; + src_ptr = &((struct sockaddr_in *)&sender_addr)->sin_addr; + } else if (sender_addr.sin6_family == AF_INET6) { + size = 16; + addr_ptr = &((struct sockaddr_in6 *)×erver_addr)->sin6_addr; + src_ptr = &((struct sockaddr_in6 *)&sender_addr)->sin6_addr; + } else { + connman_error("Not a valid family type"); + return TRUE; + } + + if(memcmp(addr_ptr, src_ptr, size) != 0) return TRUE; tv = NULL; @@ -484,36 +522,76 @@ static gboolean received_data(GIOChannel *channel, GIOCondition condition, static void start_ntp(char *server) { GIOChannel *channel; - struct sockaddr_in addr; + struct addrinfo hint; + struct addrinfo *info; + struct sockaddr * addr; + struct sockaddr_in * in4addr; + struct sockaddr_in6 in6addr; + int size; + int family; int tos = IPTOS_LOWDELAY, timestamp = 1; + int ret; if (!server) return; - DBG("server %s", server); + memset(&hint, 0, sizeof(hint)); + hint.ai_family = AF_UNSPEC; + hint.ai_socktype = SOCK_DGRAM; + hint.ai_flags = AI_NUMERICHOST | AI_PASSIVE; + ret = getaddrinfo(server, NULL, &hint, &info); - if (channel_watch > 0) - goto send; + if (ret) { + connman_error("cannot get server info"); + return; + } - transmit_fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (transmit_fd < 0) { - connman_error("Failed to open time server socket"); + family = info->ai_family; + + memcpy(×erver_addr, info->ai_addr, info->ai_addrlen); + freeaddrinfo(info); + memset(&in6addr, 0, sizeof(in6addr)); + + if (family == AF_INET) { + ((struct sockaddr_in *)×erver_addr)->sin_port = htons(123); + in4addr = (struct sockaddr_in *)&in6addr; + in4addr->sin_family = family; + addr = (struct sockaddr *)in4addr; + size = sizeof(struct sockaddr_in); + } else if (family == AF_INET6) { + timeserver_addr.sin6_port = htons(123); + in6addr.sin6_family = family; + addr = (struct sockaddr *)&in6addr; + size = sizeof(in6addr); + } else { + connman_error("Family is neither ipv4 nor ipv6"); return; } - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; + DBG("server %s family %d", server, family); - if (bind(transmit_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + if (channel_watch > 0) + goto send; + + transmit_fd = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, 0); + + if (transmit_fd <= 0) { + connman_error("Failed to open time server socket"); + return; + } + + if (bind(transmit_fd, (struct sockaddr *) addr, size) < 0) { connman_error("Failed to bind time server socket"); close(transmit_fd); return; } - if (setsockopt(transmit_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) { - connman_error("Failed to set type of service option"); - close(transmit_fd); - return; + if (family == AF_INET) { + if (setsockopt(transmit_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) { + connman_error("Failed to set type of service option"); + close(transmit_fd); + return; + } } if (setsockopt(transmit_fd, SOL_SOCKET, SO_TIMESTAMP, ×tamp, @@ -541,7 +619,7 @@ static void start_ntp(char *server) g_io_channel_unref(channel); send: - send_packet(transmit_fd, server, NTP_SEND_TIMEOUT); + send_packet(transmit_fd, (struct sockaddr*)×erver_addr, NTP_SEND_TIMEOUT); } int __connman_ntp_start(char *server) @@ -555,7 +633,6 @@ int __connman_ntp_start(char *server) g_free(timeserver); timeserver = g_strdup(server); - timeserver_addr.sin_addr.s_addr = inet_addr(server); start_ntp(timeserver); @@ -176,7 +176,7 @@ static int start_dhcp_server(struct connman_peer *peer) if (err < 0) goto error; - g_timeout_add_seconds(0, dhcp_server_started, connman_peer_ref(peer)); + g_idle_add(dhcp_server_started, connman_peer_ref(peer)); return 0; @@ -758,7 +758,7 @@ void connman_peer_set_name(struct connman_peer *peer, const char *name) void connman_peer_set_iface_address(struct connman_peer *peer, const unsigned char *iface_address) { - memset(peer->iface_address, 0, ETH_ALEN); + memset(peer->iface_address, 0, sizeof(peer->iface_address)); memcpy(peer->iface_address, iface_address, ETH_ALEN); } @@ -905,13 +905,16 @@ int connman_peer_set_state(struct connman_peer *peer, break; case CONNMAN_PEER_STATE_READY: reply_pending(peer, 0); + __connman_technology_set_connected(CONNMAN_SERVICE_TYPE_P2P, true); break; case CONNMAN_PEER_STATE_DISCONNECT: if (peer->connection_master) stop_dhcp_server(peer); + else + __connman_dhcp_stop(peer->ipconfig); peer->connection_master = false; peer->sub_device = NULL; - + __connman_technology_set_connected(CONNMAN_SERVICE_TYPE_P2P, false); break; case CONNMAN_PEER_STATE_FAILURE: if (manage_peer_error(peer) == 0) diff --git a/src/peer_service.c b/src/peer_service.c index 053672af..a457bff7 100755 --- a/src/peer_service.c +++ b/src/peer_service.c @@ -293,9 +293,6 @@ int __connman_peer_service_register(const char *owner, DBusMessage *msg, if (service) { DBG("Found one existing service %p", service); - if (g_strcmp0(service->owner, owner)) - ret = -EBUSY; - if (service->pending) ret = -EINPROGRESS; else diff --git a/src/provider.c b/src/provider.c index 521346b4..c0d69e49 100755 --- a/src/provider.c +++ b/src/provider.c @@ -141,12 +141,12 @@ int connman_provider_disconnect(struct connman_provider *provider) provider_indicate_state(provider, CONNMAN_SERVICE_STATE_DISCONNECT); - if (err < 0) { - if (err != -EINPROGRESS) - return err; + if (err < 0) + return err; - return -EINPROGRESS; - } + if (provider->vpn_service) + provider_indicate_state(provider, + CONNMAN_SERVICE_STATE_IDLE); return 0; } @@ -164,14 +164,15 @@ int connman_provider_remove(struct connman_provider *provider) return 0; } -int __connman_provider_connect(struct connman_provider *provider) +int __connman_provider_connect(struct connman_provider *provider, + const char *dbus_sender) { int err; DBG("provider %p", provider); if (provider->driver && provider->driver->connect) - err = provider->driver->connect(provider); + err = provider->driver->connect(provider, dbus_sender); else return -EOPNOTSUPP; diff --git a/src/proxy.c b/src/proxy.c index f331de92..e1bc420a 100755 --- a/src/proxy.c +++ b/src/proxy.c @@ -123,7 +123,7 @@ unsigned int connman_proxy_lookup(const char *interface, const char *url, lookup->url = g_strdup(url); lookup->service = connman_service_ref(service); - lookup->watch = g_timeout_add_seconds(0, lookup_callback, lookup); + lookup->watch = g_idle_add(lookup_callback, lookup); if (lookup->watch == 0) { g_free(lookup->url); g_free(lookup); diff --git a/src/resolver.c b/src/resolver.c index 8a7fa663..d6c20cdd 100755 --- a/src/resolver.c +++ b/src/resolver.c @@ -97,9 +97,9 @@ static int resolvfile_export(void) * MAXDNSRCH/MAXNS entries are used. */ - for (count = 0, list = g_list_last(resolvfile_list); + 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) @@ -115,9 +115,9 @@ static int resolvfile_export(void) if (count) g_string_append_printf(content, "\n"); - for (count = 0, list = g_list_last(resolvfile_list); + 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) @@ -207,7 +207,7 @@ int __connman_resolvfile_remove(int index, const char *domain, return resolvfile_export(); } -static void append_fallback_nameservers(void) +void __connman_resolver_append_fallback_nameservers(void) { GSList *list; @@ -284,7 +284,7 @@ static void remove_entries(GSList *entries) g_slist_free(entries); - append_fallback_nameservers(); + __connman_resolver_append_fallback_nameservers(); } static gboolean resolver_expire_cb(gpointer user_data) @@ -389,24 +389,6 @@ static int append_resolver(int index, const char *domain, entry->timeout = g_timeout_add_seconds(interval, resolver_refresh_cb, entry); - - /* - * We update the service only for those nameservers - * that are automagically added via netlink (lifetime > 0) - */ - if (server && entry->index >= 0) { - struct connman_service *service; - service = __connman_service_lookup_from_index(entry->index); - if (service) -#if defined TIZEN_EXT - __connman_service_nameserver_append(service, - server, true, - CONNMAN_IPCONFIG_TYPE_ALL); -#else - __connman_service_nameserver_append(service, - server, true); -#endif - } } if (entry->index >= 0 && entry->server) @@ -419,6 +401,24 @@ static int append_resolver(int index, const char *domain, else __connman_resolvfile_append(entry->index, domain, server); + /* + * We update the service only for those nameservers + * that are automagically added via netlink (lifetime > 0) + */ + if (server && entry->index >= 0 && lifetime) { + struct connman_service *service; + service = __connman_service_lookup_from_index(entry->index); + if (service) +#if defined TIZEN_EXT + __connman_service_nameserver_append(service, + server, true, + CONNMAN_IPCONFIG_TYPE_ALL); +#else + __connman_service_nameserver_append(service, + server, true); +#endif + } + return 0; } @@ -617,6 +617,28 @@ int __connman_resolver_redo_servers(int index) entry->server); } + /* + * We want to re-add all search domains back to search + * domain lists as they just got removed for RDNSS IPv6-servers + * (above). + * Removal of search domains is not necessary + * as there can be only one instance of each search domain + * in the each dns-servers search domain list. + */ + + for (list = entry_list; list; list = list->next) { + struct entry_data *entry = list->data; + + if (entry->index != index) + continue; + + if (entry->server) + continue; + + __connman_dnsproxy_append(entry->index, entry->domain, + NULL); + } + return 0; } @@ -643,6 +665,14 @@ int __connman_resolver_init(gboolean dnsproxy) DBG("dnsproxy %d", dnsproxy); + /* get autoip nameservers */ + ns = __connman_inet_get_pnp_nameservers(NULL); + for (i = 0; ns && ns[i]; i += 1) { + DBG("pnp server %s", ns[i]); + append_resolver(i, NULL, ns[i], 86400, 0); + } + g_strfreev(ns); + if (!dnsproxy) return 0; diff --git a/src/rfkill.c b/src/rfkill.c index 36426e00..fce9d720 100755 --- a/src/rfkill.c +++ b/src/rfkill.c @@ -206,7 +206,7 @@ int __connman_rfkill_init(void) DBG(""); - fd = open("/dev/rfkill", O_RDWR | O_CLOEXEC); + fd = open("/dev/rfkill", O_RDONLY | O_CLOEXEC); if (fd < 0) { connman_error("Failed to open RFKILL control device"); return -EIO; @@ -212,7 +212,9 @@ static void read_uevent(struct interface_data *interface) } else if (strcmp(line + 8, "vlan") == 0) { interface->service_type = CONNMAN_SERVICE_TYPE_ETHERNET; interface->device_type = CONNMAN_DEVICE_TYPE_ETHERNET; - + } else if (strcmp(line + 8, "bond") == 0) { + interface->service_type = CONNMAN_SERVICE_TYPE_ETHERNET; + interface->device_type = CONNMAN_DEVICE_TYPE_ETHERNET; } else { interface->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN; interface->device_type = CONNMAN_DEVICE_TYPE_UNKNOWN; @@ -551,7 +553,9 @@ static void process_newlink(unsigned short type, int index, unsigned flags, interface = NULL; #endif - } else + } else if (type == ARPHRD_ETHER && interface->device_type == CONNMAN_DEVICE_TYPE_UNKNOWN) + read_uevent(interface); + else interface = NULL; for (list = rtnl_list; list; list = list->next) { @@ -1355,7 +1359,12 @@ static void rtnl_newnduseropt(struct nlmsghdr *hdr) if (opt->nd_opt_type == 25) { /* ND_OPT_RDNSS */ char buf[40]; +#if defined TIZEN_EXT + struct connman_service *service; + service = __connman_service_lookup_from_index(index); + DBG("service: %p\n",service); +#endif servers = rtnl_nd_opt_rdnss(opt, &lifetime, &nr_servers); for (i = 0; i < nr_servers; i++) { @@ -1374,6 +1383,7 @@ static void rtnl_newnduseropt(struct nlmsghdr *hdr) connman_resolver_append_lifetime(index, NULL, buf, lifetime); } + } else if (opt->nd_opt_type == 31) { /* ND_OPT_DNSSL */ g_free(domains); @@ -1499,8 +1509,6 @@ static int process_response(guint32 seq) static void rtnl_message(void *buf, size_t len) { - DBG("buf %p len %zd", buf, len); - while (len > 0) { struct nlmsghdr *hdr = buf; struct nlmsgerr *err; diff --git a/src/service.c b/src/service.c index 033c8f8f..d0543ae5 100755 --- a/src/service.c +++ b/src/service.c @@ -112,6 +112,7 @@ struct connman_service { char **nameservers; char **nameservers_config; char **nameservers_auto; + int nameservers_timeout; char **domains; char *hostname; char *domainname; @@ -120,8 +121,13 @@ struct connman_service { /* 802.1x settings from the config files */ char *eap; char *identity; + char *anonymous_identity; char *agent_identity; char *ca_cert_file; + char *subject_match; + char *altsubject_match; + char *domain_suffix_match; + char *domain_match; char *client_cert_file; char *private_key_file; char *private_key_passphrase; @@ -174,7 +180,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); struct find_data { const char *path; @@ -327,9 +333,9 @@ enum connman_service_security __connman_service_string2security(const char *str) if (!strcmp(str, "psk")) return CONNMAN_SERVICE_SECURITY_PSK; - if (!strcmp(str, "ieee8021x")) + if (!strcmp(str, "ieee8021x") || !strcmp(str, "8021x")) return CONNMAN_SERVICE_SECURITY_8021X; - if (!strcmp(str, "none")) + if (!strcmp(str, "none") || !strcmp(str, "open")) return CONNMAN_SERVICE_SECURITY_NONE; if (!strcmp(str, "wep")) return CONNMAN_SERVICE_SECURITY_WEP; @@ -555,6 +561,19 @@ static bool is_service_owner_user_login(struct connman_service *service) #endif } +static void set_split_routing(struct connman_service *service, bool value) +{ + if (service->type != CONNMAN_SERVICE_TYPE_VPN) + return; + + service->do_split_routing = value; + + if (service->do_split_routing) + service->order = 0; + else + service->order = 10; +} + int __connman_service_load_modifiable(struct connman_service *service) { GKeyFile *keyfile; @@ -575,8 +594,10 @@ int __connman_service_load_modifiable(struct connman_service *service) case CONNMAN_SERVICE_TYPE_P2P: break; case CONNMAN_SERVICE_TYPE_VPN: - service->do_split_routing = g_key_file_get_boolean(keyfile, - service->identifier, "SplitRouting", NULL); + set_split_routing(service, g_key_file_get_boolean(keyfile, + service->identifier, + "SplitRouting", NULL)); + /* fall through */ case CONNMAN_SERVICE_TYPE_WIFI: case CONNMAN_SERVICE_TYPE_GADGET: @@ -648,8 +669,10 @@ static int service_load(struct connman_service *service) case CONNMAN_SERVICE_TYPE_P2P: break; case CONNMAN_SERVICE_TYPE_VPN: - service->do_split_routing = g_key_file_get_boolean(keyfile, - service->identifier, "SplitRouting", NULL); + set_split_routing(service, g_key_file_get_boolean(keyfile, + service->identifier, + "SplitRouting", NULL)); + autoconnect = g_key_file_get_boolean(keyfile, service->identifier, "AutoConnect", &error); if (!error) @@ -1246,15 +1269,12 @@ done: return result; } -static bool is_connecting_state(struct connman_service *service, - enum connman_service_state state) +static bool is_connecting(enum connman_service_state state) { switch (state) { 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: @@ -1267,8 +1287,7 @@ static bool is_connecting_state(struct connman_service *service, return false; } -static bool is_connected_state(const struct connman_service *service, - enum connman_service_state state) +static bool is_connected(enum connman_service_state state) { switch (state) { case CONNMAN_SERVICE_STATE_UNKNOWN: @@ -1286,57 +1305,127 @@ static bool is_connected_state(const struct connman_service *service, return false; } -static bool is_idle_state(const struct connman_service *service, - enum connman_service_state state) +static bool is_idle(enum connman_service_state state) { switch (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: - case CONNMAN_SERVICE_STATE_DISCONNECT: - case CONNMAN_SERVICE_STATE_FAILURE: break; - case CONNMAN_SERVICE_STATE_IDLE: - return true; } return false; } -static bool is_connecting(struct connman_service *service) +static int nameservers_changed_cb(void *user_data) { - return is_connecting_state(service, service->state); + struct connman_service *service = user_data; + + DBG("service %p", service); + + service->nameservers_timeout = 0; + if ((is_idle(service->state) && !service->nameservers) || + is_connected(service->state)) + dns_changed(service); + + return FALSE; } -static bool is_connected(struct connman_service *service) +static void nameservers_changed(struct connman_service *service) { - return is_connected_state(service, service->state); + if (!service->nameservers_timeout) + service->nameservers_timeout = g_idle_add(nameservers_changed_cb, + service); } static bool nameserver_available(struct connman_service *service, + enum connman_ipconfig_type type, const char *ns) { int family; family = connman_inet_check_ipaddress(ns); - if (family == AF_INET) - return is_connected_state(service, service->state_ipv4); + if (family == AF_INET) { + if (type == CONNMAN_IPCONFIG_TYPE_IPV6) + return false; + + return is_connected(service->state_ipv4); + } - if (family == AF_INET6) - return is_connected_state(service, service->state_ipv6); + if (family == AF_INET6) { + if (type == CONNMAN_IPCONFIG_TYPE_IPV4) + return false; + + return is_connected(service->state_ipv6); + } return false; } +static int searchdomain_add_all(struct connman_service *service) +{ + int index, i = 0; + + if (!is_connected(service->state)) + return -ENOTCONN; + + index = __connman_service_get_index(service); + if (index < 0) + return -ENXIO; + + if (service->domains) { + while (service->domains[i]) { + connman_resolver_append(index, service->domains[i], + NULL); + i++; + } + + return 0; + } + + if (service->domainname) + connman_resolver_append(index, service->domainname, NULL); + + return 0; + +} + +static int searchdomain_remove_all(struct connman_service *service) +{ + int index, i = 0; + + if (!is_connected(service->state)) + return -ENOTCONN; + + index = __connman_service_get_index(service); + if (index < 0) + return -ENXIO; + + while (service->domains && service->domains[i]) { + connman_resolver_remove(index, service->domains[i], NULL); + i++; + } + + if (service->domainname) + connman_resolver_remove(index, service->domainname, NULL); + + return 0; +} + 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, nameserver)) + if (!nameserver_available(service, type, nameserver)) return 0; index = __connman_service_get_index(service); @@ -1346,15 +1435,15 @@ static int nameserver_add(struct connman_service *service, #if defined TIZEN_EXT DBG("Resolver append nameserver: %s", nameserver); #endif - return connman_resolver_append(index, NULL, nameserver); + ret = connman_resolver_append(index, NULL, nameserver); + if (ret >= 0) + nameservers_changed(service); + + return ret; } -#if defined TIZEN_EXT static int nameserver_add_all(struct connman_service *service, - enum connman_ipconfig_type type) -#else -static int nameserver_add_all(struct connman_service *service) -#endif + enum connman_ipconfig_type type) { int i = 0; @@ -1375,7 +1464,7 @@ static int nameserver_add_all(struct connman_service *service) service->nameservers_config[i]) == AF_INET && service->dns_config_method_ipv4 == CONNMAN_DNSCONFIG_METHOD_MANUAL) { - nameserver_add(service, + nameserver_add(service, type, service->nameservers_config[i]); } break; @@ -1384,7 +1473,7 @@ static int nameserver_add_all(struct connman_service *service) service->nameservers_config[i]) == AF_INET6 && service->dns_config_method_ipv6 == CONNMAN_DNSCONFIG_METHOD_MANUAL) { - nameserver_add(service, + nameserver_add(service, type, service->nameservers_config[i]); } break; @@ -1393,14 +1482,14 @@ static int nameserver_add_all(struct connman_service *service) service->nameservers_config[i]) == AF_INET && service->dns_config_method_ipv4 == CONNMAN_DNSCONFIG_METHOD_MANUAL) { - nameserver_add(service, + nameserver_add(service, type, service->nameservers_config[i]); } if (connman_inet_check_ipaddress( service->nameservers_config[i]) == AF_INET6 && service->dns_config_method_ipv6 == CONNMAN_DNSCONFIG_METHOD_MANUAL) { - nameserver_add(service, + nameserver_add(service, type, service->nameservers_config[i]); } break; @@ -1412,19 +1501,12 @@ static int nameserver_add_all(struct connman_service *service) break; } #else - nameserver_add(service, service->nameservers_config[i]); + nameserver_add(service, type, + service->nameservers_config[i]); #endif i++; } -#if !defined TIZEN_EXT - return 0; -#endif - } - -#if defined TIZEN_EXT - i = 0; -#endif - if (service->nameservers) { + } else if (service->nameservers) { while (service->nameservers[i]) { #if defined TIZEN_EXT DBG("type %d service->nameservers[%d]: %s",type, @@ -1436,7 +1518,7 @@ static int nameserver_add_all(struct connman_service *service) service->nameservers[i]) == AF_INET && service->dns_config_method_ipv4 == CONNMAN_DNSCONFIG_METHOD_DHCP) { - nameserver_add(service, + nameserver_add(service, type, service->nameservers[i]); } break; @@ -1445,7 +1527,7 @@ static int nameserver_add_all(struct connman_service *service) service->nameservers[i]) == AF_INET6 && service->dns_config_method_ipv6 == CONNMAN_DNSCONFIG_METHOD_DHCP) { - nameserver_add(service, + nameserver_add(service, type, service->nameservers[i]); } break; @@ -1454,14 +1536,14 @@ static int nameserver_add_all(struct connman_service *service) service->nameservers[i]) == AF_INET && service->dns_config_method_ipv4 == CONNMAN_DNSCONFIG_METHOD_DHCP) { - nameserver_add(service, + nameserver_add(service, type, service->nameservers[i]); } if (connman_inet_check_ipaddress( service->nameservers[i]) == AF_INET6 && service->dns_config_method_ipv6 == CONNMAN_DNSCONFIG_METHOD_DHCP) { - nameserver_add(service, + nameserver_add(service, type, service->nameservers[i]); } break; @@ -1473,21 +1555,28 @@ static int nameserver_add_all(struct connman_service *service) break; } #else - nameserver_add(service, service->nameservers[i]); + nameserver_add(service, type, + service->nameservers[i]); #endif i++; } } + if (!i) + __connman_resolver_append_fallback_nameservers(); + + searchdomain_add_all(service); + return 0; } 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, nameserver)) + if (!nameserver_available(service, type, nameserver)) return 0; index = __connman_service_get_index(service); @@ -1497,15 +1586,15 @@ static int nameserver_remove(struct connman_service *service, #if defined TIZEN_EXT DBG("Resolver remove nameserver: %s", nameserver); #endif - return connman_resolver_remove(index, NULL, nameserver); + ret = connman_resolver_remove(index, NULL, nameserver); + if (ret >= 0) + nameservers_changed(service); + + return ret; } -#if defined TIZEN_EXT static int nameserver_remove_all(struct connman_service *service, - enum connman_ipconfig_type type) -#else -static int nameserver_remove_all(struct connman_service *service) -#endif + enum connman_ipconfig_type type) { #if defined TIZEN_EXT /** @@ -1534,7 +1623,7 @@ static int nameserver_remove_all(struct connman_service *service) CONNMAN_DNSCONFIG_METHOD_DHCP || service->dns_config_method_ipv4 == CONNMAN_DNSCONFIG_METHOD_MANUAL)) { - nameserver_remove(service, + nameserver_remove(service, type, service->nameservers_config[i]); } break; @@ -1545,7 +1634,7 @@ static int nameserver_remove_all(struct connman_service *service) CONNMAN_DNSCONFIG_METHOD_DHCP || service->dns_config_method_ipv6 == CONNMAN_DNSCONFIG_METHOD_MANUAL)) { - nameserver_remove(service, + nameserver_remove(service, type, service->nameservers_config[i]); } break; @@ -1556,7 +1645,7 @@ static int nameserver_remove_all(struct connman_service *service) CONNMAN_DNSCONFIG_METHOD_DHCP || service->dns_config_method_ipv4 == CONNMAN_DNSCONFIG_METHOD_MANUAL)) { - nameserver_remove(service, + nameserver_remove(service, type, service->nameservers_config[i]); } if (connman_inet_check_ipaddress( @@ -1565,7 +1654,7 @@ static int nameserver_remove_all(struct connman_service *service) CONNMAN_DNSCONFIG_METHOD_DHCP || service->dns_config_method_ipv6 == CONNMAN_DNSCONFIG_METHOD_MANUAL)) { - nameserver_remove(service, + nameserver_remove(service, type, service->nameservers_config[i]); } break; @@ -1577,7 +1666,8 @@ static int nameserver_remove_all(struct connman_service *service) break; } #else - nameserver_remove(service, service->nameservers_config[i]); + nameserver_remove(service, type, + service->nameservers_config[i]); #endif i++; } @@ -1595,7 +1685,7 @@ static int nameserver_remove_all(struct connman_service *service) CONNMAN_DNSCONFIG_METHOD_MANUAL || service->dns_config_method_ipv4 == CONNMAN_DNSCONFIG_METHOD_DHCP)) { - nameserver_remove(service, + nameserver_remove(service, type, service->nameservers[i]); } break; @@ -1606,7 +1696,7 @@ static int nameserver_remove_all(struct connman_service *service) CONNMAN_DNSCONFIG_METHOD_MANUAL || service->dns_config_method_ipv6 == CONNMAN_DNSCONFIG_METHOD_DHCP)) { - nameserver_remove(service, + nameserver_remove(service, type, service->nameservers[i]); } break; @@ -1617,7 +1707,7 @@ static int nameserver_remove_all(struct connman_service *service) CONNMAN_DNSCONFIG_METHOD_MANUAL || service->dns_config_method_ipv4 == CONNMAN_DNSCONFIG_METHOD_DHCP)) { - nameserver_remove(service, + nameserver_remove(service, type, service->nameservers[i]); } if (connman_inet_check_ipaddress( @@ -1626,7 +1716,7 @@ static int nameserver_remove_all(struct connman_service *service) CONNMAN_DNSCONFIG_METHOD_MANUAL || service->dns_config_method_ipv6 == CONNMAN_DNSCONFIG_METHOD_DHCP)) { - nameserver_remove(service, + nameserver_remove(service, type, service->nameservers[i]); } break; @@ -1638,60 +1728,12 @@ static int nameserver_remove_all(struct connman_service *service) break; } #else - nameserver_remove(service, service->nameservers[i]); + nameserver_remove(service, type, service->nameservers[i]); #endif i++; } - return 0; -} - -static int searchdomain_add_all(struct connman_service *service) -{ - int index, i = 0; - - if (!is_connected(service)) - return -ENOTCONN; - - index = __connman_service_get_index(service); - if (index < 0) - return -ENXIO; - - if (service->domains) { - while (service->domains[i]) { - connman_resolver_append(index, service->domains[i], - NULL); - i++; - } - - return 0; - } - - if (service->domainname) - connman_resolver_append(index, service->domainname, NULL); - - return 0; - -} - -static int searchdomain_remove_all(struct connman_service *service) -{ - int index, i = 0; - - if (!is_connected(service)) - return -ENOTCONN; - - index = __connman_service_get_index(service); - if (index < 0) - return -ENXIO; - - while (service->domains && service->domains[i]) { - connman_resolver_remove(index, service->domains[i], NULL); - i++; - } - - if (service->domainname) - connman_resolver_remove(index, service->domainname, NULL); + searchdomain_remove_all(service); return 0; } @@ -1750,13 +1792,11 @@ int __connman_service_nameserver_append(struct connman_service *service, #ifdef TIZEN_EXT if(type == CONNMAN_IPCONFIG_TYPE_IPV4 && - service->dns_config_method_ipv4 == - CONNMAN_DNSCONFIG_METHOD_UNKNOWN) + service->dns_config_method_ipv4 == CONNMAN_DNSCONFIG_METHOD_UNKNOWN) service->dns_config_method_ipv4 = CONNMAN_DNSCONFIG_METHOD_DHCP; if(type == CONNMAN_IPCONFIG_TYPE_IPV6 && - service->dns_config_method_ipv6 == - CONNMAN_DNSCONFIG_METHOD_UNKNOWN) + service->dns_config_method_ipv6 == CONNMAN_DNSCONFIG_METHOD_UNKNOWN) service->dns_config_method_ipv6 = CONNMAN_DNSCONFIG_METHOD_DHCP; #endif @@ -1764,12 +1804,13 @@ int __connman_service_nameserver_append(struct connman_service *service, service->nameservers_auto = nameservers; } else { service->nameservers = nameservers; -#if defined TIZEN_EXT - DBG("nameserver add: %s, type: %d", nameserver, type); -#endif - nameserver_add(service, nameserver); + nameserver_add(service, CONNMAN_IPCONFIG_TYPE_ALL, nameserver); } + nameservers_changed(service); + + searchdomain_add_all(service); + return 0; } @@ -1799,7 +1840,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; @@ -1840,8 +1881,12 @@ set_servers: service->nameservers = nameservers; #if defined TIZEN_EXT DBG("nameserver remove ip_type: %d", type); + nameserver_remove(service, type, + nameserver); +#else + nameserver_remove(service, CONNMAN_IPCONFIG_TYPE_ALL, + nameserver); #endif - nameserver_remove(service, nameserver); } return 0; @@ -1849,23 +1894,12 @@ set_servers: void __connman_service_nameserver_clear(struct connman_service *service) { -#if defined TIZEN_EXT - DBG("nameserver remove all ip_type: CONNMAN_IPCONFIG_TYPE_ALL"); nameserver_remove_all(service, CONNMAN_IPCONFIG_TYPE_ALL); -#else - nameserver_remove_all(service); -#endif g_strfreev(service->nameservers); service->nameservers = NULL; -#if defined TIZEN_EXT - DBG("nameserver add all ip_type: CONNMAN_IPCONFIG_TYPE_ALL"); nameserver_add_all(service, CONNMAN_IPCONFIG_TYPE_ALL); -#else - nameserver_add_all(service); -#endif - } static void add_nameserver_route(int family, int index, char *nameserver, @@ -1978,6 +2012,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(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(struct connman_service *service) { if (service->roaming) @@ -2107,21 +2153,21 @@ struct connman_service *connman_service_get_default_connection(void) __connman_service_type2string(service->type)); if (service->type == CONNMAN_SERVICE_TYPE_WIFI && - is_connected(service) == TRUE) { + is_connected(service->state) == TRUE) { return service; } else if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR && __connman_service_is_internet_profile(service) == TRUE) { if (default_service == NULL) default_service = service; - else if (is_connected(service) == TRUE && - is_connected(default_service) == FALSE) + else if (is_connected(service->state) == TRUE && + is_connected(default_service->state) == FALSE) default_service = service; } else if (service->type == CONNMAN_SERVICE_TYPE_ETHERNET && - is_connected(service) == TRUE) { + is_connected(service->state) == TRUE) { if (default_service == NULL) default_service = service; } else if (service->type == CONNMAN_SERVICE_TYPE_BLUETOOTH && - is_connected(service) == TRUE) { + is_connected(service->state) == TRUE) { if (default_service == NULL) default_service = service; } @@ -2140,7 +2186,7 @@ struct connman_service *__connman_service_get_default(void) service = service_list->data; - if (!is_connected(service)) + if (!is_connected(service->state)) return NULL; return service; @@ -2339,7 +2385,7 @@ static void append_ipv4(DBusMessageIter *iter, void *user_data) { struct connman_service *service = user_data; - if (!is_connected_state(service, service->state_ipv4)) + if (!is_connected(service->state_ipv4)) return; if (service->ipconfig_ipv4) @@ -2350,7 +2396,7 @@ static void append_ipv6(DBusMessageIter *iter, void *user_data) { struct connman_service *service = user_data; - if (!is_connected_state(service, service->state_ipv6)) + if (!is_connected(service->state_ipv6)) return; if (service->ipconfig_ipv6) @@ -2384,9 +2430,9 @@ static void append_nameservers(DBusMessageIter *iter, for (i = 0; servers[i]; i++) { if (service) - available = nameserver_available(service, servers[i]); - - DBG("servers[%d] %s available %d", i, servers[i], available); + available = nameserver_available(service, + CONNMAN_IPCONFIG_TYPE_ALL, + servers[i]); if (available) dbus_message_iter_append_basic(iter, @@ -2401,7 +2447,8 @@ static void append_nameserver_manual(DBusMessageIter *iter, bool available = true; if (service) - available = nameserver_available(service, server); + available = nameserver_available(service, + CONNMAN_IPCONFIG_TYPE_ALL, server); if (available) dbus_message_iter_append_basic(iter, @@ -2414,7 +2461,8 @@ static void append_nameserver_dhcp(DBusMessageIter *iter, bool available = true; if (service) - available = nameserver_available(service, server); + available = nameserver_available(service, + CONNMAN_IPCONFIG_TYPE_ALL, server); if (available) dbus_message_iter_append_basic(iter, @@ -2429,7 +2477,7 @@ static void append_dns(DBusMessageIter *iter, void *user_data) int i; #endif - if (!is_connected(service)) + if (!is_connected(service->state)) return; #ifdef TIZEN_EXT @@ -2639,8 +2687,8 @@ static void append_domain(DBusMessageIter *iter, void *user_data) { struct connman_service *service = user_data; - if (!is_connected(service) && - !is_connecting(service)) + if (!is_connected(service->state) && + !is_connecting(service->state)) return; if (service->domains) @@ -2684,7 +2732,7 @@ static void append_proxy(DBusMessageIter *iter, void *user_data) const char *method = proxymethod2string( CONNMAN_SERVICE_PROXY_METHOD_DIRECT); - if (!is_connected(service)) + if (!is_connected(service->state)) return; proxy = connman_service_get_proxy_method(service); @@ -2771,7 +2819,7 @@ static void append_provider(DBusMessageIter *iter, void *user_data) { struct connman_service *service = user_data; - if (!is_connected(service)) + if (!is_connected(service->state)) return; if (service->provider) @@ -2784,11 +2832,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", @@ -2797,8 +2847,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) @@ -2813,6 +2861,15 @@ static void ipv4_configuration_changed(struct connman_service *service) service); } +void __connman_service_notify_ipv4_configuration( + struct connman_service *service) +{ + if (!service) + return; + + ipv4_configuration_changed(service); +} + static void ipv6_configuration_changed(struct connman_service *service) { if (!allow_property_changed(service)) @@ -3074,7 +3131,7 @@ void __connman_service_notify(struct connman_service *service, if (!service) return; - if (!is_connected(service)) + if (!is_connected(service->state)) return; stats_update(service, @@ -3248,25 +3305,14 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited, connman_dbus_dict_append_basic(dict, "Frequency", DBUS_TYPE_UINT16, &frequency); } - - unsigned char *wifi_vsie; + const void *wifi_vsie; unsigned int wifi_vsie_len; - GSList *vsie_list = NULL; - - if (service->network) - vsie_list = (GSList *)connman_network_get_vsie_list(service->network); - - if (vsie_list) { - DBG("ConnMan, service->path=%s No.of elements in list: %d", service->path, g_slist_length(vsie_list)); - GSList *list; - for (list = vsie_list; list; list = list->next) { - wifi_vsie = (unsigned char *)list->data; - wifi_vsie_len = wifi_vsie[1] + 2; - - connman_dbus_dict_append_fixed_array(dict, "Vsie", DBUS_TYPE_BYTE, - &wifi_vsie, wifi_vsie_len); - } + wifi_vsie = connman_network_get_blob(service->network, "WiFi.Vsie", &wifi_vsie_len); + if(wifi_vsie_len > 0) { + DBG("ConnMan, service->path=%s vsie length=%d", service->path, wifi_vsie_len); } + connman_dbus_dict_append_fixed_array(dict, "Vsie", DBUS_TYPE_BYTE, + &wifi_vsie, wifi_vsie_len); #endif str = __connman_service_type2string(service->type); @@ -3886,6 +3932,81 @@ void __connman_service_set_identity(struct connman_service *service, service->identity); } +void __connman_service_set_anonymous_identity(struct connman_service *service, + const char *anonymous_identity) +{ + if (service->immutable || service->hidden) + return; + + g_free(service->anonymous_identity); + service->anonymous_identity = g_strdup(anonymous_identity); + + if (service->network) + connman_network_set_string(service->network, + "WiFi.AnonymousIdentity", + service->anonymous_identity); +} + +void __connman_service_set_subject_match(struct connman_service *service, + const char *subject_match) +{ + if (service->immutable || service->hidden) + return; + + g_free(service->subject_match); + service->subject_match = g_strdup(subject_match); + + if (service->network) + connman_network_set_string(service->network, + "WiFi.SubjectMatch", + service->subject_match); +} + +void __connman_service_set_altsubject_match(struct connman_service *service, + const char *altsubject_match) +{ + if (service->immutable || service->hidden) + return; + + g_free(service->altsubject_match); + service->altsubject_match = g_strdup(altsubject_match); + + if (service->network) + connman_network_set_string(service->network, + "WiFi.AltSubjectMatch", + service->altsubject_match); +} + +void __connman_service_set_domain_suffix_match(struct connman_service *service, + const char *domain_suffix_match) +{ + if (service->immutable || service->hidden) + return; + + g_free(service->domain_suffix_match); + service->domain_suffix_match = g_strdup(domain_suffix_match); + + if (service->network) + connman_network_set_string(service->network, + "WiFi.DomainSuffixMatch", + service->domain_suffix_match); +} + +void __connman_service_set_domain_match(struct connman_service *service, + const char *domain_match) +{ + if (service->immutable || service->hidden) + return; + + g_free(service->domain_match); + service->domain_match = g_strdup(domain_match); + + if (service->network) + connman_network_set_string(service->network, + "WiFi.DomainMatch", + service->domain_match); +} + void __connman_service_set_agent_identity(struct connman_service *service, const char *agent_identity) { @@ -3900,7 +4021,7 @@ void __connman_service_set_agent_identity(struct connman_service *service, service->agent_identity); } -static int check_passphrase(enum connman_service_security security, +int __connman_service_check_passphrase(enum connman_service_security security, const char *passphrase) { guint i; @@ -3979,7 +4100,7 @@ int __connman_service_set_passphrase(struct connman_service *service, service->security != CONNMAN_SERVICE_SECURITY_RSN && service->security != CONNMAN_SERVICE_SECURITY_WEP) #endif - err = check_passphrase(service->security, passphrase); + err = __connman_service_check_passphrase(service->security, passphrase); if (err < 0) return err; @@ -4009,8 +4130,6 @@ static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *reply; DBusMessageIter array, dict; - DBG("service %p", service); - reply = dbus_message_new_method_return(msg); if (!reply) return NULL; @@ -4024,6 +4143,23 @@ static DBusMessage *get_properties(DBusConnection *conn, return reply; } +static char **remove_empty_strings(char **strv) +{ + int index = 0; + char **iter = strv; + + while (*iter) { + if (**iter) + strv[index++] = *iter; + else + g_free(*iter); + iter++; + } + + strv[index] = NULL; + return strv; +} + static int update_proxy_configuration(struct connman_service *service, DBusMessageIter *array) { @@ -4137,20 +4273,24 @@ static int update_proxy_configuration(struct connman_service *service, if (servers_str) { g_strfreev(service->proxies); - if (servers_str->len > 0) - service->proxies = g_strsplit_set( + if (servers_str->len > 0) { + char **proxies = g_strsplit_set( servers_str->str, " ", 0); - else + proxies = remove_empty_strings(proxies); + service->proxies = proxies; + } else service->proxies = NULL; } if (excludes_str) { g_strfreev(service->excludes); - if (excludes_str->len > 0) - service->excludes = g_strsplit_set( + if (excludes_str->len > 0) { + char **excludes = g_strsplit_set( excludes_str->str, " ", 0); - else + excludes = remove_empty_strings(excludes); + service->excludes = excludes; + } else service->excludes = NULL; } @@ -4162,7 +4302,7 @@ static int update_proxy_configuration(struct connman_service *service, g_free(service->pac); if (url && strlen(url) > 0) - service->pac = g_strdup(url); + service->pac = g_strstrip(g_strdup(url)); else service->pac = NULL; @@ -4239,8 +4379,7 @@ int __connman_service_reset_ipconfig(struct connman_service *service, new_method = __connman_ipconfig_get_method(new_ipconfig); } - if (is_connecting_state(service, state) || - is_connected_state(service, state)) + if (is_connecting(state) || is_connected(state)) __connman_network_clear_ipconfig(service->network, ipconfig); __connman_ipconfig_unref(ipconfig); @@ -4250,8 +4389,7 @@ int __connman_service_reset_ipconfig(struct connman_service *service, else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) service->ipconfig_ipv6 = new_ipconfig; - if (is_connecting_state(service, state) || - is_connected_state(service, state)) + if (is_connecting(state) || is_connected(state)) __connman_ipconfig_enable(new_ipconfig); if (new_state && new_method != old_method) { @@ -4260,6 +4398,9 @@ int __connman_service_reset_ipconfig(struct connman_service *service, else *new_state = service->state_ipv6; + settings_changed(service, new_ipconfig); + address_updated(service, new_method); + __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO); } @@ -4286,7 +4427,7 @@ static DBusMessage *set_property(DBusConnection *conn, if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __connman_error_invalid_arguments(msg); - if (service->type == CONNMAN_SERVICE_TYPE_WIFI && is_connected(service)) { + if (service->type == CONNMAN_SERVICE_TYPE_WIFI && is_connected(service->state)) { uid_t uid; if (connman_dbus_get_connection_unix_user_sync(conn, dbus_message_get_sender(msg), @@ -4358,10 +4499,11 @@ static DBusMessage *set_property(DBusConnection *conn, gw = __connman_ipconfig_get_gateway_from_index(index, CONNMAN_IPCONFIG_TYPE_ALL); +#if !defined TIZEN_EXT if (gw && strlen(gw)) __connman_service_nameserver_del_routes(service, CONNMAN_IPCONFIG_TYPE_ALL); - +#endif dbus_message_iter_recurse(&value, &entry); #if defined TIZEN_EXT @@ -4415,12 +4557,13 @@ static DBusMessage *set_property(DBusConnection *conn, continue; } #endif - if (connman_inet_check_ipaddress(val) > 0) { - if (str->len > 0) - g_string_append_printf(str, " %s", val); - else - g_string_append(str, val); - } + if (!val[0]) + continue; + + if (str->len > 0) + g_string_append_printf(str, " %s", val); + else + g_string_append(str, val); } #if defined TIZEN_EXT @@ -4436,13 +4579,21 @@ static DBusMessage *set_property(DBusConnection *conn, DBG("%s ip_type: %d nameserver remove all", name, ip_type); nameserver_remove_all(service, ip_type); #else - nameserver_remove_all(service); + nameserver_remove_all(service, CONNMAN_IPCONFIG_TYPE_ALL); #endif g_strfreev(service->nameservers_config); if (str->len > 0) { - service->nameservers_config = - g_strsplit_set(str->str, " ", 0); + char **nameservers, **iter; + + nameservers = g_strsplit_set(str->str, " ", 0); + + for (iter = nameservers; *iter; iter++) + if (connman_inet_check_ipaddress(*iter) <= 0) + *iter[0] = '\0'; + + nameservers = remove_empty_strings(nameservers); + service->nameservers_config = nameservers; } else { service->nameservers_config = NULL; } @@ -4456,7 +4607,7 @@ static DBusMessage *set_property(DBusConnection *conn, DBG("%s ip_type: %d nameserver add all", name, ip_type); nameserver_add_all(service, ip_type); #else - nameserver_add_all(service); + nameserver_add_all(service, CONNMAN_IPCONFIG_TYPE_ALL); #endif dns_configuration_changed(service); @@ -4473,8 +4624,7 @@ static DBusMessage *set_property(DBusConnection *conn, service_save(service); } else if (g_str_equal(name, "Timeservers.Configuration")) { DBusMessageIter entry; - GSList *list = NULL; - int count = 0; + GString *str; if (service->immutable) return __connman_error_not_supported(msg); @@ -4482,35 +4632,37 @@ static DBusMessage *set_property(DBusConnection *conn, if (type != DBUS_TYPE_ARRAY) return __connman_error_invalid_arguments(msg); + str = g_string_new(NULL); + if (!str) + return __connman_error_invalid_arguments(msg); + dbus_message_iter_recurse(&value, &entry); while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { const char *val; - GSList *new_head; - dbus_message_iter_get_basic(&entry, &val); + dbus_message_iter_next(&entry); - new_head = __connman_timeserver_add_list(list, val); - if (list != new_head) { - count++; - list = new_head; - } + if (!val[0]) + continue; - dbus_message_iter_next(&entry); + if (str->len > 0) + g_string_append_printf(str, " %s", val); + else + g_string_append(str, val); } g_strfreev(service->timeservers_config); service->timeservers_config = NULL; - if (list) { - service->timeservers_config = g_new0(char *, count+1); + if (str->len > 0) { + char **timeservers = g_strsplit_set(str->str, " ", 0); + timeservers = remove_empty_strings(timeservers); + service->timeservers_config = timeservers; + } else + service->timeservers = NULL; - while (list) { - count--; - service->timeservers_config[count] = list->data; - list = g_slist_delete_link(list, list); - }; - } + g_string_free(str, TRUE); service_save(service); timeservers_configuration_changed(service); @@ -4538,6 +4690,10 @@ static DBusMessage *set_property(DBusConnection *conn, const char *val; dbus_message_iter_get_basic(&entry, &val); dbus_message_iter_next(&entry); + + if (!val[0]) + continue; + if (str->len > 0) g_string_append_printf(str, " %s", val); else @@ -4547,9 +4703,11 @@ static DBusMessage *set_property(DBusConnection *conn, searchdomain_remove_all(service); g_strfreev(service->domains); - if (str->len > 0) - service->domains = g_strsplit_set(str->str, " ", 0); - else + if (str->len > 0) { + char **domains = g_strsplit_set(str->str, " ", 0); + domains = remove_empty_strings(domains); + service->domains = domains; + } else service->domains = NULL; g_string_free(str, TRUE); @@ -4606,8 +4764,7 @@ static DBusMessage *set_property(DBusConnection *conn, &state); if (err < 0) { - if (is_connected_state(service, state) || - is_connecting_state(service, state)) { + if (is_connected(state) || is_connecting(state)) { if (type == CONNMAN_IPCONFIG_TYPE_IPV4) __connman_network_enable_ipconfig(service->network, service->ipconfig_ipv4); @@ -4624,7 +4781,8 @@ static DBusMessage *set_property(DBusConnection *conn, else ipv6_configuration_changed(service); - if (is_connecting(service) || is_connected(service)) { + if (is_connecting(service->state) || + is_connected(service->state)) { if (type == CONNMAN_IPCONFIG_TYPE_IPV4) __connman_network_enable_ipconfig(service->network, service->ipconfig_ipv4); @@ -4668,6 +4826,41 @@ static void set_error(struct connman_service *service, DBUS_TYPE_STRING, &str); } +static void remove_timeout(struct connman_service *service) +{ + if (service->timeout > 0) { + g_source_remove(service->timeout); + service->timeout = 0; + } +} + +static void reply_pending(struct connman_service *service, int error) +{ + remove_timeout(service); + + if (service->pending) { + connman_dbus_reply_pending(service->pending, error, NULL); + service->pending = NULL; + } + + if (service->provider_pending) { + connman_dbus_reply_pending(service->provider_pending, + error, service->path); + service->provider_pending = NULL; + } +} + +static void service_complete(struct connman_service *service) +{ + reply_pending(service, EIO); + + if (service->connect_reason != CONNMAN_SERVICE_CONNECT_REASON_USER) + __connman_service_auto_connect(service->connect_reason); + + g_get_current_time(&service->modified); + service_save(service); +} + static void set_idle(struct connman_service *service) { service->state = service->state_ipv4 = service->state_ipv6 = @@ -4690,8 +4883,8 @@ static DBusMessage *clear_property(DBusConnection *conn, if (g_str_equal(name, "Error")) { set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN); - g_get_current_time(&service->modified); - service_save(service); + __connman_service_clear_error(service); + service_complete(service); } else return __connman_error_invalid_property(msg); @@ -4746,6 +4939,7 @@ static void disconnect_on_last_session(enum connman_service_type type) } static int active_sessions[MAX_CONNMAN_SERVICE_TYPES] = {}; +static int always_connect[MAX_CONNMAN_SERVICE_TYPES] = {}; static int active_count = 0; void __connman_service_set_active_session(bool enable, GSList *list) @@ -4758,7 +4952,7 @@ void __connman_service_set_active_session(bool enable, GSList *list) else active_count--; - while (list != NULL) { + while (list) { enum connman_service_type type = GPOINTER_TO_INT(list->data); switch (type) { @@ -4830,7 +5024,7 @@ static GList *preferred_tech_list_get(void) for (list = service_list; list; list = list->next) { struct connman_service *service = list->data; - if (!is_connected(service)) + if (!is_connected(service->state)) break; if (service->connect_reason == @@ -4862,6 +5056,43 @@ static GList *preferred_tech_list_get(void) return tech_data.preferred_list; } +static void set_always_connecting_technologies() +{ + unsigned int *always_connected_techs = + connman_setting_get_uint_list("AlwaysConnectedTechnologies"); + int i; + for (i = 0; always_connected_techs && always_connected_techs[i]; i++) + always_connect[always_connected_techs[i]] = 1; +} + +static bool autoconnect_no_session_active(struct connman_service *service) +{ + /* + * Test active_count to see if there are no sessions set up and + * stop autoconnecting, but continue connecting if the service + * belongs to a technology which should always autoconnect. + */ + if (!active_count && !always_connect[service->type]) + return true; + + return false; +} + +static bool autoconnect_already_connecting(struct connman_service *service, + bool autoconnecting) +{ + /* + * If another service is already connecting and this service type has + * not been marked as always connecting, stop the connecting procedure. + */ + if (autoconnecting && + !active_sessions[service->type] && + !always_connect[service->type]) + return true; + + return false; +} + static bool auto_connect_service(GList *services, enum connman_service_connect_reason reason, bool preferred) @@ -4895,15 +5126,15 @@ static bool auto_connect_service(GList *services, /* Tizen takes Wi-Fi as the highest priority into consideration. */ if (service->type != CONNMAN_SERVICE_TYPE_WIFI) - if (is_connecting(service) == TRUE || is_connected(service) == TRUE) + if (is_connecting(service->state) == TRUE || is_connected(service->state) == TRUE) continue; #endif if (service->pending || - is_connecting(service) || - is_connected(service)) { - if (!active_count) - return true; + is_connecting(service->state) || + is_connected(service->state)) { + if (autoconnect_no_session_active(service)) + return true; ignore[service->type] = true; autoconnecting = true; @@ -4937,7 +5168,7 @@ static bool auto_connect_service(GList *services, CONNMAN_SERVICE_STATE_IDLE) continue; - if (autoconnecting && !active_sessions[service->type]) { + if (autoconnect_already_connecting(service, autoconnecting)) { DBG("service %p type %s has no users", service, __connman_service_type2string(service->type)); continue; @@ -4953,7 +5184,7 @@ static bool auto_connect_service(GList *services, __connman_service_connect(service, reason); - if (!active_count) + if (autoconnect_no_session_active(service)) return true; ignore[service->type] = true; @@ -5032,7 +5263,7 @@ void __connman_service_auto_connect(enum connman_service_connect_reason reason) * is ignored as its last state was FAILURE rather than IDLE */ autoconnect_timeout = g_timeout_add(500, run_auto_connect, #else - autoconnect_timeout = g_timeout_add_seconds(0, run_auto_connect, + autoconnect_timeout = g_idle_add(run_auto_connect, #endif GUINT_TO_POINTER(reason)); } @@ -5050,7 +5281,8 @@ static gboolean run_vpn_auto_connect(gpointer data) { if (service->type != CONNMAN_SERVICE_TYPE_VPN) continue; - if (is_connected(service) || is_connecting(service)) { + if (is_connected(service->state) || + is_connecting(service->state)) { if (!service->do_split_routing) need_split = true; continue; @@ -5086,31 +5318,7 @@ static void vpn_auto_connect(void) return; vpn_autoconnect_timeout = - g_timeout_add_seconds(0, run_vpn_auto_connect, NULL); -} - -static void remove_timeout(struct connman_service *service) -{ - if (service->timeout > 0) { - g_source_remove(service->timeout); - service->timeout = 0; - } -} - -static void reply_pending(struct connman_service *service, int error) -{ - remove_timeout(service); - - if (service->pending) { - connman_dbus_reply_pending(service->pending, error, NULL); - service->pending = NULL; - } - - if (service->provider_pending) { - connman_dbus_reply_pending(service->provider_pending, - error, service->path); - service->provider_pending = NULL; - } + g_idle_add(run_vpn_auto_connect, NULL); } bool @@ -5187,9 +5395,6 @@ static gboolean connect_timeout(gpointer user_data) else if (service->provider) connman_provider_disconnect(service->provider); - __connman_ipconfig_disable(service->ipconfig_ipv4); - __connman_ipconfig_disable(service->ipconfig_ipv6); - __connman_stats_service_unregister(service); if (service->pending) { @@ -5284,7 +5489,7 @@ static DBusMessage *connect_service(DBusConnection *conn, if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR) break; #endif - if (!is_connecting(temp) && !is_connected(temp)) + if (!is_connecting(temp->state) && !is_connected(temp->state)) break; if (service == temp) @@ -5339,7 +5544,7 @@ static DBusMessage *disconnect_service(DBusConnection *conn, if (connman_service_user_pdn_connection_unref_and_test(service) != TRUE) return __connman_error_failed(msg, EISCONN); - if (is_connected(service) == TRUE && + if (is_connected(service->state) == TRUE && service == connman_service_get_default_connection()) return __connman_error_failed(msg, EISCONN); } @@ -5398,8 +5603,7 @@ bool __connman_service_remove(struct connman_service *service) return false; #if !defined TIZEN_EXT - if (!service->favorite && service->state != - CONNMAN_SERVICE_STATE_FAILURE) + if (!service->favorite && !is_idle(service->state)) return false; #endif @@ -5411,6 +5615,21 @@ bool __connman_service_remove(struct connman_service *service) g_free(service->identity); service->identity = NULL; + g_free(service->anonymous_identity); + service->anonymous_identity = NULL; + + g_free(service->subject_match); + service->subject_match = NULL; + + g_free(service->altsubject_match); + service->altsubject_match = NULL; + + g_free(service->domain_suffix_match); + service->domain_suffix_match = NULL; + + g_free(service->domain_match); + service->domain_match = NULL; + g_free(service->agent_identity); service->agent_identity = NULL; @@ -5613,11 +5832,11 @@ static DBusMessage *move_service(DBusConnection *conn, return __connman_error_invalid_service(msg); } - target->do_split_routing = true; + set_split_routing(target, true); } else - target->do_split_routing = false; + set_split_routing(target, false); - service->do_split_routing = false; + set_split_routing(service, false); target4 = __connman_ipconfig_get_method(target->ipconfig_ipv4); target6 = __connman_ipconfig_get_method(target->ipconfig_ipv6); @@ -5855,10 +6074,8 @@ static bool allow_property_changed(struct connman_service *service) return FALSE; #endif if (g_hash_table_lookup_extended(services_notify->add, service->path, - NULL, NULL)) { - DBG("no property updates for service %p", service); + NULL, NULL)) return false; - } return true; } @@ -5906,6 +6123,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); service_schedule_removed(service); @@ -5964,8 +6186,13 @@ static void service_free(gpointer user_data) g_free(service->identifier); g_free(service->eap); g_free(service->identity); + g_free(service->anonymous_identity); g_free(service->agent_identity); g_free(service->ca_cert_file); + g_free(service->subject_match); + g_free(service->altsubject_match); + g_free(service->domain_suffix_match); + g_free(service->domain_match); g_free(service->client_cert_file); g_free(service->private_key_file); g_free(service->private_key_passphrase); @@ -6138,8 +6365,8 @@ static gint service_compare(gconstpointer a, gconstpointer b) state_a = service_a->state; state_b = service_b->state; - a_connected = is_connected(service_a); - b_connected = is_connected(service_b); + a_connected = is_connected(state_a); + b_connected = is_connected(state_b); if (a_connected && b_connected) { if (service_a->order > service_b->order) @@ -6164,9 +6391,9 @@ static gint service_compare(gconstpointer a, gconstpointer b) if (b_connected) return 1; - if (is_connecting(service_a)) + if (is_connecting(state_a)) return -1; - if (is_connecting(service_b)) + if (is_connecting(state_b)) return 1; } @@ -6177,6 +6404,20 @@ static gint service_compare(gconstpointer a, gconstpointer b) return 1; if (service_a->type != service_b->type) { + unsigned int *tech_array; + int i; + + tech_array = connman_setting_get_uint_list( + "PreferredTechnologies"); + if (tech_array) { + for (i = 0; tech_array[i]; i++) { + if (tech_array[i] == service_a->type) + return -1; + + if (tech_array[i] == service_b->type) + return 1; + } + } if (service_a->type == CONNMAN_SERVICE_TYPE_ETHERNET) return -1; @@ -6224,6 +6465,12 @@ static void service_list_sort(void) } } +int __connman_service_compare(const struct connman_service *a, + const struct connman_service *b) +{ + return service_compare(a, b); +} + /** * connman_service_get_type: * @service: service structure @@ -6275,7 +6522,7 @@ bool __connman_service_is_user_allowed(enum connman_service_type type, if (service->type != type) continue; - if (is_connected(service)) { + if (is_connected(service->state)) { owner_user = service->user.favorite_user; break; } @@ -6345,14 +6592,12 @@ bool __connman_service_is_connected_state(struct connman_service *service, case CONNMAN_IPCONFIG_TYPE_UNKNOWN: break; case CONNMAN_IPCONFIG_TYPE_IPV4: - return is_connected_state(service, service->state_ipv4); + return is_connected(service->state_ipv4); case CONNMAN_IPCONFIG_TYPE_IPV6: - return is_connected_state(service, service->state_ipv6); + return is_connected(service->state_ipv6); case CONNMAN_IPCONFIG_TYPE_ALL: - return is_connected_state(service, - CONNMAN_IPCONFIG_TYPE_IPV4) && - is_connected_state(service, - CONNMAN_IPCONFIG_TYPE_IPV6); + return is_connected(service->state_ipv4) && + is_connected(service->state_ipv6); } return false; @@ -6415,7 +6660,7 @@ int __connman_service_get_connected_count_of_iface( index2 = __connman_service_get_index(service2); - if (is_connected(service2) && index2 > 0 && index1 == index2) + if (is_connected(service2->state) && index2 > 0 && index1 == index2) count++; index2 = 0; @@ -6535,9 +6780,24 @@ void __connman_service_set_string(struct connman_service *service, } else if (g_str_equal(key, "Identity")) { g_free(service->identity); service->identity = g_strdup(value); + } else if (g_str_equal(key, "AnonymousIdentity")) { + g_free(service->anonymous_identity); + service->anonymous_identity = g_strdup(value); } else if (g_str_equal(key, "CACertFile")) { g_free(service->ca_cert_file); service->ca_cert_file = g_strdup(value); + } else if (g_str_equal(key, "SubjectMatch")) { + g_free(service->subject_match); + service->subject_match = g_strdup(value); + } else if (g_str_equal(key, "AltSubjectMatch")) { + g_free(service->altsubject_match); + service->altsubject_match = g_strdup(value); + } else if (g_str_equal(key, "DomainSuffixMatch")) { + g_free(service->domain_suffix_match); + service->domain_suffix_match = g_strdup(value); + } else if (g_str_equal(key, "DomainMatch")) { + g_free(service->domain_match); + service->domain_match = g_strdup(value); } else if (g_str_equal(key, "ClientCertFile")) { g_free(service->client_cert_file); service->client_cert_file = g_strdup(value); @@ -6567,17 +6827,6 @@ void __connman_service_set_search_domains(struct connman_service *service, searchdomain_add_all(service); } -static void service_complete(struct connman_service *service) -{ - reply_pending(service, EIO); - - if (service->connect_reason != CONNMAN_SERVICE_CONNECT_REASON_USER) - __connman_service_auto_connect(service->connect_reason); - - g_get_current_time(&service->modified); - service_save(service); -} - static void report_error_cb(void *user_context, bool retry, void *user_data) { @@ -6657,7 +6906,8 @@ static void request_input_cb(struct connman_service *service, if (service->hidden) __connman_service_return_error(service, - ECANCELED, user_data); + ECONNABORTED, + user_data); goto done; } else { if (service->hidden) @@ -6738,7 +6988,7 @@ static void downgrade_connected_services(void) for (list = service_list; list; list = list->next) { up_service = list->data; - if (!is_connected(up_service)) + if (!is_connected(up_service->state)) continue; if (up_service->state == CONNMAN_SERVICE_STATE_ONLINE) @@ -6781,7 +7031,7 @@ static int service_update_preferred_order(struct connman_service *default_servic #if defined TIZEN_EXT static gboolean __connman_service_can_drop(struct connman_service *service) { - if (is_connected(service) == TRUE || is_connecting(service) == TRUE) { + if (is_connected(service->state) == TRUE || is_connecting(service->state) == TRUE) { if (service->type != CONNMAN_SERVICE_TYPE_CELLULAR) return TRUE; else if (connman_service_is_no_ref_user_pdn_connection(service) == TRUE) @@ -6849,7 +7099,7 @@ static void __connman_service_connect_default(struct connman_service *current) } return; - } else if (is_connected(current) == TRUE || is_connecting(current) == TRUE) + } else if (is_connected(current->state) == TRUE || is_connecting(current->state) == TRUE) return; /* Always-on: keep default cellular connection as possible */ @@ -6871,8 +7121,8 @@ static void __connman_service_connect_default(struct connman_service *current) if (default_internet) { default_service = service; - if (is_connected(default_service) == TRUE || - is_connecting(default_service) == TRUE) + if (is_connected(default_service->state) == TRUE || + is_connecting(default_service->state) == TRUE) return; default_device = connman_network_get_device(default_service->network); @@ -6918,7 +7168,7 @@ static void single_connected_tech(struct connman_service *allowed) if (service != allowed && service->type != allowed->type && __connman_service_can_drop(service) == TRUE) #else - if (!is_connected(service)) + if (!is_connected(service->state)) break; if (service == allowed) @@ -6949,7 +7199,7 @@ static void set_priority_connected_service(void) for (list = service_list; list; list = list->next) { service = list->data; - if (is_connected(service) == FALSE) + if (is_connected(service->state) == FALSE) service->order = 5; else service->order = 6; @@ -7000,13 +7250,15 @@ static int service_indicate_state(struct connman_service *service) if (old_state == CONNMAN_SERVICE_STATE_ONLINE) __connman_notifier_leave_online(service->type); - if (is_connected_state(service, old_state) && - !is_connected_state(service, new_state)) + if (is_connected(old_state) && !is_connected(new_state)) searchdomain_remove_all(service); service->state = new_state; state_changed(service); + if (!is_connected(old_state) && is_connected(new_state)) + searchdomain_add_all(service); + switch(new_state) { case CONNMAN_SERVICE_STATE_UNKNOWN: @@ -7065,17 +7317,6 @@ static int service_indicate_state(struct connman_service *service) reply_pending(service, 0); - g_get_current_time(&service->modified); - service_save(service); - - searchdomain_add_all(service); - dns_changed(service); - domain_changed(service); - proxy_changed(service); - - if (old_state != CONNMAN_SERVICE_STATE_ONLINE) - __connman_notifier_connect(service->type); - if (service->type == CONNMAN_SERVICE_TYPE_WIFI && connman_network_get_bool(service->network, "WiFi.UseWPS")) { @@ -7090,6 +7331,15 @@ static int service_indicate_state(struct connman_service *service) "WiFi.UseWPS", false); } + g_get_current_time(&service->modified); + service_save(service); + + domain_changed(service); + proxy_changed(service); + + if (old_state != CONNMAN_SERVICE_STATE_ONLINE) + __connman_notifier_connect(service->type); + method = __connman_ipconfig_get_method(service->ipconfig_ipv6); if (method == CONNMAN_IPCONFIG_METHOD_OFF) __connman_ipconfig_disable_ipv6( @@ -7140,7 +7390,6 @@ static int service_indicate_state(struct connman_service *service) __connman_service_get_connected_count_of_iface( service) <= 0) { #endif - dns_changed(service); domain_changed(service); proxy_changed(service); #if defined TIZEN_EXT @@ -7159,9 +7408,6 @@ static int service_indicate_state(struct connman_service *service) case CONNMAN_SERVICE_STATE_FAILURE: #if defined TIZEN_EXT - - service->assoc_status_code = connman_network_get_assoc_status_code(service->network); - if (service->type == CONNMAN_SERVICE_TYPE_WIFI) service->order = 5; __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO); @@ -7289,7 +7535,7 @@ int __connman_service_indicate_default(struct connman_service *service) { DBG("service %p state %s", service, state2string(service->state)); - if (!is_connected(service)) { + if (!is_connected(service->state)) { /* * If service is not yet fully connected, then we must not * change the default yet. The default gw will be changed @@ -7483,6 +7729,26 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service, if (!ipconfig) return -EINVAL; + method = __connman_ipconfig_get_method(ipconfig); + + switch (method) { + case CONNMAN_IPCONFIG_METHOD_UNKNOWN: + case CONNMAN_IPCONFIG_METHOD_OFF: + if (new_state != CONNMAN_SERVICE_STATE_IDLE) + connman_warn("ipconfig state %d ipconfig method %d", + new_state, method); + + new_state = CONNMAN_SERVICE_STATE_IDLE; + break; + + case CONNMAN_IPCONFIG_METHOD_FIXED: + case CONNMAN_IPCONFIG_METHOD_MANUAL: + case CONNMAN_IPCONFIG_METHOD_DHCP: + case CONNMAN_IPCONFIG_METHOD_AUTO: + break; + + } + /* Any change? */ if (old_state == new_state) return -EALREADY; @@ -7507,11 +7773,9 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service, switch (new_state) { case CONNMAN_SERVICE_STATE_UNKNOWN: - case CONNMAN_SERVICE_STATE_IDLE: case CONNMAN_SERVICE_STATE_ASSOCIATION: break; case CONNMAN_SERVICE_STATE_CONFIGURATION: - __connman_ipconfig_enable(ipconfig); break; case CONNMAN_SERVICE_STATE_READY: #if defined TIZEN_EXT @@ -7523,15 +7787,20 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service, break; } #endif - if (type == CONNMAN_IPCONFIG_TYPE_IPV4) { + if (connman_setting_get_bool("EnableOnlineCheck")) { + if (type == CONNMAN_IPCONFIG_TYPE_IPV4) { #if !defined TIZEN_EXT - check_proxy_setup(service); + check_proxy_setup(service); #endif + } else { + service->online_check_count = 1; + __connman_wispr_start(service, type); + } + } else + connman_info("Online check disabled. " + "Default service remains in READY state."); + if (type == CONNMAN_IPCONFIG_TYPE_IPV4) service_rp_filter(service, true); - } else { - service->online_check_count = 1; - __connman_wispr_start(service, type); - } break; case CONNMAN_SERVICE_STATE_ONLINE: break; @@ -7543,59 +7812,26 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service, service_rp_filter(service, false); break; + + case CONNMAN_SERVICE_STATE_IDLE: case CONNMAN_SERVICE_STATE_FAILURE: - break; - } + __connman_ipconfig_disable(ipconfig); - /* Keep that state, but if the ipconfig method is OFF, then we set - the state to IDLE so that it will not affect the combined state - in the future. - */ - 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; - - case CONNMAN_IPCONFIG_METHOD_FIXED: - case CONNMAN_IPCONFIG_METHOD_MANUAL: - case CONNMAN_IPCONFIG_METHOD_DHCP: - case CONNMAN_IPCONFIG_METHOD_AUTO: - break; - } - if (is_connected_state(service, old_state) && - !is_connected_state(service, new_state)) -#if defined TIZEN_EXT - { - DBG("nameserver remove all, type: %d", type); + if (is_connected(old_state) && !is_connected(new_state)) nameserver_remove_all(service, type); -#else - nameserver_remove_all(service); -#endif -#if defined TIZEN_EXT - } -#endif if (type == CONNMAN_IPCONFIG_TYPE_IPV4) service->state_ipv4 = new_state; else service->state_ipv6 = new_state; - if (!is_connected_state(service, old_state) && - is_connected_state(service, new_state)) -#if defined TIZEN_EXT - { - DBG("nameserver add all, type: %d", type); + if (!is_connected(old_state) && is_connected(new_state)) nameserver_add_all(service, type); -#else - nameserver_add_all(service); -#endif -#if defined TIZEN_EXT - } -#endif + + __connman_timeserver_sync(service); #if defined TIZEN_EXT int ret = service_indicate_state(service); @@ -7654,10 +7890,31 @@ static void prepare_8021x(struct connman_service *service) connman_network_set_string(service->network, "WiFi.Identity", service->identity); + if (service->anonymous_identity) + connman_network_set_string(service->network, + "WiFi.AnonymousIdentity", + service->anonymous_identity); + if (service->ca_cert_file) connman_network_set_string(service->network, "WiFi.CACertFile", service->ca_cert_file); + if (service->subject_match) + connman_network_set_string(service->network, "WiFi.SubjectMatch", + service->subject_match); + + if (service->altsubject_match) + connman_network_set_string(service->network, "WiFi.AltSubjectMatch", + service->altsubject_match); + + if (service->domain_suffix_match) + connman_network_set_string(service->network, "WiFi.DomainSuffixMatch", + service->domain_suffix_match); + + if (service->domain_match) + connman_network_set_string(service->network, "WiFi.DomainMatch", + service->domain_match); + if (service->client_cert_file) connman_network_set_string(service->network, "WiFi.ClientCertFile", @@ -7697,7 +7954,7 @@ static int service_connect(struct connman_service *service) if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR) break; - if (!is_connecting(temp) && !is_connected(temp)) + if (!is_connecting(temp->state) && !is_connected(temp->state)) break; if (service == temp) @@ -7813,22 +8070,22 @@ static int service_connect(struct connman_service *service) &service->stats_roaming.data); } - if (service->ipconfig_ipv4) - __connman_ipconfig_enable(service->ipconfig_ipv4); - if (service->ipconfig_ipv6) - __connman_ipconfig_enable(service->ipconfig_ipv6); - err = __connman_network_connect(service->network); } else if (service->type == CONNMAN_SERVICE_TYPE_VPN && service->provider) - err = __connman_provider_connect(service->provider); + err = __connman_provider_connect(service->provider, + get_dbus_sender(service)); else return -EOPNOTSUPP; if (err < 0) { if (err != -EINPROGRESS) { - __connman_ipconfig_disable(service->ipconfig_ipv4); - __connman_ipconfig_disable(service->ipconfig_ipv6); + __connman_service_ipconfig_indicate_state(service, + CONNMAN_SERVICE_STATE_FAILURE, + CONNMAN_IPCONFIG_TYPE_IPV4); + __connman_service_ipconfig_indicate_state(service, + CONNMAN_SERVICE_STATE_FAILURE, + CONNMAN_IPCONFIG_TYPE_IPV6); __connman_stats_service_unregister(service); } } @@ -7846,10 +8103,10 @@ int __connman_service_connect(struct connman_service *service, reason2string(service->connect_reason), reason2string(reason)); - if (is_connected(service)) + if (is_connected(service->state)) return -EISCONN; - if (is_connecting(service)) + if (is_connecting(service->state)) return -EALREADY; switch (service->type) { @@ -7875,6 +8132,8 @@ int __connman_service_connect(struct connman_service *service, err = service_connect(service); + DBG("service %p err %d", service, err); + service->connect_reason = reason; if (err >= 0) return 0; @@ -7896,6 +8155,7 @@ int __connman_service_connect(struct connman_service *service, if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER) { if (err == -ENOKEY || err == -EPERM) { DBusMessage *pending = NULL; + const char *dbus_sender = get_dbus_sender(service); /* * We steal the reply here. The idea is that the @@ -7910,7 +8170,7 @@ int __connman_service_connect(struct connman_service *service, err = __connman_agent_request_passphrase_input(service, request_input_cb, - get_dbus_sender(service), + dbus_sender, pending); if (service->hidden && err != -EINPROGRESS) service->pending = pending; @@ -7992,7 +8252,7 @@ int __connman_service_disconnect_all(void) for (iter = service_list; iter; iter = iter->next) { service = iter->data; - if (!is_connected(service)) + if (!is_connected(service->state)) break; services = g_slist_prepend(services, service); @@ -8022,6 +8282,11 @@ static struct connman_service *lookup_by_identifier(const char *identifier) return g_hash_table_lookup(service_hash, identifier); } +struct connman_service *connman_service_lookup_from_identifier(const char* identifier) +{ + return lookup_by_identifier(identifier); +} + struct provision_user_data { const char *ident; int ret; @@ -8172,12 +8437,6 @@ static void service_lower_down(struct connman_ipconfig *ipconfig, DBG("%s lower down", ifname); - if (!is_idle_state(service, service->state_ipv4)) - __connman_ipconfig_disable(service->ipconfig_ipv4); - - if (!is_idle_state(service, service->state_ipv6)) - __connman_ipconfig_disable(service->ipconfig_ipv6); - stats_stop(service); service_save(service); } @@ -8206,9 +8465,8 @@ static void service_ip_bound(struct connman_ipconfig *ipconfig, { err = __connman_ipconfig_gateway_add(ipconfig, service); - if(err == 0) - __connman_connection_gateway_activate(service, - CONNMAN_IPCONFIG_TYPE_IPV6); + if(err < 0) + DBG("Failed to add gateway"); } #else __connman_service_ipconfig_indicate_state(service, @@ -8217,6 +8475,7 @@ static void service_ip_bound(struct connman_ipconfig *ipconfig, #endif settings_changed(service, ipconfig); + address_updated(service, type); } static void service_ip_release(struct connman_ipconfig *ipconfig, @@ -8491,12 +8750,6 @@ unsigned int __connman_service_get_order(struct connman_service *service) return order; } -void __connman_service_update_ordering(void) -{ - if (service_list && service_list->next) - service_list = g_list_sort(service_list, service_compare); -} - static enum connman_service_type convert_network_type(struct connman_network *network) { enum connman_network_type type = connman_network_get_type(network); @@ -8557,7 +8810,7 @@ int check_passphrase_ext(struct connman_network *network, str = connman_network_get_string(network, "WiFi.Security"); security = convert_wifi_security(str); - return check_passphrase(security, passphrase); + return __connman_service_check_passphrase(security, passphrase); } #endif @@ -8569,10 +8822,10 @@ static void update_from_network(struct connman_service *service, DBG("service %p network %p", service, network); - if (is_connected(service)) + if (is_connected(service->state)) return; - if (is_connecting(service)) + if (is_connecting(service->state)) return; str = connman_network_get_string(network, "Name"); @@ -8704,6 +8957,7 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne service->ipconfig_ipv6 = create_ip6config(service, index); service_register(service); + service_schedule_added(service); if (service->favorite) { device = connman_network_get_device(service->network); @@ -8749,7 +9003,6 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne } __connman_notifier_service_add(service, service->name); - service_schedule_added(service); return service; } @@ -9012,6 +9265,8 @@ int __connman_service_init(void) return err; } + set_always_connecting_technologies(); + connection = connman_dbus_get_connection(); service_hash = g_hash_table_new_full(g_str_hash, g_str_equal, @@ -9055,9 +9310,10 @@ void __connman_service_cleanup(void) if (services_notify->id != 0) { g_source_remove(services_notify->id); service_send_changed(NULL); - g_hash_table_destroy(services_notify->remove); - g_hash_table_destroy(services_notify->add); } + + g_hash_table_destroy(services_notify->remove); + g_hash_table_destroy(services_notify->add); g_free(services_notify); dbus_connection_unref(connection); diff --git a/src/session.c b/src/session.c index 08facc1d..9e3c5594 100755 --- a/src/session.c +++ b/src/session.c @@ -37,13 +37,6 @@ static GHashTable *session_hash; static GHashTable *service_hash; static struct connman_session *ecall_session; static uint32_t session_mark = 256; -static struct firewall_context *global_firewall = NULL; - -enum connman_session_state { - CONNMAN_SESSION_STATE_DISCONNECTED = 0, - CONNMAN_SESSION_STATE_CONNECTED = 1, - CONNMAN_SESSION_STATE_ONLINE = 2, -}; struct session_info { struct connman_session_config config; @@ -64,6 +57,7 @@ struct connman_session { struct connman_service *service_last; struct connman_session_config *policy_config; GSList *user_allowed_bearers; + char *user_allowed_interface; bool ecall; @@ -73,6 +67,7 @@ struct connman_session { int index; char *gateway; bool policy_routing; + bool snat_enabled; }; struct connman_service_info { @@ -80,6 +75,15 @@ struct connman_service_info { GSList *sessions; }; +struct fw_snat { + GSList *sessions; + int id; + int index; + struct firewall_context *fw; +}; + +GSList *fw_snat_list; + static struct connman_session_policy *policy; static void session_activate(struct connman_session *session); static void session_deactivate(struct connman_session *session); @@ -196,102 +200,109 @@ static char *service2bearer(enum connman_service_type type) return ""; } -static int init_firewall(void) +static struct fw_snat *fw_snat_lookup(int index) { - struct firewall_context *fw; - int err; + struct fw_snat *fw_snat; + GSList *list; - if (global_firewall) - return 0; + for (list = fw_snat_list; list; list = list->next) { + fw_snat = list->data; - fw = __connman_firewall_create(); + if (fw_snat->index == index) + return fw_snat; + } + return NULL; +} - err = __connman_firewall_add_rule(fw, "mangle", "INPUT", - "-j CONNMARK --restore-mark"); - if (err < 0) - goto err; +static int fw_snat_create(struct connman_session *session, + int index, const char *ifname, const char *addr) +{ + struct fw_snat *fw_snat; + int err; - err = __connman_firewall_add_rule(fw, "mangle", "POSTROUTING", - "-j CONNMARK --save-mark"); - if (err < 0) - goto err; + fw_snat = g_new0(struct fw_snat, 1); - err = __connman_firewall_enable(fw); - if (err < 0) + fw_snat->fw = __connman_firewall_create(); + fw_snat->index = index; + + fw_snat->id = __connman_firewall_enable_snat(fw_snat->fw, + index, ifname, addr); + if (fw_snat->id < 0) { + err = fw_snat->id; goto err; + } - global_firewall = fw; + fw_snat_list = g_slist_prepend(fw_snat_list, fw_snat); + fw_snat->sessions = g_slist_prepend(fw_snat->sessions, session); return 0; - err: - __connman_firewall_destroy(fw); - + __connman_firewall_destroy(fw_snat->fw); + g_free(fw_snat); return err; } -static void cleanup_firewall(void) +static void fw_snat_ref(struct connman_session *session, + struct fw_snat *fw_snat) { - if (!global_firewall) + if (g_slist_find(fw_snat->sessions, session)) return; + fw_snat->sessions = g_slist_prepend(fw_snat->sessions, session); +} - __connman_firewall_disable(global_firewall); - __connman_firewall_destroy(global_firewall); +static void fw_snat_unref(struct connman_session *session, + struct fw_snat *fw_snat) +{ + fw_snat->sessions = g_slist_remove(fw_snat->sessions, session); + if (fw_snat->sessions) + return; + + fw_snat_list = g_slist_remove(fw_snat_list, fw_snat); + + __connman_firewall_disable_snat(fw_snat->fw); + __connman_firewall_destroy(fw_snat->fw); + g_free(fw_snat); } static int init_firewall_session(struct connman_session *session) { struct firewall_context *fw; int err; + struct connman_ipconfig *ipconfig = NULL; + const char *addr = NULL; - if (session->policy_config->id_type == CONNMAN_SESSION_ID_TYPE_UNKNOWN) + if (session->policy_config->id_type == CONNMAN_SESSION_ID_TYPE_UNKNOWN && + !session->info->config.source_ip_rule) return 0; DBG(""); - err = init_firewall(); - if (err < 0) - return err; + if (session->info->config.source_ip_rule) { + ipconfig = __connman_service_get_ip4config(session->service); + if (session->policy_config->id_type == CONNMAN_SESSION_ID_TYPE_UNKNOWN && !ipconfig) + return 0; + } fw = __connman_firewall_create(); if (!fw) return -ENOMEM; - switch (session->policy_config->id_type) { - case CONNMAN_SESSION_ID_TYPE_UID: - err = __connman_firewall_add_rule(fw, "mangle", "OUTPUT", - "-m owner --uid-owner %s -j MARK --set-mark %d", - session->policy_config->id, - session->mark); - break; - case CONNMAN_SESSION_ID_TYPE_GID: - err = __connman_firewall_add_rule(fw, "mangle", "OUTPUT", - "-m owner --gid-owner %s -j MARK --set-mark %d", - session->policy_config->id, - session->mark); - break; - case CONNMAN_SESSION_ID_TYPE_LSM: - default: - err = -EINVAL; + if (session->info->config.source_ip_rule && ipconfig) { + addr = __connman_ipconfig_get_local(ipconfig); } - if (err < 0) - goto err; - + err =__connman_firewall_enable_marking(fw, + session->policy_config->id_type, + session->policy_config->id, + addr, session->mark); + if (err < 0) { + __connman_firewall_destroy(fw); + return err; + } session->id_type = session->policy_config->id_type; - - err = __connman_firewall_enable(fw); - if (err) - goto err; - session->fw = fw; return 0; - -err: - __connman_firewall_destroy(fw); - - return err; } static void cleanup_firewall_session(struct connman_session *session) @@ -299,7 +310,8 @@ static void cleanup_firewall_session(struct connman_session *session) if (!session->fw) return; - __connman_firewall_disable(session->fw); + __connman_firewall_disable_marking(session->fw); + __connman_firewall_disable_snat(session->fw); __connman_firewall_destroy(session->fw); session->fw = NULL; @@ -309,7 +321,11 @@ static int init_routing_table(struct connman_session *session) { int err; - if (session->policy_config->id_type == CONNMAN_SESSION_ID_TYPE_UNKNOWN) + if (session->policy_config->id_type == CONNMAN_SESSION_ID_TYPE_UNKNOWN && + !session->info->config.source_ip_rule) + return 0; + + if (!session->service) return 0; DBG(""); @@ -348,6 +364,7 @@ static void add_default_route(struct connman_session *session) { struct connman_ipconfig *ipconfig; int err; + struct in_addr addr = { INADDR_ANY }; if (!session->service) return; @@ -356,6 +373,9 @@ static void add_default_route(struct connman_session *session) session->index = __connman_ipconfig_get_index(ipconfig); session->gateway = g_strdup(__connman_ipconfig_get_gateway(ipconfig)); + if (!session->gateway) + session->gateway = g_strdup(inet_ntoa(addr)); + DBG("index %d routing table %d default gateway %s", session->index, session->mark, session->gateway); @@ -365,6 +385,62 @@ static void add_default_route(struct connman_session *session) DBG("session %p %s", session, strerror(-err)); } +static void del_nat_rules(struct connman_session *session) +{ + struct fw_snat *fw_snat; + + if (!session->snat_enabled) + return; + + session->snat_enabled = false; + fw_snat = fw_snat_lookup(session->index); + + if (!fw_snat) + return; + + fw_snat_unref(session, fw_snat); +} + +static void add_nat_rules(struct connman_session *session) +{ + struct connman_ipconfig *ipconfig; + struct fw_snat *fw_snat; + const char *addr; + int index, err; + char *ifname; + + if (!session->service) + return; + + ipconfig = __connman_service_get_ip4config(session->service); + index = __connman_ipconfig_get_index(ipconfig); + ifname = connman_inet_ifname(index); + addr = __connman_ipconfig_get_local(ipconfig); + + if (!addr) + return; + + session->snat_enabled = true; + fw_snat = fw_snat_lookup(index); + if (fw_snat) { + fw_snat_ref(session, fw_snat); + return; + } + + err = fw_snat_create(session, index, ifname, addr); + if (err < 0) { + DBG("failed to add SNAT rule"); + session->snat_enabled = false; + } + + g_free(ifname); +} + +uint32_t connman_session_firewall_get_fwmark(struct connman_session *session) +{ + return session->mark; +} + static void cleanup_routing_table(struct connman_session *session) { DBG(""); @@ -381,12 +457,24 @@ static void cleanup_routing_table(struct connman_session *session) del_default_route(session); } +static void update_firewall(struct connman_session *session) +{ + cleanup_firewall_session(session); + init_firewall_session(session); +} + static void update_routing_table(struct connman_session *session) { - del_default_route(session); + cleanup_routing_table(session); + init_routing_table(session); add_default_route(session); } +static void cleanup_nat_rules(struct connman_session *session) +{ + del_nat_rules(session); +} + static void destroy_policy_config(struct connman_session *session) { if (!policy) { @@ -407,6 +495,7 @@ static void free_session(struct connman_session *session) destroy_policy_config(session); g_slist_free(session->info->config.allowed_bearers); + g_free(session->info->config.allowed_interface); g_free(session->owner); g_free(session->session_path); g_free(session->notify_path); @@ -434,6 +523,7 @@ static void cleanup_session(gpointer user_data) DBG("remove %s", session->session_path); + cleanup_nat_rules(session); cleanup_routing_table(session); cleanup_firewall_session(session); @@ -444,6 +534,7 @@ static void cleanup_session(gpointer user_data) update_session_state(session); g_slist_free(session->user_allowed_bearers); + g_free(session->user_allowed_interface); free_session(session); } @@ -455,6 +546,8 @@ struct creation_data { /* user config */ enum connman_session_type type; GSList *allowed_bearers; + char *allowed_interface; + bool source_ip_rule; }; static void cleanup_creation_data(struct creation_data *creation_data) @@ -466,6 +559,7 @@ static void cleanup_creation_data(struct creation_data *creation_data) dbus_message_unref(creation_data->pending); g_slist_free(creation_data->allowed_bearers); + g_free(creation_data->allowed_interface); g_free(creation_data); } @@ -543,6 +637,7 @@ void connman_session_set_default_config(struct connman_session_config *config) config->ecall = FALSE; g_slist_free(config->allowed_bearers); + config->allowed_bearers = NULL; add_default_bearer_types(&config->allowed_bearers); } @@ -628,18 +723,18 @@ static int parse_bearers(DBusMessageIter *iter, GSList **list) return 0; } -static void filter_bearer(GSList *policy_bearers, - enum connman_service_type bearer, +static void filter_bearer(GSList *bearers, + enum connman_service_type policy, GSList **list) { - enum connman_service_type policy; + enum connman_service_type bearer; GSList *it; - if (!policy_bearers) + if (!bearers) return; - for (it = policy_bearers; it; it = it->next) { - policy = GPOINTER_TO_INT(it->data); + for (it = bearers; it; it = it->next) { + bearer = GPOINTER_TO_INT(it->data); if (policy != bearer) continue; @@ -652,18 +747,29 @@ static void filter_bearer(GSList *policy_bearers, static void apply_policy_on_bearers(GSList *policy_bearers, GSList *bearers, GSList **list) { - enum connman_service_type bearer; + enum connman_service_type policy_bearer; GSList *it; *list = NULL; - for (it = bearers; it; it = it->next) { - bearer = GPOINTER_TO_INT(it->data); + for (it = policy_bearers; it; it = it->next) { + policy_bearer = GPOINTER_TO_INT(it->data); - filter_bearer(policy_bearers, bearer, list); + filter_bearer(bearers, policy_bearer, list); } } +static char * apply_policy_on_interface(const char *policy_interface, + const char *user_interface) +{ + if (policy_interface) + return g_strdup(policy_interface); + else if (user_interface) + return g_strdup(user_interface); + else + return NULL; +} + const char *connman_session_get_owner(struct connman_session *session) { return session->owner; @@ -809,6 +915,28 @@ static void append_notify(DBusMessageIter *dict, info_last->config.allowed_bearers = info->config.allowed_bearers; } + if (session->append_all || + info->config.allowed_interface != info_last->config.allowed_interface) { + char *ifname = info->config.allowed_interface; + if (!ifname) + ifname = "*"; + connman_dbus_dict_append_basic(dict, "AllowedInterface", + DBUS_TYPE_STRING, + &ifname); + info_last->config.allowed_interface = info->config.allowed_interface; + } + + if (session->append_all || + info->config.source_ip_rule != info_last->config.source_ip_rule) { + dbus_bool_t source_ip_rule = FALSE; + if (info->config.source_ip_rule) + source_ip_rule = TRUE; + connman_dbus_dict_append_basic(dict, "SourceIPRule", + DBUS_TYPE_BOOLEAN, + &source_ip_rule); + info_last->config.source_ip_rule = info->config.source_ip_rule; + } + session->append_all = false; } @@ -828,7 +956,9 @@ static bool compute_notifiable_changes(struct connman_session *session) return true; if (info->config.allowed_bearers != info_last->config.allowed_bearers || - info->config.type != info_last->config.type) + info->config.type != info_last->config.type || + info->config.allowed_interface != info_last->config.allowed_interface || + info->config.source_ip_rule != info_last->config.source_ip_rule) return true; return false; @@ -882,6 +1012,7 @@ int connman_session_config_update(struct connman_session *session) { struct session_info *info = session->info; GSList *allowed_bearers; + char *allowed_interface; int err; DBG("session %p", session); @@ -907,6 +1038,10 @@ int connman_session_config_update(struct connman_session *session) session->user_allowed_bearers, &allowed_bearers); + allowed_interface = apply_policy_on_interface( + session->policy_config->allowed_interface, + session->user_allowed_interface); + if (session->active) set_active_session(session, false); @@ -916,6 +1051,9 @@ int connman_session_config_update(struct connman_session *session) g_slist_free(info->config.allowed_bearers); info->config.allowed_bearers = allowed_bearers; + g_free(info->config.allowed_interface); + info->config.allowed_interface = allowed_interface; + session_activate(session); info->config.type = apply_policy_on_type( @@ -1024,6 +1162,7 @@ static DBusMessage *change_session(DBusConnection *conn, session->active = false; session_deactivate(session); + update_session_state(session); g_slist_free(info->config.allowed_bearers); session->user_allowed_bearers = allowed_bearers; @@ -1044,6 +1183,38 @@ static DBusMessage *change_session(DBusConnection *conn, info->config.type = apply_policy_on_type( session->policy_config->type, connman_session_parse_connection_type(val)); + } else if (g_str_equal(name, "AllowedInterface")) { + dbus_message_iter_get_basic(&value, &val); + if (session->active) + set_active_session(session, false); + + session->active = false; + session_deactivate(session); + update_session_state(session); + + g_free(session->user_allowed_interface); + /* empty string means allow any interface */ + if (!g_strcmp0(val, "")) + session->user_allowed_interface = NULL; + else + session->user_allowed_interface = g_strdup(val); + + info->config.allowed_interface = apply_policy_on_interface( + session->policy_config->allowed_interface, + session->user_allowed_interface); + + session_activate(session); + } else { + goto err; + } + break; + case DBUS_TYPE_BOOLEAN: + if (g_str_equal(name, "SourceIPRule")) { + dbus_bool_t source_ip_rule; + dbus_message_iter_get_basic(&value, &source_ip_rule); + + info->config.source_ip_rule = source_ip_rule; + update_session_state(session); } else { goto err; } @@ -1149,6 +1320,7 @@ static int session_policy_config_cb(struct connman_session *session, goto err; session->policy_config = config; + session->info->config.source_ip_rule = creation_data->source_ip_rule; session->mark = session_mark++; session->index = -1; @@ -1177,11 +1349,18 @@ static int session_policy_config_cb(struct connman_session *session, session->user_allowed_bearers = creation_data->allowed_bearers; creation_data->allowed_bearers = NULL; + session->user_allowed_interface = creation_data->allowed_interface; + creation_data->allowed_interface = NULL; + apply_policy_on_bearers( session->policy_config->allowed_bearers, session->user_allowed_bearers, &info->config.allowed_bearers); + info->config.allowed_interface = apply_policy_on_interface( + session->policy_config->allowed_interface, + session->user_allowed_interface); + g_hash_table_replace(session_hash, session->session_path, session); DBG("add %s", session->session_path); @@ -1206,6 +1385,8 @@ static int session_policy_config_cb(struct connman_session *session, info_last->config.priority = info->config.priority; info_last->config.roaming_policy = info->config.roaming_policy; info_last->config.allowed_bearers = info->config.allowed_bearers; + info_last->config.allowed_interface = info->config.allowed_interface; + info_last->config.source_ip_rule = info->config.source_ip_rule; session->append_all = true; @@ -1293,11 +1474,29 @@ int __connman_session_create(DBusMessage *msg) connman_session_parse_connection_type(val); user_connection_type = true; + } else if (g_str_equal(key, "AllowedInterface")) { + dbus_message_iter_get_basic(&value, &val); + creation_data->allowed_interface = g_strdup(val); } else { err = -EINVAL; goto err; } + break; + case DBUS_TYPE_BOOLEAN: + if (g_str_equal(key, "SourceIPRule")) { + dbus_bool_t source_ip_rule; + dbus_message_iter_get_basic(&value, &source_ip_rule); + creation_data->source_ip_rule = source_ip_rule; + } else { + err = -EINVAL; + goto err; + } + break; + default: + err = -EINVAL; + goto err; } + dbus_message_iter_next(&array); } @@ -1481,7 +1680,14 @@ static void update_session_state(struct connman_session *session) DBG("session %p state %s", session, state2string(state)); + update_firewall(session); + del_nat_rules(session); update_routing_table(session); + add_nat_rules(session); + + if (policy && policy->update_session_state) + policy->update_session_state(session, state); + session_notify(session); } @@ -1490,17 +1696,31 @@ static bool session_match_service(struct connman_session *session, { enum connman_service_type bearer_type; enum connman_service_type service_type; + enum connman_service_type current_service_type; GSList *list; + char *ifname; if (policy && policy->allowed) return policy->allowed(session, service); + current_service_type = connman_service_get_type(session->service); + for (list = session->info->config.allowed_bearers; list; list = list->next) { bearer_type = GPOINTER_TO_INT(list->data); service_type = connman_service_get_type(service); + ifname = connman_service_get_interface(service); - if (bearer_type == service_type) - return true; + if (bearer_type == current_service_type) + return false; + + if (bearer_type == service_type && + (session->info->config.allowed_interface == NULL || + !g_strcmp0(session->info->config.allowed_interface, "*") || + !g_strcmp0(session->info->config.allowed_interface, ifname))) { + g_free(ifname); + return true; + } + g_free(ifname); } return false; @@ -1521,6 +1741,7 @@ static bool is_session_connected(struct connman_session *session, case CONNMAN_SERVICE_STATE_READY: if (session->info->config.type == CONNMAN_SESSION_TYPE_INTERNET) return false; + /* fall through */ case CONNMAN_SERVICE_STATE_ONLINE: return true; } @@ -1536,6 +1757,40 @@ static void session_activate(struct connman_session *session) if (!service_hash) return; + if (policy && policy->get_service_for_session) { + struct connman_service *service; + struct connman_service_info *info; + GSList *service_list = NULL; + enum connman_service_state state = CONNMAN_SESSION_STATE_DISCONNECTED; + + g_hash_table_iter_init(&iter, service_hash); + + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct connman_service_info *info = value; + state = __connman_service_get_state(info->service); + + if (is_session_connected(session, state)) + service_list = g_slist_prepend(service_list, + info->service); + } + + service_list = g_slist_reverse(service_list); + service = policy->get_service_for_session(session, service_list); + + if (service) { + info = g_hash_table_lookup(service_hash, service); + DBG("session %p add service %p", session, info->service); + + info->sessions = g_slist_prepend(info->sessions, + session); + session->service = info->service; + update_session_state(session); + } + + g_slist_free(service_list); + return; + } + g_hash_table_iter_init(&iter, service_hash); while (g_hash_table_iter_next(&iter, &key, &value)) { struct connman_service_info *info = value; @@ -1630,10 +1885,10 @@ static void handle_service_state_offline(struct connman_service *service, session->service = NULL; update_session_state(session); + session_activate(session); } } - static void service_state_changed(struct connman_service *service, enum connman_service_state state) { @@ -1693,7 +1948,7 @@ static void ipconfig_changed(struct connman_service *service, continue; if (session->service && session->service == service) { - update_routing_table(session); + update_session_state(session); if (type == CONNMAN_IPCONFIG_TYPE_IPV4) ipconfig_ipv4_changed(session); @@ -1730,12 +1985,6 @@ int __connman_session_init(void) service_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, cleanup_service); - if (__connman_firewall_is_up()) { - err = init_firewall(); - if (err < 0) - return err; - } - return 0; } @@ -1746,8 +1995,6 @@ void __connman_session_cleanup(void) if (!connection) return; - cleanup_firewall(); - connman_notifier_unregister(&session_notifier); g_hash_table_foreach(session_hash, release_session, NULL); diff --git a/src/stats.c b/src/stats.c index 26343b13..663bc382 100755 --- a/src/stats.c +++ b/src/stats.c @@ -227,18 +227,14 @@ static void stats_free(gpointer user_data) munmap(file->addr, file->len); file->addr = NULL; - TFR(close(file->fd)); + close(file->fd); file->fd = -1; - if (file->history_name) { - g_free(file->history_name); - file->history_name = NULL; - } + g_free(file->history_name); + file->history_name = NULL; - if (file->name) { - g_free(file->name); - file->name = NULL; - } + g_free(file->name); + file->name = NULL; g_free(file); } @@ -377,7 +373,8 @@ static int stats_file_setup(struct stats_file *file) connman_error("fstat error %s for %s\n", strerror(errno), file->name); - TFR(close(file->fd)); + close(file->fd); + file->fd = -1; g_free(file->name); file->name = NULL; @@ -392,7 +389,8 @@ static int stats_file_setup(struct stats_file *file) err = stats_file_remap(file, size); if (err < 0) { - TFR(close(file->fd)); + close(file->fd); + file->fd = -1; g_free(file->name); file->name = NULL; @@ -621,7 +619,7 @@ static int stats_file_close_swap(struct stats_file *history_file, stats_file_unmap(history_file); stats_file_unmap(temp_file); - TFR(close(temp_file->fd)); + close(temp_file->fd); unlink(history_file->name); @@ -629,7 +627,7 @@ static int stats_file_close_swap(struct stats_file *history_file, unlink(temp_file->name); - TFR(close(history_file->fd)); + close(history_file->fd); stats_file_cleanup(history_file); stats_file_cleanup(temp_file); @@ -649,6 +647,9 @@ static int stats_file_history_update(struct stats_file *data_file) bzero(history_file, sizeof(struct stats_file)); bzero(temp_file, sizeof(struct stats_file)); + history_file->fd = -1; + temp_file->fd = -1; + err = stats_open(history_file, data_file->history_name); if (err < 0) return err; @@ -676,17 +677,6 @@ int __connman_stats_service_register(struct connman_service *service) DBG("service %p", service); - file = g_hash_table_lookup(stats_hash, service); - if (!file) { - file = g_try_new0(struct stats_file, 1); - if (!file) - return -ENOMEM; - - g_hash_table_insert(stats_hash, service, file); - } else { - return -EALREADY; - } - dir = g_strdup_printf("%s/%s", STORAGEDIR, __connman_service_get_ident(service)); @@ -703,6 +693,18 @@ int __connman_stats_service_register(struct connman_service *service) } g_free(dir); + file = g_hash_table_lookup(stats_hash, service); + if (!file) { + file = g_try_new0(struct stats_file, 1); + if (!file) + return -ENOMEM; + + file->fd = -1; + + g_hash_table_insert(stats_hash, service, file); + } else { + return -EALREADY; + } name = g_strdup_printf("%s/%s/data", STORAGEDIR, __connman_service_get_ident(service)); diff --git a/src/storage.c b/src/storage.c index 2da54d6c..50c8e955 100755 --- a/src/storage.c +++ b/src/storage.c @@ -44,8 +44,6 @@ static GKeyFile *storage_load(const char *pathname) GKeyFile *keyfile = NULL; GError *error = NULL; - DBG("Loading %s", pathname); - keyfile = g_key_file_new(); if (!g_key_file_load_from_file(keyfile, pathname, 0, &error)) { @@ -401,8 +401,7 @@ int connman_task_stop(struct connman_task *task) if (task->pid > 0) { kill(task->pid, SIGTERM); - g_timeout_add_seconds(0, check_kill, - GINT_TO_POINTER(task->pid)); + g_idle_add(check_kill, GINT_TO_POINTER(task->pid)); } return 0; diff --git a/src/technology.c b/src/technology.c index 408c99f1..5aea9f4f 100755 --- a/src/technology.c +++ b/src/technology.c @@ -225,22 +225,27 @@ static void tethering_changed(struct connman_technology *technology) technology_save(technology); } -void connman_technology_tethering_notify(struct connman_technology *technology, +int connman_technology_tethering_notify(struct connman_technology *technology, bool enabled) { + int err; + DBG("technology %p enabled %u", technology, enabled); if (technology->tethering == enabled) - return; + return -EALREADY; - technology->tethering = enabled; + if (enabled) { + err = __connman_tethering_set_enabled(); + if (err < 0) + return err; + } else + __connman_tethering_set_disabled(); + technology->tethering = enabled; tethering_changed(technology); - if (enabled) - __connman_tethering_set_enabled(); - else - __connman_tethering_set_disabled(); + return 0; } static int set_tethering(struct connman_technology *technology, @@ -250,11 +255,9 @@ static int set_tethering(struct connman_technology *technology, int err; const char *ident, *passphrase, *bridge; GSList *tech_drivers; - bool hidden; ident = technology->tethering_ident; passphrase = technology->tethering_passphrase; - hidden = technology->tethering_hidden; __sync_synchronize(); if (!technology->enabled) @@ -275,15 +278,13 @@ static int set_tethering(struct connman_technology *technology, continue; err = driver->set_tethering(technology, ident, passphrase, - bridge, enabled, hidden); + bridge, enabled); if (result == -EINPROGRESS) continue; - if (err == -EINPROGRESS || err == 0) { + if (err == -EINPROGRESS || err == 0) result = err; - continue; - } } return result; @@ -459,15 +460,31 @@ bool __connman_technology_get_offlinemode(void) static void connman_technology_save_offlinemode(void) { GKeyFile *keyfile; + GError *error = NULL; + bool offlinemode; keyfile = __connman_storage_load_global(); - if (!keyfile) + + if (!keyfile) { keyfile = g_key_file_new(); + g_key_file_set_boolean(keyfile, "global", + "OfflineMode", global_offlinemode); - g_key_file_set_boolean(keyfile, "global", + __connman_storage_save_global(keyfile); + } + else { + offlinemode = g_key_file_get_boolean(keyfile, "global", + "OfflineMode", &error); + + if (error || offlinemode != global_offlinemode) { + g_key_file_set_boolean(keyfile, "global", "OfflineMode", global_offlinemode); + if (error) + g_clear_error(&error); - __connman_storage_save_global(keyfile); + __connman_storage_save_global(keyfile); + } + } g_key_file_free(keyfile); @@ -865,7 +882,7 @@ static DBusMessage *set_property(DBusConnection *conn, struct connman_technology *technology = data; DBusMessageIter iter, value; const char *name; - int type; + int type, err; DBG("conn %p", conn); @@ -962,30 +979,21 @@ static DBusMessage *set_property(DBusConnection *conn, if (technology->type != CONNMAN_SERVICE_TYPE_WIFI) return __connman_error_not_supported(msg); - if (strlen(str) < 8 || strlen(str) > 63) { - if (g_str_equal(str, "")) { - technology->tethering_passphrase = NULL; + err = __connman_service_check_passphrase(CONNMAN_SERVICE_SECURITY_PSK, + str); + if (err < 0) + return __connman_error_passphrase_required(msg); - connman_dbus_property_changed_basic(technology->path, - CONNMAN_TECHNOLOGY_INTERFACE, - "TetheringPassphrase", - DBUS_TYPE_STRING, - &str); - } - else - return __connman_error_passphrase_required(msg); - } else { - if (g_strcmp0(technology->tethering_passphrase, str) != 0) { - g_free(technology->tethering_passphrase); - technology->tethering_passphrase = g_strdup(str); - technology_save(technology); + if (g_strcmp0(technology->tethering_passphrase, str) != 0) { + g_free(technology->tethering_passphrase); + technology->tethering_passphrase = g_strdup(str); + technology_save(technology); - connman_dbus_property_changed_basic(technology->path, - CONNMAN_TECHNOLOGY_INTERFACE, - "TetheringPassphrase", - DBUS_TYPE_STRING, - &technology->tethering_passphrase); - } + connman_dbus_property_changed_basic(technology->path, + CONNMAN_TECHNOLOGY_INTERFACE, + "TetheringPassphrase", + DBUS_TYPE_STRING, + &technology->tethering_passphrase); } } else if (g_str_equal(name, "Hidden")) { dbus_bool_t hidden; @@ -1178,6 +1186,10 @@ static DBusMessage *scan(DBusConnection *conn, DBusMessage *msg, void *data) DBG("technology %p request from %s", technology, dbus_message_get_sender(msg)); + if (technology->type == CONNMAN_SERVICE_TYPE_P2P && + !technology->enabled) + return __connman_error_permission_denied(msg); + dbus_message_ref(msg); #if !defined TIZEN_EXT technology->scan_pending = @@ -1423,6 +1435,13 @@ static void technology_put(struct connman_technology *technology) g_slist_free(technology->device_list); + if (technology->pending_reply) { + dbus_message_unref(technology->pending_reply); + technology->pending_reply = NULL; + g_source_remove(technology->pending_timeout); + technology->pending_timeout = 0; + } + g_free(technology->path); g_free(technology->regdom); g_free(technology->tethering_ident); diff --git a/src/tethering.c b/src/tethering.c index c0c97431..891ee51f 100755 --- a/src/tethering.c +++ b/src/tethering.c @@ -346,7 +346,7 @@ static void tethering_restart(struct connman_ippool *pool, void *user_data) __connman_tethering_set_enabled(); } -void __connman_tethering_set_enabled(void) +int __connman_tethering_set_enabled(void) { int index; int err; @@ -362,12 +362,12 @@ void __connman_tethering_set_enabled(void) DBG("enabled %d", tethering_enabled + 1); if (__sync_fetch_and_add(&tethering_enabled, 1) != 0) - return; + return 0; err = __connman_bridge_create(BRIDGE_NAME); if (err < 0) { __sync_fetch_and_sub(&tethering_enabled, 1); - return; + return -EOPNOTSUPP; } index = connman_inet_ifindex(BRIDGE_NAME); @@ -377,7 +377,7 @@ void __connman_tethering_set_enabled(void) connman_error("Fail to create IP pool"); __connman_bridge_remove(BRIDGE_NAME); __sync_fetch_and_sub(&tethering_enabled, 1); - return; + return -EADDRNOTAVAIL; } gateway = __connman_ippool_get_gateway(dhcp_ippool); @@ -393,7 +393,7 @@ void __connman_tethering_set_enabled(void) __connman_ippool_unref(dhcp_ippool); __connman_bridge_remove(BRIDGE_NAME); __sync_fetch_and_sub(&tethering_enabled, 1); - return; + return -EADDRNOTAVAIL; } ns = connman_setting_get_string_list("FallbackNameservers"); @@ -429,7 +429,7 @@ void __connman_tethering_set_enabled(void) __connman_ippool_unref(dhcp_ippool); __connman_bridge_remove(BRIDGE_NAME); __sync_fetch_and_sub(&tethering_enabled, 1); - return; + return -EOPNOTSUPP; } prefixlen = connman_ipaddress_calc_netmask_len(subnet_mask); @@ -441,7 +441,7 @@ void __connman_tethering_set_enabled(void) __connman_ippool_unref(dhcp_ippool); __connman_bridge_remove(BRIDGE_NAME); __sync_fetch_and_sub(&tethering_enabled, 1); - return; + return -EOPNOTSUPP; } err = __connman_ipv6pd_setup(BRIDGE_NAME); @@ -450,6 +450,8 @@ void __connman_tethering_set_enabled(void) strerror(-err)); DBG("tethering started"); + + return 0; } void __connman_tethering_set_disabled(void) @@ -670,6 +672,8 @@ error: close(fd); g_free(iface); g_free(path); + if (pn) + g_free(pn->owner); g_free(pn); return err; } diff --git a/src/timeserver.c b/src/timeserver.c index fc83f9ae..6325eceb 100755 --- a/src/timeserver.c +++ b/src/timeserver.c @@ -305,6 +305,8 @@ static void ts_recheck_enable(void) int __connman_timeserver_sync(struct connman_service *default_service) { struct connman_service *service; + char **nameservers; + int i; if (default_service) service = default_service; @@ -328,6 +330,17 @@ int __connman_timeserver_sync(struct connman_service *default_service) if (resolv_id > 0) g_resolv_cancel_lookup(resolv, resolv_id); + g_resolv_flush_nameservers(resolv); + + nameservers = connman_service_get_nameservers(service); + if (!nameservers) + return -EINVAL; + + for (i = 0; nameservers[i]; i++) + g_resolv_add_nameserver(resolv, nameservers[i], 53, 0); + + g_strfreev(nameservers); + g_slist_free_full(ts_list, g_free); ts_list = __connman_timeserver_get_all(service); @@ -36,19 +36,24 @@ #define URANDOM "/dev/urandom" -int f = -1; +static int f = -1; int __connman_util_get_random(uint64_t *val) { - int r = 0; + int r; if (!val) return -EINVAL; - if (read(f, val, sizeof(uint64_t)) < 0) { + r = read(f, val, sizeof(uint64_t)); + if (r < 0) { r = -errno; connman_warn_once("Could not read from "URANDOM); *val = random(); + } else if (r != sizeof(uint64_t)) { + r = -EIO; + connman_warn_once("Short read from "URANDOM); + *val = random(); } return r; @@ -58,7 +63,7 @@ int __connman_util_init(void) { int r = 0; - if (f > 0) + if (f >= 0) return 0; f = open(URANDOM, O_RDONLY); @@ -81,7 +86,7 @@ int __connman_util_init(void) void __connman_util_cleanup(void) { - if (f > 0) + if (f >= 0) close(f); f = -1; diff --git a/src/wispr.c b/src/wispr.c index c3b0c9c9..adf62303 100755 --- a/src/wispr.c +++ b/src/wispr.c @@ -832,8 +832,8 @@ static int wispr_portal_detect(struct connman_wispr_portal_context *wp_context) int err = 0; int i; - DBG("wispr/portal context %p", wp_context); - DBG("service %p", wp_context->service); + DBG("wispr/portal context %p service %p", wp_context, + wp_context->service); service_type = connman_service_get_type(wp_context->service); @@ -908,8 +908,7 @@ static int wispr_portal_detect(struct connman_wispr_portal_context *wp_context) free_connman_wispr_portal_context(wp_context); } } else if (wp_context->timeout == 0) { - wp_context->timeout = - g_timeout_add_seconds(0, no_proxy_callback, wp_context); + wp_context->timeout = g_idle_add(no_proxy_callback, wp_context); } done: @@ -49,6 +49,8 @@ static void free_wpad(gpointer data) { struct connman_wpad *wpad = data; + connman_service_unref(wpad->service); + g_resolv_unref(wpad->resolv); g_strfreev(wpad->addrlist); @@ -152,7 +154,6 @@ int __connman_wpad_start(struct connman_service *service) return -ENOMEM; } - wpad->service = service; wpad->resolv = g_resolv_new(index); if (!wpad->resolv) { g_strfreev(nameservers); @@ -174,10 +175,11 @@ int __connman_wpad_start(struct connman_service *service) DBG("hostname %s", wpad->hostname); + wpad->service = connman_service_ref(service); + g_resolv_lookup_hostname(wpad->resolv, wpad->hostname, wpad_result, wpad); - connman_service_ref(service); g_hash_table_replace(wpad_list, GINT_TO_POINTER(index), wpad); return 0; @@ -196,8 +198,7 @@ void __connman_wpad_stop(struct connman_service *service) if (index < 0) return; - if (g_hash_table_remove(wpad_list, GINT_TO_POINTER(index))) - connman_service_unref(service); + g_hash_table_remove(wpad_list, GINT_TO_POINTER(index)); } int __connman_wpad_init(void) |