summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/6to4.c2
-rw-r--r--src/agent-connman.c99
-rw-r--r--src/agent.c17
-rw-r--r--src/backtrace.c138
-rw-r--r--src/bridge.c2
-rw-r--r--src/config.c148
-rw-r--r--src/connection.c93
-rw-r--r--src/connman-wait-online.service.in15
-rw-r--r--src/connman.h54
-rw-r--r--src/connman.service.in13
-rw-r--r--src/connmand-wait-online.c461
-rw-r--r--src/device.c48
-rw-r--r--src/dhcp.c110
-rw-r--r--src/dhcpv6.c31
-rw-r--r--src/dnsproxy.c303
-rw-r--r--src/firewall-iptables.c (renamed from src/firewall.c)265
-rw-r--r--src/firewall-nftables.c1133
-rw-r--r--src/inet.c367
-rw-r--r--src/ipconfig.c96
-rw-r--r--src/ippool.c45
-rw-r--r--src/iptables.c96
-rw-r--r--src/log.c105
-rw-r--r--src/main.c102
-rw-r--r--src/main.conf26
-rw-r--r--src/nat.c64
-rw-r--r--src/network.c148
-rw-r--r--src/ntp.c202
-rw-r--r--src/peer.c9
-rw-r--r--src/peer_service.c3
-rw-r--r--src/provider.c15
-rw-r--r--src/proxy.c2
-rw-r--r--src/resolver.c98
-rw-r--r--src/rfkill.c2
-rw-r--r--src/rtnl.c10
-rw-r--r--src/service.c1057
-rw-r--r--src/session.c427
-rw-r--r--src/stats.c50
-rw-r--r--src/storage.c2
-rw-r--r--src/task.c3
-rw-r--r--src/technology.c62
-rw-r--r--src/tethering.c18
-rw-r--r--src/timeserver.c13
-rw-r--r--src/util.c15
-rw-r--r--src/wispr.c7
-rw-r--r--src/wpad.c9
45 files changed, 4610 insertions, 1375 deletions
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 <yann.morin.1998@free.fr>. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <execinfo.h>
+#include <dlfcn.h>
+
+#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 <connman/backtrace.h>
+
#include <connman/option.h>
#include <connman/setting.h>
@@ -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 <connman/resolver.h>
int __connman_resolver_init(gboolean dnsproxy);
void __connman_resolver_cleanup(void);
+void __connman_resolver_append_fallback_nameservers(void);
int __connman_resolvfile_append(int index, const char *domain, const char *server);
int __connman_resolvfile_remove(int index, const char *domain, const char *server);
int __connman_resolver_redo_servers(int index);
@@ -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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <dbus/dbus.h>
+
+#include <gdbus.h>
+#include <connman/dbus.h>
+
+static DBusConnection *connection;
+static GMainLoop *main_loop;
+static int timeout = 0;
+static int exit_value = 0;
+
+static gboolean option_version = FALSE;
+static gchar *option_interface = NULL;
+static gchar *option_ignore = NULL;
+static gint option_timeout = 120;
+
+struct devices {
+ char **interface;
+ char **ignore;
+};
+
+static GOptionEntry options[] = {
+ { "interface", 'i', 0, G_OPTION_ARG_STRING, &option_interface,
+ "Specify networking device or interface", "DEV" },
+ { "ignore", 'I', 0, G_OPTION_ARG_STRING, &option_ignore,
+ "Specify networking device or interface to ignore", "DEV" },
+ { "timeout", 0, 0, G_OPTION_ARG_INT, &option_timeout,
+ "Time to wait for network going online. Default is 120 seconds.",
+ "seconds" },
+ { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
+ "Show version information and exit" },
+ { NULL },
+};
+
+static bool compare_interface(const char *interface, struct devices *devices)
+{
+ int i;
+
+ if (!interface || !devices)
+ return false;
+
+ for (i = 0; devices->ignore && devices->ignore[i]; i++)
+ if (!strcmp(interface, devices->ignore[i]))
+ return false;
+
+ if (!devices->interface)
+ return true;
+
+ for (i = 0; devices->interface[i]; i++)
+ if (!strcmp(interface, devices->interface[i]))
+ return true;
+
+ return false;
+}
+
+static bool state_online(DBusMessageIter *iter)
+{
+ char *str;
+ DBusMessageIter variant;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+ return false;
+
+ dbus_message_iter_get_basic(iter, &str);
+ if (strcmp(str, "State"))
+ return false;
+
+ dbus_message_iter_next(iter);
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT)
+ return false;
+
+ dbus_message_iter_recurse(iter, &variant);
+
+ if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_STRING)
+ return false;
+
+ dbus_message_iter_get_basic(&variant, &str);
+ if (strcmp(str, "ready") && strcmp(str, "online"))
+ return false;
+
+ return true;
+}
+
+static bool service_properties_online(DBusMessageIter *array_entry,
+ struct devices *devices)
+{
+ bool interface = !devices;
+ bool state = false;
+ DBusMessageIter dict, dict_entry, variant, eth_array, eth_dict,
+ eth_variant;
+ char *str;
+
+ for (dbus_message_iter_recurse(array_entry, &dict);
+ dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY;
+ dbus_message_iter_next(&dict)) {
+
+ dbus_message_iter_recurse(&dict, &dict_entry);
+ if (dbus_message_iter_get_arg_type(&dict_entry)
+ != DBUS_TYPE_STRING)
+ continue;
+
+ if (state_online(&dict_entry)) {
+ state = true;
+ continue;
+ }
+
+ dbus_message_iter_recurse(&dict, &dict_entry);
+
+ dbus_message_iter_get_basic(&dict_entry, &str);
+
+ if (devices && !strcmp(str, "Ethernet")) {
+ dbus_message_iter_next(&dict_entry);
+
+ if (dbus_message_iter_get_arg_type(&dict_entry)
+ != DBUS_TYPE_VARIANT)
+ break;
+
+ dbus_message_iter_recurse(&dict_entry, &variant);
+ if (dbus_message_iter_get_arg_type(&variant)
+ != DBUS_TYPE_ARRAY)
+ break;
+
+ for (dbus_message_iter_recurse(&variant, &eth_array);
+ dbus_message_iter_get_arg_type(&eth_array)
+ == DBUS_TYPE_DICT_ENTRY;
+ dbus_message_iter_next(&eth_array)) {
+
+ dbus_message_iter_recurse(&eth_array, &eth_dict);
+
+ if (dbus_message_iter_get_arg_type(&eth_dict)
+ != DBUS_TYPE_STRING)
+ continue;
+
+ dbus_message_iter_get_basic(&eth_dict, &str);
+ if (!strcmp(str, "Interface")) {
+
+ dbus_message_iter_next(&eth_dict);
+ if (dbus_message_iter_get_arg_type(&eth_dict)
+ != DBUS_TYPE_VARIANT)
+ break;
+
+ dbus_message_iter_recurse(&eth_dict,
+ &eth_variant);
+ if (dbus_message_iter_get_arg_type(&eth_variant)
+ != DBUS_TYPE_STRING)
+ break;
+
+ dbus_message_iter_get_basic(&eth_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)) {
@@ -751,37 +747,6 @@ int connman_device_set_scanning(struct connman_device *device,
}
/**
- * connman_device_set_disconnected:
- * @device: device structure
- * @disconnected: disconnected state
- *
- * Change disconnected state of device (only for device with networks)
- */
-int connman_device_set_disconnected(struct connman_device *device,
- bool disconnected)
-{
- DBG("device %p disconnected %d", device, disconnected);
-
- if (device->disconnected == disconnected)
- return -EALREADY;
-
- device->disconnected = disconnected;
-
- return 0;
-}
-
-/**
- * connman_device_get_disconnected:
- * @device: device structure
- *
- * Get device disconnected state
- */
-bool connman_device_get_disconnected(struct connman_device *device)
-{
- return device->disconnected;
-}
-
-/**
* connman_device_set_string:
* @device: device structure
* @key: unique identifier
@@ -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 <errno.h>
#include <string.h>
#include <stdlib.h>
+#include <net/ethernet.h>
+
+#ifndef IPV6_MIN_MTU
+#define IPV6_MIN_MTU 1280
+#endif
#include <connman/ipconfig.h>
#include <include/setting.h>
@@ -54,10 +59,11 @@ struct connman_dhcp {
GDHCPClient *dhcp_client;
char *ipv4ll_debug_prefix;
char *dhcp_debug_prefix;
+
+ bool ipv4ll_running;
};
static GHashTable *ipconfig_table;
-static bool ipv4ll_running;
static void dhcp_free(struct connman_dhcp *dhcp)
{
@@ -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.c b/src/firewall-iptables.c
index 90c3d3c1..45943a82 100644
--- a/src/firewall.c
+++ b/src/firewall-iptables.c
@@ -2,7 +2,7 @@
*
* Connection Manager
*
- * Copyright (C) 2013 BMW Car IT GmbH.
+ * Copyright (C) 2013,2015 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -46,6 +46,7 @@ struct connman_managed_table {
};
struct fw_rule {
+ bool enabled;
char *table;
char *chain;
char *rule_spec;
@@ -56,8 +57,8 @@ struct firewall_context {
};
static GSList *managed_tables;
-
-static bool firewall_is_up;
+static struct firewall_context *connmark_ctx;
+static unsigned int connmark_ref;
static int chain_to_index(const char *chain_name)
{
@@ -267,7 +268,55 @@ void __connman_firewall_destroy(struct firewall_context *ctx)
g_free(ctx);
}
-int __connman_firewall_add_rule(struct firewall_context *ctx,
+static int enable_rule(struct fw_rule *rule)
+{
+ int err;
+
+ if (rule->enabled)
+ return -EALREADY;
+
+ DBG("%s %s %s", rule->table, rule->chain, rule->rule_spec);
+
+ err = insert_managed_rule(rule->table, rule->chain, rule->rule_spec);
+ if (err < 0)
+ return err;
+
+ err = __connman_iptables_commit(rule->table);
+ if (err < 0)
+ return err;
+
+ rule->enabled = true;
+
+ return 0;
+}
+
+static int disable_rule(struct fw_rule *rule)
+{
+ int err;
+
+ if (!rule->enabled)
+ return -EALREADY;
+
+ err = delete_managed_rule(rule->table, rule->chain, rule->rule_spec);
+ if (err < 0) {
+ connman_error("Cannot remove previously installed "
+ "iptables rules: %s", strerror(-err));
+ return err;
+ }
+
+ err = __connman_iptables_commit(rule->table);
+ if (err < 0) {
+ connman_error("Cannot remove previously installed "
+ "iptables rules: %s", strerror(-err));
+ return err;
+ }
+
+ rule->enabled = false;
+
+ return 0;
+}
+
+static void firewall_add_rule(struct firewall_context *ctx,
const char *table,
const char *chain,
const char *rule_fmt, ...)
@@ -284,85 +333,208 @@ int __connman_firewall_add_rule(struct firewall_context *ctx,
rule = g_new0(struct fw_rule, 1);
+ rule->enabled = false;
rule->table = g_strdup(table);
rule->chain = g_strdup(chain);
rule->rule_spec = rule_spec;
ctx->rules = g_list_append(ctx->rules, rule);
-
- return 0;
}
-static int firewall_disable(GList *rules)
+static void firewall_remove_rules(struct firewall_context *ctx)
{
struct fw_rule *rule;
GList *list;
- int err;
- for (list = rules; list; list = g_list_previous(list)) {
+ for (list = g_list_last(ctx->rules); list;
+ list = g_list_previous(list)) {
rule = list->data;
- err = delete_managed_rule(rule->table,
- rule->chain, rule->rule_spec);
- if (err < 0) {
- connman_error("Cannot remove previously installed "
- "iptables rules: %s", strerror(-err));
- return err;
- }
+ ctx->rules = g_list_remove(ctx->rules, rule);
+ cleanup_fw_rule(rule);
+ }
+}
- err = __connman_iptables_commit(rule->table);
- if (err < 0) {
- connman_error("Cannot remove previously installed "
- "iptables rules: %s", strerror(-err));
- return err;
- }
+static int firewall_enable_rules(struct firewall_context *ctx)
+{
+ struct fw_rule *rule;
+ GList *list;
+ int err = -ENOENT;
+
+ for (list = g_list_first(ctx->rules); list; list = g_list_next(list)) {
+ rule = list->data;
+
+ err = enable_rule(rule);
+ if (err < 0)
+ break;
}
- return 0;
+ return err;
}
-int __connman_firewall_enable(struct firewall_context *ctx)
+static int firewall_disable_rules(struct firewall_context *ctx)
{
struct fw_rule *rule;
GList *list;
- int err;
+ int e;
+ int err = -ENOENT;
- for (list = g_list_first(ctx->rules); list;
- list = g_list_next(list)) {
+ for (list = g_list_last(ctx->rules); list;
+ list = g_list_previous(list)) {
rule = list->data;
- DBG("%s %s %s", rule->table, rule->chain, rule->rule_spec);
+ e = disable_rule(rule);
- err = insert_managed_rule(rule->table,
- rule->chain, rule->rule_spec);
- if (err < 0)
- goto err;
-
- err = __connman_iptables_commit(rule->table);
- if (err < 0)
- goto err;
+ /* Report last error back */
+ if (e == 0 && err == -ENOENT)
+ err = 0;
+ else if (e < 0)
+ err = e;
}
- firewall_is_up = true;
+ return err;
+}
+
+int __connman_firewall_enable_nat(struct firewall_context *ctx,
+ char *address, unsigned char prefixlen,
+ char *interface)
+{
+ char *cmd;
+ int err;
+ cmd = g_strdup_printf("-s %s/%d -o %s -j MASQUERADE",
+ address, prefixlen, interface);
+
+ firewall_add_rule(ctx, "nat", "POSTROUTING", cmd);
+ g_free(cmd);
+ err = firewall_enable_rules(ctx);
+ if (err)
+ firewall_remove_rules(ctx);
+ return err;
+}
+
+int __connman_firewall_disable_nat(struct firewall_context *ctx)
+{
+ int err;
+
+ err = firewall_disable_rules(ctx);
+ if (err < 0) {
+ DBG("could not disable NAT rule");
+ return err;
+ }
+
+ firewall_remove_rules(ctx);
return 0;
+}
-err:
- connman_warn("Failed to install iptables rules: %s", strerror(-err));
+int __connman_firewall_enable_snat(struct firewall_context *ctx,
+ int index, const char *ifname,
+ const char *addr)
+{
+ int err;
- firewall_disable(g_list_previous(list));
+ firewall_add_rule(ctx, "nat", "POSTROUTING",
+ "-o %s -j SNAT --to-source %s",
+ ifname, addr);
+ err = firewall_enable_rules(ctx);
+ if (err)
+ firewall_remove_rules(ctx);
return err;
}
-int __connman_firewall_disable(struct firewall_context *ctx)
+int __connman_firewall_disable_snat(struct firewall_context *ctx)
+{
+ int err;
+
+ err = firewall_disable_rules(ctx);
+ if (err < 0) {
+ DBG("could not disable SNAT rule");
+ return err;
+ }
+
+ firewall_remove_rules(ctx);
+ return 0;
+}
+
+static int firewall_enable_connmark(void)
{
- return firewall_disable(g_list_last(ctx->rules));
+ int err;
+
+ if (connmark_ref > 0) {
+ connmark_ref++;
+ return 0;
+ }
+
+ connmark_ctx = __connman_firewall_create();
+
+ firewall_add_rule(connmark_ctx, "mangle", "INPUT",
+ "-j CONNMARK --restore-mark");
+ firewall_add_rule(connmark_ctx, "mangle", "POSTROUTING",
+ "-j CONNMARK --save-mark");
+ err = firewall_enable_rules(connmark_ctx);
+ if (err) {
+ __connman_firewall_destroy(connmark_ctx);
+ connmark_ctx = NULL;
+ return err;
+ }
+ connmark_ref++;
+ return 0;
}
-bool __connman_firewall_is_up(void)
+static void firewall_disable_connmark(void)
{
- return firewall_is_up;
+ connmark_ref--;
+ if (connmark_ref > 0)
+ return;
+
+ firewall_disable_rules(connmark_ctx);
+ __connman_firewall_destroy(connmark_ctx);
+ connmark_ctx = NULL;
+}
+
+int __connman_firewall_enable_marking(struct firewall_context *ctx,
+ enum connman_session_id_type id_type,
+ char *id, const char *src_ip,
+ uint32_t mark)
+{
+ int err;
+
+ err = firewall_enable_connmark();
+ if (err)
+ return err;
+
+ switch (id_type) {
+ case CONNMAN_SESSION_ID_TYPE_UID:
+ firewall_add_rule(ctx, "mangle", "OUTPUT",
+ "-m owner --uid-owner %s -j MARK --set-mark %d",
+ id, mark);
+ break;
+ case CONNMAN_SESSION_ID_TYPE_GID:
+ firewall_add_rule(ctx, "mangle", "OUTPUT",
+ "-m owner --gid-owner %s -j MARK --set-mark %d",
+ id, mark);
+ break;
+ case CONNMAN_SESSION_ID_TYPE_UNKNOWN:
+ break;
+ case CONNMAN_SESSION_ID_TYPE_LSM:
+ default:
+ return -EINVAL;
+ }
+
+ if (src_ip) {
+ firewall_add_rule(ctx, "mangle", "OUTPUT",
+ "-s %s -j MARK --set-mark %d",
+ src_ip, mark);
+ }
+
+ return firewall_enable_rules(ctx);
+}
+
+int __connman_firewall_disable_marking(struct firewall_context *ctx)
+{
+ firewall_disable_connmark();
+ return firewall_disable_rules(ctx);
}
static void iterate_chains_cb(const char *chain_name, void *user_data)
@@ -432,12 +604,9 @@ static void flush_all_tables(void)
if (!g_file_test("/proc/net/ip_tables_names",
G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
- firewall_is_up = false;
return;
}
- firewall_is_up = true;
-
flush_table("filter");
flush_table("mangle");
flush_table("nat");
@@ -447,6 +616,7 @@ int __connman_firewall_init(void)
{
DBG("");
+ __connman_iptables_init();
flush_all_tables();
return 0;
@@ -457,4 +627,5 @@ void __connman_firewall_cleanup(void)
DBG("");
g_slist_free_full(managed_tables, cleanup_managed_table);
+ __connman_iptables_cleanup();
}
diff --git a/src/firewall-nftables.c b/src/firewall-nftables.c
new file mode 100644
index 00000000..1febce44
--- /dev/null
+++ b/src/firewall-nftables.c
@@ -0,0 +1,1133 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2016 BMW Car IT GmbH.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+/*
+ * This file is based on the libnftnl examples:
+ * https://git.netfilter.org/libnftnl/tree/examples
+ * by Pablo Neira Ayuso. and inspiration from systemd nft implemention
+ * https://github.com/zonque/systemd/blob/rfc-nftnl/src/shared/firewall-util.c
+ * by Daniel Mack.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <errno.h>
+#include <stdint.h>
+#include <alloca.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+#include <sys/types.h>
+#include <pwd.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_nat.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <libmnl/libmnl.h>
+#include <libnftnl/table.h>
+#include <libnftnl/chain.h>
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+
+#include <glib.h>
+
+#include "connman.h"
+
+#define CONNMAN_TABLE "connman"
+#define CONNMAN_CHAIN_NAT_PRE "nat-prerouting"
+#define CONNMAN_CHAIN_NAT_POST "nat-postrouting"
+#define CONNMAN_CHAIN_ROUTE_OUTPUT "route-output"
+
+static bool debug_enabled = true;
+
+struct firewall_handle {
+ uint64_t handle;
+ const char *chain;
+};
+
+struct firewall_context {
+ struct firewall_handle rule;
+};
+
+struct nftables_info {
+ struct firewall_handle ct;
+};
+
+static struct nftables_info *nft_info;
+
+enum callback_return_type {
+ CALLBACK_RETURN_NONE = 0,
+ CALLBACK_RETURN_HANDLE,
+ CALLBACK_RETURN_BYTE_COUNTER,
+ _CALLBACK_RETURN_MAX,
+};
+
+struct callback_data {
+ enum callback_return_type type;
+ uint64_t value;
+ bool success;
+};
+
+static void debug_netlink_dump_rule(struct nftnl_rule *nlr)
+{
+ char buf[4096];
+
+ if (!debug_enabled)
+ return;
+
+ nftnl_rule_snprintf(buf, sizeof(buf), nlr, 0, 0);
+ fprintf(stdout, "%s\n", buf);
+}
+
+static void debug_mnl_dump_rule(const void *req, size_t req_size)
+{
+ if (!debug_enabled)
+ return;
+
+ mnl_nlmsg_fprintf(stdout, req, req_size, 0);
+ printf("\n");
+}
+
+static int rule_expr_cb(struct nftnl_expr *expr, void *data) {
+
+ struct callback_data *cb = data;
+ const char *name;
+
+ name = nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
+
+ if (strcmp(name, "counter")) {
+ cb->value = nftnl_expr_get_u64(expr, NFTNL_EXPR_CTR_BYTES);
+ cb->success = true;
+ }
+
+ return 0;
+}
+
+static int rule_cb(const struct nlmsghdr *nlh, int event,
+ struct callback_data *cb)
+{
+ struct nftnl_rule *rule;
+
+ rule = nftnl_rule_alloc();
+ if (!rule)
+ return MNL_CB_OK;
+
+ if (nftnl_rule_nlmsg_parse(nlh, rule) < 0)
+ goto out;
+
+ switch (cb->type) {
+ case CALLBACK_RETURN_HANDLE:
+ cb->value = nftnl_rule_get_u64(rule, NFTNL_RULE_HANDLE);
+ cb->success = true;
+ break;
+
+ case CALLBACK_RETURN_BYTE_COUNTER:
+ nftnl_expr_foreach(rule, rule_expr_cb, cb);
+ break;
+
+ default:
+ DBG("unhandled callback type %d\n", cb->type);
+ break;
+ }
+
+out:
+ nftnl_rule_free(rule);
+ return MNL_CB_STOP;
+}
+
+static int chain_cb(const struct nlmsghdr *nlh, int event,
+ struct callback_data *cb)
+{
+ struct nftnl_chain *chain;
+
+ chain = nftnl_chain_alloc();
+ if (!chain)
+ return MNL_CB_OK;
+
+ if (nftnl_chain_nlmsg_parse(nlh, chain) < 0)
+ goto out;
+
+ switch (cb->type) {
+ case CALLBACK_RETURN_HANDLE:
+ cb->value = nftnl_chain_get_u64(chain, NFTNL_CHAIN_HANDLE);
+ cb->success = true;
+ break;
+
+ default:
+ DBG("unhandled callback type %d\n", cb->type);
+ break;
+ }
+
+out:
+ nftnl_chain_free(chain);
+ return MNL_CB_OK;
+}
+
+static const char *event_to_str(enum nf_tables_msg_types type)
+{
+ const char *table[] = {
+ "NFT_MSG_NEWTABLE",
+ "NFT_MSG_GETTABLE",
+ "NFT_MSG_DELTABLE",
+ "NFT_MSG_NEWCHAIN",
+ "NFT_MSG_GETCHAIN",
+ "NFT_MSG_DELCHAIN",
+ "NFT_MSG_NEWRULE",
+ "NFT_MSG_GETRULE",
+ "NFT_MSG_DELRULE",
+ "NFT_MSG_NEWSET",
+ "NFT_MSG_GETSET",
+ "NFT_MSG_DELSET",
+ "NFT_MSG_NEWSETELEM",
+ "NFT_MSG_GETSETELEM",
+ "NFT_MSG_DELSETELEM",
+ "NFT_MSG_NEWGEN",
+ "NFT_MSG_GETGEN",
+ "NFT_MSG_TRACE"
+ };
+
+ if (type < sizeof(table)/sizeof(table[0]))
+ return table[type];
+
+ return "unknown";
+}
+
+static int events_cb(const struct nlmsghdr *nlh, void *data)
+{
+ int event = NFNL_MSG_TYPE(nlh->nlmsg_type);
+ struct callback_data *cb = data;
+ int err = MNL_CB_OK;
+
+ if (!cb || cb->type == CALLBACK_RETURN_NONE)
+ return err;
+
+ DBG("handle event %s", event_to_str(event));
+
+ switch(event) {
+ case NFT_MSG_NEWCHAIN:
+ err = chain_cb(nlh, event, cb);
+ break;
+
+ case NFT_MSG_NEWRULE:
+ err = rule_cb(nlh, event, cb);
+ break;
+ default:
+ DBG("unhandled event type %s", event_to_str(event));
+ break;
+ }
+
+ return err;
+}
+
+static int socket_open_and_bind(struct mnl_socket **n)
+{
+
+ struct mnl_socket *nl = NULL;
+ int err;
+
+ nl = mnl_socket_open(NETLINK_NETFILTER);
+ if (!nl)
+ return -errno;
+
+ err = mnl_socket_bind(nl, 1 << (NFNLGRP_NFTABLES-1),
+ MNL_SOCKET_AUTOPID);
+ if (err < 0) {
+ err = errno;
+ mnl_socket_close(nl);
+ return -err;
+ }
+
+ *n = nl;
+ return 0;
+}
+
+static int send_and_dispatch(struct mnl_socket *nl, const void *req,
+ size_t req_size, enum callback_return_type callback_type,
+ uint64_t *callback_value)
+{
+ struct callback_data cb = {};
+ uint32_t portid;
+ int err;
+
+ debug_mnl_dump_rule(req, req_size);
+
+ err = mnl_socket_sendto(nl, req, req_size);
+ if (err < 0)
+ return -errno;
+
+ portid = mnl_socket_get_portid(nl);
+ cb.type = callback_type;
+
+ for (;;) {
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+
+ err = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ if (err <= 0)
+ break;
+
+ err = mnl_cb_run(buf, err, 0, portid, events_cb, &cb);
+ if (err <= 0)
+ break;
+ }
+
+ if (err < 0)
+ return -errno;
+
+ if (callback_type == CALLBACK_RETURN_NONE)
+ return 0;
+
+ if (cb.success) {
+ if (callback_value)
+ *callback_value = cb.value;
+
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+static void put_batch_headers(char *buf, uint16_t type, uint32_t seq)
+{
+
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfg;
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = type;
+ nlh->nlmsg_flags = NLM_F_REQUEST;
+ nlh->nlmsg_seq = seq;
+
+ nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+ nfg->nfgen_family = AF_INET;
+ nfg->version = NFNETLINK_V0;
+ nfg->res_id = NFNL_SUBSYS_NFTABLES;
+}
+
+static int add_payload(struct nftnl_rule *rule, uint32_t base,
+ uint32_t dreg, uint32_t offset, uint32_t len)
+{
+ struct nftnl_expr *expr;
+
+ expr = nftnl_expr_alloc("payload");
+ if (!expr)
+ return -ENOMEM;
+
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_BASE, base);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_DREG, dreg);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_OFFSET, offset);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_LEN, len);
+
+ nftnl_rule_add_expr(rule, expr);
+
+ return 0;
+}
+
+static int add_bitwise(struct nftnl_rule *rule, int reg, const void *mask,
+ size_t len)
+{
+ struct nftnl_expr *expr;
+ uint8_t *xor;
+
+ expr = nftnl_expr_alloc("bitwise");
+ if (!expr)
+ return -ENOMEM;
+
+ xor = alloca(len);
+ memset(xor, 0, len);
+
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_SREG, reg);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_DREG, reg);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_LEN, len);
+ nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_MASK, mask, len);
+ nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_XOR, xor, len);
+
+ nftnl_rule_add_expr(rule, expr);
+
+ return 0;
+}
+
+static int add_cmp(struct nftnl_rule *rule, uint32_t sreg, uint32_t op,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr *expr;
+
+ expr = nftnl_expr_alloc("cmp");
+ if (!expr)
+ return -ENOMEM;
+
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_CMP_SREG, sreg);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_CMP_OP, op);
+ nftnl_expr_set(expr, NFTNL_EXPR_CMP_DATA, data, data_len);
+
+ nftnl_rule_add_expr(rule, expr);
+
+ return 0;
+}
+
+static int table_cmd(struct mnl_socket *nl, struct nftnl_table *t,
+ uint16_t cmd, uint16_t family, uint16_t type)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct mnl_nlmsg_batch *batch;
+ struct nlmsghdr *nlh;
+ uint32_t seq = 0;
+ int err;
+
+ batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
+ nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
+ mnl_nlmsg_batch_next(batch);
+
+ nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
+ cmd, family, type, seq++);
+ nftnl_table_nlmsg_build_payload(nlh, t);
+ nftnl_table_free(t);
+ mnl_nlmsg_batch_next(batch);
+
+ nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
+ mnl_nlmsg_batch_next(batch);
+
+ /* The current table commands do not support any callback returns. */
+ err = send_and_dispatch(nl, mnl_nlmsg_batch_head(batch),
+ mnl_nlmsg_batch_size(batch), 0, NULL);
+
+ mnl_nlmsg_batch_stop(batch);
+ return err;
+}
+
+static int chain_cmd(struct mnl_socket *nl, struct nftnl_chain *chain,
+ uint16_t cmd, int family, uint16_t type,
+ enum callback_return_type cb_type, uint64_t *cb_val)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct mnl_nlmsg_batch *batch;
+ struct nlmsghdr *nlh;
+ uint32_t seq = 0;
+ int err;
+
+ batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
+ nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
+ mnl_nlmsg_batch_next(batch);
+
+ nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
+ cmd, family, type, seq++);
+ nftnl_chain_nlmsg_build_payload(nlh, chain);
+ nftnl_chain_free(chain);
+ mnl_nlmsg_batch_next(batch);
+
+ nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
+ mnl_nlmsg_batch_next(batch);
+
+ err = send_and_dispatch(nl, mnl_nlmsg_batch_head(batch),
+ mnl_nlmsg_batch_size(batch), cb_type, cb_val);
+
+ mnl_nlmsg_batch_stop(batch);
+ return err;
+}
+
+static int rule_cmd(struct mnl_socket *nl, struct nftnl_rule *rule,
+ uint16_t cmd, uint16_t family, uint16_t type,
+ enum callback_return_type callback_type,
+ uint64_t *callback_value)
+{
+
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct mnl_nlmsg_batch *batch;
+ struct nlmsghdr *nlh;
+ uint32_t seq = 0;
+ int err;
+
+ debug_netlink_dump_rule(rule);
+
+ batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
+ put_batch_headers(mnl_nlmsg_batch_current(batch),
+ NFNL_MSG_BATCH_BEGIN, seq++);
+ mnl_nlmsg_batch_next(batch);
+
+ nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
+ cmd, family, type, seq++);
+ nftnl_rule_nlmsg_build_payload(nlh, rule);
+ mnl_nlmsg_batch_next(batch);
+
+ put_batch_headers(mnl_nlmsg_batch_current(batch),
+ NFNL_MSG_BATCH_END, seq++);
+ mnl_nlmsg_batch_next(batch);
+
+ err = send_and_dispatch(nl, mnl_nlmsg_batch_head(batch),
+ mnl_nlmsg_batch_size(batch),
+ callback_type, callback_value);
+ mnl_nlmsg_batch_stop(batch);
+
+ return err;
+}
+
+static int rule_delete(struct firewall_handle *handle)
+{
+ struct nftnl_rule *rule;
+ struct mnl_socket *nl;
+ int err;
+
+ DBG("");
+
+ rule = nftnl_rule_alloc();
+ if (!rule)
+ return -ENOMEM;
+
+ nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
+ nftnl_rule_set(rule, NFTNL_RULE_CHAIN, handle->chain);
+ nftnl_rule_set_u64(rule, NFTNL_RULE_HANDLE, handle->handle);
+
+ err = socket_open_and_bind(&nl);
+ if (err < 0) {
+ nftnl_rule_free(rule);
+ return err;
+ }
+
+ err = rule_cmd(nl, rule, NFT_MSG_DELRULE, NFPROTO_IPV4,
+ NLM_F_ACK, 0, NULL);
+ nftnl_rule_free(rule);
+ mnl_socket_close(nl);
+
+ return err;
+}
+
+struct firewall_context *__connman_firewall_create(void)
+{
+ struct firewall_context *ctx;
+
+ DBG("");
+
+ ctx = g_new0(struct firewall_context, 1);
+
+ return ctx;
+}
+
+void __connman_firewall_destroy(struct firewall_context *ctx)
+{
+ DBG("");
+
+ g_free(ctx);
+}
+
+static int build_rule_nat(const char *address, unsigned char prefixlen,
+ const char *interface, struct nftnl_rule **res)
+{
+ struct nftnl_rule *rule;
+ struct in_addr ipv4_addr, ipv4_mask;
+ struct nftnl_expr *expr;
+ int err;
+
+ /*
+ * # nft --debug netlink add rule connman nat-postrouting \
+ * oifname eth0 ip saddr 10.10.0.0/24 masquerade
+ *
+ * ip connman nat-postrouting
+ * [ meta load oifname => reg 1 ]
+ * [ cmp eq reg 1 0x30687465 0x00000000 0x00000000 0x00000000 ]
+ * [ payload load 4b @ network header + 12 => reg 1 ]
+ * [ bitwise reg 1 = (reg=1 & 0x00ffffff ) ^ 0x00000000 ]
+ * [ cmp eq reg 1 0x00000a0a ]
+ * [ masq ]
+ */
+
+ rule = nftnl_rule_alloc();
+ if (!rule)
+ return -ENOMEM;
+
+ nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
+ nftnl_rule_set(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_NAT_POST);
+
+ /* family ipv4 */
+ nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, NFPROTO_IPV4);
+
+ /* oifname */
+ expr = nftnl_expr_alloc("meta");
+ if (!expr)
+ goto err;
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, NFT_META_OIFNAME);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_META_DREG, NFT_REG_1);
+ nftnl_rule_add_expr(rule, expr);
+ err = add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, interface,
+ strlen(interface) + 1);
+ if (err < 0)
+ goto err;
+
+ /* source */
+ ipv4_mask.s_addr = htonl((0xffffffff << (32 - prefixlen)) & 0xffffffff);
+ ipv4_addr.s_addr = inet_addr(address);
+ ipv4_addr.s_addr &= ipv4_mask.s_addr;
+
+ err = add_payload(rule, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
+ offsetof(struct iphdr, saddr), sizeof(struct in_addr));
+ if (err < 0)
+ goto err;
+ err = add_bitwise(rule, NFT_REG_1, &ipv4_mask.s_addr,
+ sizeof(struct in_addr));
+ if (err < 0)
+ goto err;
+ err = add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, &ipv4_addr.s_addr,
+ sizeof(struct in_addr));
+ if (err < 0)
+ goto err;
+
+ /* masquerade */
+ expr = nftnl_expr_alloc("masq");
+ if (!expr)
+ goto err;
+ nftnl_rule_add_expr(rule, expr);
+
+ *res = rule;
+ return 0;
+
+err:
+ nftnl_rule_free(rule);
+ return -ENOMEM;
+}
+
+int __connman_firewall_enable_nat(struct firewall_context *ctx,
+ char *address, unsigned char prefixlen,
+ char *interface)
+{
+ struct mnl_socket *nl;
+ struct nftnl_rule *rule;
+ int err;
+
+ DBG("address %s/%d interface %s", address, (int)prefixlen, interface);
+
+ err = socket_open_and_bind(&nl);
+ if (err < 0)
+ return err;
+
+ err = build_rule_nat(address, prefixlen, interface, &rule);
+ if (err)
+ goto out;
+
+ ctx->rule.chain = CONNMAN_CHAIN_NAT_POST;
+ err = rule_cmd(nl, rule, NFT_MSG_NEWRULE, NFPROTO_IPV4,
+ NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK,
+ CALLBACK_RETURN_HANDLE, &ctx->rule.handle);
+ nftnl_rule_free(rule);
+out:
+ mnl_socket_close(nl);
+ return err;
+}
+
+int __connman_firewall_disable_nat(struct firewall_context *ctx)
+{
+ return rule_delete(&ctx->rule);
+}
+
+static int build_rule_snat(int index, const char *address,
+ struct nftnl_rule **res)
+{
+ struct nftnl_rule *rule;
+ struct nftnl_expr *expr;
+ uint32_t snat;
+ int err;
+
+ /*
+ * # nft --debug netlink add rule connman nat-postrouting \
+ * oif eth0 snat 1.2.3.4
+ * ip connman nat-postrouting
+ * [ meta load oif => reg 1 ]
+ * [ cmp eq reg 1 0x0000000b ]
+ * [ immediate reg 1 0x04030201 ]
+ * [ nat snat ip addr_min reg 1 addr_max reg 0 ]
+ */
+
+ rule = nftnl_rule_alloc();
+ if (!rule)
+ return -ENOMEM;
+
+ nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
+ nftnl_rule_set(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_NAT_POST);
+
+ /* IOF */
+ expr = nftnl_expr_alloc("meta");
+ if (!expr)
+ goto err;
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, NFT_META_OIF);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_META_DREG, NFT_REG_1);
+ nftnl_rule_add_expr(rule, expr);
+ err = add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, &index, sizeof(index));
+ if (err < 0)
+ goto err;
+
+ /* snat */
+ expr = nftnl_expr_alloc("immediate");
+ if (!expr)
+ goto err;
+ snat = inet_addr(address);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG_1);
+ nftnl_expr_set(expr, NFTNL_EXPR_IMM_DATA, &snat, sizeof(snat));
+ nftnl_rule_add_expr(rule, expr);
+
+ expr = nftnl_expr_alloc("nat");
+ if (!expr)
+ goto err;
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_NAT_TYPE, NFT_NAT_SNAT);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_NAT_FAMILY, NFPROTO_IPV4);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_NAT_REG_ADDR_MIN, NFT_REG_1);
+ nftnl_rule_add_expr(rule, expr);
+
+ *res = rule;
+ return 0;
+
+err:
+ nftnl_rule_free(rule);
+ return -ENOMEM;
+}
+
+int __connman_firewall_enable_snat(struct firewall_context *ctx,
+ int index, const char *ifname, const char *addr)
+{
+ struct nftnl_rule *rule;
+ struct mnl_socket *nl;
+ int err;
+
+ DBG("");
+
+ err = socket_open_and_bind(&nl);
+ if (err < 0)
+ return err;
+
+ err = build_rule_snat(index, addr, &rule);
+ if (err)
+ goto out;
+
+ ctx->rule.chain = CONNMAN_CHAIN_NAT_POST;
+ err = rule_cmd(nl, rule, NFT_MSG_NEWRULE, NFPROTO_IPV4,
+ NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK,
+ CALLBACK_RETURN_HANDLE, &ctx->rule.handle);
+ nftnl_rule_free(rule);
+out:
+ mnl_socket_close(nl);
+ return err;
+}
+
+int __connman_firewall_disable_snat(struct firewall_context *ctx)
+{
+ DBG("");
+
+ return rule_delete(&ctx->rule);
+}
+
+static int build_rule_marking(uid_t uid, uint32_t mark, struct nftnl_rule **res)
+{
+ struct nftnl_rule *rule;
+ struct nftnl_expr *expr;
+ int err;
+
+ /*
+ * http://wiki.nftables.org/wiki-nftables/index.php/Setting_packet_metainformation
+ * http://wiki.nftables.org/wiki-nftables/index.php/Matching_packet_metainformation
+ *
+ * # nft --debug netlink add rule connman route-output \
+ * meta skuid wagi mark set 1234
+ *
+ * ip connman route-output
+ * [ meta load skuid => reg 1 ]
+ * [ cmp eq reg 1 0x000003e8 ]
+ * [ immediate reg 1 0x000004d2 ]
+ * [ meta set mark with reg 1 ]
+ */
+
+ rule = nftnl_rule_alloc();
+ if (!rule)
+ return -ENOMEM;
+
+ nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
+ nftnl_rule_set(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_ROUTE_OUTPUT);
+
+ expr = nftnl_expr_alloc("meta");
+ if (!expr)
+ goto err;
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, NFT_META_SKUID);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_META_DREG, NFT_REG_1);
+ nftnl_rule_add_expr(rule, expr);
+ err = add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, &uid, sizeof(uid));
+ if (err < 0)
+ goto err;
+
+ expr = nftnl_expr_alloc("immediate");
+ if (!expr)
+ goto err;
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG_1);
+ nftnl_expr_set(expr, NFTNL_EXPR_IMM_DATA, &mark, sizeof(mark));
+ nftnl_rule_add_expr(rule, expr);
+
+ expr = nftnl_expr_alloc("meta");
+ if (!expr)
+ goto err;
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, NFT_META_MARK);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_META_SREG, NFT_REG_1);
+ nftnl_rule_add_expr(rule, expr);
+
+ *res = rule;
+ return 0;
+
+err:
+ return -ENOMEM;
+}
+
+static int build_rule_src_ip(const char *src_ip, uint32_t mark, struct nftnl_rule **res)
+{
+ struct nftnl_rule *rule;
+ struct nftnl_expr *expr;
+ int err;
+ in_addr_t s_addr;
+
+ /*
+ * # nft --debug netlink add rule connman route-output \
+ * ip saddr 192.168.10.31 mark set 1234
+ *
+ * ip connman route-output
+ * [ payload load 4b @ network header + 12 => reg 1 ]
+ * [ cmp eq reg 1 0x1f0aa8c0 ]
+ * [ immediate reg 1 0x000004d2 ]
+ * [ meta set mark with reg 1 ]
+ */
+
+ rule = nftnl_rule_alloc();
+ if (!rule)
+ return -ENOMEM;
+
+ nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
+ nftnl_rule_set(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_ROUTE_OUTPUT);
+
+ /* family ipv4 */
+ nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, NFPROTO_IPV4);
+
+ /* source IP */
+ err = add_payload(rule, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
+ offsetof(struct iphdr, saddr), sizeof(struct in_addr));
+ if (err < 0)
+ goto err;
+
+ s_addr = inet_addr(src_ip);
+ err = add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, &s_addr, sizeof(s_addr));
+ if (err < 0)
+ goto err;
+
+ expr = nftnl_expr_alloc("immediate");
+ if (!expr)
+ goto err;
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG_1);
+ nftnl_expr_set(expr, NFTNL_EXPR_IMM_DATA, &mark, sizeof(mark));
+ nftnl_rule_add_expr(rule, expr);
+
+ expr = nftnl_expr_alloc("meta");
+ if (!expr)
+ goto err;
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, NFT_META_MARK);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_META_SREG, NFT_REG_1);
+ nftnl_rule_add_expr(rule, expr);
+
+ *res = rule;
+ return 0;
+
+err:
+ return -ENOMEM;
+}
+
+int __connman_firewall_enable_marking(struct firewall_context *ctx,
+ enum connman_session_id_type id_type,
+ char *id, const char *src_ip,
+ uint32_t mark)
+{
+ struct nftnl_rule *rule;
+ struct mnl_socket *nl;
+ struct passwd *pw;
+ uid_t uid;
+ int err;
+
+ DBG("");
+
+ if (id_type == CONNMAN_SESSION_ID_TYPE_UID) {
+ pw = getpwnam(id);
+ if (!pw)
+ return -EINVAL;
+ uid = pw->pw_uid;
+ }
+ else if (!src_ip)
+ return -ENOTSUP;
+
+ err = socket_open_and_bind(&nl);
+ if (err < 0)
+ return err;
+
+ if (id_type == CONNMAN_SESSION_ID_TYPE_UID) {
+ err = build_rule_marking(uid, mark, &rule);
+ if (err < 0)
+ goto out;
+
+ ctx->rule.chain = CONNMAN_CHAIN_ROUTE_OUTPUT;
+ err = rule_cmd(nl, rule, NFT_MSG_NEWRULE, NFPROTO_IPV4,
+ NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK,
+ CALLBACK_RETURN_HANDLE, &ctx->rule.handle);
+
+ nftnl_rule_free(rule);
+ }
+
+ if (src_ip) {
+ err = build_rule_src_ip(src_ip, mark, &rule);
+ if (err < 0)
+ goto out;
+
+ ctx->rule.chain = CONNMAN_CHAIN_ROUTE_OUTPUT;
+ err = rule_cmd(nl, rule, NFT_MSG_NEWRULE, NFPROTO_IPV4,
+ NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK,
+ CALLBACK_RETURN_HANDLE, &ctx->rule.handle);
+
+ nftnl_rule_free(rule);
+ }
+out:
+ mnl_socket_close(nl);
+ return err;
+}
+
+int __connman_firewall_disable_marking(struct firewall_context *ctx)
+{
+ int err;
+
+ DBG("");
+
+ err = rule_delete(&ctx->rule);
+ return err;
+}
+
+static struct nftnl_table *build_table(const char *name, uint16_t family)
+{
+ struct nftnl_table *table;
+
+ table = nftnl_table_alloc();
+ if (!table)
+ return NULL;
+
+ nftnl_table_set_u32(table, NFTNL_TABLE_FAMILY, family);
+ nftnl_table_set_str(table, NFTNL_TABLE_NAME, name);
+
+ return table;
+}
+
+
+static struct nftnl_chain *build_chain(const char *name, const char *table,
+ const char *type, int hooknum, int prio)
+{
+ struct nftnl_chain *chain;
+
+ chain = nftnl_chain_alloc();
+ if (!chain)
+ return NULL;
+
+ nftnl_chain_set(chain, NFTNL_CHAIN_TABLE, table);
+ nftnl_chain_set(chain, NFTNL_CHAIN_NAME, name);
+
+ if (type)
+ nftnl_chain_set_str(chain, NFTNL_CHAIN_TYPE, type);
+
+ if (hooknum >= 0)
+ nftnl_chain_set_u32(chain, NFTNL_CHAIN_HOOKNUM, hooknum);
+
+ if (prio >= 0)
+ nftnl_chain_set_u32(chain, NFTNL_CHAIN_PRIO, prio);
+
+ return chain;
+}
+
+static int create_table_and_chains(struct nftables_info *nft_info)
+{
+ struct mnl_socket *nl;
+ struct nftnl_table *table;
+ struct nftnl_chain *chain;
+ int err;
+
+
+ DBG("");
+
+ err = socket_open_and_bind(&nl);
+ if (err < 0)
+ return err;
+
+ /*
+ * Add table
+ * http://wiki.nftables.org/wiki-nftables/index.php/Configuring_tables
+ */
+
+ /*
+ * # nft add table connman
+ */
+ table = build_table(CONNMAN_TABLE, NFPROTO_IPV4);
+ if (!table) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = table_cmd(nl, table, NFT_MSG_NEWTABLE, NFPROTO_IPV4,
+ NLM_F_CREATE|NLM_F_ACK);
+ if (err < 0)
+ goto out;
+
+ /*
+ * Add basic chains
+ * http://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains
+ */
+
+ /*
+ * # nft add chain connman nat-prerouting \
+ * { type nat hook prerouting priortiy 0 ; }
+ */
+ chain = build_chain(CONNMAN_CHAIN_NAT_PRE, CONNMAN_TABLE,
+ "nat", NF_INET_PRE_ROUTING, 0);
+ if (!chain) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = chain_cmd(nl, chain, NFT_MSG_NEWCHAIN,
+ NFPROTO_IPV4, NLM_F_CREATE | NLM_F_ACK,
+ CALLBACK_RETURN_NONE, NULL);
+ if (err < 0)
+ goto out;
+
+ /*
+ * # nft add chain connman nat-postrouting \
+ * { type nat hook postrouting priortiy 0 ; }
+ */
+ chain = build_chain(CONNMAN_CHAIN_NAT_POST, CONNMAN_TABLE,
+ "nat", NF_INET_POST_ROUTING, 0);
+ if (!chain) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = chain_cmd(nl, chain, NFT_MSG_NEWCHAIN,
+ NFPROTO_IPV4, NLM_F_CREATE | NLM_F_ACK,
+ CALLBACK_RETURN_NONE, NULL);
+ if (err < 0)
+ goto out;
+
+ /*
+ * # nft add chain connman route-output \
+ * { type route hook output priority 0 ; }
+ */
+ chain = build_chain(CONNMAN_CHAIN_ROUTE_OUTPUT, CONNMAN_TABLE,
+ "route", NF_INET_LOCAL_OUT, 0);
+ if (!chain) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = chain_cmd(nl, chain, NFT_MSG_NEWCHAIN,
+ NFPROTO_IPV4, NLM_F_CREATE | NLM_F_ACK,
+ CALLBACK_RETURN_NONE, NULL);
+ if (err < 0)
+ goto out;
+
+out:
+ if (err)
+ connman_warn("Failed to create basic chains: %s",
+ strerror(-err));
+ mnl_socket_close(nl);
+ return err;
+}
+
+static int cleanup_table_and_chains(void)
+{
+ struct nftnl_table *table;
+ struct mnl_socket *nl;
+ int err;
+
+ DBG("");
+
+ err = socket_open_and_bind(&nl);
+ if (err < 0)
+ return -ENOMEM;
+
+ /*
+ * Cleanup everythying in one go. There is little point in
+ * step-by-step removal of rules and chains if you can get it
+ * as simple as this.
+ */
+ /*
+ * # nft delete table connman
+ */
+ table = build_table(CONNMAN_TABLE, NFPROTO_IPV4);
+ err = table_cmd(nl, table, NFT_MSG_DELTABLE, NFPROTO_IPV4, NLM_F_ACK);
+
+ mnl_socket_close(nl);
+ return err;
+}
+
+int __connman_firewall_init(void)
+{
+ int err;
+
+ DBG("");
+
+ if (getenv("CONNMAN_NFTABLES_DEBUG"))
+ debug_enabled = true;
+
+ /*
+ * EAFNOSUPPORT is return whenever the nf_tables_ipv4 hasn't been
+ * loaded yet. ENOENT is return in case the table is missing.
+ */
+ err = cleanup_table_and_chains();
+ if (err < 0 && (err != EAFNOSUPPORT && err != -ENOENT)) {
+ connman_warn("initializing nftable failed with '%s' %d. Check if kernel module nf_tables_ipv4 is missing\n",
+ strerror(-err), err);
+ return err;
+ }
+
+ nft_info = g_new0(struct nftables_info, 1);
+ err = create_table_and_chains(nft_info);
+ if (err) {
+ g_free(nft_info);
+ nft_info = NULL;
+ }
+
+ return err;
+}
+
+void __connman_firewall_cleanup(void)
+{
+ int err;
+
+ DBG("");
+
+ err = cleanup_table_and_chains();
+ if (err < 0)
+ connman_warn("cleanup table and chains failed with '%s' %d\n",
+ strerror(-err), err);
+
+ g_free(nft_info);
+ nft_info = NULL;
+}
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 <sys/errno.h>
#include <sys/socket.h>
#include <xtables.h>
+#include <inttypes.h>
#include <linux/netfilter_ipv4/ip_tables.h>
@@ -154,6 +155,7 @@ struct error_target {
struct connman_iptables_entry {
int offset;
int builtin;
+ int counter_idx;
struct ipt_entry *entry;
};
@@ -251,8 +253,9 @@ static int print_entry(struct ipt_entry *entry, int builtin, unsigned int hook,
{
iterate_entries_cb_t cb = user_data;
- DBG("entry %p hook %d offset %d size %d", entry, hook,
- offset, entry->next_offset);
+ DBG("entry %p hook %u offset %u size %u packets %"PRIu64" bytes %"PRIu64,
+ entry, hook, offset, (unsigned int) entry->next_offset,
+ (uint64_t) entry->counters.pcnt, (uint64_t) entry->counters.bcnt);
return cb(entry, builtin, hook, size, offset, NULL);
}
@@ -460,7 +463,7 @@ static void update_targets_reference(struct connman_iptables *table,
static int iptables_add_entry(struct connman_iptables *table,
struct ipt_entry *entry, GList *before,
- int builtin)
+ int builtin, int counter_idx)
{
struct connman_iptables_entry *e, *entry_before;
@@ -473,6 +476,7 @@ static int iptables_add_entry(struct connman_iptables *table,
e->entry = entry;
e->builtin = builtin;
+ e->counter_idx = counter_idx;
table->entries = g_list_insert_before(table->entries, before, e);
table->num_entries++;
@@ -620,7 +624,7 @@ static int iptables_add_chain(struct connman_iptables *table,
error->t.u.user.target_size = ALIGN(sizeof(struct error_target));
g_stpcpy(error->error, name);
- if (iptables_add_entry(table, entry_head, last, -1) < 0)
+ if (iptables_add_entry(table, entry_head, last, -1, -1) < 0)
goto err_head;
/* tail entry */
@@ -638,7 +642,7 @@ static int iptables_add_chain(struct connman_iptables *table,
ALIGN(sizeof(struct ipt_standard_target));
standard->verdict = XT_RETURN;
- if (iptables_add_entry(table, entry_return, last, -1) < 0)
+ if (iptables_add_entry(table, entry_return, last, -1, -1) < 0)
goto err;
return 0;
@@ -826,7 +830,7 @@ static int iptables_append_rule(struct connman_iptables *table,
if (!new_entry)
return -EINVAL;
- ret = iptables_add_entry(table, new_entry, chain_tail->prev, builtin);
+ ret = iptables_add_entry(table, new_entry, chain_tail->prev, builtin, -1);
if (ret < 0)
g_free(new_entry);
@@ -857,7 +861,7 @@ static int iptables_insert_rule(struct connman_iptables *table,
if (builtin == -1)
chain_head = chain_head->next;
- ret = iptables_add_entry(table, new_entry, chain_head, builtin);
+ ret = iptables_add_entry(table, new_entry, chain_head, builtin, -1);
if (ret < 0)
g_free(new_entry);
@@ -1128,6 +1132,8 @@ static int iptables_change_policy(struct connman_iptables *table,
target = ipt_get_target(entry->entry);
t = (struct xt_standard_target *)target;
+ if (t->verdict != verdict)
+ entry->counter_idx = -1;
t->verdict = verdict;
return 0;
@@ -1405,6 +1411,19 @@ static int iptables_replace(struct connman_iptables *table,
return 0;
}
+static int iptables_add_counters(struct connman_iptables *table,
+ struct xt_counters_info *c)
+{
+ int err;
+
+ err = setsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_SET_ADD_COUNTERS, c,
+ sizeof(*c) + sizeof(struct xt_counters) * c->num_counters);
+ if (err < 0)
+ return -errno;
+
+ return 0;
+}
+
static int add_entry(struct ipt_entry *entry, int builtin, unsigned int hook,
size_t size, unsigned offset, void *user_data)
{
@@ -1417,7 +1436,8 @@ static int add_entry(struct ipt_entry *entry, int builtin, unsigned int hook,
memcpy(new_entry, entry, entry->next_offset);
- return iptables_add_entry(table, new_entry, NULL, builtin);
+ return iptables_add_entry(table, new_entry, NULL, builtin,
+ table->num_entries);
}
static void table_cleanup(struct connman_iptables *table)
@@ -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;
}
@@ -1988,6 +2020,9 @@ static int parse_rule_spec(struct connman_iptables *table,
ctx->xt_m = g_list_append(ctx->xt_m, xt_m);
break;
+ case 'p':
+ ctx->proto = xtables_parse_protocol(optarg);
+ break;
case 'j':
/* Target */
ctx->xt_t = prepare_target(table, optarg);
@@ -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 <stdlib.h>
#include <string.h>
#include <syslog.h>
-#include <execinfo.h>
#include <dlfcn.h>
#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;
@@ -289,6 +308,17 @@ static void parse_config(GKeyFile *config)
g_clear_error(&error);
str_list = __connman_config_get_string_list(config, "General",
+ CONF_ALWAYS_CONNECTED_TECHS, &len, &error);
+
+ if (!error)
+ connman_settings.always_connected_techs =
+ parse_service_types(str_list, len);
+
+ g_strfreev(str_list);
+
+ g_clear_error(&error);
+
+ str_list = __connman_config_get_string_list(config, "General",
CONF_FALLBACK_NAMESERVERS, &len, &error);
if (!error)
@@ -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 <errno.h>
-#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
#include "connman.h"
@@ -42,46 +45,52 @@ struct connman_nat {
static int enable_ip_forward(bool enable)
{
- FILE *f;
+ static char value = 0;
+ int f, err = 0;
- f = fopen("/proc/sys/net/ipv4/ip_forward", "r+");
- if (!f)
+ if ((f = open("/proc/sys/net/ipv4/ip_forward", O_CLOEXEC | O_RDWR)) < 0)
return -errno;
- if (enable)
- fprintf(f, "1");
- else
- fprintf(f, "0");
+ if (!value) {
+ if (read(f, &value, sizeof(value)) < 0)
+ value = 0;
- fclose(f);
+ if (lseek(f, 0, SEEK_SET) < 0)
+ return -errno;
+ }
- return 0;
+ if (enable) {
+ char allow = '1';
+
+ if (write (f, &allow, sizeof(allow)) < 0)
+ err = -errno;
+ } else {
+ char deny = '0';
+
+ if (value)
+ deny = value;
+
+ if (write(f, &deny, sizeof(deny)) < 0)
+ err = -errno;
+
+ value = 0;
+ }
+
+ close(f);
+
+ return err;
}
static int enable_nat(struct connman_nat *nat)
{
- char *cmd;
- int err;
-
g_free(nat->interface);
nat->interface = g_strdup(default_interface);
if (!nat->interface)
return 0;
- /* Enable masquerading */
- cmd = g_strdup_printf("-s %s/%d -o %s -j MASQUERADE",
- nat->address,
- nat->prefixlen,
- nat->interface);
-
- err = __connman_firewall_add_rule(nat->fw, "nat",
- "POSTROUTING", cmd);
- g_free(cmd);
- if (err < 0)
- return err;
-
- return __connman_firewall_enable(nat->fw);
+ return __connman_firewall_enable_nat(nat->fw, nat->address,
+ nat->prefixlen, nat->interface);
}
static void disable_nat(struct connman_nat *nat)
@@ -89,8 +98,7 @@ static void disable_nat(struct connman_nat *nat)
if (!nat->interface)
return;
- /* Disable masquerading */
- __connman_firewall_disable(nat->fw);
+ __connman_firewall_disable_nat(nat->fw);
}
int __connman_nat_enable(const char *name, const char *address,
diff --git a/src/network.c b/src/network.c
index 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 <stdlib.h>
#include <string.h>
#include <sys/time.h>
+#include <sys/timex.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
+#include <netdb.h>
#include <glib.h>
@@ -65,9 +67,13 @@ struct ntp_msg {
#define OFFSET_1900_1970 2208988800UL /* 1970 - 1900 in seconds */
-#define STEPTIME_MIN_OFFSET 0.128
+#define STEPTIME_MIN_OFFSET 0.4
#define LOGTOD(a) ((a) < 0 ? 1. / (1L << -(a)) : 1L << (int)(a))
+#define NSEC_PER_SEC ((uint64_t)1000000000ULL)
+#ifndef ADJ_SETOFFSET
+#define ADJ_SETOFFSET 0x0100 /* add 'time' to current time */
+#endif
#define NTP_SEND_TIMEOUT 2
#define NTP_SEND_RETRIES 3
@@ -117,12 +123,12 @@ static struct timespec mtx_time;
static int transmit_fd = 0;
static char *timeserver = NULL;
-static struct sockaddr_in timeserver_addr;
+static struct sockaddr_in6 timeserver_addr;
static gint poll_id = 0;
static gint timeout_id = 0;
static guint retries = 0;
-static void send_packet(int fd, const char *server, uint32_t timeout);
+static void send_packet(int fd, struct sockaddr *server, uint32_t timeout);
static void next_server(void)
{
@@ -143,17 +149,19 @@ static gboolean send_timeout(gpointer user_data)
if (retries++ == NTP_SEND_RETRIES)
next_server();
else
- send_packet(transmit_fd, timeserver, timeout << 1);
+ send_packet(transmit_fd, (struct sockaddr *)&timeserver_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 *)&timeserver_addr)->sin_addr);
+ } else if (server->sa_family == AF_INET6) {
+ size = sizeof(struct sockaddr_in6);
+ addr = (void *)&timeserver_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 *)&timeserver_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 *)&timeserver_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 *)&timeserver_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(&timeserver_addr, info->ai_addr, info->ai_addrlen);
+ freeaddrinfo(info);
+ memset(&in6addr, 0, sizeof(in6addr));
+
+ if (family == AF_INET) {
+ ((struct sockaddr_in *)&timeserver_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, &timestamp,
@@ -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*)&timeserver_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)