From 9362752a471a5c892d679548fbf2828d5fc5684b Mon Sep 17 00:00:00 2001 From: Seonah Moon Date: Mon, 8 Jan 2018 13:42:54 +0900 Subject: Imported Upstream version 1.35 Change-Id: I174854914d9fd06a813270b57d1f7bc2bac63c6a Signed-off-by: Seonah Moon --- src/6to4.c | 2 +- src/agent-connman.c | 99 +++- src/agent.c | 17 +- src/backtrace.c | 138 +++++ src/bridge.c | 2 +- src/config.c | 148 ++++- src/connection.c | 93 +-- src/connman-wait-online.service.in | 15 + src/connman.h | 54 +- src/connman.service.in | 13 +- src/connmand-wait-online.c | 461 +++++++++++++++ src/device.c | 48 +- src/dhcp.c | 110 +++- src/dhcpv6.c | 31 +- src/dnsproxy.c | 303 ++++++---- src/firewall-iptables.c | 631 ++++++++++++++++++++ src/firewall-nftables.c | 1133 ++++++++++++++++++++++++++++++++++++ src/firewall.c | 460 --------------- src/inet.c | 367 +++++++++++- src/ipconfig.c | 96 +-- src/ippool.c | 45 +- src/iptables.c | 96 ++- src/log.c | 105 +--- src/main.c | 102 +++- src/main.conf | 26 +- src/nat.c | 64 +- src/network.c | 148 ++--- src/ntp.c | 202 +++++-- src/peer.c | 9 +- src/peer_service.c | 3 - src/provider.c | 15 +- src/proxy.c | 2 +- src/resolver.c | 98 ++-- src/rfkill.c | 2 +- src/rtnl.c | 10 +- src/service.c | 1057 +++++++++++++++++++++------------ src/session.c | 427 +++++++++++--- src/stats.c | 50 +- src/storage.c | 2 - src/task.c | 3 +- src/technology.c | 62 +- src/tethering.c | 18 +- src/timeserver.c | 13 + src/util.c | 15 +- src/wispr.c | 7 +- src/wpad.c | 9 +- 46 files changed, 5023 insertions(+), 1788 deletions(-) create mode 100644 src/backtrace.c create mode 100644 src/connman-wait-online.service.in create mode 100644 src/connmand-wait-online.c create mode 100644 src/firewall-iptables.c create mode 100644 src/firewall-nftables.c delete mode 100644 src/firewall.c (limited to 'src') diff --git a/src/6to4.c b/src/6to4.c index 0e3a7a15..71a28827 100644 --- a/src/6to4.c +++ b/src/6to4.c @@ -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 84404510..fca7cc1f 100644 --- a/src/agent-connman.c +++ b/src/agent-connman.c @@ -100,58 +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")) { + DBusMessageIter array_iter; + 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_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; - dbus_message_iter_get_fixed_array(&value, &name, + } + dbus_message_iter_get_fixed_array(&array_iter, &name, &name_len); } dbus_message_iter_next(&dict); @@ -396,17 +439,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); } @@ -702,8 +761,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); @@ -712,9 +773,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 100644 --- 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/backtrace.c b/src/backtrace.c new file mode 100644 index 00000000..6a66c0ac --- /dev/null +++ b/src/backtrace.c @@ -0,0 +1,138 @@ +/* + * + * Connection Manager + * + * Copyright (C) 2007-2013 Intel Corporation. All rights reserved. + * Copyright (C) 2016 Yann E. MORIN . 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 +#endif + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include "connman.h" + +void print_backtrace(const char* program_path, const char* program_exec, + unsigned int offset) +{ + void *frames[99]; + size_t n_ptrs; + unsigned int i; + int outfd[2], infd[2]; + int pathlen; + pid_t pid; + + if (!program_exec) + return; + + pathlen = strlen(program_path); + + n_ptrs = backtrace(frames, G_N_ELEMENTS(frames)); + if (n_ptrs < offset) + return; + + if (pipe(outfd) < 0) + return; + + if (pipe(infd) < 0) { + close(outfd[0]); + close(outfd[1]); + return; + } + + pid = fork(); + if (pid < 0) { + close(outfd[0]); + close(outfd[1]); + close(infd[0]); + close(infd[1]); + return; + } + + if (pid == 0) { + close(outfd[1]); + close(infd[0]); + + dup2(outfd[0], STDIN_FILENO); + dup2(infd[1], STDOUT_FILENO); + + execlp("addr2line", "-C", "-f", "-e", program_exec, NULL); + + exit(EXIT_FAILURE); + } + + close(outfd[0]); + close(infd[1]); + + connman_error("++++++++ backtrace ++++++++"); + + for (i = offset; i < n_ptrs - 1; i++) { + Dl_info info; + char addr[20], buf[PATH_MAX * 2]; + int len, written; + char *ptr, *pos; + + dladdr(frames[i], &info); + + len = snprintf(addr, sizeof(addr), "%p\n", frames[i]); + if (len < 0) + break; + + written = write(outfd[1], addr, len); + if (written < 0) + break; + + len = read(infd[0], buf, sizeof(buf) - 1); + if (len < 0) + break; + + buf[len] = '\0'; + + pos = strchr(buf, '\n'); + *pos++ = '\0'; + + if (strcmp(buf, "??") == 0) { + connman_error("#%-2u %p in %s", i - offset, + frames[i], info.dli_fname); + continue; + } + + ptr = strchr(pos, '\n'); + *ptr++ = '\0'; + + if (strncmp(pos, program_path, pathlen) == 0) + pos += pathlen + 1; + + connman_error("#%-2u %p in %s() at %s", i - offset, + frames[i], buf, pos); + } + + connman_error("+++++++++++++++++++++++++++"); + + kill(pid, SIGTERM); + + close(outfd[1]); + close(infd[0]); +} diff --git a/src/bridge.c b/src/bridge.c index ba200969..cd2d9cee 100644 --- 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 a4c117e1..a8c3da89 100644 --- 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); @@ -698,7 +753,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); @@ -891,10 +957,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); @@ -916,7 +982,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); } @@ -953,6 +1019,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); } @@ -1025,10 +1096,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); @@ -1306,7 +1397,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; } @@ -1326,22 +1417,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; @@ -1425,7 +1529,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; @@ -1455,7 +1559,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(""); @@ -1491,13 +1595,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; } @@ -1511,13 +1616,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); @@ -1550,10 +1658,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 aa4e1c05..6b005e7f 100644 --- 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; } } @@ -755,40 +738,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) { @@ -1026,12 +975,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-wait-online.service.in b/src/connman-wait-online.service.in new file mode 100644 index 00000000..c2ad5cc9 --- /dev/null +++ b/src/connman-wait-online.service.in @@ -0,0 +1,15 @@ +[Unit] +Description=Wait for network to be configured by ConnMan +Requisite=connman.service +After=connman.service +Before=network-online.target +DefaultDependencies=no +Conflicts=shutdown.target + +[Service] +Type=oneshot +ExecStart=@sbindir@/connmand-wait-online +RemainAfterExit=yes + +[Install] +WantedBy=network-online.target diff --git a/src/connman.h b/src/connman.h index cbd88d88..21b70802 100644 --- a/src/connman.h +++ b/src/connman.h @@ -135,6 +135,8 @@ void __connman_log_cleanup(gboolean backtrace); void __connman_log_enable(struct connman_debug_desc *start, struct connman_debug_desc *stop); +#include + #include #include @@ -242,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 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); @@ -491,8 +497,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(); @@ -619,7 +623,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); @@ -642,7 +646,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); @@ -655,6 +660,9 @@ int __connman_service_load_modifiable(struct connman_service *service); void __connman_service_list_struct(DBusMessageIter *iter); +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); @@ -672,6 +680,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); @@ -679,7 +689,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); @@ -695,8 +704,6 @@ int __connman_service_set_ignore(struct connman_service *service, bool ignore); void __connman_service_set_search_domains(struct connman_service *service, char **domains); -void __connman_service_update_search_domains(struct connman_service *service, - char **domains); void __connman_service_set_string(struct connman_service *service, const char *key, const char *value); @@ -772,11 +779,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); @@ -929,7 +948,6 @@ int __connman_dnsproxy_add_listener(int index); void __connman_dnsproxy_remove_listener(int index); int __connman_dnsproxy_append(int index, const char *domain, const char *server); int __connman_dnsproxy_remove(int index, const char *domain, const char *server); -void __connman_dnsproxy_flush(void); int __connman_6to4_probe(struct connman_service *service); void __connman_6to4_remove(struct connman_ipconfig *ipconfig); @@ -987,13 +1005,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/connman.service.in b/src/connman.service.in index ba4dedd7..9f5c10fe 100644 --- a/src/connman.service.in +++ b/src/connman.service.in @@ -1,9 +1,11 @@ [Unit] Description=Connection service -Requires=dbus.socket -After=dbus.socket -Before=remote-fs-pre.target -Wants=remote-fs-pre.target +DefaultDependencies=false +Conflicts=shutdown.target +RequiresMountsFor=@localstatedir@/lib/connman +After=dbus.service network-pre.target systemd-sysusers.service +Before=network.target multi-user.target shutdown.target +Wants=network.target [Service] Type=dbus @@ -11,6 +13,9 @@ BusName=net.connman Restart=on-failure ExecStart=@sbindir@/connmand -n StandardOutput=null +CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SYS_TIME CAP_SYS_MODULE +ProtectHome=true +ProtectSystem=full [Install] WantedBy=multi-user.target 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 +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + +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 c0683abd..a563f464 100644 --- 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)) { @@ -750,37 +746,6 @@ int connman_device_set_scanning(struct connman_device *device, return 0; } -/** - * 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 @@ -1352,6 +1317,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) diff --git a/src/dhcp.c b/src/dhcp.c index 09f462bf..1af1eb52 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -26,6 +26,11 @@ #include #include #include +#include + +#ifndef IPV6_MIN_MTU +#define IPV6_MIN_MTU 1280 +#endif #include #include @@ -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) { @@ -80,7 +86,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; @@ -108,12 +114,16 @@ 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++) { __connman_service_nameserver_remove(service, dhcp->nameservers[i], false); } + g_strfreev(dhcp->nameservers); + dhcp->nameservers = NULL; } return true; @@ -223,7 +233,7 @@ static int ipv4ll_start_client(struct connman_dhcp *dhcp) return err; } - ipv4ll_running = true; + dhcp->ipv4ll_running = true; return 0; } @@ -244,13 +254,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) + if (dhcp->ipv4ll_running) return; err = ipv4ll_start_client(dhcp); @@ -258,7 +271,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) @@ -323,6 +336,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); @@ -399,7 +426,8 @@ static bool apply_lease_available_on_network(GDHCPClient *dhcp_client, dhcp->pac); } - __connman_6to4_probe(service); + if (connman_setting_get_bool("Enable6to4")) + __connman_6to4_probe(service); return true; } @@ -408,10 +436,11 @@ 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; - bool ip_change; + bool ip_change = false; DBG("Lease available"); @@ -443,17 +472,40 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data) DBG("c_address %s", c_address); - if (g_strcmp0(address, c_address)) + if (g_strcmp0(address, c_address)) { ip_change = true; - else if (g_strcmp0(gateway, c_gateway)) + if (c_address) { + /* Remove old ip address */ + __connman_ipconfig_address_remove(dhcp->ipconfig); + } + } + if (g_strcmp0(gateway, c_gateway)) { ip_change = true; - else if (prefixlen != c_prefixlen) + if (c_gateway) { + /* Remove gateway ip address */ + __connman_ipconfig_gateway_remove(dhcp->ipconfig); + } + } else if (prefixlen != c_prefixlen) ip_change = true; - else - ip_change = false; + old_method = __connman_ipconfig_get_method(dhcp->ipconfig); __connman_ipconfig_set_method(dhcp->ipconfig, CONNMAN_IPCONFIG_METHOD_DHCP); + + /* + * 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); @@ -461,11 +513,12 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data) } if (!apply_lease_available_on_network(dhcp_client, dhcp)) - return; + goto done; if (ip_change) dhcp_valid(dhcp); +done: g_free(address); g_free(netmask); g_free(gateway); @@ -474,6 +527,7 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data) static void ipv4ll_available_cb(GDHCPClient *ipv4ll_client, gpointer user_data) { struct connman_dhcp *dhcp = user_data; + enum connman_ipconfig_method old_method; char *address, *netmask; unsigned char prefixlen; @@ -484,8 +538,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); @@ -501,6 +572,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); @@ -538,10 +610,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 bdb3b987..cbf7974f 100644 --- 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 */ @@ -240,6 +234,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; } @@ -446,7 +441,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; @@ -457,6 +451,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); @@ -505,7 +500,7 @@ static int set_other_addresses(GDHCPClient *dhcp_client, for (i = 0, list = option; list; list = list->next, i++) domains[i] = g_strdup(list->data); - __connman_service_update_search_domains(service, domains); + __connman_service_set_search_domains(service, domains); g_strfreev(domains); } } @@ -1192,12 +1187,17 @@ static int check_restart(struct connman_dhcpv6 *dhcp) g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, NULL, NULL, &expired); + + /* infinite lifetime for an DHCPv6 address */ + if (expired == 0xffffffff) + return -EISCONN; + current = time(NULL); if (current >= expired) { DBG("expired by %d secs", (int)(current - expired)); - g_timeout_add(0, dhcpv6_restart, dhcp); + g_idle_add(dhcpv6_restart, dhcp); return -ETIMEDOUT; } @@ -1456,8 +1456,7 @@ int __connman_dhcpv6_start_renew(struct connman_network *network, /* RFC 3315, chapter 18.1.3, start rebind */ DBG("start rebind immediately"); - dhcp->timeout = g_timeout_add_seconds(0, start_rebind, - dhcp); + dhcp->timeout = g_idle_add(start_rebind, dhcp); } else if ((unsigned)current < (unsigned)started + T1) { delta = started + T1 - current; @@ -2159,7 +2158,7 @@ static int check_pd_restart(struct connman_dhcpv6 *dhcp) if (current > expired) { DBG("expired by %d secs", (int)(current - expired)); - g_timeout_add(0, dhcpv6_restart, dhcp); + g_idle_add(dhcpv6_restart, dhcp); return -ETIMEDOUT; } diff --git a/src/dnsproxy.c b/src/dnsproxy.c index cf3490c4..40b4f159 100644 --- a/src/dnsproxy.c +++ b/src/dnsproxy.c @@ -41,6 +41,8 @@ #include "connman.h" +#define debug(fmt...) do { } while (0) + #if __BYTE_ORDER == __LITTLE_ENDIAN struct domain_hdr { uint16_t id; @@ -282,7 +284,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; @@ -333,14 +335,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; @@ -355,7 +357,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) @@ -452,7 +454,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); @@ -464,7 +466,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); } @@ -475,7 +477,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; @@ -485,7 +487,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; @@ -537,7 +539,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); @@ -579,7 +581,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)); } @@ -597,7 +599,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; @@ -667,7 +669,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; @@ -676,7 +678,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; @@ -701,7 +703,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) @@ -720,7 +722,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) @@ -766,7 +768,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; @@ -836,7 +838,7 @@ static struct cache_entry *cache_check(gpointer request, int *qtype, int proto) static int get_name(int counter, unsigned char *pkt, unsigned char *start, unsigned char *max, unsigned char *output, int output_max, int *output_len, - unsigned char **end, char *name, int *name_len) + unsigned char **end, char *name, size_t max_name, int *name_len) { unsigned char *p; @@ -857,7 +859,7 @@ static int get_name(int counter, return get_name(counter + 1, pkt, pkt + offset, max, output, output_max, output_len, end, - name, name_len); + name, max_name, name_len); } else { unsigned label_len = *p; @@ -867,6 +869,9 @@ static int get_name(int counter, if (*output_len > output_max) return -ENOBUFS; + if ((*name_len + 1 + label_len + 1) > max_name) + return -ENOBUFS; + /* * We need the original name in order to check * if this answer is the correct one. @@ -898,14 +903,14 @@ static int parse_rr(unsigned char *buf, unsigned char *start, unsigned char *response, unsigned int *response_size, uint16_t *type, uint16_t *class, int *ttl, int *rdlen, unsigned char **end, - char *name) + char *name, size_t max_name) { struct domain_rr *rr; int err, offset; int name_len = 0, output_len = 0, max_rsp = *response_size; err = get_name(0, buf, start, max, response, max_rsp, - &output_len, end, name, &name_len); + &output_len, end, name, max_name, &name_len); if (err < 0) return err; @@ -980,7 +985,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) @@ -1031,7 +1036,8 @@ static int parse_response(unsigned char *buf, int buflen, memset(rsp, 0, sizeof(rsp)); ret = parse_rr(buf, ptr, buf + buflen, rsp, &rsp_len, - type, class, ttl, &rdlen, &next, name); + type, class, ttl, &rdlen, &next, name, + sizeof(name) - 1); if (ret != 0) { err = ret; goto out; @@ -1097,7 +1103,7 @@ static int parse_response(unsigned char *buf, int buflen, */ ret = get_name(0, buf, next - rdlen, buf + buflen, rsp, rsp_len, &output_len, &end, - name, &name_len); + name, sizeof(name) - 1, &name_len); if (ret != 0) { /* just ignore the error at this point */ ptr = next; @@ -1225,7 +1231,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, @@ -1289,7 +1295,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; @@ -1321,7 +1327,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]); } @@ -1357,7 +1363,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]; @@ -1398,7 +1404,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) @@ -1578,7 +1584,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, @@ -1604,7 +1610,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 @@ -1641,7 +1647,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); @@ -1701,7 +1707,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); @@ -1723,7 +1729,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; } @@ -1733,7 +1739,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; } @@ -1752,7 +1758,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 */ @@ -1774,7 +1780,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; @@ -1818,7 +1824,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; } @@ -1929,13 +1935,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; @@ -1991,7 +1997,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; } @@ -2050,21 +2056,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; /* @@ -2080,7 +2086,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; } @@ -2145,10 +2151,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); @@ -2157,7 +2163,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) { @@ -2182,7 +2188,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); @@ -2190,7 +2196,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); @@ -2250,7 +2256,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 @@ -2259,9 +2265,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; @@ -2303,7 +2311,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, @@ -2328,7 +2336,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); @@ -2387,7 +2395,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) @@ -2434,7 +2442,7 @@ static gboolean tcp_idle_timeout(gpointer user_data) { struct server_data *server = user_data; - DBG(""); + debug(""); if (!server) return FALSE; @@ -2449,7 +2457,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, @@ -2463,7 +2471,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) { @@ -2523,6 +2531,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; + } +} + static struct server_data *create_server(int index, const char *domain, const char *server, int protocol) @@ -2609,6 +2636,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); @@ -2630,7 +2659,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; @@ -2649,7 +2678,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; @@ -2680,10 +2709,52 @@ 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; + + list = request_list; + while (list) { + struct request_data *req = list->data; + + list = list->next; + + if (ns_resolv(server, req, req->request, req->name)) { + /* + * A cached result was sent, + * so the request can be released + */ + request_list = + g_slist_remove(request_list, req); + destroy_request_data(req); + continue; } + + if (req->timeout > 0) + g_source_remove(req->timeout); + + req->timeout = g_timeout_add_seconds(5, request_timeout, req); } } @@ -2719,6 +2790,8 @@ int __connman_dnsproxy_append(int index, const char *domain, if (!data) return -EIO; + flush_requests(data); + return 0; } @@ -2726,12 +2799,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, @@ -2739,9 +2822,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; @@ -2754,33 +2843,6 @@ int __connman_dnsproxy_remove(int index, const char *domain, return 0; } -void __connman_dnsproxy_flush(void) -{ - GSList *list; - - list = request_list; - while (list) { - struct request_data *req = list->data; - - list = list->next; - - if (resolv(req, req->request, req->name)) { - /* - * A cached result was sent, - * so the request can be released - */ - request_list = - g_slist_remove(request_list, req); - destroy_request_data(req); - continue; - } - - if (req->timeout > 0) - g_source_remove(req->timeout); - req->timeout = g_timeout_add_seconds(5, request_timeout, req); - } -} - static void dnsproxy_offline_mode(bool enabled) { GSList *list; @@ -2805,6 +2867,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; @@ -2829,12 +2892,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(); } @@ -2859,7 +2926,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); @@ -2897,7 +2964,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. @@ -2913,7 +2980,7 @@ static int parse_request(unsigned char *buf, int len, } } - DBG("query %s", name); + debug("query %s", name); return 0; } @@ -2924,7 +2991,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); @@ -2968,14 +3035,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; @@ -2984,24 +3051,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)); @@ -3042,7 +3109,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 @@ -3059,7 +3126,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) { @@ -3114,7 +3181,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, @@ -3130,12 +3197,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); @@ -3197,7 +3264,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)); @@ -3214,7 +3281,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)); @@ -3237,7 +3304,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)) { @@ -3267,9 +3334,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; } @@ -3303,9 +3370,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) { @@ -3329,11 +3396,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)); @@ -3341,14 +3408,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)); @@ -3360,7 +3427,7 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition, * that is the reason to -2 below. */ if (msg_len != (unsigned int)(len - 2)) { - 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; @@ -3424,7 +3491,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)) { @@ -3501,7 +3568,7 @@ static GIOChannel *get_listener(int family, int protocol, int index) int sk, type; char *interface; - 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: @@ -3736,7 +3803,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-iptables.c b/src/firewall-iptables.c new file mode 100644 index 00000000..45943a82 --- /dev/null +++ b/src/firewall-iptables.c @@ -0,0 +1,631 @@ +/* + * + * Connection Manager + * + * 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 + * 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 +#endif + +#include + +#include +#include + +#include "connman.h" + +#define CHAIN_PREFIX "connman-" + +static const char *builtin_chains[] = { + [NF_IP_PRE_ROUTING] = "PREROUTING", + [NF_IP_LOCAL_IN] = "INPUT", + [NF_IP_FORWARD] = "FORWARD", + [NF_IP_LOCAL_OUT] = "OUTPUT", + [NF_IP_POST_ROUTING] = "POSTROUTING", +}; + +struct connman_managed_table { + char *name; + unsigned int chains[NF_INET_NUMHOOKS]; +}; + +struct fw_rule { + bool enabled; + char *table; + char *chain; + char *rule_spec; +}; + +struct firewall_context { + GList *rules; +}; + +static GSList *managed_tables; +static struct firewall_context *connmark_ctx; +static unsigned int connmark_ref; + +static int chain_to_index(const char *chain_name) +{ + if (!g_strcmp0(builtin_chains[NF_IP_PRE_ROUTING], chain_name)) + return NF_IP_PRE_ROUTING; + if (!g_strcmp0(builtin_chains[NF_IP_LOCAL_IN], chain_name)) + return NF_IP_LOCAL_IN; + if (!g_strcmp0(builtin_chains[NF_IP_FORWARD], chain_name)) + return NF_IP_FORWARD; + if (!g_strcmp0(builtin_chains[NF_IP_LOCAL_OUT], chain_name)) + return NF_IP_LOCAL_OUT; + if (!g_strcmp0(builtin_chains[NF_IP_POST_ROUTING], chain_name)) + return NF_IP_POST_ROUTING; + + return -1; +} + +static int managed_chain_to_index(const char *chain_name) +{ + if (!g_str_has_prefix(chain_name, CHAIN_PREFIX)) + return -1; + + return chain_to_index(chain_name + strlen(CHAIN_PREFIX)); +} + +static int insert_managed_chain(const char *table_name, int id) +{ + char *rule, *managed_chain; + int err; + + managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX, + builtin_chains[id]); + + err = __connman_iptables_new_chain(table_name, managed_chain); + if (err < 0) + goto out; + + rule = g_strdup_printf("-j %s", managed_chain); + err = __connman_iptables_insert(table_name, builtin_chains[id], rule); + g_free(rule); + if (err < 0) { + __connman_iptables_delete_chain(table_name, managed_chain); + goto out; + } + +out: + g_free(managed_chain); + + return err; +} + +static int delete_managed_chain(const char *table_name, int id) +{ + char *rule, *managed_chain; + int err; + + managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX, + builtin_chains[id]); + + rule = g_strdup_printf("-j %s", managed_chain); + err = __connman_iptables_delete(table_name, builtin_chains[id], rule); + g_free(rule); + + if (err < 0) + goto out; + + err = __connman_iptables_delete_chain(table_name, managed_chain); + +out: + g_free(managed_chain); + + return err; +} + +static int insert_managed_rule(const char *table_name, + const char *chain_name, + const char *rule_spec) +{ + struct connman_managed_table *mtable = NULL; + GSList *list; + char *chain; + int id, err; + + id = chain_to_index(chain_name); + if (id < 0) { + /* This chain is not managed */ + chain = g_strdup(chain_name); + goto out; + } + + for (list = managed_tables; list; list = list->next) { + mtable = list->data; + + if (g_strcmp0(mtable->name, table_name) == 0) + break; + + mtable = NULL; + } + + if (!mtable) { + mtable = g_new0(struct connman_managed_table, 1); + mtable->name = g_strdup(table_name); + + managed_tables = g_slist_prepend(managed_tables, mtable); + } + + if (mtable->chains[id] == 0) { + DBG("table %s add managed chain for %s", + table_name, chain_name); + + err = insert_managed_chain(table_name, id); + if (err < 0) + return err; + } + + mtable->chains[id]++; + chain = g_strdup_printf("%s%s", CHAIN_PREFIX, chain_name); + +out: + err = __connman_iptables_append(table_name, chain, rule_spec); + + g_free(chain); + + return err; + } + +static int delete_managed_rule(const char *table_name, + const char *chain_name, + const char *rule_spec) + { + struct connman_managed_table *mtable = NULL; + GSList *list; + int id, err; + char *managed_chain; + + id = chain_to_index(chain_name); + if (id < 0) { + /* This chain is not managed */ + return __connman_iptables_delete(table_name, chain_name, + rule_spec); + } + + managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX, chain_name); + + err = __connman_iptables_delete(table_name, managed_chain, + rule_spec); + + for (list = managed_tables; list; list = list->next) { + mtable = list->data; + + if (g_strcmp0(mtable->name, table_name) == 0) + break; + + mtable = NULL; + } + + if (!mtable) { + err = -ENOENT; + goto out; + } + + mtable->chains[id]--; + if (mtable->chains[id] > 0) + goto out; + + DBG("table %s remove managed chain for %s", + table_name, chain_name); + + err = delete_managed_chain(table_name, id); + + out: + g_free(managed_chain); + + return err; +} + +static void cleanup_managed_table(gpointer user_data) +{ + struct connman_managed_table *table = user_data; + + g_free(table->name); + g_free(table); +} + +static void cleanup_fw_rule(gpointer user_data) +{ + struct fw_rule *rule = user_data; + + g_free(rule->rule_spec); + g_free(rule->chain); + g_free(rule->table); + g_free(rule); +} + +struct firewall_context *__connman_firewall_create(void) +{ + struct firewall_context *ctx; + + ctx = g_new0(struct firewall_context, 1); + + return ctx; +} + +void __connman_firewall_destroy(struct firewall_context *ctx) +{ + g_list_free_full(ctx->rules, cleanup_fw_rule); + g_free(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, ...) +{ + va_list args; + char *rule_spec; + struct fw_rule *rule; + + va_start(args, rule_fmt); + + rule_spec = g_strdup_vprintf(rule_fmt, args); + + va_end(args); + + 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); +} + +static void firewall_remove_rules(struct firewall_context *ctx) +{ + struct fw_rule *rule; + GList *list; + + for (list = g_list_last(ctx->rules); list; + list = g_list_previous(list)) { + rule = list->data; + + ctx->rules = g_list_remove(ctx->rules, rule); + cleanup_fw_rule(rule); + } +} + +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 err; +} + +static int firewall_disable_rules(struct firewall_context *ctx) +{ + struct fw_rule *rule; + GList *list; + int e; + int err = -ENOENT; + + for (list = g_list_last(ctx->rules); list; + list = g_list_previous(list)) { + rule = list->data; + + e = disable_rule(rule); + + /* Report last error back */ + if (e == 0 && err == -ENOENT) + err = 0; + else if (e < 0) + err = e; + } + + 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; +} + +int __connman_firewall_enable_snat(struct firewall_context *ctx, + int index, const char *ifname, + const char *addr) +{ + int err; + + 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_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) +{ + 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; +} + +static void firewall_disable_connmark(void) +{ + 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) +{ + GSList **chains = user_data; + int id; + + id = managed_chain_to_index(chain_name); + if (id < 0) + return; + + *chains = g_slist_prepend(*chains, GINT_TO_POINTER(id)); +} + +static void flush_table(const char *table_name) +{ + GSList *chains = NULL, *list; + char *rule, *managed_chain; + int id, err; + + __connman_iptables_iterate_chains(table_name, iterate_chains_cb, + &chains); + + for (list = chains; list; list = list->next) { + id = GPOINTER_TO_INT(list->data); + + managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX, + builtin_chains[id]); + + rule = g_strdup_printf("-j %s", managed_chain); + err = __connman_iptables_delete(table_name, + builtin_chains[id], rule); + if (err < 0) { + connman_warn("Failed to delete jump rule '%s': %s", + rule, strerror(-err)); + } + g_free(rule); + + err = __connman_iptables_flush_chain(table_name, managed_chain); + if (err < 0) { + connman_warn("Failed to flush chain '%s': %s", + managed_chain, strerror(-err)); + } + err = __connman_iptables_delete_chain(table_name, managed_chain); + if (err < 0) { + connman_warn("Failed to delete chain '%s': %s", + managed_chain, strerror(-err)); + } + + g_free(managed_chain); + } + + err = __connman_iptables_commit(table_name); + if (err < 0) { + connman_warn("Failed to flush table '%s': %s", + table_name, strerror(-err)); + } + + g_slist_free(chains); +} + +static void flush_all_tables(void) +{ + /* Flush the tables ConnMan might have modified + * But do so if only ConnMan has done something with + * iptables */ + + if (!g_file_test("/proc/net/ip_tables_names", + G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) { + return; + } + + flush_table("filter"); + flush_table("mangle"); + flush_table("nat"); +} + +int __connman_firewall_init(void) +{ + DBG(""); + + __connman_iptables_init(); + flush_all_tables(); + + return 0; +} + +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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#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; +} diff --git a/src/firewall.c b/src/firewall.c deleted file mode 100644 index 90c3d3c1..00000000 --- a/src/firewall.c +++ /dev/null @@ -1,460 +0,0 @@ -/* - * - * Connection Manager - * - * Copyright (C) 2013 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 - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include -#include - -#include "connman.h" - -#define CHAIN_PREFIX "connman-" - -static const char *builtin_chains[] = { - [NF_IP_PRE_ROUTING] = "PREROUTING", - [NF_IP_LOCAL_IN] = "INPUT", - [NF_IP_FORWARD] = "FORWARD", - [NF_IP_LOCAL_OUT] = "OUTPUT", - [NF_IP_POST_ROUTING] = "POSTROUTING", -}; - -struct connman_managed_table { - char *name; - unsigned int chains[NF_INET_NUMHOOKS]; -}; - -struct fw_rule { - char *table; - char *chain; - char *rule_spec; -}; - -struct firewall_context { - GList *rules; -}; - -static GSList *managed_tables; - -static bool firewall_is_up; - -static int chain_to_index(const char *chain_name) -{ - if (!g_strcmp0(builtin_chains[NF_IP_PRE_ROUTING], chain_name)) - return NF_IP_PRE_ROUTING; - if (!g_strcmp0(builtin_chains[NF_IP_LOCAL_IN], chain_name)) - return NF_IP_LOCAL_IN; - if (!g_strcmp0(builtin_chains[NF_IP_FORWARD], chain_name)) - return NF_IP_FORWARD; - if (!g_strcmp0(builtin_chains[NF_IP_LOCAL_OUT], chain_name)) - return NF_IP_LOCAL_OUT; - if (!g_strcmp0(builtin_chains[NF_IP_POST_ROUTING], chain_name)) - return NF_IP_POST_ROUTING; - - return -1; -} - -static int managed_chain_to_index(const char *chain_name) -{ - if (!g_str_has_prefix(chain_name, CHAIN_PREFIX)) - return -1; - - return chain_to_index(chain_name + strlen(CHAIN_PREFIX)); -} - -static int insert_managed_chain(const char *table_name, int id) -{ - char *rule, *managed_chain; - int err; - - managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX, - builtin_chains[id]); - - err = __connman_iptables_new_chain(table_name, managed_chain); - if (err < 0) - goto out; - - rule = g_strdup_printf("-j %s", managed_chain); - err = __connman_iptables_insert(table_name, builtin_chains[id], rule); - g_free(rule); - if (err < 0) { - __connman_iptables_delete_chain(table_name, managed_chain); - goto out; - } - -out: - g_free(managed_chain); - - return err; -} - -static int delete_managed_chain(const char *table_name, int id) -{ - char *rule, *managed_chain; - int err; - - managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX, - builtin_chains[id]); - - rule = g_strdup_printf("-j %s", managed_chain); - err = __connman_iptables_delete(table_name, builtin_chains[id], rule); - g_free(rule); - - if (err < 0) - goto out; - - err = __connman_iptables_delete_chain(table_name, managed_chain); - -out: - g_free(managed_chain); - - return err; -} - -static int insert_managed_rule(const char *table_name, - const char *chain_name, - const char *rule_spec) -{ - struct connman_managed_table *mtable = NULL; - GSList *list; - char *chain; - int id, err; - - id = chain_to_index(chain_name); - if (id < 0) { - /* This chain is not managed */ - chain = g_strdup(chain_name); - goto out; - } - - for (list = managed_tables; list; list = list->next) { - mtable = list->data; - - if (g_strcmp0(mtable->name, table_name) == 0) - break; - - mtable = NULL; - } - - if (!mtable) { - mtable = g_new0(struct connman_managed_table, 1); - mtable->name = g_strdup(table_name); - - managed_tables = g_slist_prepend(managed_tables, mtable); - } - - if (mtable->chains[id] == 0) { - DBG("table %s add managed chain for %s", - table_name, chain_name); - - err = insert_managed_chain(table_name, id); - if (err < 0) - return err; - } - - mtable->chains[id]++; - chain = g_strdup_printf("%s%s", CHAIN_PREFIX, chain_name); - -out: - err = __connman_iptables_append(table_name, chain, rule_spec); - - g_free(chain); - - return err; - } - -static int delete_managed_rule(const char *table_name, - const char *chain_name, - const char *rule_spec) - { - struct connman_managed_table *mtable = NULL; - GSList *list; - int id, err; - char *managed_chain; - - id = chain_to_index(chain_name); - if (id < 0) { - /* This chain is not managed */ - return __connman_iptables_delete(table_name, chain_name, - rule_spec); - } - - managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX, chain_name); - - err = __connman_iptables_delete(table_name, managed_chain, - rule_spec); - - for (list = managed_tables; list; list = list->next) { - mtable = list->data; - - if (g_strcmp0(mtable->name, table_name) == 0) - break; - - mtable = NULL; - } - - if (!mtable) { - err = -ENOENT; - goto out; - } - - mtable->chains[id]--; - if (mtable->chains[id] > 0) - goto out; - - DBG("table %s remove managed chain for %s", - table_name, chain_name); - - err = delete_managed_chain(table_name, id); - - out: - g_free(managed_chain); - - return err; -} - -static void cleanup_managed_table(gpointer user_data) -{ - struct connman_managed_table *table = user_data; - - g_free(table->name); - g_free(table); -} - -static void cleanup_fw_rule(gpointer user_data) -{ - struct fw_rule *rule = user_data; - - g_free(rule->rule_spec); - g_free(rule->chain); - g_free(rule->table); - g_free(rule); -} - -struct firewall_context *__connman_firewall_create(void) -{ - struct firewall_context *ctx; - - ctx = g_new0(struct firewall_context, 1); - - return ctx; -} - -void __connman_firewall_destroy(struct firewall_context *ctx) -{ - g_list_free_full(ctx->rules, cleanup_fw_rule); - g_free(ctx); -} - -int __connman_firewall_add_rule(struct firewall_context *ctx, - const char *table, - const char *chain, - const char *rule_fmt, ...) -{ - va_list args; - char *rule_spec; - struct fw_rule *rule; - - va_start(args, rule_fmt); - - rule_spec = g_strdup_vprintf(rule_fmt, args); - - va_end(args); - - rule = g_new0(struct fw_rule, 1); - - 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) -{ - struct fw_rule *rule; - GList *list; - int err; - - for (list = 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; - } - - err = __connman_iptables_commit(rule->table); - if (err < 0) { - connman_error("Cannot remove previously installed " - "iptables rules: %s", strerror(-err)); - return err; - } - } - - return 0; -} - -int __connman_firewall_enable(struct firewall_context *ctx) -{ - struct fw_rule *rule; - GList *list; - int err; - - for (list = g_list_first(ctx->rules); list; - list = g_list_next(list)) { - rule = list->data; - - 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) - goto err; - - err = __connman_iptables_commit(rule->table); - if (err < 0) - goto err; - } - - firewall_is_up = true; - - return 0; - -err: - connman_warn("Failed to install iptables rules: %s", strerror(-err)); - - firewall_disable(g_list_previous(list)); - - return err; -} - -int __connman_firewall_disable(struct firewall_context *ctx) -{ - return firewall_disable(g_list_last(ctx->rules)); -} - -bool __connman_firewall_is_up(void) -{ - return firewall_is_up; -} - -static void iterate_chains_cb(const char *chain_name, void *user_data) -{ - GSList **chains = user_data; - int id; - - id = managed_chain_to_index(chain_name); - if (id < 0) - return; - - *chains = g_slist_prepend(*chains, GINT_TO_POINTER(id)); -} - -static void flush_table(const char *table_name) -{ - GSList *chains = NULL, *list; - char *rule, *managed_chain; - int id, err; - - __connman_iptables_iterate_chains(table_name, iterate_chains_cb, - &chains); - - for (list = chains; list; list = list->next) { - id = GPOINTER_TO_INT(list->data); - - managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX, - builtin_chains[id]); - - rule = g_strdup_printf("-j %s", managed_chain); - err = __connman_iptables_delete(table_name, - builtin_chains[id], rule); - if (err < 0) { - connman_warn("Failed to delete jump rule '%s': %s", - rule, strerror(-err)); - } - g_free(rule); - - err = __connman_iptables_flush_chain(table_name, managed_chain); - if (err < 0) { - connman_warn("Failed to flush chain '%s': %s", - managed_chain, strerror(-err)); - } - err = __connman_iptables_delete_chain(table_name, managed_chain); - if (err < 0) { - connman_warn("Failed to delete chain '%s': %s", - managed_chain, strerror(-err)); - } - - g_free(managed_chain); - } - - err = __connman_iptables_commit(table_name); - if (err < 0) { - connman_warn("Failed to flush table '%s': %s", - table_name, strerror(-err)); - } - - g_slist_free(chains); -} - -static void flush_all_tables(void) -{ - /* Flush the tables ConnMan might have modified - * But do so if only ConnMan has done something with - * iptables */ - - 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"); -} - -int __connman_firewall_init(void) -{ - DBG(""); - - flush_all_tables(); - - return 0; -} - -void __connman_firewall_cleanup(void) -{ - DBG(""); - - g_slist_free_full(managed_tables, cleanup_managed_table); -} diff --git a/src/inet.c b/src/inet.c index cd220ffc..b887aa0b 100644 --- a/src/inet.c +++ b/src/inet.c @@ -1252,13 +1252,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; @@ -1280,7 +1279,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; } @@ -1291,7 +1289,6 @@ static int icmpv6_recv(int fd, gpointer user_data) return 0; cb(hdr, len, data->user_data); - xs_cleanup(data); return len; } @@ -1299,18 +1296,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 */ @@ -1667,13 +1667,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; @@ -1712,17 +1711,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; } @@ -1797,13 +1799,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; @@ -1825,7 +1826,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; } @@ -1844,7 +1844,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; } @@ -1853,18 +1852,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, @@ -2188,9 +2190,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; @@ -2229,10 +2230,8 @@ static int inet_rtnl_recv(GIOChannel *chan, gpointer user_data) h = ptr; - if (!NLMSG_OK(h, len)) { + if (!NLMSG_OK(h, len)) return -1; - break; - } if (h->nlmsg_seq != rth->seq) { /* Skip this msg */ @@ -2274,17 +2273,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, @@ -2293,13 +2295,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)); @@ -2313,7 +2314,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); @@ -2651,8 +2651,14 @@ char **__connman_inet_get_running_interfaces(void) g_free(ifr); - if (count < numif) + if (count < numif) { + char **prev_result = result; result = g_try_realloc(result, (count + 1) * sizeof(char *)); + if (!result) { + g_free(prev_result); + return NULL; + } + } return result; @@ -2961,3 +2967,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 f8c148be..25657733 100644 --- 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; @@ -400,34 +399,15 @@ static void free_ipdevice(gpointer data) g_free(ipdevice->address); - set_ipv6_state(ifname, ipdevice->ipv6_enabled); - set_ipv6_privacy(ifname, ipdevice->ipv6_privacy); + if (ifname) { + set_ipv6_state(ifname, ipdevice->ipv6_enabled); + set_ipv6_privacy(ifname, ipdevice->ipv6_privacy); + } g_free(ifname); 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); -} - -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) { @@ -577,11 +557,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); } @@ -622,8 +597,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)); } @@ -1133,8 +1106,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; @@ -1142,7 +1113,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) @@ -1162,7 +1132,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; @@ -1183,8 +1153,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; @@ -1192,7 +1160,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); @@ -1203,7 +1170,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; } @@ -1336,8 +1303,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: @@ -1361,13 +1326,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: @@ -1389,13 +1350,9 @@ int __connman_ipconfig_address_unset(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: @@ -1426,8 +1383,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; @@ -1446,8 +1401,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; @@ -1600,8 +1553,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, @@ -1676,8 +1627,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); @@ -1786,8 +1735,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; @@ -1803,8 +1750,6 @@ void __connman_ipconfig_append_ipv4(struct connman_ipconfig *ipconfig, struct connman_ipaddress *append_addr = NULL; const char *str; - DBG(""); - if (ipconfig->type != CONNMAN_IPCONFIG_TYPE_IPV4) return; @@ -1817,13 +1762,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; @@ -1860,8 +1805,6 @@ void __connman_ipconfig_append_ipv6(struct connman_ipconfig *ipconfig, struct connman_ipaddress *append_addr = NULL; const char *str, *privacy; - DBG(""); - if (ipconfig->type != CONNMAN_IPCONFIG_TYPE_IPV6) return; @@ -1918,8 +1861,6 @@ void __connman_ipconfig_append_ipv6config(struct connman_ipconfig *ipconfig, { const char *str, *privacy; - DBG(""); - str = __connman_ipconfig_method2string(ipconfig->method); if (!str) return; @@ -1962,8 +1903,6 @@ void __connman_ipconfig_append_ipv4config(struct connman_ipconfig *ipconfig, { const char *str; - DBG(""); - str = __connman_ipconfig_method2string(ipconfig->method); if (!str) return; @@ -2014,8 +1953,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; @@ -2285,6 +2222,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); @@ -2296,9 +2247,6 @@ int __connman_ipconfig_load(struct connman_ipconfig *ipconfig, g_free(key); break; - - case CONNMAN_IPCONFIG_METHOD_AUTO: - break; } return 0; @@ -2398,6 +2346,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 100644 --- 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 8d583eae..5ef757a3 100644 --- a/src/iptables.c +++ b/src/iptables.c @@ -31,6 +31,7 @@ #include #include #include +#include #include @@ -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) @@ -1546,6 +1566,9 @@ struct xtables_globals iptables_globals = { .option_offset = 0, .opts = iptables_opts, .orig_opts = iptables_opts, +#if XTABLES_VERSION_CODE > 10 + .compat_rev = xtables_compatible_revision, +#endif }; static struct xtables_target *prepare_target(struct connman_iptables *table, @@ -1745,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) @@ -1784,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) @@ -1799,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) @@ -1813,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; } @@ -1987,6 +2019,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 */ @@ -2303,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); @@ -2311,21 +2350,44 @@ int __connman_iptables_commit(const char *table_name) return -EINVAL; repl = iptables_blob(table); + if (!repl) + return -ENOMEM; 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) @@ -2364,8 +2426,10 @@ int __connman_iptables_iterate_chains(const char *table_name, struct connman_iptables *table; table = get_table(table_name); - if (!table) + if (!table) { + g_free(cbd); return -EINVAL; + } iterate_entries(table->blob_entries->entrytable, table->info->valid_hooks, diff --git a/src/log.c b/src/log.c index a693bd00..9bae4a3d 100644 --- a/src/log.c +++ b/src/log.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include "connman.h" @@ -110,113 +109,11 @@ void connman_debug(const char *format, ...) va_end(ap); } -static void print_backtrace(unsigned int offset) -{ - void *frames[99]; - size_t n_ptrs; - unsigned int i; - int outfd[2], infd[2]; - int pathlen; - pid_t pid; - - if (!program_exec) - return; - - pathlen = strlen(program_path); - - n_ptrs = backtrace(frames, G_N_ELEMENTS(frames)); - if (n_ptrs < offset) - return; - - if (pipe(outfd) < 0) - return; - - if (pipe(infd) < 0) { - close(outfd[0]); - close(outfd[1]); - return; - } - - pid = fork(); - if (pid < 0) { - close(outfd[0]); - close(outfd[1]); - close(infd[0]); - close(infd[1]); - return; - } - - if (pid == 0) { - close(outfd[1]); - close(infd[0]); - - dup2(outfd[0], STDIN_FILENO); - dup2(infd[1], STDOUT_FILENO); - - execlp("addr2line", "-C", "-f", "-e", program_exec, NULL); - - exit(EXIT_FAILURE); - } - - close(outfd[0]); - close(infd[1]); - - connman_error("++++++++ backtrace ++++++++"); - - for (i = offset; i < n_ptrs - 1; i++) { - Dl_info info; - char addr[20], buf[PATH_MAX * 2]; - int len, written; - char *ptr, *pos; - - dladdr(frames[i], &info); - - len = snprintf(addr, sizeof(addr), "%p\n", frames[i]); - if (len < 0) - break; - - written = write(outfd[1], addr, len); - if (written < 0) - break; - - len = read(infd[0], buf, sizeof(buf) - 1); - if (len < 0) - break; - - buf[len] = '\0'; - - pos = strchr(buf, '\n'); - *pos++ = '\0'; - - if (strcmp(buf, "??") == 0) { - connman_error("#%-2u %p in %s", i - offset, - frames[i], info.dli_fname); - continue; - } - - ptr = strchr(pos, '\n'); - *ptr++ = '\0'; - - if (strncmp(pos, program_path, pathlen) == 0) - pos += pathlen + 1; - - connman_error("#%-2u %p in %s() at %s", i - offset, - frames[i], buf, pos); - } - - connman_error("+++++++++++++++++++++++++++"); - - kill(pid, SIGTERM); - - close(outfd[1]); - close(infd[0]); -} - static void signal_handler(int signo) { connman_error("Aborting (signal %d) [%s]", signo, program_exec); - print_backtrace(2); + print_backtrace(program_path, program_exec, 2); exit(EXIT_FAILURE); } diff --git a/src/main.c b/src/main.c index 1c179912..b78a0461 100644 --- a/src/main.c +++ b/src/main.c @@ -57,6 +57,8 @@ static char *default_blacklist[] = { "vboxnet", "virbr", "ifb", + "ve-", + "vb-", NULL }; @@ -65,6 +67,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; @@ -73,11 +76,15 @@ static struct { bool single_tech; char **tethering_technologies; bool persistent_tethering_mode; + bool enable_6to4; + char *vendor_class_id; + bool enable_online_check; } connman_settings = { .bg_scan = true, .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, @@ -86,11 +93,15 @@ static struct { .single_tech = false, .tethering_technologies = NULL, .persistent_tethering_mode = false, + .enable_6to4 = false, + .vendor_class_id = NULL, + .enable_online_check = true, }; #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" @@ -100,11 +111,15 @@ static struct { #define CONF_SINGLE_TECH "SingleConnectedTechnology" #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" 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, @@ -114,6 +129,9 @@ static const char *supported_options[] = { CONF_SINGLE_TECH, CONF_TETHERING_TECHNOLOGIES, CONF_PERSISTENT_TETHERING_MODE, + CONF_ENABLE_6TO4, + CONF_VENDOR_CLASS_ID, + CONF_ENABLE_ONLINE_CHECK, NULL }; @@ -236,6 +254,7 @@ static void parse_config(GKeyFile *config) char **interfaces; char **str_list; char **tethering; + char *vendor_class_id; gsize len; int timeout; @@ -288,6 +307,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); @@ -354,6 +384,30 @@ static void parse_config(GKeyFile *config) connman_settings.persistent_tethering_mode = boolean; g_clear_error(&error); + + boolean = __connman_config_get_bool(config, "General", + CONF_ENABLE_6TO4, &error); + if (!error) + connman_settings.enable_6to4 = boolean; + + 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); } static int config_init(const char *file) @@ -463,10 +517,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; } @@ -484,7 +562,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" }, @@ -504,6 +582,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"; @@ -528,6 +609,12 @@ bool connman_setting_get_bool(const char *key) if (g_str_equal(key, CONF_PERSISTENT_TETHERING_MODE)) return connman_settings.persistent_tethering_mode; + 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; } @@ -556,6 +643,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; } @@ -653,7 +743,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(); @@ -662,7 +751,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(); @@ -674,6 +762,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(); @@ -716,7 +805,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 93c7a501..68870b28 100644 --- a/src/main.conf +++ b/src/main.conf @@ -57,8 +57,8 @@ # 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- # Allow connman to change the system hostname. This can # happen for example if we receive DHCP hostname option. @@ -95,3 +95,25 @@ # re-enabling a technology, and after restarts and reboots. # Default value is false. # PersistentTetheringMode = false + +# Automatically enable Anycast 6to4 if possible. This is not recommended, as +# the use of 6to4 will generally lead to a severe degradation of connection +# quality. See RFC6343. Default value is false (as recommended by RFC6343 +# 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 = diff --git a/src/nat.c b/src/nat.c index 063f0851..fb557101 100644 --- a/src/nat.c +++ b/src/nat.c @@ -25,7 +25,10 @@ #endif #include -#include +#include +#include +#include +#include #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 badb7702..ed56210f 100644 --- a/src/network.c +++ b/src/network.c @@ -78,8 +78,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; @@ -135,8 +140,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, @@ -168,6 +171,8 @@ static void dhcp_success(struct connman_network *network) if (err < 0) goto err; + __connman_service_save(service); + return; err: @@ -218,8 +223,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); @@ -246,6 +251,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); @@ -285,13 +291,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; @@ -556,8 +557,6 @@ static void autoconf_ipv6_set(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); if (!service) return; @@ -566,6 +565,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); @@ -647,10 +648,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; @@ -800,23 +811,6 @@ static void network_remove(struct connman_network *network) network->driver = NULL; } -static void network_change(struct connman_network *network) -{ - DBG("network %p name %s", network, network->name); - - if (!network->connected) - return; - - connman_device_set_disconnected(network->device, true); - - if (network->driver && network->driver->disconnect) { - network->driver->disconnect(network); - return; - } - - network->connected = false; -} - static void probe_driver(struct connman_network_driver *driver) { GSList *list; @@ -839,20 +833,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; @@ -889,11 +869,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) @@ -906,8 +893,13 @@ 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); @@ -939,14 +931,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); @@ -961,6 +949,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; } @@ -1265,6 +1255,16 @@ static void set_connect_error(struct connman_network *network) CONNMAN_SERVICE_ERROR_CONNECT_FAILED); } +static void set_blocked_error(struct connman_network *network) +{ + struct connman_service *service; + + service = connman_service_lookup_from_network(network); + + __connman_service_indicate_error(service, + CONNMAN_SERVICE_ERROR_BLOCKED); +} + void connman_network_set_ipv4_method(struct connman_network *network, enum connman_ipconfig_method method) { @@ -1304,9 +1304,6 @@ void connman_network_set_error(struct connman_network *network, { DBG("network %p error %d", network, error); - network->connecting = false; - network->associating = false; - switch (error) { case CONNMAN_NETWORK_ERROR_UNKNOWN: return; @@ -1322,9 +1319,12 @@ void connman_network_set_error(struct connman_network *network, case CONNMAN_NETWORK_ERROR_CONNECT_FAIL: set_connect_error(network); break; + case CONNMAN_NETWORK_ERROR_BLOCKED: + set_blocked_error(network); + break; } - network_change(network); + __connman_network_disconnect(network); } /** @@ -1345,8 +1345,7 @@ int connman_network_set_connected(struct connman_network *network, !connected) { connman_network_set_error(network, CONNMAN_NETWORK_ERROR_CONNECT_FAIL); - if (__connman_network_disconnect(network) == 0) - return 0; + return 0; } if (network->connected == connected) @@ -1737,8 +1736,6 @@ int connman_network_set_name(struct connman_network *network, int connman_network_set_strength(struct connman_network *network, uint8_t strength) { - DBG("network %p strengh %d", network, strength); - network->strength = strength; return 0; @@ -1752,8 +1749,6 @@ uint8_t connman_network_get_strength(struct connman_network *network) int connman_network_set_frequency(struct connman_network *network, uint16_t frequency) { - DBG("network %p frequency %d", network, frequency); - network->frequency = frequency; return 0; @@ -1767,8 +1762,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; @@ -1790,8 +1783,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) { - DBG("network %p key %s value %s", network, key, value); - if (g_strcmp0(key, "Name") == 0) return connman_network_set_name(network, value); @@ -1816,12 +1807,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); @@ -1854,8 +1860,6 @@ int connman_network_set_string(struct connman_network *network, const char *connman_network_get_string(struct connman_network *network, const char *key) { - DBG("network %p key %s", network, key); - if (g_str_equal(key, "Path")) return network->path; else if (g_str_equal(key, "Name")) @@ -1872,10 +1876,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")) @@ -1901,8 +1915,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) { - DBG("network %p key %s value %d", network, key, value); - if (g_strcmp0(key, "Roaming") == 0) network->roaming = value; else if (g_strcmp0(key, "WiFi.WPS") == 0) @@ -1923,8 +1935,6 @@ int connman_network_set_bool(struct connman_network *network, bool connman_network_get_bool(struct connman_network *network, const char *key) { - DBG("network %p key %s", network, key); - if (g_str_equal(key, "Roaming")) return network->roaming; else if (g_str_equal(key, "WiFi.WPS")) @@ -1947,8 +1957,6 @@ bool connman_network_get_bool(struct connman_network *network, int connman_network_set_blob(struct connman_network *network, const char *key, const void *data, unsigned int size) { - DBG("network %p key %s size %d", network, key, size); - if (g_str_equal(key, "WiFi.SSID")) { g_free(network->wifi.ssid); network->wifi.ssid = g_try_malloc(size); @@ -1975,8 +1983,6 @@ int connman_network_set_blob(struct connman_network *network, const void *connman_network_get_blob(struct connman_network *network, const char *key, unsigned int *size) { - DBG("network %p key %s", network, key); - if (g_str_equal(key, "WiFi.SSID")) { if (size) *size = network->wifi.ssid_len; diff --git a/src/ntp.c b/src/ntp.c index abb2caa2..0e80c3e5 100644 --- a/src/ntp.c +++ b/src/ntp.c @@ -30,10 +30,12 @@ #include #include #include +#include #include #include #include #include +#include #include @@ -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,6 +251,7 @@ 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; + struct timex tmx = {}; if (len < sizeof(*msg)) { connman_error("Invalid response from time server"); @@ -324,47 +341,52 @@ 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 (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; + 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; + } - 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; + connman_info("ntp: adjust (jump): %+.6f sec", offset); + } - DBG("setting time"); + 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; - if (settimeofday(&cur, NULL) < 0) { - connman_error("Failed to set time"); - return; - } - - 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); } 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; @@ -373,6 +395,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"); @@ -397,8 +422,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; @@ -423,36 +460,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, @@ -480,7 +557,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) @@ -494,7 +571,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); diff --git a/src/peer.c b/src/peer.c index 206b799b..340cbcc2 100644 --- a/src/peer.c +++ b/src/peer.c @@ -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 100644 --- 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 693552ee..9c71a200 100644 --- 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 100644 --- 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 652f4fd9..75ea5ba6 100644 --- a/src/resolver.c +++ b/src/resolver.c @@ -35,6 +35,9 @@ #include "connman.h" +#define RESOLV_CONF_STATEDIR STATEDIR"/resolv.conf" +#define RESOLV_CONF_ETC "/etc/resolv.conf" + #define RESOLVER_FLAG_PUBLIC (1 << 0) /* @@ -97,9 +100,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 +118,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) @@ -130,11 +133,19 @@ static int resolvfile_export(void) old_umask = umask(022); - fd = open("/etc/resolv.conf", O_RDWR | O_CREAT | O_CLOEXEC, + fd = open(RESOLV_CONF_STATEDIR, O_RDWR | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (fd < 0) { - err = -errno; - goto done; + connman_warn_once("Cannot create "RESOLV_CONF_STATEDIR" " + "falling back to "RESOLV_CONF_ETC); + + fd = open(RESOLV_CONF_ETC, O_RDWR | O_CREAT | O_CLOEXEC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + if (fd < 0) { + err = -errno; + goto done; + } } if (ftruncate(fd, 0) < 0) { @@ -207,7 +218,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; @@ -283,6 +294,8 @@ static void remove_entries(GSList *entries) } g_slist_free(entries); + + __connman_resolver_append_fallback_nameservers(); } static gboolean resolver_expire_cb(gpointer user_data) @@ -376,18 +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) - __connman_service_nameserver_append(service, - server, true); - } } if (entry->index >= 0 && entry->server) @@ -400,6 +401,18 @@ 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) + __connman_service_nameserver_append(service, + server, true); + } + return 0; } @@ -562,21 +575,6 @@ int connman_resolver_remove_all(int index) return 0; } -/** - * connman_resolver_flush: - * - * Flush pending resolver requests - */ -void connman_resolver_flush(void) -{ - append_fallback_nameservers(); - - if (dnsproxy_enabled) - __connman_dnsproxy_flush(); - - return; -} - int __connman_resolver_redo_servers(int index) { GSList *list; @@ -613,6 +611,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; } @@ -639,6 +659,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 2bfb0928..d9bed4d2 100644 --- a/src/rfkill.c +++ b/src/rfkill.c @@ -196,7 +196,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; diff --git a/src/rtnl.c b/src/rtnl.c index d1b851fe..a094e257 100644 --- a/src/rtnl.c +++ b/src/rtnl.c @@ -173,7 +173,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; @@ -469,7 +471,9 @@ static void process_newlink(unsigned short type, int index, unsigned flags, if (type == ARPHRD_ETHER) read_uevent(interface); - } 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) { @@ -1372,8 +1376,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 29a632e1..02cd51f2 100644 --- a/src/service.c +++ b/src/service.c @@ -92,6 +92,7 @@ struct connman_service { char **nameservers; char **nameservers_config; char **nameservers_auto; + int nameservers_timeout; char **domains; char *hostname; char *domainname; @@ -100,8 +101,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; @@ -132,7 +138,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; @@ -241,9 +247,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; @@ -314,6 +320,8 @@ static const char *error2string(enum connman_service_error error) return "auth-failed"; case CONNMAN_SERVICE_ERROR_INVALID_KEY: return "invalid-key"; + case CONNMAN_SERVICE_ERROR_BLOCKED: + return "blocked"; } return NULL; @@ -347,6 +355,19 @@ static enum connman_service_proxy_method string2proxymethod(const char *method) return CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN; } +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; @@ -367,8 +388,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: @@ -421,8 +444,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) @@ -473,6 +498,8 @@ static int service_load(struct connman_service *service) connman_network_set_blob(service->network, "WiFi.SSID", ssid, hex_ssid_len / 2); + + g_free(ssid); } g_free(hex_ssid); @@ -863,15 +890,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: @@ -884,8 +908,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: @@ -903,175 +926,212 @@ 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 int nameserver_get_index(struct connman_service *service) +static bool nameserver_available(struct connman_service *service, + enum connman_ipconfig_type type, + const char *ns) { - switch (combine_state(service->state_ipv4, service->state_ipv6)) { - case CONNMAN_SERVICE_STATE_UNKNOWN: - case CONNMAN_SERVICE_STATE_IDLE: - case CONNMAN_SERVICE_STATE_ASSOCIATION: - case CONNMAN_SERVICE_STATE_CONFIGURATION: - case CONNMAN_SERVICE_STATE_FAILURE: - case CONNMAN_SERVICE_STATE_DISCONNECT: - return -1; - case CONNMAN_SERVICE_STATE_READY: - case CONNMAN_SERVICE_STATE_ONLINE: - break; + int family; + + family = connman_inet_check_ipaddress(ns); + + if (family == AF_INET) { + if (type == CONNMAN_IPCONFIG_TYPE_IPV6) + return false; + + return is_connected(service->state_ipv4); } - return __connman_service_get_index(service); + if (family == AF_INET6) { + if (type == CONNMAN_IPCONFIG_TYPE_IPV4) + return false; + + return is_connected(service->state_ipv6); + } + + return false; } -static void remove_nameservers(struct connman_service *service, - int index, char **ns) +static int searchdomain_add_all(struct connman_service *service) { - int i; + int index, i = 0; - if (!ns) - return; + if (!is_connected(service->state)) + return -ENOTCONN; + index = __connman_service_get_index(service); if (index < 0) - index = nameserver_get_index(service); + return -ENXIO; - if (index < 0) - return; + 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; - for (i = 0; ns[i]; i++) - connman_resolver_remove(index, NULL, ns[i]); } -static void remove_searchdomains(struct connman_service *service, - int index, char **sd) +static int searchdomain_remove_all(struct connman_service *service) { - int i; + int index, i = 0; - if (!sd) - return; + if (!is_connected(service->state)) + return -ENOTCONN; + index = __connman_service_get_index(service); if (index < 0) - index = nameserver_get_index(service); + return -ENXIO; - if (index < 0) - return; + while (service->domains && service->domains[i]) { + connman_resolver_remove(index, service->domains[i], NULL); + i++; + } - for (i = 0; sd[i]; i++) - connman_resolver_remove(index, sd[i], NULL); + if (service->domainname) + connman_resolver_remove(index, service->domainname, NULL); + + return 0; } -static bool nameserver_available(struct connman_service *service, char *ns) +static int nameserver_add(struct connman_service *service, + enum connman_ipconfig_type type, + const char *nameserver) { - int family; + int index, ret; - family = connman_inet_check_ipaddress(ns); + if (!nameserver_available(service, type, nameserver)) + return 0; - if (family == AF_INET) - return is_connected_state(service, service->state_ipv4); + index = __connman_service_get_index(service); + if (index < 0) + return -ENXIO; - if (family == AF_INET6) - return is_connected_state(service, service->state_ipv6); + ret = connman_resolver_append(index, NULL, nameserver); + if (ret >= 0) + nameservers_changed(service); - return false; + return ret; } -static void update_nameservers(struct connman_service *service) +static int nameserver_add_all(struct connman_service *service, + enum connman_ipconfig_type type) { - int index; - char *ns; - - index = __connman_service_get_index(service); - if (index < 0) - return; + int i = 0; - switch (combine_state(service->state_ipv4, service->state_ipv6)) { - case CONNMAN_SERVICE_STATE_UNKNOWN: - case CONNMAN_SERVICE_STATE_IDLE: - case CONNMAN_SERVICE_STATE_ASSOCIATION: - case CONNMAN_SERVICE_STATE_CONFIGURATION: - return; - case CONNMAN_SERVICE_STATE_FAILURE: - case CONNMAN_SERVICE_STATE_DISCONNECT: - connman_resolver_remove_all(index); - return; - case CONNMAN_SERVICE_STATE_READY: - case CONNMAN_SERVICE_STATE_ONLINE: - break; + if (service->nameservers_config) { + while (service->nameservers_config[i]) { + nameserver_add(service, type, + service->nameservers_config[i]); + i++; + } + } else if (service->nameservers) { + while (service->nameservers[i]) { + nameserver_add(service, type, + service->nameservers[i]); + i++; + } } - if (service->nameservers_config) { - int i; + if (!i) + __connman_resolver_append_fallback_nameservers(); - remove_nameservers(service, index, service->nameservers); + searchdomain_add_all(service); - i = g_strv_length(service->nameservers_config); - while (i != 0) { - i--; + return 0; +} - ns = service->nameservers_config[i]; +static int nameserver_remove(struct connman_service *service, + enum connman_ipconfig_type type, + const char *nameserver) +{ + int index, ret; - if (nameserver_available(service, ns)) - connman_resolver_append(index, NULL, ns); - } - } else if (service->nameservers) { - int i; + if (!nameserver_available(service, type, nameserver)) + return 0; - remove_nameservers(service, index, service->nameservers); + index = __connman_service_get_index(service); + if (index < 0) + return -ENXIO; - i = g_strv_length(service->nameservers); - while (i != 0) { - i--; + ret = connman_resolver_remove(index, NULL, nameserver); + if (ret >= 0) + nameservers_changed(service); - ns = service->nameservers[i]; + return ret; +} - if (nameserver_available(service, ns)) - connman_resolver_append(index, NULL, ns); - } - } +static int nameserver_remove_all(struct connman_service *service, + enum connman_ipconfig_type type) +{ + int index, i = 0; - if (service->domains) { - char *searchdomains[2] = {NULL, NULL}; - int i; + index = __connman_service_get_index(service); + if (index < 0) + return -ENXIO; - searchdomains[0] = service->domainname; - remove_searchdomains(service, index, searchdomains); + while (service->nameservers_config && service->nameservers_config[i]) { - i = g_strv_length(service->domains); - while (i != 0) { - i--; - connman_resolver_append(index, service->domains[i], - NULL); - } - } else if (service->domainname) - connman_resolver_append(index, service->domainname, NULL); + nameserver_remove(service, type, + service->nameservers_config[i]); + i++; + } + + i = 0; + while (service->nameservers && service->nameservers[i]) { + nameserver_remove(service, type, service->nameservers[i]); + i++; + } + + searchdomain_remove_all(service); - connman_resolver_flush(); + return 0; } /* @@ -1111,18 +1171,19 @@ int __connman_service_nameserver_append(struct connman_service *service, return -ENOMEM; nameservers[len] = g_strdup(nameserver); - if (!nameservers[len]) - return -ENOMEM; - nameservers[len + 1] = NULL; if (is_auto) { service->nameservers_auto = nameservers; } else { service->nameservers = nameservers; - update_nameservers(service); + nameserver_add(service, CONNMAN_IPCONFIG_TYPE_ALL, nameserver); } + nameservers_changed(service); + + searchdomain_add_all(service); + return 0; } @@ -1146,7 +1207,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; @@ -1158,13 +1219,8 @@ int __connman_service_nameserver_remove(struct connman_service *service, len = g_strv_length(nameservers); if (len == 1) { - g_strfreev(nameservers); - if (is_auto) - service->nameservers_auto = NULL; - else - service->nameservers = NULL; - - return 0; + servers = NULL; + goto set_servers; } servers = g_try_new0(char *, len); @@ -1172,15 +1228,17 @@ int __connman_service_nameserver_remove(struct connman_service *service, return -ENOMEM; for (i = 0, j = 0; i < len; i++) { - if (g_strcmp0(nameservers[i], nameserver) != 0) { - servers[j] = g_strdup(nameservers[i]); - if (!servers[j]) - return -ENOMEM; + if (g_strcmp0(nameservers[i], nameserver)) { + servers[j] = nameservers[i]; j++; - } + } else + g_free(nameservers[i]); + + nameservers[i] = NULL; } servers[len - 1] = NULL; +set_servers: g_strfreev(nameservers); nameservers = servers; @@ -1188,7 +1246,8 @@ int __connman_service_nameserver_remove(struct connman_service *service, service->nameservers_auto = nameservers; } else { service->nameservers = nameservers; - update_nameservers(service); + nameserver_remove(service, CONNMAN_IPCONFIG_TYPE_ALL, + nameserver); } return 0; @@ -1196,10 +1255,12 @@ int __connman_service_nameserver_remove(struct connman_service *service, void __connman_service_nameserver_clear(struct connman_service *service) { + nameserver_remove_all(service, CONNMAN_IPCONFIG_TYPE_ALL); + g_strfreev(service->nameservers); service->nameservers = NULL; - update_nameservers(service); + nameserver_add_all(service, CONNMAN_IPCONFIG_TYPE_ALL); } static void add_nameserver_route(int family, int index, char *nameserver, @@ -1312,6 +1373,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) @@ -1409,7 +1482,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; @@ -1598,7 +1671,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) @@ -1609,7 +1682,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) @@ -1643,9 +1716,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, @@ -1657,7 +1730,7 @@ static void append_dns(DBusMessageIter *iter, void *user_data) { struct connman_service *service = user_data; - if (!is_connected(service)) + if (!is_connected(service->state)) return; if (service->nameservers_config) { @@ -1741,8 +1814,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) @@ -1786,7 +1859,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); @@ -1873,7 +1946,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) @@ -1886,11 +1959,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", @@ -1899,8 +1974,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) @@ -1915,6 +1988,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)) @@ -2176,7 +2258,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, @@ -2782,6 +2864,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) { @@ -2796,7 +2953,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; @@ -2863,7 +3020,7 @@ int __connman_service_set_passphrase(struct connman_service *service, service->security != CONNMAN_SERVICE_SECURITY_8021X) return -EINVAL; - err = check_passphrase(service->security, passphrase); + err = __connman_service_check_passphrase(service->security, passphrase); if (err < 0) return err; @@ -2893,8 +3050,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; @@ -2908,6 +3063,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) { @@ -3021,20 +3193,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; } @@ -3046,7 +3222,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; @@ -3123,8 +3299,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); @@ -3134,8 +3309,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) { @@ -3144,6 +3318,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); } @@ -3233,20 +3410,30 @@ static DBusMessage *set_property(DBusConnection *conn, const char *val; dbus_message_iter_get_basic(&entry, &val); dbus_message_iter_next(&entry); - 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); } - remove_nameservers(service, -1, service->nameservers_config); + nameserver_remove_all(service, CONNMAN_IPCONFIG_TYPE_ALL); 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; } @@ -3256,7 +3443,7 @@ static DBusMessage *set_property(DBusConnection *conn, if (gw && strlen(gw)) __connman_service_nameserver_add_routes(service, gw); - update_nameservers(service); + nameserver_add_all(service, CONNMAN_IPCONFIG_TYPE_ALL); dns_configuration_changed(service); if (__connman_service_is_connected_state(service, @@ -3272,8 +3459,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); @@ -3281,35 +3467,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); @@ -3337,23 +3525,29 @@ 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 g_string_append(str, val); } - remove_searchdomains(service, -1, service->domains); + 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); - update_nameservers(service); + searchdomain_add_all(service); domain_configuration_changed(service); domain_changed(service); @@ -3405,11 +3599,12 @@ static DBusMessage *set_property(DBusConnection *conn, &state); if (err < 0) { - if (is_connected_state(service, state) || - is_connecting_state(service, state)) { - __connman_network_enable_ipconfig(service->network, + if (is_connected(state) || is_connecting(state)) { + if (type == CONNMAN_IPCONFIG_TYPE_IPV4) + __connman_network_enable_ipconfig(service->network, service->ipconfig_ipv4); - __connman_network_enable_ipconfig(service->network, + else + __connman_network_enable_ipconfig(service->network, service->ipconfig_ipv6); } @@ -3421,11 +3616,14 @@ static DBusMessage *set_property(DBusConnection *conn, else ipv6_configuration_changed(service); - if (is_connecting(service) || is_connected(service)) { - __connman_network_enable_ipconfig(service->network, - service->ipconfig_ipv4); - __connman_network_enable_ipconfig(service->network, - service->ipconfig_ipv6); + if (is_connecting(service->state) || + is_connected(service->state)) { + if (type == CONNMAN_IPCONFIG_TYPE_IPV4) + __connman_network_enable_ipconfig(service->network, + service->ipconfig_ipv4); + else + __connman_network_enable_ipconfig(service->network, + service->ipconfig_ipv6); } service_save(service); @@ -3461,6 +3659,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 DBusMessage *clear_property(DBusConnection *conn, DBusMessage *msg, void *user_data) { @@ -3475,8 +3708,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); @@ -3531,6 +3764,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) @@ -3543,7 +3777,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) { @@ -3615,7 +3849,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 == @@ -3636,6 +3870,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) @@ -3660,10 +3931,10 @@ static bool auto_connect_service(GList *services, } 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; @@ -3685,7 +3956,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; @@ -3696,7 +3967,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; @@ -3738,7 +4009,7 @@ void __connman_service_auto_connect(enum connman_service_connect_reason reason) if (!__connman_session_policy_autoconnect(reason)) return; - autoconnect_timeout = g_timeout_add_seconds(0, run_auto_connect, + autoconnect_timeout = g_idle_add(run_auto_connect, GUINT_TO_POINTER(reason)); } @@ -3755,7 +4026,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; @@ -3791,31 +4063,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 @@ -3892,9 +4140,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) { @@ -3941,7 +4186,7 @@ static DBusMessage *connect_service(DBusConnection *conn, for (list = service_list; list; list = list->next) { struct connman_service *temp = list->data; - if (!is_connecting(temp) && !is_connected(temp)) + if (!is_connecting(temp->state) && !is_connected(temp->state)) break; if (service == temp) @@ -4006,8 +4251,7 @@ bool __connman_service_remove(struct connman_service *service) __connman_provider_is_immutable(service->provider)) return false; - if (!service->favorite && service->state != - CONNMAN_SERVICE_STATE_FAILURE) + if (!service->favorite && !is_idle(service->state)) return false; __connman_service_disconnect(service); @@ -4018,6 +4262,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; @@ -4154,11 +4413,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); @@ -4362,10 +4621,8 @@ static void service_schedule_removed(struct connman_service *service) static bool allow_property_changed(struct connman_service *service) { 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; } @@ -4410,6 +4667,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); @@ -4468,8 +4730,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); @@ -4629,8 +4896,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) @@ -4655,9 +4922,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; } @@ -4668,6 +4935,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; @@ -4715,6 +4996,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 @@ -4802,14 +5089,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; @@ -4941,9 +5226,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); @@ -4963,43 +5263,14 @@ void __connman_service_set_string(struct connman_service *service, void __connman_service_set_search_domains(struct connman_service *service, char **domains) { - int index; - - index = __connman_service_get_index(service); - if (index < 0) - return; + searchdomain_remove_all(service); - if (service->domains) { - remove_searchdomains(service, index, service->domains); + if (service->domains) g_strfreev(service->domains); - service->domains = g_strdupv(domains); - - update_nameservers(service); - } -} - -/* - * This variant is used when domain search list is updated via - * dhcp and in that case the service is not yet fully connected so - * we cannot do same things as what the set() variant is doing. - */ -void __connman_service_update_search_domains(struct connman_service *service, - char **domains) -{ - g_strfreev(service->domains); service->domains = g_strdupv(domains); -} - -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); + searchdomain_add_all(service); } static void report_error_cb(void *user_context, bool retry, @@ -5074,7 +5345,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) @@ -5155,7 +5427,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) @@ -5206,7 +5478,7 @@ static void single_connected_tech(struct connman_service *allowed) for (iter = service_list; iter; iter = iter->next) { service = iter->data; - if (!is_connected(service)) + if (!is_connected(service->state)) break; if (service == allowed) @@ -5268,9 +5540,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(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: @@ -5329,16 +5607,6 @@ static int service_indicate_state(struct connman_service *service) reply_pending(service, 0); - g_get_current_time(&service->modified); - service_save(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")) { @@ -5353,6 +5621,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( @@ -5387,7 +5664,6 @@ static int service_indicate_state(struct connman_service *service) __connman_wpad_stop(service); - dns_changed(service); domain_changed(service); proxy_changed(service); @@ -5491,7 +5767,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 @@ -5676,6 +5952,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; @@ -5688,20 +5984,23 @@ 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 (type == CONNMAN_IPCONFIG_TYPE_IPV4) { - check_proxy_setup(service); + if (connman_setting_get_bool("EnableOnlineCheck")) + if (type == CONNMAN_IPCONFIG_TYPE_IPV4) { + check_proxy_setup(service); + } 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; @@ -5713,35 +6012,26 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service, service_rp_filter(service, false); break; - case CONNMAN_SERVICE_STATE_FAILURE: - break; - } - /* 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_SERVICE_STATE_IDLE: + case CONNMAN_SERVICE_STATE_FAILURE: + __connman_ipconfig_disable(ipconfig); - case CONNMAN_IPCONFIG_METHOD_FIXED: - case CONNMAN_IPCONFIG_METHOD_MANUAL: - case CONNMAN_IPCONFIG_METHOD_DHCP: - case CONNMAN_IPCONFIG_METHOD_AUTO: break; - } + if (is_connected(old_state) && !is_connected(new_state)) + nameserver_remove_all(service, type); + if (type == CONNMAN_IPCONFIG_TYPE_IPV4) service->state_ipv4 = new_state; else service->state_ipv6 = new_state; - update_nameservers(service); + if (!is_connected(old_state) && is_connected(new_state)) + nameserver_add_all(service, type); + + __connman_timeserver_sync(service); return service_indicate_state(service); } @@ -5787,10 +6077,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", @@ -5903,22 +6214,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); } } @@ -5936,10 +6247,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) { @@ -5965,6 +6276,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) @@ -5987,6 +6300,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 @@ -6001,7 +6315,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; @@ -6072,7 +6386,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); @@ -6102,6 +6416,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; @@ -6252,12 +6571,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); } @@ -6284,6 +6597,7 @@ static void service_ip_bound(struct connman_ipconfig *ipconfig, CONNMAN_IPCONFIG_TYPE_IPV6); settings_changed(service, ipconfig); + address_updated(service, type); } static void service_ip_release(struct connman_ipconfig *ipconfig, @@ -6536,12 +6850,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); @@ -6594,10 +6902,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"); @@ -6729,6 +7037,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); @@ -6761,7 +7070,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; } @@ -7017,6 +7325,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, @@ -7060,9 +7370,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 100644 --- 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 100644 --- 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 7d031303..5e877ef1 100644 --- a/src/storage.c +++ b/src/storage.c @@ -43,8 +43,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)) { diff --git a/src/task.c b/src/task.c index 8b9e1d93..953cc409 100644 --- a/src/task.c +++ b/src/task.c @@ -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 55303a0c..d2f0ae2b 100644 --- a/src/technology.c +++ b/src/technology.c @@ -211,22 +211,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, @@ -265,10 +270,8 @@ static int set_tethering(struct connman_technology *technology, if (result == -EINPROGRESS) continue; - if (err == -EINPROGRESS || err == 0) { + if (err == -EINPROGRESS || err == 0) result = err; - continue; - } } return result; @@ -444,15 +447,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); @@ -841,7 +860,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); @@ -923,7 +942,9 @@ 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) + err = __connman_service_check_passphrase(CONNMAN_SERVICE_SECURITY_PSK, + str); + if (err < 0) return __connman_error_passphrase_required(msg); if (g_strcmp0(technology->tethering_passphrase, str) != 0) { @@ -1056,6 +1077,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); technology->scan_pending = g_slist_prepend(technology->scan_pending, msg); @@ -1145,6 +1170,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 ceeec746..c929ba71 100644 --- a/src/tethering.c +++ b/src/tethering.c @@ -181,7 +181,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; @@ -197,12 +197,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); @@ -212,7 +212,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); @@ -228,7 +228,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"); @@ -264,7 +264,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); @@ -276,7 +276,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); @@ -285,6 +285,8 @@ void __connman_tethering_set_enabled(void) strerror(-err)); DBG("tethering started"); + + return 0; } void __connman_tethering_set_disabled(void) @@ -505,6 +507,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 f0d33e5e..0e555a73 100644 --- a/src/timeserver.c +++ b/src/timeserver.c @@ -291,6 +291,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; @@ -314,6 +316,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); diff --git a/src/util.c b/src/util.c index da32cc55..732d4512 100644 --- a/src/util.c +++ b/src/util.c @@ -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 ef4bdaba..03b38bb8 100644 --- 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); @@ -906,8 +906,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: diff --git a/src/wpad.c b/src/wpad.c index d40959be..f066feee 100644 --- a/src/wpad.c +++ b/src/wpad.c @@ -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); @@ -172,10 +173,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; @@ -194,8 +196,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) -- cgit v1.2.3