tools/netlink-test
unit/test-ippool
unit/test-nat
+unit/test-iptables
doc/*.bak
doc/*.stamp
Maxin B. John <maxin.john@intel.com>
Heghedus Razvan <razvan.heghedus@ni.com>
Guillaume Deroire <guillaume.deroire@hach.com>
+Angus Gratton <gus@projectgus.com>
+Gerald Loacker <Gerald.Loacker@wolfvision.net>
+Mikhail Kovyazin <kovyazin@protei.ru>
+Mounesh Sutar <sutar.mounesh@gmail.com>
+Christophe Ronco <c.ronco@kerlink.fr>
+Jonas Bonn <jonas@southpole.se>
+Eliott Dumeix <e.dumeix@overkiz.com>
+Santtu Lakkala <inz@inz.fi>
+Bertrand Jacquin <bertrand@jacquin.bzh>
+Peter Meerwald-Stadler <pmeerw@pmeerw.net>
+André Draszik <andre.draszik@jci.com>
+Chris Novakovic <chris@chrisn.me.uk>
+Ryan Schaefer <ryan.schaefer@flukenetworks.com>
+Nicolas Cornu <n.cornu@overkiz.com>
+Rahul Jain <rahul.jain@samsung.com
+Benoît Monin <benoit.monin@gmx.fr>
+Jussi Laakkonen <jussi.laakkonen@jolla.com>
+Vivien Henriet <v.henriet@overkiz.com>
+Rikard Falkeborn <rikard.falkeborn@gmail.com>
+Volodymyr Ostap <ostvolodymyr@gmail.com>
+Artem Yamshanov <me@anticode.ninja>
+Matthias Berndt <matthias_berndt@gmx.de>
+Henrik Persson <Henrik.Persson@verisure.com>
+ver 1.37:
+ Fix issue with handling invalid gateway addresses.
+ Fix issue with handling updates of default gateway.
+ Fix issue with DHCP servers that require broadcast flag.
+ Add support for option to use gateways as time servers.
+ Add support for option to select default technology.
+ Add support for Address Conflict Detection (ACD).
+ Add support for IPv6 iptables management.
+
+ver 1.36:
+ Fix issue with DNS short response on error handling.
+ Fix issue with handling incoming DNS requests.
+ Fix issue with handling empty timeserver list.
+ Fix issue with incorrect DHCP byte order.
+ Fix issue with AllowDomainnameUpdates handling.
+ Fix issue with IPv4 link-local IP conflict error.
+ Fix issue with handling WISPr over TLS connections.
+ Fix issue with WiFi background scanning handling.
+ Fix issue with WiFi disconnect+connect race condition.
+ Fix issue with WiFi scanning and tethering operation.
+ Fix issue with WiFi security change handling.
+ Fix issue with missing signal for WPS changes.
+ Fix issue with online check retry handling.
+ Add support for systemd-resolved backend.
+ Add support for mDNS configuration setup.
+
ver 1.35:
Fix issue with malformed DNS response handling.
Fix issue with routing and disabled online check.
include/device.h include/network.h include/inet.h \
include/storage.h include/provision.h \
include/session.h include/ipaddress.h include/agent.h \
- include/inotify.h include/peer.h include/machine.h
+ include/inotify.h include/peer.h include/machine.h \
+ include/acd.h include/tethering.h
nodist_include_HEADERS = include/version.h
gdbus/mainloop.c gdbus/watch.c \
gdbus/object.c gdbus/client.c gdbus/polkit.c
+if BACKTRACE
+backtrace_sources = src/backtrace.c
+endif
+
gdhcp_sources = gdhcp/gdhcp.h gdhcp/common.h gdhcp/common.c gdhcp/client.c \
- gdhcp/server.c gdhcp/ipv4ll.h gdhcp/ipv4ll.c gdhcp/unaligned.h
+ gdhcp/server.c gdhcp/unaligned.h
gweb_sources = gweb/gweb.h gweb/gweb.c gweb/gresolv.h gweb/gresolv.c
gweb_sources += gweb/giognutls.h gweb/gionotls.c
endif
-if BACKTRACE
-backtrace_sources = src/backtrace.c
+if STATS
+stats_sources = src/stats.c
+else
+stats_sources = src/nostats.c
endif
shared_sources = src/shared/util.h src/shared/util.c \
- src/shared/netlink.h src/shared/netlink.c
+ src/shared/netlink.h src/shared/netlink.c \
+ src/shared/arp.h src/shared/arp.c
if DATAFILES
sbin_PROGRAMS = src/connmand src/connmand-wait-online
-src_connmand_SOURCES = $(gdhcp_sources) $(gweb_sources) $(backtrace_sources) \
- $(builtin_sources) $(shared_sources) src/connman.ver \
- src/main.c src/connman.h src/log.c \
+src_connmand_SOURCES = $(gdhcp_sources) $(gweb_sources) $(stats_sources) \
+ $(backtrace_sources) $(builtin_sources) $(shared_sources) \
+ src/connman.ver src/main.c src/connman.h src/log.c \
src/error.c src/plugin.c src/task.c \
src/device.c src/network.c src/connection.c \
src/manager.c src/service.c \
src/storage.c src/dbus.c src/config.c \
src/technology.c src/counter.c src/ntp.c \
src/session.c src/tethering.c src/wpad.c src/wispr.c \
- src/stats.c src/dnsproxy.c src/6to4.c \
- src/ippool.c src/bridge.c src/nat.c src/ipaddress.c \
- src/inotify.c src/ipv6pd.c src/peer.c \
- src/peer_service.c src/machine.c src/util.c
+ src/6to4.c src/ippool.c src/bridge.c src/nat.c \
+ src/ipaddress.c src/inotify.c src/ipv6pd.c src/peer.c \
+ src/peer_service.c src/machine.c src/util.c \
+ src/acd.c
+
+if INTERNAL_DNS_BACKEND
+src_connmand_SOURCES += src/dnsproxy.c
+endif
+if SYSTEMD_RESOLVED_DNS_BACKEND
+src_connmand_SOURCES += src/dns-systemd-resolved.c
+endif
src_connmand_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \
@GLIB_LIBS@ @DBUS_LIBS@ @GNUTLS_LIBS@ \
endif
BUILT_SOURCES = $(local_headers) src/builtin.h $(service_files) \
- scripts/connman scripts/connman_resolvconf.conf
+ scripts/connman
+
+if INTERNAL_DNS_BACKEND
+BUILT_SOURCES += scripts/connman_resolvconf.conf
+endif
if VPN
BUILT_SOURCES += vpn/builtin.h
client/input.h client/input.c \
client/agent.h client/agent.c \
client/peers.h client/peers.c \
+ client/tethering.h client/tethering.c \
client/vpnconnections.h client/vpnconnections.c \
client/main.c
tools_stats_tool_LDADD = @GLIB_LIBS@
-tools_dhcp_test_SOURCES = $(gdhcp_sources) tools/dhcp-test.c
-tools_dhcp_test_LDADD = @GLIB_LIBS@
+tools_dhcp_test_SOURCES = $(backtrace_sources) src/log.c src/util.c \
+ $(gdhcp_sources) src/inet.c tools/dhcp-test.c src/shared/arp.c
+tools_dhcp_test_LDADD = @GLIB_LIBS@ -ldl
-tools_dhcp_server_test_SOURCES = $(gdhcp_sources) tools/dhcp-server-test.c
-tools_dhcp_server_test_LDADD = @GLIB_LIBS@
+tools_dhcp_server_test_SOURCES = $(backtrace_sources) src/log.c src/util.c \
+ $(gdhcp_sources) src/inet.c tools/dhcp-server-test.c src/shared/arp.c
+tools_dhcp_server_test_LDADD = @GLIB_LIBS@ -ldl
tools_dbus_test_SOURCES = tools/dbus-test.c
tools_dbus_test_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@
@GLIB_LIBS@ @DBUS_LIBS@ -ldl
if XTABLES
-noinst_PROGRAMS += tools/iptables-test tools/iptables-unit
+noinst_PROGRAMS += tools/iptables-test tools/ip6tables-test tools/iptables-unit \
+ unit/test-iptables
tools_iptables_test_SOURCES = $(backtrace_sources) src/log.c src/iptables.c \
tools/iptables-test.c
tools_iptables_test_LDADD = @GLIB_LIBS@ @XTABLES_LIBS@ -ldl
+tools_ip6tables_test_SOURCES = $(backtrace_sources) src/log.c src/iptables.c \
+ tools/ip6tables-test.c
+tools_ip6tables_test_LDADD = @GLIB_LIBS@ @XTABLES_LIBS@ -ldl
+
tools_iptables_unit_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @XTABLES_CFLAGS@ \
- -DIPTABLES_SAVE=\""${IPTABLES_SAVE}"\"
+ -DIPTABLES_SAVE=\""${IPTABLES_SAVE}"\" \
+ -DIP6TABLES_SAVE=\""${IP6TABLES_SAVE}"\"
tools_iptables_unit_SOURCES = $(backtrace_sources) src/log.c \
src/iptables.c src/firewall-iptables.c src/nat.c \
tools/iptables-unit.c
tools_iptables_unit_LDADD = gdbus/libgdbus-internal.la \
@GLIB_LIBS@ @DBUS_LIBS@ @XTABLES_LIBS@ -ldl
+
+TESTS += unit/test-iptables
+
+unit_test_iptables_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS) @GLIB_CFLAGS@
+unit_test_iptables_SOURCES = $(backtrace_sources) src/connman.h src/log.c \
+ unit/test-iptables.c src/iptables.c
+unit_test_iptables_LDADD = @GLIB_LIBS@ -ldl
+
endif
tools_dnsproxy_test_SOURCES = tools/dnsproxy-test.c
tools_dnsproxy_test_LDADD = @GLIB_LIBS@
-tools_netlink_test_SOURCES = $(shared_sources) tools/netlink-test.c
+tools_netlink_test_SOURCES = src/shared/util.c src/shared/netlink.c \
+ tools/netlink-test.c
tools_netlink_test_LDADD = @GLIB_LIBS@
endif
- Generic plugin infrastructure
- Device and network abstraction (with basic storage support)
- IPv4, IPv4-LL (link-local) and DHCP
+ - IPv4 address conflict detection (ACD) according to RFC 5227
- IPv6, DHCPv6 and 6to4 tunnels
- Advanced routing and DNS configuration
- Built-in DNS proxy and intelligent caching
# semodule -i connman-task.pp
in order to enable the dbus access.
+ --with-dns-backend=TYPE
+
+ Enable support for a DNS resolving backend
+
+ Select a DNS backend to use. Supported values are "internal"
+ and "systemd-resolved". If "internal" is selected, ConnMan
+ will be build with a caching DNS proxy. If "systemd-resolved"
+ is selected, ConnMan configures systemd-resolved to do DNS
+ resolving. The default value is "internal".
+
Activating debugging
====================
trigger the autostart of wpa_supplicant. However please keep in mind
that this trigger only happens once. If wpa_supplicant stops or crashes,
ConnMan does not periodically try to autostart it. It is up to systemd or
-similar service management tool to autostart it.
+similar service management tool to autostart it. In case wpa_supplicant
+is not started by ConnMan then make sure option "-u" is used in order
+to enable its D-Bus control interface and ensure ConnMan can communicate
+with it.
VPN
--mandir=/usr/share/man \
--localstatedir=/var \
--sysconfdir=/etc \
+ --with-firewall=nftables \
--disable-datafiles \
--enable-openconnect=builtin \
--enable-openvpn=builtin \
#include "dbus_helpers.h"
#include "input.h"
#include "services.h"
+#include "tethering.h"
#include "peers.h"
#include "commands.h"
#include "agent.h"
DBusMessageIter entry;
if (error) {
- fprintf(stderr, "Error: %s", error);
+ fprintf(stderr, "Error: %s\n", error);
return 0;
}
DBusMessageIter entry;
if (error) {
- fprintf(stderr, "Error: %s", error);
+ fprintf(stderr, "Error: %s\n", error);
return 0;
}
return 0;
}
+static int tethering_clients_list(DBusMessageIter *iter,
+ const char *error, void *user_data)
+{
+ if (!error) {
+ __connmanctl_tethering_clients_list(iter);
+ fprintf(stdout, "\n");
+ } else
+ fprintf(stderr, "Error: %s\n", error);
+
+ return 0;
+}
+
static int object_properties(DBusMessageIter *iter,
const char *error, void *user_data)
{
return tether_set(args[1], set_tethering);
}
+static int cmd_tethering_clients(char *args[], int num, struct connman_option *options)
+{
+ if (num > 1)
+ return -E2BIG;
+
+ return __connmanctl_dbus_method_call(connection,
+ CONNMAN_SERVICE, CONNMAN_PATH,
+ "net.connman.Manager", "GetTetheringClients",
+ tethering_clients_list, NULL, NULL, NULL);
+}
+
static int scan_return(DBusMessageIter *iter, const char *error,
void *user_data)
{
dbus_message_iter_append_basic(iter,
DBUS_TYPE_OBJECT_PATH, &path);
-
- return;
}
static int cmd_service_move_before(char *args[], int num,
dbus_message_iter_append_basic(iter,
DBUS_TYPE_OBJECT_PATH, &path);
-
- return;
}
static int cmd_service_move_after(char *args[], int num,
int values;
};
+struct session_options {
+ char **args;
+ int num;
+ char *notify_path;
+ struct connman_option *options;
+};
+
static void config_append_ipv4(DBusMessageIter *iter,
void *user_data)
{
config_return, g_strdup(service_name),
NULL, NULL);
break;
+
+ case 'm':
+ switch (parse_boolean(*opt_start)) {
+ case 1:
+ val = TRUE;
+ break;
+ case 0:
+ val = FALSE;
+ break;
+ default:
+ res = -EINVAL;
+ break;
+ }
+ if (res == 0) {
+ res = __connmanctl_dbus_set_property(connection,
+ path, "net.connman.Service",
+ config_return,
+ g_strdup(service_name),
+ "mDNS.Configuration",
+ DBUS_TYPE_BOOLEAN, &val);
+ }
+ index++;
+ break;
+
default:
res = -EINVAL;
break;
void *user_data)
{
if (error) {
- fprintf(stderr, "Error: %s", error);
+ fprintf(stderr, "Error: %s\n", error);
return 0;
}
void *user_data)
{
if (error)
- fprintf(stderr, "Error: %s", error);
+ fprintf(stderr, "Error: %s\n", error);
return 0;
}
return -EINPROGRESS;
}
+static void session_config_append_array(DBusMessageIter *iter,
+ void *user_data)
+{
+ struct config_append *append = user_data;
+ char **opts = append->opts;
+ int i = 1;
+
+ if (!opts)
+ return;
+
+ while (opts[i] && strncmp(opts[i], "--", 2) != 0) {
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+ &opts[i]);
+ i++;
+ }
+
+ append->values = i;
+}
+
+static void session_create_append_dict(DBusMessageIter *iter, void *user_data)
+{
+ struct session_options *args_struct = user_data;
+ int index = 0, res = 0;
+ struct config_append append;
+ char c;
+ char *ifname;
+ dbus_bool_t source_ip_rule;
+
+ while (index < args_struct->num && args_struct->args[index]) {
+ append.opts = &args_struct->args[index];
+ append.values = 0;
+
+ c = parse_args(args_struct->args[index], args_struct->options);
+
+ switch (c) {
+ case 'b':
+ __connmanctl_dbus_append_dict_string_array(iter, "AllowedBearers",
+ session_config_append_array,
+ &append);
+ break;
+ case 't':
+ if (! args_struct->args[index + 1]) {
+ res = -EINVAL;
+ break;
+ }
+ __connmanctl_dbus_append_dict_entry(iter, "ConnectionType",
+ DBUS_TYPE_STRING,
+ &args_struct->args[index + 1]);
+ append.values = 2;
+ break;
+ case 'i':
+ if (index + 1 < args_struct->num)
+ ifname = args_struct->args[index + 1];
+ else
+ ifname = "";
+ __connmanctl_dbus_append_dict_entry(iter, "AllowedInterface",
+ DBUS_TYPE_STRING,
+ &ifname);
+ append.values = 2;
+ break;
+ case 's':
+ if (! args_struct->args[index + 1]) {
+ res = -EINVAL;
+ break;
+ }
+ switch (parse_boolean( args_struct->args[index + 1])) {
+ case 1:
+ source_ip_rule = TRUE;
+ break;
+ case 0:
+ source_ip_rule = FALSE;
+ break;
+ default:
+ res = -EINVAL;
+ break;
+ }
+ __connmanctl_dbus_append_dict_entry(iter, "SourceIPRule",
+ DBUS_TYPE_BOOLEAN,
+ &source_ip_rule);
+ append.values = 2;
+ break;
+ case 'c':
+ if (!args_struct->args[index + 1]) {
+ res = -EINVAL;
+ break;
+ }
+ __connmanctl_dbus_append_dict_entry(iter, "ContextIdentifier",
+ DBUS_TYPE_STRING,
+ &args_struct->args[index + 1]);
+ append.values = 2;
+ break;
+ default:
+ res = -EINVAL;
+ }
+
+ if (res < 0 && res != -EINPROGRESS) {
+ printf("Error '%s': %s\n", args_struct->args[index],
+ strerror(-res));
+ return;
+ }
+
+ index += append.values;
+ }
+}
+
static void session_create_append(DBusMessageIter *iter, void *user_data)
{
- const char *notify_path = user_data;
+ struct session_options *args_struct = user_data;
- __connmanctl_dbus_append_dict(iter, NULL, NULL);
+ __connmanctl_dbus_append_dict(iter, session_create_append_dict,
+ args_struct);
dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
- ¬ify_path);
+ &args_struct->notify_path);
}
-static int session_create(gboolean connect)
+static int session_create(gboolean connect, char *args[], int num,
+ struct connman_option *options)
{
int res;
char *notify_path;
+ struct session_options args_struct;
+ args_struct.args = args;
+ args_struct.num = num;
+ args_struct.options = options;
notify_path = g_strdup_printf("/net/connman/connmanctl%d", getpid());
session_notify_add(notify_path);
+ args_struct.notify_path = notify_path;
res = __connmanctl_dbus_method_call(connection, "net.connman", "/",
"net.connman.Manager", "CreateSession",
session_create_cb, GINT_TO_POINTER(connect),
- session_create_append, notify_path);
+ session_create_append, &args_struct);
g_free(notify_path);
return 0;
}
-static void session_config_append_array(DBusMessageIter *iter,
- void *user_data)
-{
- struct config_append *append = user_data;
- char **opts = append->opts;
- int i = 1;
-
- if (!opts)
- return;
-
- while (opts[i] && strncmp(opts[i], "--", 2) != 0) {
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
- &opts[i]);
- i++;
- }
-
- append->values = i;
-}
-
static int session_config(char *args[], int num,
struct connman_option *options)
{
DBUS_TYPE_BOOLEAN, &source_ip_rule);
append.values = 2;
break;
+ case 'c':
+ if (!args[index + 1]) {
+ res = -EINVAL;
+ break;
+ }
+
+ res = __connmanctl_dbus_session_change(connection,
+ session_path, session_config_return,
+ "ctxid", "ctxid", DBUS_TYPE_STRING,
+ &args[index + 1]);
+ append.values = 2;
+ break;
default:
res = -EINVAL;
case 1:
if (session_path)
return -EALREADY;
- return session_create(FALSE);
+ return session_create(FALSE, &args[2], num - 2, options);
default:
if (!strcmp(command, "connect")) {
if (!session_path)
- return session_create(TRUE);
+ return session_create(TRUE, &args[2], num - 2,
+ options);
return session_connect();
{"nameservers", 'n', "<dns1> [<dns2>] [<dns3>]"},
{"timeservers", 't', "<ntp1> [<ntp2>] [...]"},
{"domains", 'd', "<domain1> [<domain2>] [...]"},
+ {"mdns", 'm', "yes|no"},
{"ipv6", 'v', "off|auto [enable|disable|preferred]|\n"
"\t\t\tmanual <address> <prefixlength> <gateway>"},
{"proxy", 'x', "direct|auto <URL>|manual <URL1> [<URL2>] [...]\n"
{"type", 't', "local|internet|any"},
{"ifname", 'i', "[<interface_name>]"},
{"srciprule", 's', "yes|no"},
+ {"ctxid", 'c', "<context_identifier>"},
{ NULL, }
};
NULL, cmd_tether,
"Enable, disable tethering, set SSID and passphrase for wifi",
lookup_tether },
+ { "tethering_clients", NULL, NULL, cmd_tethering_clients,
+ "Display tethering clients", NULL },
{ "services", "[<service>]", service_options, cmd_services,
"Display services", lookup_service_arg },
{ "peers", "[peer]", NULL, cmd_peers,
void *user_data)
{
if (error) {
- fprintf(stderr, "Error getting technologies: %s", error);
+ fprintf(stderr, "Error getting technologies: %s\n", error);
return 0;
}
--- /dev/null
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ * Copyright (C) 2018 GlobalLogic. 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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
+ *
+ */
+
+#include <stdio.h>
+
+#include "tethering.h"
+
+void __connmanctl_tethering_clients_list(DBusMessageIter *iter)
+{
+ DBusMessageIter array;
+ char *addr = NULL;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+ return;
+
+ dbus_message_iter_recurse(iter, &array);
+ while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
+ dbus_message_iter_get_basic(&array, &addr);
+
+ fprintf(stdout, "%s", addr);
+
+ if (dbus_message_iter_has_next(&array))
+ fprintf(stdout, "\n");
+
+ dbus_message_iter_next(&array);
+ }
+
+ dbus_message_iter_next(iter);
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+ return;
+
+ dbus_message_iter_recurse(iter, &array);
+ while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
+ dbus_message_iter_get_basic(&array, &addr);
+
+ fprintf(stdout, "\n%s %s", "removed", addr);
+
+ if (dbus_message_iter_has_next(&array))
+ fprintf(stdout, "\n");
+
+ dbus_message_iter_next(&array);
+ }
+}
--- /dev/null
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ * Copyright (C) 2018 GlobalLogic. 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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
+ *
+ */
+
+#ifndef __CONNMANCTL_TETHERING_H
+#define __CONNMANCTL_TETHERING_H
+
+#include <dbus/dbus.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __connmanctl_tethering_clients_list(DBusMessageIter *iter);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMANCTL_TETHERING_H */
AC_PREREQ(2.60)
-AC_INIT(connman, 1.35)
+AC_INIT(connman, 1.37)
AC_CONFIG_MACRO_DIR([m4])
AC_SUBST(abs_top_builddir)
AC_LANG_C
+AC_USE_SYSTEM_EXTENSIONS
AC_PROG_CC
AM_PROG_CC_C_O
AC_CHECK_HEADERS([execinfo.h])
AM_CONDITIONAL([BACKTRACE], [test "${ac_cv_header_execinfo_h}" = "yes"])
+AC_CHECK_MEMBERS([struct in6_pktinfo.ipi6_addr], [], [], [[#include <netinet/in.h>]])
+
AC_CHECK_FUNC(signalfd, dummy=yes,
AC_MSG_ERROR(signalfd support is required))
AC_DEFINE_UNQUOTED([STATS_MAX_FILE_SIZE], (${stats_max_file_size}), [Maximal size of a statistics round robin file])
-PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.28, dummy=yes,
- AC_MSG_ERROR(GLib >= 2.28 is required))
+PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.40, dummy=yes,
+ AC_MSG_ERROR(GLib >= 2.40 is required))
AC_SUBST(GLIB_CFLAGS)
AC_SUBST(GLIB_LIBS)
AC_ARG_WITH(dbusconfdir, AC_HELP_STRING([--with-dbusconfdir=PATH],
[path to D-Bus config directory]), [path_dbusconf=${withval}],
- [path_dbusconf="`$PKG_CONFIG --variable=sysconfdir dbus-1`"])
+ [path_dbusconf="`$PKG_CONFIG --variable=datadir dbus-1`"])
if (test -z "${path_dbusconf}"); then
- DBUS_CONFDIR="${sysconfdir}/dbus-1/system.d"
+ DBUS_CONFDIR="${datadir}/dbus-1/system.d"
else
DBUS_CONFDIR="${path_dbusconf}/dbus-1/system.d"
fi
[enable_tools=${enableval}])
AM_CONDITIONAL(TOOLS, test "${enable_tools}" != "no")
+AC_ARG_ENABLE(stats, AC_HELP_STRING([--disable-stats],
+ [disable statistics round robin file generation]),
+ [enable_stats=${enableval}])
+AM_CONDITIONAL(STATS, test "${enable_stats}" != "no")
+
if (test "${enable_tools}" != "no"); then
AC_PATH_PROGS(IPTABLES_SAVE, [iptables-save], [],
$PATH:/sbin:/usr/sbin)
+ AC_PATH_PROGS(IP6TABLES_SAVE, [ip6tables-save], [],
+ $PATH:/sbin:/usr/sbin)
IPTABLES_SAVE=$ac_cv_path_IPTABLES_SAVE
+ IP6TABLES_SAVE=$ac_cv_path_IP6TABLES_SAVE
else
IPTABLES_SAVE=""
+ IP6TABLES_SAVE=""
fi
AC_SUBST(IPTABLES_SAVE)
+AC_SUBST(IP6TABLES_SAVE)
AC_ARG_ENABLE(client, AC_HELP_STRING([--disable-client],
[disable command line client]),
"${enable_l2tp}" != "no" -o \
"${enable_pptp}" != "no")
+AC_MSG_CHECKING(which DNS backend to use)
+AC_ARG_WITH(dns-backend, AC_HELP_STRING([--with-dns-backend=TYPE],
+ [specify which DNS backend to use: internal or systemd-resolved [default=internal]]),
+ [dns_backend=${withval}],
+ [dns_backend="internal"])
+
+if (test "${dns_backend}" != "internal" -a \
+ "${dns_backend}" != "systemd-resolved"); then
+ AC_MSG_ERROR(no suitable DNS backend defined)
+fi
+AM_CONDITIONAL(INTERNAL_DNS_BACKEND, test "${dns_backend}" = "internal")
+AM_CONDITIONAL(SYSTEMD_RESOLVED_DNS_BACKEND, test "${dns_backend}" = "systemd-resolved")
+AC_MSG_RESULT(${dns_backend})
+
AC_OUTPUT(Makefile include/version.h connman.pc)
keys are the field names and the values are the
actual fields. Alternatively an error indicating that
the request got canceled can be returned.
+ OperationAborted will be return on a successfull
+ cancel request.
Most common return field names are "Name" and of
course "Passphrase".
- SearchDomains: Comma separated list of DNS search domains
- Timeservers: Comma separated list of timeservers
- Domain: Domain name to be used
+- mDNS: Boolean value (true or false). True means that mDNS is enabled.
+ mDNS domains can be resolved and hostname is registered. False means
+ that all mDNS functionality for this service is disabled. Note that
+ not all DNS backends support mDNS: currently systemd-resolved is
+ the only DNS backend with mDNS.
If IPv4 address is missing then DHCP is used. If IPv6 address is missing,
then SLAAC or DHCPv6 is used.
used for multiple services at once.
.SH "FILE FORMAT"
.P
-The configuration file format is key file format.
-It consists of sections (groups) of key-value pairs.
+The configuration file consists of sections (groups) of key-value pairs.
Lines beginning with a '#' and blank lines are considered comments.
Sections are started by a header line containing the section enclosed
in '[' and ']', and ended implicitly by the start of the next section
If set to \fBtrue\fP, then this AP is hidden. If missing or set to
\fBfalse\fP, then AP is not hidden.
.TP
+The following keys are used for WPA EAP (when \fBSecurity=ieee8021x\fP):
+.TP
.B EAP=tls \fR|\fB ttls \fR|\fB peap
EAP type to use. Only \fBtls\fP, \fBttls\fP and \fBpeap\fP are supported.
.TP
.BI AnonymousIdentity= identity
Anonymous identity string for EAP.
.TP
+.BI SubjectMatch= substring
+Substring to be matched against the subject of the
+authentication server certificate for EAP.
+.TP
+.BI AltSubjectMatch= substring
+Semicolon separated string of entries to be matched against the alternative
+subject name of the authentication server certificate for EAP.
+.TP
+.BI DomainSuffixMatch= domain
+Constraint for server domain name. If set, this FQDN is used as a suffix match
+requirement for the authentication server certificate for EAP.
+.TP
+.BI DomainMatch= domain
+This FQDN is used as a full match requirement for the
+authentication server certificate for EAP.
+.TP
.BI Phase2= type
Inner authentication type with for \fBEAP=tls\fP or \fBEAP=ttls\fP. Prefix
the value with \fBEAP-\fP to indicate usage of EAP-based authentication
file, but multiple connections can be specified in the same file.
.SH "FILE FORMAT"
.P
-The configuration file format is key file format.
-It consists of sections (groups) of key-value pairs.
+The configuration file consists of sections (groups) of key-value pairs.
Lines beginning with a '#' and blank lines are considered comments.
Sections are started by a header line containing the section enclosed
in '[' and ']', and ended implicitly by the start of the next section
the \fB\-\-config= \fRargument for \fBconnman-vpn\fP(8).
.SH "FILE FORMAT"
.P
-The configuration file format is key file format.
-It consists of sections (groups) of key-value pairs.
+The configuration file consists of sections (groups) of key-value pairs.
Lines beginning with a '#' and blank lines are considered comments.
Sections are started by a header line containing the section enclosed
in '[' and ']', and ended implicitly by the start of the next section
the controlling terminal in addition to syslog.
.TP
.BR \-r ", " \-\-nodnsproxy
-Do not act as a DNS proxy. By default ConnMan will direct all DNS traffic
-to itself by setting nameserver to 127.0.0.1 in \fBresolv.conf\fP(5) file.
-If this is not desired and you want that all programs call directly some
-DNS server, then you can use the \fB--nodnsproxy\fP option.
-If this option is used, then ConnMan is not able to cache the DNS queries
-because the DNS traffic is not going through ConnMan and that can cause
-some extra network traffic.
+Do not act as a DNS proxy or support external DNS resolving. Depending
+on how ConnMan is compiled, it will by default direct all DNS traffic
+to itself by setting nameserver to 127.0.0.1 in \fBresolv.conf\fP(5)
+file or leave DNS management to an external entity, such as
+systemd-resolved. If this is not desired and you want that all programs
+call directly some DNS server, then you can use the \fB--nodnsproxy\fP
+option. If this option is used, then ConnMan is not able to cache the
+DNS queries because the DNS traffic is not going through ConnMan and that
+can cause some extra network traffic.
.SH SEE ALSO
.BR connmanctl (1), \ connman.conf (5), \ connman-service.config (5), \c
.BR \ connman-vpn (8)
the \fB\-\-config= \fRargument for \fBconnman\fP(8).
.SH "FILE FORMAT"
.P
-The configuration file format is key file format.
-It consists of sections (groups) of key-value pairs.
+The configuration file consists of sections (groups) of key-value pairs.
Lines beginning with a '#' and blank lines are considered comments.
Sections are started by a header line containing the section enclosed
in '[' and ']', and ended implicitly by the start of the next section
.TP
.BI BackgroundScanning=true\ \fR|\fB\ false
Enable background scanning. Default is true.
-Background scanning will start every 5 minutes unless
-the scan list is empty. In that case, a simple backoff
-mechanism starting from 10s up to 5 minutes will run.
+If wifi is disconnected, the background scanning will follow a simple
+backoff mechanism from 3s up to 5 minutes. Then, it will stay in 5
+minutes unless user specifically asks for scanning through a D-Bus
+call. If so, the mechanism will start again from 3s. This feature
+activates also the background scanning while being connected, which
+is required for roaming on wifi.
+When BackgroundScanning is false, ConnMan will not perform any scan
+regardless of wifi is connected or not, unless it is requested by
+the user through a D-Bus call.
+.TP
+.BI UseGatewaysAsTimeservers=true \fR|\fB\ false
+Assume that service gateways also function as timeservers.
+Default is false.
.TP
.BI FallbackTimeservers= server\fR[,...]
List of Fallback timeservers separated by ",".
These timeservers are used for NTP sync when there are
-no timeserver set by the user or by the service.
-These can contain mixed combination of fully qualified
-domain names, IPv4 and IPv6 addresses.
+no timeservers set by the user or by the service, and
+when UseGatewaysAsTimeservers = false. These can contain
+a mixed combination of fully qualified domain names, IPv4
+and IPv6 addresses.
.TP
.BI FallbackNameservers= server\fR[,...]
List of fallback nameservers separated by "," appended
Services that are automatically connected must have been
set up and saved to storage beforehand.
.TP
+.BI DefaultFavoriteTechnologies= technology\fR[,...]
+List of technologies that are marked favorite by default,
+separated by commas ",". The default value for this entry
+when empty is ethernet. Connects to services from this
+technology even if not setup and saved to storage.
+.TP
.BI AlwaysConnectedTechnologies= technology\fR[,...]
List of technoolgies which are always connected regardless
of PreferredTechnologies setting (AutoConnect = true). The
happen for example if we receive DHCP hostname option.
Default value is true.
.TP
+.BI AllowDomainnameUpdates=true\ \fR|\fB\ false
+Allow connman to change the system domainname. This can
+happen for example if we receive DHCP domainname option.
+Default value is true.
+.TP
.BI SingleConnectedTechnology=true\ \fR|\fB\ false
Keep only a single connected technology at any time. When a new
service is connected by the user or a better one is found according
transitioned to ONLINE state.
If this setting is false, the default service will remain in READY state.
Default value is true.
+.TP
+.BI AutoConnectRoamingServices=true\ \fR|\fB\ false
+Automatically connect roaming services. This is not recommended unless you know
+you won't have any billing problem.
+Default value is false.
+.TP
+.BI AddressConflictDetection=true\ \fR|\fB\ false
+Enable or disable the implementation of IPv4 address conflict detection
+according to RFC5227. ConnMan will send probe ARP packets to see if an
+IPv4 address is already in use before assigning the address to an interface.
+If an address conflict occurs for a statically configured address, an IPv4LL
+address will be chosen instead (according to RFC3927). If an address conflict
+occurs for an address offered via DHCP, ConnMan send a DHCP DECLINE once and
+for the second conflict resort to finding an IPv4LL address.
+Default value is false.
.SH "EXAMPLE"
The following example configuration disables hostname updates and enables
ethernet tethering.
.nf
[General]
AllowHostnameUpdates = false
+AllowDomainnameUpdates = false
TetheringTechnologies = ethernet,wifi,bluetooth,gadget
.fi
.SH "SEE ALSO"
settings will be asked from the network and \fBmanual\fR means
that the given arguments will be used as IPv6 settings.
.IR address " and " gateway
-must be valid IPv4 addresses. \fIprefixlength\fR is the length
+must be valid IPv6 addresses. \fIprefixlength\fR is the length
of the prefix in bits. See the \fBEXAMPLE\fR section of this man
page for details.
.PP
Possible Errors: [service].Error.InvalidArguments
+ array{string} GetTetheringClients() [experimental]
+
+ Returns a sorted list of MAC addresses of clients
+ connected to tethered technologies.
+
object ConnectProvider(dict provider) [deprecated]
Connect to a VPN specified by the given provider
ServicesChanged(array{object, dict}, array{object})
- Signals a list of services that have been changed
- via the first array. And a list of service that
- have been removed via the second array.
+ This signal indicates a change in the services.
+ List of all services currently registered is passed
+ via the first array. And a list of services that have
+ been removed via the second array.
- The list of added services is sorted. The dictionary
+ The list of all services is sorted. The dictionary
with the properties might be empty in case none of
the properties have changed. Or only contains the
properties that have changed.
PeersChanged(array{object, dict}, array{object}) [experimental]
- Signals a list of peers that have been changed via the
- first array. And a list of peer that have been removed
- via the second array.
+ This signal indicates a change in the peers. List of
+ all peers currently registered is passed via the first
+ array. And a list of peers that have been removed via
+ the second array.
- The list of changed peers is sorted. The dictionary
+ The list of all peers is sorted. The dictionary
with the properties might be empty in case none of the
properties have changed. Or only contains the
properties that have changed.
object changes. For that it is required to watch the
PropertyChanged signal of the peer object.
+ TetheringClientsChanged(array{string}, array{string}) [experimental]
+
+ This signal indicates a change in the tethering clients.
+ List of all tethering clients currently registered connman is
+ passed via the first array. And a list of tethering clients that
+ have been removed via the second array.
+
PropertyChanged(string name, variant value)
This signal indicates a changed value of the given
=============
The Connection Manager supports plugins for various actions. The basic plugin
-contains of plugin description via CONNMAN_PLUGIN_DEFINE and also init/exit
+contains a plugin description via CONNMAN_PLUGIN_DEFINE and also init/exit
callbacks defined through that description.
#include <connman/plugin.h>
present and contains the list of security methods
or key management settings.
- Possible values are "none", "wep", "psk", "ieee8021x"
- and also "wps".
+ Possible values are "none", "wep", "psk", "ieee8021x",
+ and also "wps" and "wps_advertising".
+
+ Value "wps" means that the service supports WPS. A
+ service advertising itself as WPS registrar contains
+ the additional value "wps_advertising" for as long as
+ it is advertising. That is, while "wps_advertising" is
+ listed, WPS is active and it should be possible to
+ connect to the corresponding service via WPS.
This property might be only present for WiFi
services.
Possible values are "half" and "full".
This information is not available.
+
+ bool mDNS [readonly]
+
+ Whether or not mDNS support is enabled. Note
+ that mDNS requires a DNS backend which
+ supports it. Currently the only DNS backend
+ which supports mDNS is systemd-resolved.
+
+ bool mDNS.Configuration [readwrite]
+
+ Same values as mDNS property. The mDNS
+ represents the actual system configuration
+ while this allows user configuration.
+
+ dict LastAddressConflict [readonly]
+
+ This property contains information about the previously detected
+ address conflict. If there has been no address conflict then
+ IPv4 Address is "0.0.0.0", Ethernet Address is "00:00:00:00:00:00",
+ Timestamp is zero and Resolved is true.
+
+ dict IPv4 [readonly]
+
+ string Address [readonly]
+
+ The IPv4 address which had a conflict.
+
+ dict Ethernet [readonly]
+
+ string Address [readonly]
+
+ The ethernet device address (MAC address) of the conflicting
+ host.
+
+ int64 Timestamp [readonly]
+
+ A timestamp when the conflict was detected in microseconds
+ since January 1, 1970 UTC.
+
+ bool Resolved [readonly]
+
+ Set to false when an address conflict occurs.
+ If a previous conflict could be resolved by probing another
+ IPv4 address (which is not an IPv4LL) then this boolean is set
+ to true.
a default route. When the source IP rule is enabled,
an application can select which session/interface to
send traffic on, using bind-before-connect mechanism.
+
+ string ContextIdentifier [readwrite] [experimental]
+
+ The application can provide an identifier for a
+ session. If an application runs several session
+ at the same time, the additional information
+ can be used by ConnMan to assign different
+ bearers according the identifier. For example
+ a web browser creates per tab a session. For
+ each session a different should bearer be
+ assigned.
Possible Errors: [connection].Error.InvalidArguments
[connection].Error.InProgress
+ void Connect2(string dbus_sender) [experimental]
+
+ Connect this VPN connection. The Connect2() is a
+ wrapper for Connect() allowing to pass original D-Bus
+ sender when proxying the connection request. The
+ Connect2() will wait until the connection is created or
+ there is an error. The error description is returned in
+ dbus error.
+
+ Possible Errors: [connection].Error.InvalidArguments
+ [connection].Error.InProgress
+
+
void Disconnect() [experimental]
Disconnect this VPN connection. If the connection is
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <glib.h>
+#include "../src/connman.h"
+#include "../src/shared/arp.h"
#include "gdhcp.h"
#include "common.h"
-#include "ipv4ll.h"
#define DISCOVER_TIMEOUT 5
#define DISCOVER_RETRIES 6
REBOOTING,
REQUESTING,
BOUND,
+ DECLINED,
RENEWING,
REBINDING,
RELEASED,
GList *request_list;
GHashTable *code_value_hash;
GHashTable *send_value_hash;
+ GHashTable *secs_bcast_hash;
GDHCPClientEventFunc lease_available_cb;
gpointer lease_available_data;
GDHCPClientEventFunc ipv4ll_available_cb;
* versa. In the receiving side we then find out what kind of packet
* the server can send.
*/
+ dhcp_client->request_bcast = dhcp_client->retry_times % 2;
+
+ if (dhcp_client->request_bcast)
+ g_hash_table_add(dhcp_client->secs_bcast_hash,
+ GINT_TO_POINTER(packet.secs));
+
return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
INADDR_BROADCAST, SERVER_PORT,
MAC_BCAST_ADDR, dhcp_client->ifindex,
- dhcp_client->retry_times % 2);
+ dhcp_client->request_bcast);
+}
+
+int g_dhcp_client_decline(GDHCPClient *dhcp_client, uint32_t requested)
+{
+ struct dhcp_packet packet;
+
+ dhcp_client->state = DECLINED;
+ dhcp_client->retry_times = 0;
+
+ debug(dhcp_client, "sending DHCP decline");
+
+ init_packet(dhcp_client, &packet, DHCPDECLINE);
+
+ packet.xid = dhcp_client->xid;
+ packet.secs = dhcp_attempt_secs(dhcp_client);
+
+ if (requested)
+ dhcp_add_option_uint32(&packet, DHCP_REQUESTED_IP, requested);
+
+ add_send_options(dhcp_client, &packet);
+
+ return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
+ INADDR_BROADCAST, SERVER_PORT, MAC_BCAST_ADDR,
+ dhcp_client->ifindex, true);
}
static int send_request(GDHCPClient *dhcp_client)
debug(dhcp_client, "sending DHCP release request");
init_packet(dhcp_client, &packet, DHCPRELEASE);
- dhcp_get_random(&rand);
+ __connman_util_get_random(&rand);
packet.xid = rand;
packet.ciaddr = htonl(ciaddr);
/* if requested_ip is not valid, pick a new address*/
if (dhcp_client->requested_ip == 0) {
debug(dhcp_client, "pick a new random address");
- dhcp_client->requested_ip = ipv4ll_random_ip();
+ dhcp_client->requested_ip = arp_random_ip();
}
debug(dhcp_client, "sending IPV4LL probe request");
dhcp_client->state = IPV4LL_PROBE;
switch_listening_mode(dhcp_client, L_ARP);
}
- ipv4ll_send_arp_packet(dhcp_client->mac_address, 0,
+ arp_send_packet(dhcp_client->mac_address, 0,
dhcp_client->requested_ip, dhcp_client->ifindex);
if (dhcp_client->retry_times < PROBE_NUM) {
/*add a random timeout in range of PROBE_MIN to PROBE_MAX*/
- timeout = ipv4ll_random_delay_ms(PROBE_MAX-PROBE_MIN);
+ timeout = __connman_util_random_delay_ms(PROBE_MAX-PROBE_MIN);
timeout += PROBE_MIN*1000;
} else
timeout = (ANNOUNCE_WAIT * 1000);
debug(dhcp_client, "sending IPV4LL announce request");
- ipv4ll_send_arp_packet(dhcp_client->mac_address,
+ arp_send_packet(dhcp_client->mac_address,
dhcp_client->requested_ip,
dhcp_client->requested_ip,
dhcp_client->ifindex);
return TRUE;
}
-static void get_interface_mac_address(int index, uint8_t *mac_address)
-{
- struct ifreq ifr;
- int sk, err;
-
- sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (sk < 0) {
- perror("Open socket error");
- return;
- }
-
- memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_ifindex = index;
-
- err = ioctl(sk, SIOCGIFNAME, &ifr);
- if (err < 0) {
- perror("Get interface name error");
- goto done;
- }
-
- err = ioctl(sk, SIOCGIFHWADDR, &ifr);
- if (err < 0) {
- perror("Get mac address error");
- goto done;
- }
-
- memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
-
-done:
- close(sk);
-}
-
void g_dhcpv6_client_set_retransmit(GDHCPClient *dhcp_client)
{
if (!dhcp_client)
(*duid)[0] = 0;
(*duid)[1] = 1;
- get_interface_mac_address(index, &(*duid)[2 + 2 + 4]);
+ __connman_inet_get_interface_mac_address(index, &(*duid)[2 + 2 + 4]);
(*duid)[2] = 0;
(*duid)[3] = type;
duid_time = time(NULL) - DUID_TIME_EPOCH;
(*duid)[0] = 0;
(*duid)[1] = 3;
- get_interface_mac_address(index, &(*duid)[2 + 2]);
+ __connman_inet_get_interface_mac_address(index, &(*duid)[2 + 2]);
(*duid)[2] = 0;
(*duid)[3] = type;
break;
{
uint8_t buf[6];
- get_interface_mac_address(index, buf);
+ __connman_inet_get_interface_mac_address(index, buf);
memcpy(iaid, &buf[2], 4);
dhcp_client->iaid = iaid[0] << 24 |
goto error;
}
- get_interface_mac_address(ifindex, dhcp_client->mac_address);
+ __connman_inet_get_interface_mac_address(ifindex, dhcp_client->mac_address);
dhcp_client->listener_sockfd = -1;
dhcp_client->listen_mode = L_NONE;
g_direct_equal, NULL, remove_option_value);
dhcp_client->send_value_hash = g_hash_table_new_full(g_direct_hash,
g_direct_equal, NULL, g_free);
+ dhcp_client->secs_bcast_hash = g_hash_table_new(g_direct_hash,
+ g_direct_equal);
dhcp_client->request_list = NULL;
dhcp_client->require_list = NULL;
dhcp_client->duid = NULL;
dhcp_client->retry_times = 0;
dhcp_client->requested_ip = 0;
- dhcp_client->requested_ip = ipv4ll_random_ip();
+ dhcp_client->requested_ip = arp_random_ip();
/*first wait a random delay to avoid storm of arp request on boot*/
- timeout = ipv4ll_random_delay_ms(PROBE_WAIT);
+ timeout = __connman_util_random_delay_ms(PROBE_WAIT);
dhcp_client->retry_times++;
dhcp_client->timeout = g_timeout_add_full(G_PRIORITY_HIGH,
uint32_t ip_requested;
int source_conflict;
int target_conflict;
+ guint timeout_ms;
memset(&arp, 0, sizeof(arp));
bytes = read(dhcp_client->listener_sockfd, &arp, sizeof(arp));
arp.arp_op != htons(ARPOP_REQUEST))
return -EINVAL;
+ if (memcmp(arp.arp_sha, dhcp_client->mac_address, ETH_ALEN) == 0)
+ return 0;
+
ip_requested = htonl(dhcp_client->requested_ip);
source_conflict = !memcmp(arp.arp_spa, &ip_requested,
sizeof(ip_requested));
ipv4ll_stop(dhcp_client);
- if (dhcp_client->conflicts < MAX_CONFLICTS) {
- /*restart whole state machine*/
- dhcp_client->retry_times++;
- dhcp_client->timeout =
- g_timeout_add_full(G_PRIORITY_HIGH,
- ipv4ll_random_delay_ms(PROBE_WAIT),
- send_probe_packet,
- dhcp_client,
- NULL);
- }
- /* Here we got a lot of conflicts, RFC3927 states that we have
+ /* If we got a lot of conflicts, RFC3927 states that we have
* to wait RATE_LIMIT_INTERVAL before retrying,
- * but we just report failure.
*/
- else if (dhcp_client->no_lease_cb)
- dhcp_client->no_lease_cb(dhcp_client,
- dhcp_client->no_lease_data);
+ if (dhcp_client->conflicts < MAX_CONFLICTS)
+ timeout_ms = __connman_util_random_delay_ms(PROBE_WAIT);
+ else
+ timeout_ms = RATE_LIMIT_INTERVAL * 1000;
+ dhcp_client->retry_times++;
+ dhcp_client->timeout =
+ g_timeout_add_full(G_PRIORITY_HIGH,
+ timeout_ms,
+ send_probe_packet,
+ dhcp_client,
+ NULL);
return 0;
}
return FALSE;
}
+static void listener_watch_destroy(gpointer user_data)
+{
+ GDHCPClient *dhcp_client = user_data;
+ g_dhcp_client_unref(dhcp_client);
+}
+
static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
gpointer user_data);
dhcp_client->interface,
AF_INET);
} else if (listen_mode == L_ARP)
- listener_sockfd = ipv4ll_arp_socket(dhcp_client->ifindex);
+ listener_sockfd = arp_socket(dhcp_client->ifindex);
else
return -EIO;
dhcp_client->listener_watch =
g_io_add_watch_full(listener_channel, G_PRIORITY_HIGH,
G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
- listener_event, dhcp_client,
- NULL);
+ listener_event, g_dhcp_client_ref(dhcp_client),
+ listener_watch_destroy);
g_io_channel_unref(listener_channel);
return 0;
/*recalculate remaining rebind time*/
dhcp_client->T2 >>= 1;
if (dhcp_client->T2 > 60) {
- dhcp_get_random(&rand);
+ __connman_util_get_random(&rand);
dhcp_client->t2_timeout =
g_timeout_add_full(G_PRIORITY_HIGH,
dhcp_client->T2 * 1000 + (rand % 2000) - 1000,
dhcp_client->T1 >>= 1;
if (dhcp_client->T1 > 60) {
- dhcp_get_random(&rand);
+ __connman_util_get_random(&rand);
dhcp_client->t1_timeout = g_timeout_add_full(G_PRIORITY_HIGH,
dhcp_client->T1 * 1000 + (rand % 2000) - 1000,
continue_renew,
dhcp_client->state = REQUESTING;
- if (dst_addr.sin_addr.s_addr == INADDR_BROADCAST)
- dhcp_client->request_bcast = true;
- else
- dhcp_client->request_bcast = false;
+ /*
+ * RFC2131:
+ *
+ * If unicasting is not possible, the message MAY be
+ * sent as an IP broadcast using an IP broadcast address
+ * (preferably 0xffffffff) as the IP destination address
+ * and the link-layer broadcast address as the link-layer
+ * destination address.
+ *
+ * For interoperability reasons, if the response is an IP
+ * broadcast, let's reuse broadcast flag from DHCPDISCOVER
+ * to which the server has responded. Some servers are picky
+ * about this flag.
+ */
+ dhcp_client->request_bcast =
+ dst_addr.sin_addr.s_addr == INADDR_BROADCAST &&
+ g_hash_table_contains(dhcp_client->secs_bcast_hash,
+ GINT_TO_POINTER(packet.secs));
- debug(dhcp_client, "init ip %s -> %sadding broadcast flag",
- inet_ntoa(dst_addr.sin_addr),
- dhcp_client->request_bcast ? "" : "not ");
+ debug(dhcp_client, "init ip %s secs %hu -> broadcast flag %s",
+ inet_ntoa(dst_addr.sin_addr), packet.secs,
+ dhcp_client->request_bcast ? "on" : "off");
start_request(dhcp_client);
int re;
uint32_t addr;
uint64_t rand;
+ ClientState oldstate = dhcp_client->state;
remove_timeouts(dhcp_client);
if (re != 0)
return re;
- dhcp_get_random(&rand);
+ __connman_util_get_random(&rand);
dhcp_client->xid = rand;
dhcp_client->start = time(NULL);
+ g_hash_table_remove_all(dhcp_client->secs_bcast_hash);
}
- if (!last_address) {
+ if (!last_address || oldstate == DECLINED) {
addr = 0;
} else {
addr = ntohl(inet_addr(last_address));
case REBOOTING:
case REQUESTING:
case RELEASED:
+ case DECLINED:
case IPV4LL_PROBE:
case IPV4LL_ANNOUNCE:
case INFORMATION_REQ:
g_hash_table_destroy(dhcp_client->code_value_hash);
g_hash_table_destroy(dhcp_client->send_value_hash);
+ g_hash_table_destroy(dhcp_client->secs_bcast_hash);
g_free(dhcp_client);
}
#include "gdhcp.h"
#include "common.h"
+#include "../src/connman.h"
static const DHCPOption client_options[] = {
{ OPTION_IP, 0x01 }, /* subnet-mask */
{ OPTION_UNKNOWN, 0x00 },
};
-#define URANDOM "/dev/urandom"
-static int random_fd = -1;
-
-int dhcp_get_random(uint64_t *val)
-{
- int r;
-
- if (random_fd < 0) {
- random_fd = open(URANDOM, O_RDONLY);
- if (random_fd < 0) {
- r = -errno;
- *val = random();
-
- return r;
- }
- }
-
- if (read(random_fd, val, sizeof(uint64_t)) < 0) {
- r = -errno;
- *val = random();
-
- return r;
- }
-
- return 0;
-}
-
-void dhcp_cleanup_random(void)
-{
- if (random_fd < 0)
- return;
-
- close(random_fd);
- random_fd = -1;
-}
-
GDHCPOptionType dhcp_get_code_type(uint8_t code)
{
int i;
put_be32(data, option + OPT_DATA);
dhcp_add_binary_option(packet, option);
-
- return;
}
void dhcp_add_option_uint16(struct dhcp_packet *packet, uint8_t code,
put_be16(data, option + OPT_DATA);
dhcp_add_binary_option(packet, option);
-
- return;
}
void dhcp_add_option_uint8(struct dhcp_packet *packet, uint8_t code,
option[OPT_DATA] = data;
dhcp_add_binary_option(packet, option);
-
- return;
}
void dhcp_init_header(struct dhcp_packet *packet, char type)
packet->message = type;
- dhcp_get_random(&rand);
+ __connman_util_get_random(&rand);
id = rand;
packet->transaction_id[0] = (id >> 16) & 0xff;
*
*/
+#include <config.h>
#include <netinet/udp.h>
#include <netinet/ip.h>
[OPTION_U32] = 4,
};
-/* already defined within netinet/in.h if using GNU compiler */
-#ifndef __USE_GNU
+/* already defined within netinet/in.h if using glibc or musl */
+#ifndef HAVE_STRUCT_IN6_PKTINFO_IPI6_ADDR
struct in6_pktinfo {
struct in6_addr ipi6_addr; /* src/dst IPv6 address */
unsigned int ipi6_ifindex; /* send/recv interface index */
int g_dhcp_client_start(GDHCPClient *client, const char *last_address);
void g_dhcp_client_stop(GDHCPClient *client);
+int g_dhcp_client_decline(GDHCPClient *client, uint32_t requested);
GDHCPClient *g_dhcp_client_ref(GDHCPClient *client);
void g_dhcp_client_unref(GDHCPClient *client);
void g_dhcp_server_set_lease_added_cb(GDHCPServer *dhcp_server,
GDHCPLeaseAddedCb cb);
-int dhcp_get_random(uint64_t *val);
-void dhcp_cleanup_random(void);
-
#ifdef __cplusplus
}
#endif
#include <glib.h>
#include "ipv4ll.h"
#include "common.h"
+#include "../src/connman.h"
/**
* Return a random link local IP (in host byte order)
uint64_t rand;
do {
- dhcp_get_random(&rand);
+ __connman_util_get_random(&rand);
tmp = rand;
tmp = tmp & IN_CLASSB_HOST;
} while (tmp > (IN_CLASSB_HOST - 0x0200));
return ((LINKLOCAL_ADDR + 0x0100) + tmp);
}
-/**
- * Return a random delay in range of zero to secs*1000
- */
-guint ipv4ll_random_delay_ms(guint secs)
-{
- uint64_t rand;
-
- dhcp_get_random(&rand);
- return rand % (secs * 1000);
-}
-
-int ipv4ll_send_arp_packet(uint8_t* source_eth, uint32_t source_ip,
- uint32_t target_ip, int ifindex)
-{
- struct sockaddr_ll dest;
- struct ether_arp p;
- uint32_t ip_source;
- uint32_t ip_target;
- int fd, n;
-
- fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (fd < 0)
- return -errno;
-
- memset(&dest, 0, sizeof(dest));
- memset(&p, 0, sizeof(p));
-
- dest.sll_family = AF_PACKET;
- dest.sll_protocol = htons(ETH_P_ARP);
- dest.sll_ifindex = ifindex;
- dest.sll_halen = ETH_ALEN;
- memset(dest.sll_addr, 0xFF, ETH_ALEN);
- if (bind(fd, (struct sockaddr *)&dest, sizeof(dest)) < 0) {
- int err = errno;
- close(fd);
- return -err;
- }
-
- ip_source = htonl(source_ip);
- ip_target = htonl(target_ip);
- p.arp_hrd = htons(ARPHRD_ETHER);
- p.arp_pro = htons(ETHERTYPE_IP);
- p.arp_hln = ETH_ALEN;
- p.arp_pln = 4;
- p.arp_op = htons(ARPOP_REQUEST);
-
- memcpy(&p.arp_sha, source_eth, ETH_ALEN);
- memcpy(&p.arp_spa, &ip_source, sizeof(p.arp_spa));
- memcpy(&p.arp_tpa, &ip_target, sizeof(p.arp_tpa));
-
- n = sendto(fd, &p, sizeof(p), 0,
- (struct sockaddr*) &dest, sizeof(dest));
- if (n < 0)
- n = -errno;
-
- close(fd);
-
- return n;
-}
-
-int ipv4ll_arp_socket(int ifindex)
-{
- int fd;
- struct sockaddr_ll sock;
-
- fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (fd < 0)
- return fd;
-
- memset(&sock, 0, sizeof(sock));
-
- sock.sll_family = AF_PACKET;
- sock.sll_protocol = htons(ETH_P_ARP);
- sock.sll_ifindex = ifindex;
-
- if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) != 0) {
- int err = errno;
- close(fd);
- return -err;
- }
-
- return fd;
-}
dhcp_server->ref_count = 1;
dhcp_server->ifindex = ifindex;
dhcp_server->listener_sockfd = -1;
- dhcp_server->listener_watch = -1;
+ dhcp_server->listener_watch = 0;
dhcp_server->listener_channel = NULL;
dhcp_server->save_lease_func = NULL;
dhcp_server->debug_func = NULL;
debug(dhcp_server, "Received REQUEST NIP %d",
requested_nip);
if (requested_nip == 0) {
- requested_nip = packet.ciaddr;
+ requested_nip = ntohl(packet.ciaddr);
if (requested_nip == 0)
break;
}
void (*network_changed) (GSupplicantNetwork *network,
const char *property);
void (*network_associated) (GSupplicantNetwork *network);
+ void (*sta_authorized) (GSupplicantInterface *interface,
+ const char *addr);
+ void (*sta_deauthorized) (GSupplicantInterface *interface,
+ const char *addr);
void (*peer_found) (GSupplicantPeer *peer);
void (*peer_lost) (GSupplicantPeer *peer);
void (*peer_changed) (GSupplicantPeer *peer,
callbacks_pointer->network_associated(network);
}
+static void callback_sta_authorized(GSupplicantInterface *interface,
+ const char *addr)
+{
+ if (!callbacks_pointer)
+ return;
+
+ if (!callbacks_pointer->sta_authorized)
+ return;
+
+ callbacks_pointer->sta_authorized(interface, addr);
+}
+
+static void callback_sta_deauthorized(GSupplicantInterface *interface,
+ const char *addr)
+{
+ if (!callbacks_pointer)
+ return;
+
+ if (!callbacks_pointer->sta_deauthorized)
+ return;
+
+ callbacks_pointer->sta_deauthorized(interface, addr);
+}
+
static void callback_peer_found(GSupplicantPeer *peer)
{
if (!callbacks_pointer)
{
struct g_supplicant_bss *bss = data;
+ supplicant_dbus_property_call_cancel_all(bss);
+
g_free(bss->path);
g_free(bss);
}
static void interface_network_removed(DBusMessageIter *iter, void *user_data)
{
SUPPLICANT_DBG("");
- return;
}
static char *create_name(unsigned char *ssid, int ssid_len)
GSupplicantInterface *interface = bss->interface;
GSupplicantNetwork *network;
char *group;
+ bool is_new_network;
group = create_group(bss);
SUPPLICANT_DBG("New group created: %s", group);
if (network) {
g_free(group);
SUPPLICANT_DBG("Network %s already exist", network->name);
+ is_new_network = false;
goto done;
}
+ is_new_network = true;
+
network = g_try_new0(GSupplicantNetwork, 1);
if (!network) {
g_free(group);
network->frequency = bss->frequency;
network->best_bss = bss;
+ if ((bss->keymgmt & G_SUPPLICANT_KEYMGMT_WPS) != 0) {
+ network->wps = TRUE;
+ network->wps_capabilities = bss->wps_capabilities;
+ }
+
SUPPLICANT_DBG("New network %s created", network->name);
network->bss_table = g_hash_table_new_full(g_str_hash, g_str_equal,
/* We update network's WPS properties if only bss provides WPS. */
if ((bss->keymgmt & G_SUPPLICANT_KEYMGMT_WPS) != 0) {
network->wps = TRUE;
- network->wps_capabilities |= bss->wps_capabilities;
+ network->wps_capabilities = bss->wps_capabilities;
+
+ if (!is_new_network)
+ callback_network_changed(network, "WPSCapabilities");
}
/*
supplicant_dbus_property_get_all(bss->path,
SUPPLICANT_INTERFACE ".BSS",
- bss_property, bss, NULL);
+ bss_property, bss, bss);
bss_compute_security(bss);
if (add_or_replace_bss_to_network(bss) < 0)
GSupplicantNetwork *network;
struct g_supplicant_bss *bss = NULL;
const char *path = NULL;
+ bool is_current_network_bss = false;
dbus_message_iter_get_basic(iter, &path);
if (!path)
if (network->best_bss == bss) {
network->best_bss = NULL;
network->signal = BSS_UNKNOWN_STRENGTH;
+ is_current_network_bss = true;
}
g_hash_table_remove(bss_mapping, path);
update_network_signal(network);
- if (g_hash_table_size(network->bss_table) == 0)
+ if (g_hash_table_size(network->bss_table) == 0) {
g_hash_table_remove(interface->network_table, network->group);
+ } else {
+ if (is_current_network_bss && network->best_bss)
+ callback_network_changed(network, "");
+ }
}
static void set_config_methods(DBusMessageIter *iter, void *user_data)
interface_network_removed(iter, interface);
}
+static void signal_sta_authorized(const char *path, DBusMessageIter *iter)
+{
+ GSupplicantInterface *interface;
+ const char *addr = NULL;
+
+ SUPPLICANT_DBG("");
+
+ interface = g_hash_table_lookup(interface_table, path);
+ if (!interface)
+ return;
+
+ dbus_message_iter_get_basic(iter, &addr);
+ if (!addr)
+ return;
+
+ callback_sta_authorized(interface, addr);
+}
+
+static void signal_sta_deauthorized(const char *path, DBusMessageIter *iter)
+{
+ GSupplicantInterface *interface;
+ const char *addr = NULL;
+
+ SUPPLICANT_DBG("");
+
+ interface = g_hash_table_lookup(interface_table, path);
+ if (!interface)
+ return;
+
+ dbus_message_iter_get_basic(iter, &addr);
+ if (!addr)
+ return;
+
+ callback_sta_deauthorized(interface, addr);
+}
+
static void signal_bss_changed(const char *path, DBusMessageIter *iter)
{
GSupplicantInterface *interface;
GSupplicantNetwork *network;
GSupplicantSecurity old_security;
+ unsigned int old_wps_capabilities;
struct g_supplicant_bss *bss;
SUPPLICANT_DBG("");
if (old_security != bss->security) {
struct g_supplicant_bss *new_bss;
- SUPPLICANT_DBG("New network security for %s", bss->ssid);
+ SUPPLICANT_DBG("New network security for %s with path %s",
+ bss->ssid, bss->path);
- /* Security change policy:
- * - we first copy the current bss into a new one with
- * its own pointer (path)
- * - we remove the current bss related network which will
- * tell the plugin about such removal. This is done due
- * to the fact that a security change means a group change
- * so a complete network change.
- * (current bss becomes invalid as well)
- * - we add the new bss: it adds new network and tell the
- * plugin about it. */
+ /*
+ * Security change policy:
+ * - We first copy the current bss into a new one with
+ * its own pointer (path)
+ * - Clear the old bss pointer and remove the network completely
+ * if there are no more BSSs in the bss table.
+ * - The new bss will be added either to an existing network
+ * or an additional network will be created
+ */
new_bss = g_try_new0(struct g_supplicant_bss, 1);
if (!new_bss)
memcpy(new_bss, bss, sizeof(struct g_supplicant_bss));
new_bss->path = g_strdup(bss->path);
- g_hash_table_remove(interface->network_table, network->group);
+ if (network->best_bss == bss) {
+ network->best_bss = NULL;
+ network->signal = BSS_UNKNOWN_STRENGTH;
+ }
+
+ g_hash_table_remove(bss_mapping, path);
+
+ g_hash_table_remove(interface->bss_mapping, path);
+ g_hash_table_remove(network->bss_table, path);
+
+ update_network_signal(network);
+
+ if (g_hash_table_size(network->bss_table) == 0)
+ g_hash_table_remove(interface->network_table,
+ network->group);
if (add_or_replace_bss_to_network(new_bss) < 0) {
- /* Remove entries in hash tables to handle the
- * failure in add_or_replace_bss_to_network
+ /*
+ * Prevent a memory leak on failure in
+ * add_or_replace_bss_to_network
*/
- g_hash_table_remove(bss_mapping, path);
- g_hash_table_remove(interface->bss_mapping, path);
- g_hash_table_remove(network->bss_table, path);
+ SUPPLICANT_DBG("Failed to add bss %s to network table",
+ new_bss->path);
+ g_free(new_bss->path);
+ g_free(new_bss);
}
return;
}
+ old_wps_capabilities = network->wps_capabilities;
+
+ if (old_wps_capabilities != bss->wps_capabilities) {
+ network->wps_capabilities = bss->wps_capabilities;
+ callback_network_changed(network, "WPSCapabilities");
+ }
+
/* Consider only property changes of the connected BSS */
if (network == interface->current_network && bss != network->best_bss)
return;
{ SUPPLICANT_INTERFACE ".Interface", "BSSRemoved", signal_bss_removed },
{ SUPPLICANT_INTERFACE ".Interface", "NetworkAdded", signal_network_added },
{ SUPPLICANT_INTERFACE ".Interface", "NetworkRemoved", signal_network_removed },
+ { SUPPLICANT_INTERFACE ".Interface", "StaAuthorized", signal_sta_authorized },
+ { SUPPLICANT_INTERFACE ".Interface", "StaDeauthorized", signal_sta_deauthorized },
{ SUPPLICANT_INTERFACE ".BSS", "PropertiesChanged", signal_bss_changed },
supplicant_dbus_dict_append_basic(&dict, "Type",
DBUS_TYPE_STRING, &type);
- supplicant_dbus_dict_append_array(&dict, "SSIDs",
- DBUS_TYPE_STRING,
- append_ssids,
- data->scan_params);
+ if (data->scan_params->ssids) {
+ supplicant_dbus_dict_append_array(&dict, "SSIDs",
+ DBUS_TYPE_STRING,
+ append_ssids,
+ data->scan_params);
+ }
supplicant_add_scan_frequency(&dict, add_scan_frequencies,
data->scan_params);
} else
}
- if (g_str_has_prefix(ssid->phase2_auth, "EAP-")) {
+ if(g_strcmp0(ssid->phase2_auth, "GTC") == 0 && g_strcmp0(ssid->eap, "ttls") == 0)
+ phase2_auth = g_strdup_printf("autheap=%s", ssid->phase2_auth);
+ else if (g_str_has_prefix(ssid->phase2_auth, "EAP-")) {
phase2_auth = g_strdup_printf("autheap=%s",
ssid->phase2_auth + strlen("EAP-"));
} else
gpointer user_data)
{
GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source;
- GIOFunc func = (GIOFunc) callback;
+ GIOFunc func = (GIOFunc) (void (*) (void)) callback;
GIOCondition condition = watch->pollfd.revents;
DBG("source %p condition %u", source, condition);
DBG("");
- gnutls_channel = g_new(GIOGnuTLSChannel, 1);
+ gnutls_channel = g_new0(GIOGnuTLSChannel, 1);
channel = (GIOChannel *) gnutls_channel;
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
+#include <stdio.h>
#include <resolv.h>
#include <sys/types.h>
#include <sys/socket.h>
status = lookup->ipv4_status;
}
- debug(lookup->resolv, "lookup %p received %d results", lookup, n);
+ debug(lookup->resolv, "lookup %p received %d results", lookup, n-1);
g_queue_remove(lookup->resolv->lookup_queue, lookup);
destroy_lookup(lookup);
switch (rcode) {
case ns_r_noerror:
- status = G_RESOLV_RESULT_STATUS_SUCCESS;
+ if (count > 0)
+ status = G_RESOLV_RESULT_STATUS_SUCCESS;
+ else
+ status = G_RESOLV_RESULT_STATUS_NO_ANSWER;
break;
case ns_r_formerr:
status = G_RESOLV_RESULT_STATUS_FORMAT_ERROR;
G_RESOLV_RESULT_STATUS_NAME_ERROR,
G_RESOLV_RESULT_STATUS_NOT_IMPLEMENTED,
G_RESOLV_RESULT_STATUS_REFUSED,
+ G_RESOLV_RESULT_STATUS_NO_ANSWER,
} GResolvResultStatus;
typedef void (*GResolvResultFunc)(GResolvResultStatus status,
--- /dev/null
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2018 Commend International. 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.
+ *
+ */
+
+/*
+ * Address Conflict Detection (RFC 5227)
+ *
+ * based on DHCP client library with GLib integration,
+ * Copyright (C) 2009-2014 Intel Corporation. All rights reserved.
+ *
+ */
+
+#ifndef __CONNMAN_ACD_H
+#define __CONNMAN_ACD_H
+
+#include <stdint.h>
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct acd_host;
+
+struct acd_host *acd_host_new(int ifindex, const char* path);
+void acd_host_free(struct acd_host *acd);
+int acd_host_start(struct acd_host *acd, uint32_t ip);
+void acd_host_stop(struct acd_host *acd);
+
+typedef void (*acd_host_cb_t) (struct acd_host *acd, gpointer user_data);
+
+enum acd_host_event {
+ ACD_HOST_EVENT_IPV4_AVAILABLE,
+ ACD_HOST_EVENT_IPV4_LOST,
+ ACD_HOST_EVENT_IPV4_CONFLICT,
+ ACD_HOST_EVENT_IPV4_MAXCONFLICT,
+};
+
+void acd_host_register_event(struct acd_host *acd,
+ enum acd_host_event event,
+ acd_host_cb_t func,
+ gpointer user_data);
+
+void acd_host_append_dbus_property(struct acd_host *acd, DBusMessageIter *dict);
+
+unsigned int acd_host_get_conflicts_count(struct acd_host *acd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_ACD_H */
#define CONNMAN_SERVICE "net.connman"
#define CONNMAN_PATH "/net/connman"
-#define CONNMAN_DEBUG_INTERFACE CONNMAN_SERVICE ".Debug"
#define CONNMAN_ERROR_INTERFACE CONNMAN_SERVICE ".Error"
#define CONNMAN_AGENT_INTERFACE CONNMAN_SERVICE ".Agent"
#define CONNMAN_COUNTER_INTERFACE CONNMAN_SERVICE ".Counter"
#define CONNMAN_CLOCK_INTERFACE CONNMAN_SERVICE ".Clock"
#define CONNMAN_TASK_INTERFACE CONNMAN_SERVICE ".Task"
#define CONNMAN_SERVICE_INTERFACE CONNMAN_SERVICE ".Service"
-#define CONNMAN_PROVIDER_INTERFACE CONNMAN_SERVICE ".Provider"
#define CONNMAN_TECHNOLOGY_INTERFACE CONNMAN_SERVICE ".Technology"
#define CONNMAN_SESSION_INTERFACE CONNMAN_SERVICE ".Session"
#define CONNMAN_NOTIFICATION_INTERFACE CONNMAN_SERVICE ".Notification"
#define CONNMAN_DEVICE_PRIORITY_DEFAULT 0
#define CONNMAN_DEVICE_PRIORITY_HIGH 100
+struct connman_device_scan_params {
+ enum connman_service_type type;
+ const char *ssid;
+ unsigned int ssid_len;
+ const char *identity;
+ const char* passphrase;
+ const char *security;
+ bool force_full_scan;
+ void *user_data;
+};
+
struct connman_device;
struct connman_device *connman_device_create(const char *node,
bool connman_device_get_powered(struct connman_device *device);
int connman_device_set_scanning(struct connman_device *device,
enum connman_service_type type, bool scanning);
-bool connman_device_get_scanning(struct connman_device *device);
+bool connman_device_get_scanning(struct connman_device *device,
+ enum connman_service_type type);
void connman_device_reset_scanning(struct connman_device *device);
int connman_device_set_string(struct connman_device *device,
void (*remove) (struct connman_device *device);
int (*enable) (struct connman_device *device);
int (*disable) (struct connman_device *device);
- int (*scan)(enum connman_service_type type,
- struct connman_device *device,
- const char *ssid, unsigned int ssid_len,
- const char *identity, const char* passphrase,
- const char *security, void *user_data);
+ int (*scan)(struct connman_device *device,
+ struct connman_device_scan_params *params);
+ void (*stop_scan) (enum connman_service_type type,
+ struct connman_device *device);
int (*set_regdom) (struct connman_device *device,
const char *alpha2);
};
int connman_inet_ifup(int index);
int connman_inet_ifdown(int index);
+bool connman_inet_is_ifup(int index);
int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress);
int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress);
#include <stdbool.h>
#include <stdint.h>
+#include <dbus/dbus.h>
+
#include <connman/device.h>
#include <connman/ipconfig.h>
int connman_network_set_connected(struct connman_network *network,
bool connected);
bool connman_network_get_connected(struct connman_network *network);
+void connman_network_set_connected_dhcp_later(struct connman_network *network,
+ uint32_t sec);
bool connman_network_get_associating(struct connman_network *network);
int connman_network_driver_register(struct connman_network_driver *driver);
void connman_network_driver_unregister(struct connman_network_driver *driver);
+void connman_network_append_acddbus(DBusMessageIter *dict,
+ struct connman_network *network);
+
#ifdef __cplusplus
}
#endif
void (*idle_state) (bool idle);
};
-int connman_notifier_register(struct connman_notifier *notifier);
-void connman_notifier_unregister(struct connman_notifier *notifier);
+int connman_notifier_register(const struct connman_notifier *notifier);
+void connman_notifier_unregister(const struct connman_notifier *notifier);
#ifdef __cplusplus
}
const char *file, int line, const char *caller);
enum connman_service_type connman_service_get_type(struct connman_service *service);
+enum connman_service_state connman_service_get_state(struct connman_service *service);
char *connman_service_get_interface(struct connman_service *service);
+const char *connman_service_get_identifier(struct connman_service *service);
const char *connman_service_get_domainname(struct connman_service *service);
+const char *connman_service_get_dbuspath(struct connman_service *service);
char **connman_service_get_nameservers(struct connman_service *service);
char **connman_service_get_timeservers_config(struct connman_service *service);
char **connman_service_get_timeservers(struct connman_service *service);
bool connman_service_get_favorite(struct connman_service *service);
bool connman_service_get_autoconnect(struct connman_service *service);
+/* Return non-zero value to terminate the loop, zero to continue */
+typedef int (* connman_service_iterate_cb) (struct connman_service *service,
+ void *user_data);
+int connman_service_iterate_services(connman_service_iterate_cb cb,
+ void *user_data);
+
+struct connman_service *connman_service_get_default(void);
struct connman_service *connman_service_lookup_from_network(struct connman_network *network);
struct connman_service *connman_service_lookup_from_identifier(const char* identifier);
GSList *allowed_bearers;
char *allowed_interface;
bool source_ip_rule;
+ char *context_identifier;
};
typedef int (* connman_session_config_func_t) (struct connman_session *session,
void connman_technology_regdom_notify(struct connman_technology *technology,
const char *alpha2);
+enum connman_service_type connman_technology_get_type
+ (struct connman_technology *technology);
bool connman_technology_get_wifi_tethering(const char **ssid,
const char **psk);
bool connman_technology_is_tethering_allowed(enum connman_service_type type);
--- /dev/null
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2007-2013 Intel Corporation. All rights reserved.
+ * Copyright (C) 2018 GlobalLogic. 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
+ *
+ */
+
+#ifndef __CONNMAN_TETHERING_H
+#define __CONNMAN_TETHERING_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __connman_tethering_client_register(const char *addr);
+void __connman_tethering_client_unregister(const char *addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_TETHERING_H */
#define PROPERTY_CHANGED "PropertyChanged"
#define GET_CONNECTIONS "GetConnections"
#define VPN_CONNECT "Connect"
+#define VPN_CONNECT2 "Connect2"
#define VPN_DISCONNECT "Disconnect"
#define VPN_REMOVE "Remove"
#include <net/if.h>
#include <string.h>
#include <sys/ioctl.h>
+#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
if (connman_device_add_network(device, network) < 0) {
connman_network_unref(network);
+ g_free(ifname);
return;
}
if (!eth_tethering) {
- char group[16] = "cable";
+ char group[25] = "cable";
int vid, dsaport;
vid = get_vlan_vid(ifname);
connman_inet_ifup(index);
connman_inet_add_to_bridge(index, bridge);
+
+ gadget_tethering = true;
}
}
connman_inet_ifdown(index);
connman_technology_tethering_notify(technology, false);
+
+ gadget_tethering = false;
}
}
send_indication(IOSPM_FLIGHT_MODE, enabled);
}
-static struct connman_notifier iospm_notifier = {
+static const struct connman_notifier iospm_notifier = {
.name = "iospm",
.priority = CONNMAN_NOTIFIER_PRIORITY_DEFAULT,
.service_enabled= iospm_service_enabled,
#define IWD_AGENT_ERROR_INTERFACE "net.connman.iwd.Agent.Error"
#define AGENT_PATH "/net/connman/iwd_agent"
-enum iwd_device_state {
- IWD_DEVICE_STATE_UNKNOWN,
- IWD_DEVICE_STATE_CONNECTED,
- IWD_DEVICE_STATE_DISCONNECTED,
- IWD_DEVICE_STATE_CONNECTING,
- IWD_DEVICE_STATE_DISCONNECTING,
-};
-
struct iwd_adapter {
GDBusProxy *proxy;
char *path;
char *adapter;
char *name;
char *address;
- enum iwd_device_state state;
bool powered;
bool scanning;
struct connman_network *network;
};
-static enum iwd_device_state string2state(const char *str)
-{
- if (!strcmp(str, "connected"))
- return IWD_DEVICE_STATE_CONNECTED;
- else if (!strcmp(str, "disconnected"))
- return IWD_DEVICE_STATE_DISCONNECTED;
- else if (!strcmp(str, "connecting"))
- return IWD_DEVICE_STATE_CONNECTING;
- else if (!strcmp(str, "disconnecting"))
- return IWD_DEVICE_STATE_DISCONNECTING;
-
- return IWD_DEVICE_STATE_UNKNOWN;
-}
-
-static const char *state2string(enum iwd_device_state state)
-{
- switch (state) {
- case IWD_DEVICE_STATE_CONNECTED:
- return "connected";
- case IWD_DEVICE_STATE_DISCONNECTED:
- return "disconnected";
- case IWD_DEVICE_STATE_CONNECTING:
- return "connecting";
- case IWD_DEVICE_STATE_DISCONNECTING:
- return "disconnecting";
- default:
- break;
- }
-
- return "unknown";
-}
-
static const char *proxy_get_string(GDBusProxy *proxy, const char *property)
{
DBusMessageIter iter;
DBG("GetOrderedNetworks() failed");
}
+static const char *security_remap(const char *security)
+{
+ if (!g_strcmp0(security, "open"))
+ return "none";
+ else if (!g_strcmp0(security, "psk"))
+ return "psk";
+ else if (!g_strcmp0(security, "8021x"))
+ return "ieee8021x";
+
+ return "unknown";
+}
+
+static char *create_identifier(const char *path, const char *security)
+{
+ char *start, *end, *identifier;
+ char *_path = g_strdup(path);
+
+ /*
+ * _path is something like
+ * /0/4/5363686970686f6c5f427573696e6573735f454150_8021x
+ */
+ start = strrchr(_path, '/');
+ start++;
+ end = strchr(start, '_');
+ *end = '\0';
+
+ /*
+ * Create an ident which is identical to the corresponding
+ * wpa_supplicant identifier.
+ */
+ identifier = g_strdup_printf("%s_managed_%s", start,
+ security_remap(security));
+ g_free(_path);
+
+ return identifier;
+}
+
static void add_network(const char *path, struct iwd_network *iwdn)
{
struct iwd_device *iwdd;
- const char *identifier;
+ char *identifier;
iwdd = g_hash_table_lookup(devices, iwdn->device);
if (!iwdd)
return;
- identifier = strrchr(path, '/');
- identifier++; /* strip leading slash as well */
+ identifier = create_identifier(path, iwdn->type);
iwdn->network = connman_network_create(identifier,
CONNMAN_NETWORK_TYPE_WIFI);
connman_network_set_data(iwdn->network, iwdn);
strlen(iwdn->name));
connman_network_set_string(iwdn->network, "WiFi.Security",
iwdn->type);
+ connman_network_set_string(iwdn->network, "WiFi.Mode", "managed");
if (connman_device_add_network(iwdd->device, iwdn->network) < 0) {
connman_network_unref(iwdn->network);
connman_network_set_available(iwdn->network, true);
connman_network_set_group(iwdn->network, identifier);
+
+ g_free(identifier);
}
static void remove_network(struct iwd_network *iwdn)
iwdd->name = g_strdup(name);
DBG("%p name %s", path, iwdd->name);
- } else if (!strcmp(name, "State")) {
- const char *state;
-
- dbus_message_iter_get_basic(iter, &state);
- iwdd->state = string2state(state);
-
- DBG("%s state %s", path, state2string(iwdd->state));
} else if (!strcmp(name, "Powered")) {
dbus_bool_t powered;
iwdd->adapter = g_strdup(proxy_get_string(proxy, "Adapter"));
iwdd->name = g_strdup(proxy_get_string(proxy, "Name"));
iwdd->address = g_strdup(proxy_get_string(proxy, "Address"));
- iwdd->state = string2state(proxy_get_string(proxy, "State"));
iwdd->powered = proxy_get_bool(proxy, "Powered");
iwdd->scanning = proxy_get_bool(proxy, "Scanning");
- DBG("adapter %s name %s address %s state %s powered %d scanning %d",
+ DBG("adapter %s name %s address %s powered %d scanning %d",
iwdd->adapter, iwdd->name, iwdd->address,
- state2string(iwdd->state),
iwdd->powered, iwdd->scanning);
g_dbus_proxy_set_property_watch(iwdd->proxy,
current_service = NULL;
}
-static struct connman_notifier notifier = {
+static const struct connman_notifier notifier = {
.name = "nmcompat",
.priority = CONNMAN_NOTIFIER_PRIORITY_DEFAULT,
.default_changed = default_changed,
if (modem->context_list == NULL)
return;
- for (list = modem->context_list; list; list = list->next) {
+ list = modem->context_list;
+ while (list) {
struct network_context *context = list->data;
remove_cm_context(modem, context);
+
+ list = modem->context_list;
}
g_slist_free(modem->context_list);
modem->context_list = NULL;
create_proxy_configuration();
}
-static struct connman_notifier pacrunner_notifier = {
+static const struct connman_notifier pacrunner_notifier = {
.name = "pacrunner",
.default_changed = default_service_changed,
.proxy_changed = proxy_changed,
DBG("session %p uid %d", policy->session, uid);
- if (err < 0) {
- cleanup_config(policy);
+ if (err < 0)
goto err;
- }
pwd = getpwuid((uid_t)uid);
if (!pwd) {
return;
err:
- failed_create(NULL, cb, cbd->user_data, err);
+ failed_create(policy, cb, cbd->user_data, err);
g_free(cbd);
g_free(groups);
}
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <connman/dbus.h>
#include <connman/provider.h>
#include <connman/ipaddress.h>
+#include <connman/notifier.h>
#include <connman/vpn-dbus.h>
#include <connman/inet.h>
#include <gweb/gresolv.h>
struct connman_provider *provider;
int index;
DBusPendingCall *call;
+ DBusPendingCall *disconnect_call;
bool connect_pending;
struct config_create_data *cb_data;
+ char *service_ident;
char *state;
char *type;
g_free(cb_data);
}
+static bool provider_is_connected(struct connection_data *data)
+{
+ return data && (g_str_equal(data->state, "ready") ||
+ g_str_equal(data->state, "configuration"));
+}
+
static void set_provider_state(struct connection_data *data)
{
enum connman_provider_state state = CONNMAN_PROVIDER_STATE_UNKNOWN;
DBG("provider %p new state %s", data->provider, data->state);
+ if (!provider_is_connected(data)) {
+ g_free(data->service_ident);
+ data->service_ident = NULL;
+ }
+
if (g_str_equal(data->state, "ready")) {
state = CONNMAN_PROVIDER_STATE_READY;
goto set;
if (dbus_set_error_from_message(&error, reply)) {
int err = errorstr2val(error.name);
+
if (err != -EINPROGRESS) {
connman_error("Connect reply: %s (%s)", error.message,
error.name);
- dbus_error_free(&error);
-
DBG("data %p cb_data %p", data, cb_data);
+
if (cb_data) {
cb_data->callback(cb_data->message, err, NULL);
free_config_cb_data(cb_data);
data->cb_data = NULL;
}
- goto done;
}
+
dbus_error_free(&error);
}
* state.
*/
-done:
dbus_message_unref(reply);
dbus_pending_call_unref(call);
DBusPendingCall *call;
DBusMessage *message;
struct config_create_data *cb_data = user_data;
+ struct connman_service *transport = connman_service_get_default();
DBG("data %p user %p path %s sender %s", data, cb_data, data->path,
dbus_sender);
- data->connect_pending = false;
+ if (!transport) {
+ DBG("no default service, refusing to connect");
+ return -EINVAL;
+ }
-#define VPN_CONNECT2 "Connect2"
+ data->connect_pending = false;
/* We need to pass original dbus sender to connman-vpnd,
- * use a Connect2 method for that.
+ * use a Connect2 method for that if the original dbus sender is set.
+ * Connect method requires no parameter, Connect2 requires dbus sender
+ * name to be set.
*/
message = dbus_message_new_method_call(VPN_SERVICE, data->path,
VPN_CONNECTION_INTERFACE,
- VPN_CONNECT2);
+ dbus_sender && *dbus_sender ?
+ VPN_CONNECT2 : VPN_CONNECT);
if (!message)
return -ENOMEM;
- if (dbus_sender)
+ if (dbus_sender && *dbus_sender)
dbus_message_append_args(message, DBUS_TYPE_STRING,
&dbus_sender, NULL);
else
if (!dbus_connection_send_with_reply(connection, message,
&call, DBUS_TIMEOUT)) {
connman_error("Unable to call %s.%s()",
- VPN_CONNECTION_INTERFACE, VPN_CONNECT2);
+ VPN_CONNECTION_INTERFACE, dbus_sender && *dbus_sender ?
+ VPN_CONNECT2 : VPN_CONNECT);
dbus_message_unref(message);
return -EINVAL;
}
cb_data->path = g_strdup(data->path);
}
+ /*
+ * This is the service which (most likely) will be used
+ * as a transport for VPN connection.
+ */
+ g_free(data->service_ident);
+ data->service_ident =
+ g_strdup(connman_service_get_identifier(transport));
+ DBG("transport %s", data->service_ident);
+
dbus_pending_call_set_notify(call, connect_reply, data, NULL);
dbus_message_unref(message);
static void disconnect_reply(DBusPendingCall *call, void *user_data)
{
+ struct connection_data *data = user_data;
DBusMessage *reply;
DBusError error;
- if (!dbus_pending_call_get_completed(call))
- return;
-
DBG("user %p", user_data);
reply = dbus_pending_call_steal_reply(call);
done:
dbus_message_unref(reply);
-
dbus_pending_call_unref(call);
+ data->disconnect_call = NULL;
}
static int disconnect_provider(struct connection_data *data)
{
- DBusPendingCall *call;
+ bool sent;
DBusMessage *message;
DBG("data %p path %s", data, data->path);
+ if (data->disconnect_call) {
+ DBG("already disconnecting");
+ return -EINVAL;
+ }
+
message = dbus_message_new_method_call(VPN_SERVICE, data->path,
VPN_CONNECTION_INTERFACE,
VPN_DISCONNECT);
if (!message)
return -ENOMEM;
- if (!dbus_connection_send_with_reply(connection, message,
- &call, DBUS_TIMEOUT)) {
+ sent = dbus_connection_send_with_reply(connection, message,
+ &data->disconnect_call, DBUS_TIMEOUT);
+ dbus_message_unref(message);
+
+ if (!sent || !data->disconnect_call) {
connman_error("Unable to call %s.%s()",
VPN_CONNECTION_INTERFACE, VPN_DISCONNECT);
- dbus_message_unref(message);
return -EINVAL;
}
- if (!call) {
- dbus_message_unref(message);
- return -EINVAL;
- }
+ dbus_pending_call_set_notify(data->disconnect_call, disconnect_reply,
+ data, NULL);
- dbus_pending_call_set_notify(call, disconnect_reply, NULL, NULL);
-
- dbus_message_unref(message);
+ g_free(data->service_ident);
+ data->service_ident = NULL;
connman_provider_set_state(data->provider,
CONNMAN_PROVIDER_STATE_DISCONNECT);
- /*
- * We return 0 here instead of -EINPROGRESS because
- * __connman_service_disconnect() needs to return something
- * to gdbus so that gdbus will not call Disconnect() more
- * than once. This way we do not need to pass the dbus reply
- * message around the code.
- */
- return 0;
+ return -EINPROGRESS;
}
static int provider_disconnect(struct connman_provider *provider)
if (!data)
return -EINVAL;
- if (g_str_equal(data->state, "ready") ||
- g_str_equal(data->state, "configuration"))
+ if (provider_is_connected(data))
return disconnect_provider(data);
return 0;
{
DBG("data %p", data);
- if (g_str_equal(data->state, "ready") ||
- g_str_equal(data->state, "configuration"))
+ if (provider_is_connected(data))
connman_provider_disconnect(data->provider);
- if (data->call)
- dbus_pending_call_cancel(data->call);
-
connman_provider_set_data(data->provider, NULL);
-
connman_provider_remove(data->provider);
-
data->provider = NULL;
}
if (data->provider)
destroy_provider(data);
+ if (data->call) {
+ dbus_pending_call_cancel(data->call);
+ dbus_pending_call_unref(data->call);
+ }
+
+ if (data->disconnect_call) {
+ dbus_pending_call_cancel(data->disconnect_call);
+ dbus_pending_call_unref(data->disconnect_call);
+ }
+
+ g_free(data->service_ident);
g_free(data->path);
g_free(data->ident);
g_free(data->state);
g_free(data->type);
g_free(data->name);
g_free(data->host);
- g_free(data->host_ip);
+ g_strfreev(data->host_ip);
g_free(data->domain);
g_hash_table_destroy(data->server_routes);
g_hash_table_destroy(data->user_routes);
return TRUE;
}
+static int vpn_find_online_transport_cb(struct connman_service *service,
+ void *user_data)
+{
+ if (connman_service_get_type(service) != CONNMAN_SERVICE_TYPE_VPN) {
+ switch (connman_service_get_state(service)) {
+ case CONNMAN_SERVICE_STATE_ONLINE:
+ *((struct connman_service**)user_data) = service;
+ return 1;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static struct connman_service *vpn_find_online_transport()
+{
+ struct connman_service *service = NULL;
+
+ connman_service_iterate_services(vpn_find_online_transport_cb,
+ &service);
+ return service;
+}
+
+static bool vpn_is_valid_transport(struct connman_service *transport)
+{
+ if (transport) {
+ struct connman_service *online;
+
+ switch (connman_service_get_state(transport)) {
+ case CONNMAN_SERVICE_STATE_READY:
+ online = vpn_find_online_transport();
+
+ /* Stay connected if there are no online services */
+ if (!online)
+ return true;
+
+ DBG("%s is ready, %s is online, disconnecting",
+ connman_service_get_identifier(transport),
+ connman_service_get_identifier(online));
+ break;
+
+ case CONNMAN_SERVICE_STATE_ONLINE:
+ online = vpn_find_online_transport();
+
+ /* Check if our transport is still the default */
+ if (online == transport)
+ return true;
+
+ DBG("%s is replaced by %s as default, disconnecting",
+ connman_service_get_identifier(transport),
+ connman_service_get_identifier(online));
+ break;
+
+ default:
+ break;
+ }
+ } else {
+ DBG("transport gone");
+ }
+
+ return false;
+}
+
+static void vpn_disconnect_check_provider(struct connection_data *data)
+{
+ if (data->service_ident && provider_is_connected(data)) {
+ struct connman_service *service =
+ connman_service_lookup_from_identifier
+ (data->service_ident);
+
+ if (!vpn_is_valid_transport(service)) {
+ disconnect_provider(data);
+ }
+ }
+}
+
+static void vpn_disconnect_check()
+{
+ GHashTableIter iter;
+ gpointer value;
+
+ DBG("");
+ g_hash_table_iter_init(&iter, vpn_connections);
+ while (g_hash_table_iter_next(&iter, NULL, &value))
+ vpn_disconnect_check_provider(value);
+}
+
+static void vpn_service_add(struct connman_service *service, const char *name)
+{
+ vpn_disconnect_check();
+}
+
+static void vpn_service_list_changed(struct connman_service *service)
+{
+ vpn_disconnect_check();
+}
+
+static void vpn_service_state_changed(struct connman_service *service,
+ enum connman_service_state state)
+{
+ vpn_disconnect_check();
+}
+
+static const struct connman_notifier vpn_notifier = {
+ .name = "vpn",
+ .priority = CONNMAN_NOTIFIER_PRIORITY_DEFAULT,
+ .default_changed = vpn_service_list_changed,
+ .service_add = vpn_service_add,
+ .service_remove = vpn_service_list_changed,
+ .service_state_changed = vpn_service_state_changed
+};
+
static int vpn_init(void)
{
int err;
vpnd_created(connection, &provider_driver);
}
+ connman_notifier_register(&vpn_notifier);
return err;
remove:
g_dbus_remove_watch(connection, removed_watch);
g_dbus_remove_watch(connection, property_watch);
+ connman_notifier_unregister(&vpn_notifier);
connman_provider_driver_unregister(&provider_driver);
if (vpn_connections)
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
-#include <linux/if_arp.h>
-#include <linux/wireless.h>
#include <net/ethernet.h>
+#include <linux/wireless.h>
#ifndef IFF_LOWER_UP
#define IFF_LOWER_UP 0x10000
#include <connman/provision.h>
#include <connman/utsname.h>
#include <connman/machine.h>
+#include <connman/tethering.h>
#include <gsupplicant/gsupplicant.h>
#define FAVORITE_MAXIMUM_RETRIES 2
#define BGSCAN_DEFAULT "simple:30:-45:300"
-#define AUTOSCAN_DEFAULT "exponential:3:300"
+#define AUTOSCAN_EXPONENTIAL "exponential:3:300"
+#define AUTOSCAN_SINGLE "single:3"
#define P2P_FIND_TIMEOUT 30
#define P2P_CONNECTION_TIMEOUT 100
WIFI_AP_NOT_SUPPORTED = 2,
};
+enum wifi_scanning_type {
+ WIFI_SCANNING_UNKNOWN = 0,
+ WIFI_SCANNING_PASSIVE = 1,
+ WIFI_SCANNING_ACTIVE = 2,
+};
+
struct hidden_params {
char ssid[32];
unsigned int ssid_len;
* autoscan "emulation".
*/
struct autoscan_params *autoscan;
-
+ enum wifi_scanning_type scanning_type;
GSupplicantScanParams *scan_params;
unsigned int p2p_find_timeout;
unsigned int p2p_connection_timeout;
static GList *pending_wifi_device = NULL;
static GList *p2p_iface_list = NULL;
-bool wfd_service_registered = false;
+static bool wfd_service_registered = false;
static void start_autoscan(struct connman_device *device);
static int tech_set_tethering(struct connman_technology *technology,
params = fill_in_peer_service_params(specification,
specification_length, query,
query_length, version);
- if (!params) {
- ret = -ENOMEM;
+ if (!params)
continue;
- }
if (!found) {
ret_f = g_supplicant_interface_p2p_add_service(iface,
params = fill_in_peer_service_params(specification,
specification_length, query,
query_length, version);
- if (!params) {
- ret = -ENOMEM;
+ if (!params)
continue;
- }
ret = g_supplicant_interface_p2p_del_service(iface, params);
if (ret != 0 && ret != -EINPROGRESS)
autoscan = wifi->autoscan;
- if (autoscan->timeout == 0 && autoscan->interval == 0)
+ autoscan->interval = 0;
+
+ if (autoscan->timeout == 0)
return;
g_source_remove(autoscan->timeout);
-
autoscan->timeout = 0;
- autoscan->interval = 0;
connman_device_unref(device);
}
remove_pending_wifi_device(wifi);
- if (wifi->p2p_find_timeout) {
+ if (connman_device_get_scanning(device, CONNMAN_SERVICE_TYPE_P2P)) {
g_source_remove(wifi->p2p_find_timeout);
connman_device_unref(wifi->device);
}
if (wifi->tethering)
return -EBUSY;
- if (connman_device_get_scanning(device))
+ if (connman_device_get_scanning(device, CONNMAN_SERVICE_TYPE_WIFI))
return -EALREADY;
connman_device_ref(device);
return scan_callback(ret, interface, user_data);
}
- scanning = connman_device_get_scanning(device);
+ scanning = connman_device_get_scanning(device, CONNMAN_SERVICE_TYPE_WIFI);
if (scanning) {
connman_device_set_scanning(device,
throw_wifi_scan(wifi->device, scan_callback_hidden);
+ /*
+ * In case BackgroundScanning is disabled, interval will reach the
+ * limit exactly after the very first passive scanning. It allows
+ * to ensure at most one passive scan is performed in such cases.
+ */
+ if (!connman_setting_get_bool("BackgroundScanning") &&
+ interval == autoscan->limit) {
+ g_source_remove(autoscan->timeout);
+ autoscan->timeout = 0;
+
+ connman_device_unref(device);
+
+ return FALSE;
+ }
+
set_interval:
DBG("interval %d", interval);
int limit;
int base;
- DBG("Emulating autoscan");
+ DBG("");
list_params = g_strsplit(params, ":", 0);
if (list_params == 0)
return NULL;
- if (g_strv_length(list_params) < 3) {
+ if (!g_strcmp0(list_params[0], "exponential") &&
+ g_strv_length(list_params) == 3) {
+ base = atoi(list_params[1]);
+ limit = atoi(list_params[2]);
+ } else if (!g_strcmp0(list_params[0], "single") &&
+ g_strv_length(list_params) == 2)
+ base = limit = atoi(list_params[1]);
+ else {
g_strfreev(list_params);
return NULL;
}
- base = atoi(list_params[1]);
- limit = atoi(list_params[2]);
+ DBG("Setup %s autoscanning", list_params[0]);
g_strfreev(list_params);
static void setup_autoscan(struct wifi_data *wifi)
{
- if (!wifi->autoscan)
- wifi->autoscan = parse_autoscan_params(AUTOSCAN_DEFAULT);
+ /*
+ * If BackgroundScanning is enabled, setup exponential
+ * autoscanning if it has not been previously done.
+ */
+ if (connman_setting_get_bool("BackgroundScanning")) {
+ wifi->autoscan = parse_autoscan_params(AUTOSCAN_EXPONENTIAL);
+ return;
+ }
- start_autoscan(wifi->device);
+ /*
+ * On the contrary, if BackgroundScanning is disabled, update autoscan
+ * parameters based on the type of scanning that is being performed.
+ */
+ if (wifi->autoscan) {
+ g_free(wifi->autoscan);
+ wifi->autoscan = NULL;
+ }
+
+ switch (wifi->scanning_type) {
+ case WIFI_SCANNING_PASSIVE:
+ /* Do not setup autoscan. */
+ break;
+ case WIFI_SCANNING_ACTIVE:
+ /* Setup one single passive scan after active. */
+ wifi->autoscan = parse_autoscan_params(AUTOSCAN_SINGLE);
+ break;
+ case WIFI_SCANNING_UNKNOWN:
+ /* Setup autoscan in this case but we should never fall here. */
+ wifi->autoscan = parse_autoscan_params(AUTOSCAN_SINGLE);
+ break;
+ }
}
static void finalize_interface_creation(struct wifi_data *wifi)
connman_device_set_powered(wifi->device, true);
- if (!connman_setting_get_bool("BackgroundScanning"))
- return;
-
if (wifi->p2p_device)
return;
- setup_autoscan(wifi);
+ if (!wifi->autoscan)
+ setup_autoscan(wifi);
+
+ start_autoscan(wifi->device);
}
static void interface_create_callback(int result,
stop_autoscan(device);
- if (wifi->p2p_find_timeout) {
+ if (connman_device_get_scanning(device, CONNMAN_SERVICE_TYPE_P2P)) {
g_source_remove(wifi->p2p_find_timeout);
wifi->p2p_find_timeout = 0;
connman_device_set_scanning(device, CONNMAN_SERVICE_TYPE_P2P, false);
}
/* In case of a user scan, device is still referenced */
- if (connman_device_get_scanning(device)) {
+ if (connman_device_get_scanning(device, CONNMAN_SERVICE_TYPE_WIFI)) {
connman_device_set_scanning(device,
CONNMAN_SERVICE_TYPE_WIFI, false);
connman_device_unref(wifi->device);
return num_ssids;
}
+static void wifi_update_scanner_type(struct wifi_data *wifi,
+ enum wifi_scanning_type new_type)
+{
+ DBG("");
+
+ if (!wifi || wifi->scanning_type == new_type)
+ return;
+
+ wifi->scanning_type = new_type;
+
+ setup_autoscan(wifi);
+}
+
static int wifi_scan_simple(struct connman_device *device)
{
+ struct wifi_data *wifi = connman_device_get_data(device);
+
reset_autoscan(device);
+ /* Distinguish between devices performing passive and active scanning */
+ if (wifi)
+ wifi_update_scanner_type(wifi, WIFI_SCANNING_PASSIVE);
+
return throw_wifi_scan(device, scan_callback_hidden);
}
connman_device_set_scanning(device, CONNMAN_SERVICE_TYPE_P2P, false);
connman_device_unref(device);
- reset_autoscan(device);
+ start_autoscan(device);
return FALSE;
}
* Note that the hidden scan is only used when connecting to this specific
* hidden AP first time. It is not used when system autoconnects to hidden AP.
*/
-static int wifi_scan(enum connman_service_type type,
- struct connman_device *device,
- const char *ssid, unsigned int ssid_len,
- const char *identity, const char* passphrase,
- const char *security, void *user_data)
+static int wifi_scan(struct connman_device *device,
+ struct connman_device_scan_params *params)
{
struct wifi_data *wifi = connman_device_get_data(device);
GSupplicantScanParams *scan_params = NULL;
if (wifi->tethering)
return -EBUSY;
- if (type == CONNMAN_SERVICE_TYPE_P2P)
+ if (params->type == CONNMAN_SERVICE_TYPE_P2P)
return p2p_find(device);
- DBG("device %p wifi %p hidden ssid %s", device, wifi->interface, ssid);
+ DBG("device %p wifi %p hidden ssid %s", device, wifi->interface,
+ params->ssid);
- scanning = connman_device_get_scanning(device);
+ scanning = connman_device_get_scanning(device, CONNMAN_SERVICE_TYPE_WIFI);
- if (!ssid || ssid_len == 0 || ssid_len > 32) {
+ if (!params->ssid || params->ssid_len == 0 || params->ssid_len > 32) {
if (scanning)
return -EALREADY;
return -ENOMEM;
}
- memcpy(scan_ssid->ssid, ssid, ssid_len);
- scan_ssid->ssid_len = ssid_len;
+ memcpy(scan_ssid->ssid, params->ssid, params->ssid_len);
+ scan_ssid->ssid_len = params->ssid_len;
scan_params->ssids = g_slist_prepend(scan_params->ssids,
scan_ssid);
scan_params->num_ssids = 1;
wifi->hidden = NULL;
}
- memcpy(hidden->ssid, ssid, ssid_len);
- hidden->ssid_len = ssid_len;
- hidden->identity = g_strdup(identity);
- hidden->passphrase = g_strdup(passphrase);
- hidden->security = g_strdup(security);
- hidden->user_data = user_data;
+ memcpy(hidden->ssid, params->ssid, params->ssid_len);
+ hidden->ssid_len = params->ssid_len;
+ hidden->identity = g_strdup(params->identity);
+ hidden->passphrase = g_strdup(params->passphrase);
+ hidden->security = g_strdup(params->security);
+ hidden->user_data = params->user_data;
wifi->hidden = hidden;
if (scanning) {
} else if (wifi->connected) {
g_supplicant_free_scan_params(scan_params);
return wifi_scan_simple(device);
- } else {
+ } else if (!params->force_full_scan) {
ret = get_latest_connections(driver_max_ssids, scan_params);
if (ret <= 0) {
g_supplicant_free_scan_params(scan_params);
}
}
+ /* Distinguish between devices performing passive and active scanning */
+ wifi_update_scanner_type(wifi, WIFI_SCANNING_ACTIVE);
+
connman_device_ref(device);
reset_autoscan(device);
return ret;
}
+static void wifi_stop_scan(enum connman_service_type type,
+ struct connman_device *device)
+{
+ struct wifi_data *wifi = connman_device_get_data(device);
+
+ DBG("device %p wifi %p", device, wifi);
+
+ if (!wifi)
+ return;
+
+ if (type == CONNMAN_SERVICE_TYPE_P2P) {
+ if (connman_device_get_scanning(device, CONNMAN_SERVICE_TYPE_P2P)) {
+ g_source_remove(wifi->p2p_find_timeout);
+ p2p_find_stop(device);
+ }
+ }
+}
+
static void wifi_regdom_callback(int result,
const char *alpha2,
void *user_data)
.enable = wifi_enable,
.disable = wifi_disable,
.scan = wifi_scan,
+ .stop_scan = wifi_stop_scan,
.set_regdom = wifi_set_regdom,
};
return;
}
- if (wifi->network) {
+ if (wifi->network != wifi->pending_network)
connman_network_set_connected(wifi->network, false);
- wifi->network = NULL;
- }
+ wifi->network = NULL;
wifi->disconnecting = false;
wifi->connected = false;
if (!wifi)
return;
+ device = wifi->device;
+ if (!device)
+ return;
+
if (state == G_SUPPLICANT_STATE_COMPLETED) {
if (wifi->tethering_param) {
g_free(wifi->tethering_param->ssid);
g_free(wifi->tethering_param);
wifi->tethering_param = NULL;
}
- }
- device = wifi->device;
- if (!device)
- return;
+ if (wifi->tethering)
+ stop_autoscan(device);
+ }
if (g_supplicant_interface_get_ready(interface) &&
!wifi->interface_ready) {
default:
break;
}
- connman_network_set_connected(network, false);
- connman_network_set_associating(network, false);
+
+ if (network != wifi->pending_network) {
+ connman_network_set_connected(network, false);
+ connman_network_set_associating(network, false);
+ }
wifi->disconnecting = false;
start_autoscan(device);
g_free(wifi->tethering_param);
wifi->tethering_param = NULL;
}
-
- return;
}
static unsigned char calculate_strength(GSupplicantNetwork *supplicant_network)
connman_network_set_strength(network,
calculate_strength(supplicant_network));
connman_network_set_bool(network, "WiFi.WPS", wps);
+ connman_network_set_bool(network, "WiFi.WPSAdvertising",
+ wps_advertizing);
if (wps) {
/* Is AP advertizing for WPS association?
struct wifi_data *wifi;
const char *name, *identifier;
struct connman_network *connman_network;
+ bool update_needed;
interface = g_supplicant_network_get_interface(network);
wifi = g_supplicant_interface_get_data(interface);
if (!connman_network)
return;
- if (g_str_equal(property, "Signal")) {
- connman_network_set_strength(connman_network,
+ if (g_str_equal(property, "WPSCapabilities")) {
+ bool wps;
+ bool wps_pbc;
+ bool wps_ready;
+ bool wps_advertizing;
+
+ wps = g_supplicant_network_get_wps(network);
+ wps_pbc = g_supplicant_network_is_wps_pbc(network);
+ wps_ready = g_supplicant_network_is_wps_active(network);
+ wps_advertizing =
+ g_supplicant_network_is_wps_advertizing(network);
+
+ connman_network_set_bool(connman_network, "WiFi.WPS", wps);
+ connman_network_set_bool(connman_network,
+ "WiFi.WPSAdvertising", wps_advertizing);
+
+ if (wps) {
+ /*
+ * Is AP advertizing for WPS association?
+ * If so, we decide to use WPS by default
+ */
+ if (wps_ready && wps_pbc && wps_advertizing)
+ connman_network_set_bool(connman_network,
+ "WiFi.UseWPS", true);
+ }
+
+ update_needed = true;
+ } else if (g_str_equal(property, "Signal")) {
+ connman_network_set_strength(connman_network,
calculate_strength(network));
- connman_network_update(connman_network);
- }
+ update_needed = true;
+ } else
+ update_needed = false;
+
+ if (update_needed)
+ connman_network_update(connman_network);
}
static void network_associated(GSupplicantNetwork *network)
if (!wifi)
return;
+ /* P2P networks must not be treated as WiFi networks */
+ if (wifi->p2p_connecting || wifi->p2p_device)
+ return;
+
identifier = g_supplicant_network_get_identifier(network);
connman_network = connman_device_get_network(wifi->device, identifier);
interface_state(interface);
}
+static void sta_authorized(GSupplicantInterface *interface,
+ const char *addr)
+{
+ struct wifi_data *wifi = g_supplicant_interface_get_data(interface);
+
+ DBG("wifi %p station %s authorized", wifi, addr);
+
+ if (!wifi || !wifi->tethering)
+ return;
+
+ __connman_tethering_client_register(addr);
+}
+
+static void sta_deauthorized(GSupplicantInterface *interface,
+ const char *addr)
+{
+ struct wifi_data *wifi = g_supplicant_interface_get_data(interface);
+
+ DBG("wifi %p station %s deauthorized", wifi, addr);
+
+ if (!wifi || !wifi->tethering)
+ return;
+
+ __connman_tethering_client_unregister(addr);
+}
+
static void apply_peer_services(GSupplicantPeer *peer,
struct connman_peer *connman_peer)
{
.network_removed = network_removed,
.network_changed = network_changed,
.network_associated = network_associated,
+ .sta_authorized = sta_authorized,
+ .sta_deauthorized = sta_deauthorized,
.peer_found = peer_found,
.peer_lost = peer_lost,
.peer_changed = peer_changed,
d @runstatedir@/connman - - - -
-L+ /etc/resolv.conf - - - - @runstatedir@/connman/resolv.conf
+L /etc/resolv.conf - - - - @runstatedir@/connman/resolv.conf
--- /dev/null
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2018 Commend International GmbH. 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.
+ *
+ */
+
+/*
+ * Address Conflict Detection (RFC 5227)
+ *
+ * based on DHCP client library with GLib integration,
+ * Copyright (C) 2009-2014 Intel Corporation. All rights reserved.
+ *
+ */
+
+#include <netinet/if_ether.h>
+#include <net/if_arp.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "connman.h"
+#include <connman/acd.h>
+#include <connman/log.h>
+#include <connman/inet.h>
+#include <connman/dbus.h>
+#include <glib.h>
+#include "src/shared/arp.h"
+
+enum acd_state {
+ ACD_STATE_PROBE,
+ ACD_STATE_ANNOUNCE,
+ ACD_STATE_MONITOR,
+ ACD_STATE_DEFEND,
+};
+
+static const char* acd_state_texts[] = {
+ "PROBE",
+ "ANNOUNCE",
+ "MONITOR",
+ "DEFEND"
+};
+
+struct acd_host {
+ enum acd_state state;
+ int ifindex;
+ char *interface;
+ uint8_t mac_address[6];
+ uint32_t requested_ip; /* host byte order */
+
+ /* address conflict fields */
+ uint32_t ac_ip; /* host byte order */
+ uint8_t ac_mac[6];
+ gint64 ac_timestamp;
+ bool ac_resolved;
+ const char *path;
+
+ bool listen_on;
+ int listener_sockfd;
+ unsigned int retry_times;
+ unsigned int conflicts;
+ guint timeout;
+ guint listener_watch;
+
+ acd_host_cb_t ipv4_available_cb;
+ gpointer ipv4_available_data;
+ acd_host_cb_t ipv4_lost_cb;
+ gpointer ipv4_lost_data;
+ acd_host_cb_t ipv4_conflict_cb;
+ gpointer ipv4_conflict_data;
+ acd_host_cb_t ipv4_max_conflicts_cb;
+ gpointer ipv4_max_conflicts_data;
+};
+
+static int start_listening(struct acd_host *acd);
+static void stop_listening(struct acd_host *acd);
+static gboolean acd_listener_event(GIOChannel *channel, GIOCondition condition,
+ gpointer acd_data);
+static int acd_recv_arp_packet(struct acd_host *acd);
+static void send_probe_packet(gpointer acd_data);
+static gboolean acd_probe_timeout(gpointer acd_data);
+static gboolean send_announce_packet(gpointer acd_data);
+static gboolean acd_announce_timeout(gpointer acd_data);
+static gboolean acd_defend_timeout(gpointer acd_data);
+
+/* for D-Bus property */
+static void report_conflict(struct acd_host *acd, const struct ether_arp* arp);
+
+static void debug(struct acd_host *acd, const char *format, ...)
+{
+ char str[256];
+ va_list ap;
+
+ va_start(ap, format);
+
+ if (vsnprintf(str, sizeof(str), format, ap) > 0)
+ connman_info("ACD index %d: %s", acd->ifindex, str);
+
+ va_end(ap);
+}
+
+void acd_host_free(struct acd_host *acd)
+{
+ if (!acd)
+ return;
+
+ g_free(acd->interface);
+ g_free(acd);
+}
+
+struct acd_host *acd_host_new(int ifindex, const char *path)
+{
+ struct acd_host *acd;
+
+ if (ifindex < 0) {
+ connman_error("Invalid interface index %d", ifindex);
+ return NULL;
+ }
+
+ acd = g_try_new0(struct acd_host, 1);
+ if (!acd) {
+ connman_error("Could not allocate ACD data structure");
+ return NULL;
+ }
+
+ acd->interface = connman_inet_ifname(ifindex);
+ if (!acd->interface) {
+ connman_error("Interface with index %d is not available", ifindex);
+ goto error;
+ }
+
+ if (!connman_inet_is_ifup(ifindex)) {
+ connman_error("Interface with index %d and name %s is down", ifindex,
+ acd->interface);
+ goto error;
+ }
+
+ __connman_inet_get_interface_mac_address(ifindex, acd->mac_address);
+
+ acd->listener_sockfd = -1;
+ acd->listen_on = false;
+ acd->ifindex = ifindex;
+ acd->listener_watch = 0;
+ acd->retry_times = 0;
+
+ acd->ipv4_available_cb = NULL;
+ acd->ipv4_lost_cb = NULL;
+ acd->ipv4_conflict_cb = NULL;
+ acd->ipv4_max_conflicts_cb = NULL;
+
+ acd->ac_ip = 0;
+ memset(acd->ac_mac, 0, sizeof(acd->ac_mac));
+ acd->ac_timestamp = 0;
+ acd->ac_resolved = true;
+ acd->path = path;
+
+ return acd;
+
+error:
+ acd_host_free(acd);
+ return NULL;
+}
+
+static void remove_timeout(struct acd_host *acd)
+{
+ if (acd->timeout > 0)
+ g_source_remove(acd->timeout);
+
+ acd->timeout = 0;
+}
+
+static int start_listening(struct acd_host *acd)
+{
+ GIOChannel *listener_channel;
+ int listener_sockfd;
+
+ if (acd->listen_on)
+ return 0;
+
+ debug(acd, "start listening");
+
+ listener_sockfd = arp_socket(acd->ifindex);
+ if (listener_sockfd < 0)
+ return -EIO;
+
+ listener_channel = g_io_channel_unix_new(listener_sockfd);
+ if (!listener_channel) {
+ /* Failed to create listener channel */
+ close(listener_sockfd);
+ return -EIO;
+ }
+
+ acd->listen_on = true;
+ acd->listener_sockfd = listener_sockfd;
+
+ g_io_channel_set_close_on_unref(listener_channel, TRUE);
+ acd->listener_watch =
+ g_io_add_watch_full(listener_channel, G_PRIORITY_HIGH,
+ G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
+ acd_listener_event, acd,
+ NULL);
+ g_io_channel_unref(listener_channel);
+
+ return 0;
+}
+
+static void stop_listening(struct acd_host *acd)
+{
+ if (!acd->listen_on)
+ return;
+
+ if (acd->listener_watch > 0)
+ g_source_remove(acd->listener_watch);
+ acd->listen_on = FALSE;
+ acd->listener_sockfd = -1;
+ acd->listener_watch = 0;
+}
+
+static gboolean acd_listener_event(GIOChannel *channel, GIOCondition condition,
+ gpointer acd_data)
+{
+ struct acd_host *acd = acd_data;
+
+ if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+ acd->listener_watch = 0;
+ return FALSE;
+ }
+
+ if (!acd->listen_on)
+ return FALSE;
+
+ acd_recv_arp_packet(acd);
+
+ return TRUE;
+}
+
+static bool is_link_local(uint32_t ip)
+{
+ return (ip & LINKLOCAL_ADDR) == LINKLOCAL_ADDR;
+}
+
+static int acd_recv_arp_packet(struct acd_host *acd)
+{
+ ssize_t cnt;
+ struct ether_arp arp;
+ uint32_t ip_n; /* network byte order */
+ struct in_addr addr;
+ int source_conflict;
+ int target_conflict;
+ bool probe;
+ char* confltxt;
+ uint8_t* mac;
+ uint8_t* omac;
+
+ memset(&arp, 0, sizeof(arp));
+ cnt = read(acd->listener_sockfd, &arp, sizeof(arp));
+ if (cnt != sizeof(arp))
+ return -EINVAL;
+
+ if (arp.arp_op != htons(ARPOP_REPLY) &&
+ arp.arp_op != htons(ARPOP_REQUEST))
+ return -EINVAL;
+
+ if (memcmp(arp.arp_sha, acd->mac_address, ETH_ALEN) == 0)
+ return 0;
+
+ ip_n = htonl(acd->requested_ip);
+ source_conflict = !memcmp(arp.arp_spa, &ip_n, sizeof(uint32_t));
+ probe = !memcmp(arp.arp_spa, "\0\0\0\0", sizeof(uint32_t));
+ target_conflict = probe &&
+ !memcmp(arp.arp_tpa, &ip_n, sizeof(uint32_t));
+
+ if (!source_conflict && !target_conflict)
+ return 0;
+
+ acd->conflicts++;
+
+ confltxt = target_conflict ? "target" : "source";
+
+ addr.s_addr = ip_n;
+ debug(acd, "IPv4 %d %s conflicts detected for address %s. "
+ "State=%s", acd->conflicts, confltxt, inet_ntoa(addr),
+ acd_state_texts[acd->state]);
+ mac = acd->mac_address;
+ omac = arp.arp_sha;
+ debug(acd, "Our MAC: %02x:%02x:%02x:%02x:%02x:%02x"
+ " other MAC: %02x:%02x:%02x:%02x:%02x:%02x",
+ mac[0], mac[1], mac[2],mac[3], mac[4], mac[5],
+ omac[0], omac[1], omac[2],omac[3], omac[4], omac[5]);
+
+ if (acd->state == ACD_STATE_MONITOR) {
+ if (!source_conflict)
+ return 0;
+
+ acd->state = ACD_STATE_DEFEND;
+ debug(acd, "DEFEND mode conflicts: %d", acd->conflicts);
+ /* Try to defend with a single announce. */
+ send_announce_packet(acd);
+ return 0;
+ } else if (acd->state == ACD_STATE_DEFEND) {
+ if (!source_conflict)
+ return 0;
+
+ debug(acd, "LOST IPv4 address %s", inet_ntoa(addr));
+ if (!is_link_local(acd->requested_ip))
+ report_conflict(acd, &arp);
+
+ if (acd->ipv4_lost_cb)
+ acd->ipv4_lost_cb(acd, acd->ipv4_lost_data);
+ return 0;
+ }
+
+ if (acd->conflicts < MAX_CONFLICTS) {
+ if (!is_link_local(acd->requested_ip))
+ report_conflict(acd, &arp);
+
+ acd_host_stop(acd);
+
+ /* we need a new request_ip */
+ if (acd->ipv4_conflict_cb)
+ acd->ipv4_conflict_cb(acd, acd->ipv4_conflict_data);
+ } else {
+ acd_host_stop(acd);
+
+ /*
+ * Here we got a lot of conflicts, RFC3927 and RFC5227 state that we
+ * have to wait RATE_LIMIT_INTERVAL before retrying.
+ */
+ if (acd->ipv4_max_conflicts_cb)
+ acd->ipv4_max_conflicts_cb(acd, acd->ipv4_max_conflicts_data);
+ }
+
+ return 0;
+}
+
+int acd_host_start(struct acd_host *acd, uint32_t ip)
+{
+ guint timeout;
+ int err;
+
+ remove_timeout(acd);
+
+ err = start_listening(acd);
+ if (err)
+ return err;
+
+ acd->retry_times = 0;
+ acd->requested_ip = ip;
+
+ /* First wait a random delay to avoid storm of ARP requests on boot */
+ timeout = __connman_util_random_delay_ms(PROBE_WAIT);
+ acd->state = ACD_STATE_PROBE;
+
+ acd->timeout = g_timeout_add_full(G_PRIORITY_HIGH,
+ timeout,
+ acd_probe_timeout,
+ acd,
+ NULL);
+
+ return 0;
+}
+
+void acd_host_stop(struct acd_host *acd)
+{
+ stop_listening(acd);
+
+ remove_timeout(acd);
+
+ if (acd->listener_watch > 0) {
+ g_source_remove(acd->listener_watch);
+ acd->listener_watch = 0;
+ }
+
+ acd->state = ACD_STATE_PROBE;
+ acd->retry_times = 0;
+ acd->requested_ip = 0;
+}
+
+static void send_probe_packet(gpointer acd_data)
+{
+ guint timeout;
+ struct acd_host *acd = acd_data;
+
+ debug(acd, "sending ARP probe request");
+ remove_timeout(acd);
+ if (acd->retry_times == 1) {
+ acd->state = ACD_STATE_PROBE;
+ start_listening(acd);
+ }
+ arp_send_packet(acd->mac_address, 0,
+ acd->requested_ip, acd->ifindex);
+
+ if (acd->retry_times < PROBE_NUM) {
+ /* Add a random timeout in range of PROBE_MIN to PROBE_MAX. */
+ timeout = __connman_util_random_delay_ms(PROBE_MAX-PROBE_MIN);
+ timeout += PROBE_MIN * 1000;
+ } else
+ timeout = ANNOUNCE_WAIT * 1000;
+
+ acd->timeout = g_timeout_add_full(G_PRIORITY_HIGH,
+ timeout,
+ acd_probe_timeout,
+ acd,
+ NULL);
+}
+
+static gboolean acd_probe_timeout(gpointer acd_data)
+{
+ struct acd_host *acd = acd_data;
+
+ acd->timeout = 0;
+
+ debug(acd, "acd probe timeout (retries %d)", acd->retry_times);
+ if (acd->retry_times == PROBE_NUM) {
+ acd->state = ACD_STATE_ANNOUNCE;
+ acd->retry_times = 1;
+
+ send_announce_packet(acd);
+ return FALSE;
+ }
+
+ acd->retry_times++;
+ send_probe_packet(acd);
+
+ return FALSE;
+}
+
+static gboolean send_announce_packet(gpointer acd_data)
+{
+ struct acd_host *acd = acd_data;
+
+ debug(acd, "sending ACD announce request");
+
+ arp_send_packet(acd->mac_address,
+ acd->requested_ip,
+ acd->requested_ip,
+ acd->ifindex);
+
+ remove_timeout(acd);
+
+ if (acd->state == ACD_STATE_DEFEND)
+ acd->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
+ DEFEND_INTERVAL,
+ acd_defend_timeout,
+ acd,
+ NULL);
+ else
+ acd->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
+ ANNOUNCE_INTERVAL,
+ acd_announce_timeout,
+ acd,
+ NULL);
+ return TRUE;
+}
+
+static gboolean acd_announce_timeout(gpointer acd_data)
+{
+ struct acd_host *acd = acd_data;
+
+ acd->timeout = 0;
+
+ debug(acd, "acd announce timeout (retries %d)", acd->retry_times);
+ if (acd->retry_times != ANNOUNCE_NUM) {
+ acd->retry_times++;
+ send_announce_packet(acd);
+ return FALSE;
+ }
+
+ debug(acd, "switching to monitor mode");
+ acd->state = ACD_STATE_MONITOR;
+
+ if (!acd->ac_resolved && !is_link_local(acd->requested_ip))
+ report_conflict(acd, NULL);
+
+ if (acd->ipv4_available_cb)
+ acd->ipv4_available_cb(acd,
+ acd->ipv4_available_data);
+ acd->conflicts = 0;
+
+ return FALSE;
+}
+
+static gboolean acd_defend_timeout(gpointer acd_data)
+{
+ struct acd_host *acd = acd_data;
+
+ debug(acd, "back to MONITOR mode");
+ acd->timeout = 0;
+ acd->conflicts = 0;
+ acd->state = ACD_STATE_MONITOR;
+
+ return FALSE;
+}
+
+void acd_host_register_event(struct acd_host *acd,
+ enum acd_host_event event,
+ acd_host_cb_t func,
+ gpointer user_data)
+{
+ switch (event) {
+ case ACD_HOST_EVENT_IPV4_AVAILABLE:
+ acd->ipv4_available_cb = func;
+ acd->ipv4_available_data = user_data;
+ break;
+ case ACD_HOST_EVENT_IPV4_LOST:
+ acd->ipv4_lost_cb = func;
+ acd->ipv4_lost_data = user_data;
+ break;
+ case ACD_HOST_EVENT_IPV4_CONFLICT:
+ acd->ipv4_conflict_cb = func;
+ acd->ipv4_conflict_data = user_data;
+ break;
+ case ACD_HOST_EVENT_IPV4_MAXCONFLICT:
+ acd->ipv4_max_conflicts_cb = func;
+ acd->ipv4_max_conflicts_data = user_data;
+ break;
+ default:
+ connman_warn("%s unknown event %d.", __FUNCTION__, event);
+ break;
+ }
+}
+
+static void append_ac_mac(DBusMessageIter *iter, void *user_data)
+{
+ struct acd_host *acd = user_data;
+ char mac[32];
+ uint8_t *m = acd->ac_mac;
+ const char *str = mac;
+
+ snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x",
+ m[0], m[1], m[2], m[3], m[4], m[5]);
+ connman_dbus_dict_append_basic(iter, "Address", DBUS_TYPE_STRING, &str);
+}
+
+static void append_ac_ipv4(DBusMessageIter *iter, void *user_data)
+{
+ struct acd_host *acd = user_data;
+ struct in_addr addr;
+ char *a;
+
+ addr.s_addr = htonl(acd->ac_ip);
+ a = inet_ntoa(addr);
+ if (!a)
+ a = "";
+ connman_dbus_dict_append_basic(iter, "Address", DBUS_TYPE_STRING, &a);
+}
+
+static void append_ac_property(DBusMessageIter *iter, void *user_data)
+{
+ struct acd_host *acd = user_data;
+
+ connman_dbus_dict_append_dict(iter, "IPv4", append_ac_ipv4, acd);
+ connman_dbus_dict_append_dict(iter, "Ethernet", append_ac_mac, acd);
+ connman_dbus_dict_append_basic(iter, "Timestamp", DBUS_TYPE_INT64,
+ &acd->ac_timestamp);
+ connman_dbus_dict_append_basic(iter, "Resolved", DBUS_TYPE_BOOLEAN,
+ &acd->ac_resolved);
+}
+
+void acd_host_append_dbus_property(struct acd_host *acd, DBusMessageIter *dict)
+{
+ connman_dbus_dict_append_dict(dict, "LastAddressConflict",
+ append_ac_property, acd);
+}
+
+static void report_conflict(struct acd_host *acd, const struct ether_arp* arp)
+{
+ if (arp) {
+ acd->ac_ip = acd->requested_ip;
+ memcpy(acd->ac_mac, arp->arp_sha, sizeof(acd->ac_mac));
+ acd->ac_timestamp = g_get_real_time();
+ acd->ac_resolved = false;
+ } else {
+ acd->ac_resolved = true;
+ }
+
+ connman_dbus_property_changed_dict(acd->path, CONNMAN_SERVICE_INTERFACE,
+ "LastAddressConflict", append_ac_property, acd);
+}
+
+unsigned int acd_host_get_conflicts_count(struct acd_host *acd)
+{
+ return acd->conflicts;
+}
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
buf[len] = '\0';
pos = strchr(buf, '\n');
+ if (!pos) {
+ connman_error("Error in backtrace format");
+ break;
+ }
+
*pos++ = '\0';
if (strcmp(buf, "??") == 0) {
}
ptr = strchr(pos, '\n');
+ if (!ptr) {
+ connman_error("Error in backtrace format");
+ break;
+ }
+
*ptr++ = '\0';
if (strncmp(pos, program_path, pathlen) == 0)
char *ipv6_gateway;
char *ipv6_privacy;
char *mac;
+ bool mdns;
char **nameservers;
char **search_domains;
char **timeservers;
#define SERVICE_KEY_PASSPHRASE "Passphrase"
#define SERVICE_KEY_SECURITY "Security"
#define SERVICE_KEY_HIDDEN "Hidden"
+#define SERVICE_KEY_MDNS "mDNS"
#define SERVICE_KEY_IPv4 "IPv4"
#define SERVICE_KEY_IPv6 "IPv6"
SERVICE_KEY_IPv6,
SERVICE_KEY_IPv6_PRIVACY,
SERVICE_KEY_MAC,
+ SERVICE_KEY_MDNS,
SERVICE_KEY_NAMESERVERS,
SERVICE_KEY_SEARCH_DOMAINS,
SERVICE_KEY_TIMESERVERS,
list = list->next) {
service_id = list->data;
- service = __connman_service_lookup_from_ident(service_id);
+ service = connman_service_lookup_from_identifier(service_id);
if (service) {
__connman_service_set_immutable(service, false);
__connman_service_set_config(service, NULL, NULL);
g_strfreev(strlist);
}
+ service->mdns = __connman_config_get_bool(keyfile, group,
+ SERVICE_KEY_MDNS, NULL);
+
return true;
err:
g_free(service->type);
service->type = str;
} else {
- DBG("Type of the configured service is missing for group %s",
- group);
+ connman_warn("Type of the configured service is missing "
+ "for group %s", group);
goto err;
}
groups = g_key_file_get_groups(keyfile, NULL);
for (i = 0; groups[i]; i++) {
- if (!g_str_has_prefix(groups[i], "service_"))
+ if (!g_str_has_prefix(groups[i], "service_")) {
+ connman_warn("Ignore group named '%s' because prefix "
+ "is not 'service_'", groups[i]);
continue;
+ }
if (load_service(keyfile, groups[i], config))
found = true;
}
ssid = connman_network_get_blob(network, "WiFi.SSID",
&ssid_len);
- if (!ssid) {
- connman_error("Network SSID not set");
- return -EINVAL;
- }
+ if (!ssid)
+ return -ENOENT;
if (!config->ssid || ssid_len != config->ssid_len)
return -ENOENT;
}
DBG("service %p ident %s", service,
- __connman_service_get_ident(service));
+ connman_service_get_identifier(service));
if (config->mac) {
struct connman_device *device;
__connman_service_disconnect(service);
- service_id = __connman_service_get_ident(service);
+ service_id = connman_service_get_identifier(service);
config->service_identifiers =
g_slist_prepend(config->service_identifiers,
g_strdup(service_id));
__connman_service_set_search_domains(service,
config->search_domains);
+ __connman_service_set_mdns(service, config->mdns);
+
if (config->timeservers)
__connman_service_set_timeservers(service,
config->timeservers);
}
g_free(entries);
- return;
}
bool __connman_config_address_provisioned(const char *address,
"0.0.0.0") == 0) {
if (connman_inet_set_gateway_interface(index) < 0)
return;
+ data->ipv4_gateway->active = true;
goto done;
}
"::") == 0) {
if (connman_inet_set_ipv6_gateway_interface(index) < 0)
return;
+ data->ipv6_gateway->active = true;
goto done;
}
g_strcmp0(data->ipv4_gateway->gateway,
"0.0.0.0") == 0) {
connman_inet_clear_gateway_interface(index);
+ data->ipv4_gateway->active = false;
return;
}
g_strcmp0(data->ipv6_gateway->gateway,
"::") == 0) {
connman_inet_clear_ipv6_gateway_interface(index);
+ data->ipv6_gateway->active = false;
return;
}
{
struct connman_service *service;
- service = __connman_service_get_default();
+ service = connman_service_get_default();
if (!service)
return NULL;
}
}
- if (updated && default_gateway) {
- if (default_gateway->ipv4_gateway)
+ /*
+ * Set default gateway if it has been updated or if it has not been
+ * set as active yet.
+ */
+ if (default_gateway) {
+ if (default_gateway->ipv4_gateway &&
+ (updated || !default_gateway->ipv4_gateway->active))
set_default_gateway(default_gateway,
CONNMAN_IPCONFIG_TYPE_IPV4);
- if (default_gateway->ipv6_gateway)
+ if (default_gateway->ipv6_gateway &&
+ (updated || !default_gateway->ipv6_gateway->active))
set_default_gateway(default_gateway,
CONNMAN_IPCONFIG_TYPE_IPV6);
}
const char *broadcast);
int __connman_inet_get_interface_address(int index, int family, void *address);
int __connman_inet_get_interface_ll_address(int index, int family, void *address);
+int __connman_inet_get_interface_mac_address(int index, uint8_t *mac_address);
+
+bool __connman_inet_is_any_addr(const char *address, int family);
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
int __connman_inet_add_fwmark_rule(uint32_t table_id, int family, uint32_t fwmark);
int __connman_inet_del_fwmark_rule(uint32_t table_id, int family, uint32_t fwmark);
int __connman_inet_add_default_to_table(uint32_t table_id, int ifindex, const char *gateway);
+int __connman_inet_add_subnet_to_table(uint32_t table_id, int ifindex,
+ const char *gateway, unsigned char prefixlen);
int __connman_inet_del_default_from_table(uint32_t table_id, int ifindex, const char *gateway);
+int __connman_inet_del_subnet_from_table(uint32_t table_id, int ifindex,
+ const char *gateway, unsigned char prefixlen);
int __connman_inet_get_address_netmask(int ifindex,
struct sockaddr_in *address, struct sockaddr_in *netmask);
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);
+int __connman_resolver_set_mdns(int index, bool enabled);
GKeyFile *__connman_storage_open_global(void);
GKeyFile *__connman_storage_load_global(void);
const char *timeserver);
GSList *__connman_timeserver_get_all(struct connman_service *service);
int __connman_timeserver_sync(struct connman_service *service);
-void __connman_timeserver_sync_next();
enum __connman_dhcpv6_status {
CONNMAN_DHCPV6_STATUS_FAIL = 0,
struct connman_network *network, dhcp_cb callback,
gpointer user_data);
void __connman_dhcp_stop(struct connman_ipconfig *ipconfig);
+void __connman_dhcp_decline(struct connman_ipconfig *ipconfig);
int __connman_dhcp_init(void);
void __connman_dhcp_cleanup(void);
int __connman_dhcpv6_init(void);
bool __connman_connection_update_gateway(void);
-int __connman_ntp_start(char *server);
+typedef void (*__connman_ntp_cb_t) (bool success, void *user_data);
+int __connman_ntp_start(char *server, __connman_ntp_cb_t callback,
+ void *user_data);
void __connman_ntp_stop();
int __connman_wpad_init(void);
enum connman_service_type __connman_device_get_service_type(struct connman_device *device);
struct connman_device *__connman_device_find_device(enum connman_service_type type);
int __connman_device_request_scan(enum connman_service_type type);
+int __connman_device_request_scan_full(enum connman_service_type type);
int __connman_device_request_hidden_scan(struct connman_device *device,
const char *ssid, unsigned int ssid_len,
const char *identity, const char *passphrase,
const char *security, void *user_data);
+void __connman_device_stop_scan(enum connman_service_type type);
bool __connman_device_isfiltered(const char *devname);
bool __connman_config_address_provisioned(const char *address,
const char *netmask);
+#include <connman/tethering.h>
+
int __connman_tethering_init(void);
void __connman_tethering_cleanup(void);
const char *__connman_tethering_get_bridge(void);
int __connman_tethering_set_enabled(void);
void __connman_tethering_set_disabled(void);
+void __connman_tethering_list_clients(DBusMessageIter *array);
int __connman_private_network_request(DBusMessage *msg, const char *owner);
int __connman_private_network_release(const char *path);
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);
struct connman_service *__connman_service_create_from_provider(struct connman_provider *provider);
bool __connman_service_index_is_default(int index);
-struct connman_service *__connman_service_get_default(void);
void __connman_service_update_from_network(struct connman_network *network);
void __connman_service_remove_from_network(struct connman_network *network);
void __connman_service_read_ip4config(struct connman_service *service);
struct connman_service *service, int family);
void __connman_service_notify_ipv4_configuration(
struct connman_service *service);
+void __connman_service_wispr_start(struct connman_service *service,
+ enum connman_ipconfig_type type);
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);
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);
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);
bool ignore);
void __connman_service_set_search_domains(struct connman_service *service,
char **domains);
+int __connman_service_set_mdns(struct connman_service *service,
+ bool enabled);
void __connman_service_set_string(struct connman_service *service,
const char *key, const char *value);
void __connman_peer_list_struct(DBusMessageIter *array);
const char *__connman_peer_get_path(struct connman_peer *peer);
+void __connman_peer_disconnect_all(void);
int __connman_peer_service_init(void);
void __connman_peer_service_cleanup(void);
#include <connman/session.h>
-typedef void (* service_iterate_cb) (struct connman_service *service,
- void *user_data);
-
-int __connman_service_iterate_services(service_iterate_cb cb, void *user_data);
-
void __connman_service_mark_dirty();
void __connman_service_save(struct connman_service *service);
bool roaming,
struct connman_stats_data *data);
-int __connman_iptables_dump(const char *table_name);
-int __connman_iptables_new_chain(const char *table_name,
- const char *chain);
-int __connman_iptables_delete_chain(const char *table_name,
- const char *chain);
-int __connman_iptables_flush_chain(const char *table_name,
- const char *chain);
-int __connman_iptables_change_policy(const char *table_name,
- const char *chain,
- const char *policy);
-int __connman_iptables_append(const char *table_name,
- const char *chain,
- const char *rule_spec);
-int __connman_iptables_insert(const char *table_name,
- const char *chain,
- const char *rule_spec);
-int __connman_iptables_delete(const char *table_name,
- const char *chain,
- const char *rule_spec);
+int __connman_iptables_dump(int type,
+ const char *table_name);
+int __connman_iptables_new_chain(int type,
+ const char *table_name,
+ const char *chain);
+int __connman_iptables_delete_chain(int type,
+ const char *table_name,
+ const char *chain);
+int __connman_iptables_flush_chain(int type,
+ const char *table_name,
+ const char *chain);
+int __connman_iptables_find_chain(int type,
+ const char *table_name,
+ const char *chain);
+int __connman_iptables_change_policy(int type,
+ const char *table_name,
+ const char *chain,
+ const char *policy);
+int __connman_iptables_append(int type,
+ const char *table_name,
+ const char *chain,
+ const char *rule_spec);
+int __connman_iptables_insert(int type,
+ const char *table_name,
+ const char *chain,
+ const char *rule_spec);
+int __connman_iptables_delete(int type,
+ const char *table_name,
+ const char *chain,
+ const char *rule_spec);
typedef void (*connman_iptables_iterate_chains_cb_t) (const char *chain_name,
void *user_data);
-int __connman_iptables_iterate_chains(const char *table_name,
+int __connman_iptables_iterate_chains(int type,
+ const char *table_name,
connman_iptables_iterate_chains_cb_t cb,
void *user_data);
int __connman_iptables_init(void);
void __connman_iptables_cleanup(void);
-int __connman_iptables_commit(const char *table_name);
+int __connman_iptables_commit(int type, const char *table_name);
int __connman_dnsproxy_init(void);
void __connman_dnsproxy_cleanup(void);
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);
+int __connman_dnsproxy_set_mdns(int index, bool enabled);
int __connman_6to4_probe(struct connman_service *service);
void __connman_6to4_remove(struct connman_ipconfig *ipconfig);
int __connman_ippool_init(void);
void __connman_ippool_cleanup(void);
-#define __connman_ippool_ref(ipconfig) \
- __connman_ippool_ref_debug(ipconfig, __FILE__, __LINE__, __func__)
-#define __connman_ippool_unref(ipconfig) \
- __connman_ippool_unref_debug(ipconfig, __FILE__, __LINE__, __func__)
-
-struct connman_ippool *__connman_ippool_ref_debug(struct connman_ippool *pool,
- const char *file, int line, const char *caller);
-void __connman_ippool_unref_debug(struct connman_ippool *pool,
- const char *file, int line, const char *caller);
+void __connman_ippool_free(struct connman_ippool *pool);
struct connman_ippool *__connman_ippool_create(int index,
unsigned int start,
void __connman_machine_cleanup(void);
int __connman_util_get_random(uint64_t *val);
+unsigned int __connman_util_random_delay_ms(unsigned int secs);
int __connman_util_init(void);
void __connman_util_cleanup(void);
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
+CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SYS_TIME CAP_SYS_MODULE CAP_SYS_ADMIN
ProtectHome=true
ProtectSystem=full
* request
*/
bool powered;
- bool scanning;
+ bool scanning[MAX_CONNMAN_SERVICE_TYPES];
char *name;
char *node;
char *address;
return CONNMAN_SERVICE_TYPE_UNKNOWN;
}
+static bool device_has_service_type(struct connman_device *device,
+ enum connman_service_type service_type)
+{
+ enum connman_service_type device_service_type =
+ __connman_device_get_service_type(device);
+
+ /*
+ * For devices whose device_service_type is unknown we should
+ * allow to decide whether they support specific service_type
+ * by themself.
+ */
+ if (device_service_type == CONNMAN_SERVICE_TYPE_UNKNOWN)
+ return true;
+
+ if (device_service_type == CONNMAN_SERVICE_TYPE_WIFI) {
+ return service_type == CONNMAN_SERVICE_TYPE_WIFI ||
+ service_type == CONNMAN_SERVICE_TYPE_P2P;
+ }
+
+ return service_type == device_service_type;
+}
+
static gboolean device_pending_reset(gpointer user_data)
{
struct connman_device *device = user_data;
return -EBUSY;
if (device->powered_pending == PENDING_ENABLE)
- return -EALREADY;
+ return -EINPROGRESS;
if (device->powered_pending == PENDING_NONE && device->powered)
return -EALREADY;
return -EBUSY;
if (device->powered_pending == PENDING_DISABLE)
- return -EALREADY;
+ return -EINPROGRESS;
if (device->powered_pending == PENDING_NONE && !device->powered)
return -EALREADY;
int connman_device_set_powered(struct connman_device *device,
bool powered)
{
+ struct connman_device_scan_params params;
enum connman_service_type type;
+ int i;
DBG("device %p powered %d", device, powered);
__connman_technology_enabled(type);
- device->scanning = false;
+ for (i = 0; i < MAX_CONNMAN_SERVICE_TYPES; i++)
+ device->scanning[i] = false;
+
+ if (device->driver && device->driver->scan) {
+ memset(¶ms, 0, sizeof(params));
+ params.type = CONNMAN_SERVICE_TYPE_UNKNOWN;
- if (device->driver && device->driver->scan)
- device->driver->scan(CONNMAN_SERVICE_TYPE_UNKNOWN, device,
- NULL, 0, NULL, NULL, NULL, NULL);
+ device->driver->scan(device, ¶ms);
+ }
return 0;
}
}
static int device_scan(enum connman_service_type type,
- struct connman_device *device)
+ struct connman_device *device,
+ bool force_full_scan)
{
+ struct connman_device_scan_params params;
+
if (!device->driver || !device->driver->scan)
return -EOPNOTSUPP;
if (!device->powered)
return -ENOLINK;
- return device->driver->scan(type, device, NULL, 0,
- NULL, NULL, NULL, NULL);
+ memset(¶ms, 0, sizeof(params));
+ params.type = type;
+ params.force_full_scan = force_full_scan;
+
+ return device->driver->scan(device, ¶ms);
}
int __connman_device_disconnect(struct connman_device *device)
{
struct connman_network *network = value;
- if (connman_network_get_connected(network))
+ if (connman_network_get_connected(network) ||
+ connman_network_get_connecting(network))
return FALSE;
if (connman_network_get_available(network))
remove_unavailable_network, NULL);
}
-bool connman_device_get_scanning(struct connman_device *device)
+bool connman_device_get_scanning(struct connman_device *device,
+ enum connman_service_type type)
{
- return device->scanning;
+ int i;
+
+ if (type != CONNMAN_SERVICE_TYPE_UNKNOWN)
+ return device->scanning[type];
+
+ for (i = 0; i < MAX_CONNMAN_SERVICE_TYPES; i++)
+ if (device->scanning[i])
+ return true;
+
+ return false;
}
void connman_device_reset_scanning(struct connman_device *device)
if (!device->driver || !device->driver->scan)
return -EINVAL;
- if (device->scanning == scanning)
+ if (type == CONNMAN_SERVICE_TYPE_UNKNOWN)
+ return -EINVAL;
+
+ if (device->scanning[type] == scanning)
return -EALREADY;
- device->scanning = scanning;
+ device->scanning[type] = scanning;
if (scanning) {
__connman_technology_scan_started(device);
__connman_technology_notify_regdom_by_device(device, result, alpha2);
}
-int __connman_device_request_scan(enum connman_service_type type)
+static int connman_device_request_scan(enum connman_service_type type,
+ bool force_full_scan)
{
bool success = false;
int last_err = -ENOSYS;
for (list = device_list; list; list = list->next) {
struct connman_device *device = list->data;
- enum connman_service_type service_type =
- __connman_device_get_service_type(device);
- if (service_type != CONNMAN_SERVICE_TYPE_UNKNOWN) {
- if (type == CONNMAN_SERVICE_TYPE_P2P) {
- if (service_type != CONNMAN_SERVICE_TYPE_WIFI)
- continue;
- } else if (service_type != type)
- continue;
- }
+ if (!device_has_service_type(device, type))
+ continue;
- err = device_scan(type, device);
+ err = device_scan(type, device, force_full_scan);
if (err == 0 || err == -EALREADY || err == -EINPROGRESS) {
success = true;
} else {
return last_err;
}
+int __connman_device_request_scan(enum connman_service_type type)
+{
+ return connman_device_request_scan(type, false);
+}
+
+int __connman_device_request_scan_full(enum connman_service_type type)
+{
+ return connman_device_request_scan(type, true);
+}
+
int __connman_device_request_hidden_scan(struct connman_device *device,
const char *ssid, unsigned int ssid_len,
const char *identity, const char *passphrase,
const char *security, void *user_data)
{
+ struct connman_device_scan_params params;
+
DBG("device %p", device);
if (!device || !device->driver ||
!device->driver->scan)
return -EINVAL;
- return device->driver->scan(CONNMAN_SERVICE_TYPE_UNKNOWN,
- device, ssid, ssid_len, identity,
- passphrase, security, user_data);
+ params.type = CONNMAN_SERVICE_TYPE_UNKNOWN;
+ params.ssid = ssid;
+ params.ssid_len = ssid_len;
+ params.identity = identity;
+ params.passphrase = passphrase;
+ params.security = security;
+ params.user_data = user_data;
+
+ return device->driver->scan(device, ¶ms);
+}
+
+void __connman_device_stop_scan(enum connman_service_type type)
+{
+ GSList *list;
+
+ for (list = device_list; list; list = list->next) {
+ struct connman_device *device = list->data;
+
+ if (!device_has_service_type(device, type))
+ continue;
+
+ if (device->driver && device->driver->stop_scan)
+ device->driver->stop_scan(type, device);
+ }
}
static char *index2ident(int index, const char *prefix)
}
}
+void __connman_dhcp_decline(struct connman_ipconfig *ipconfig)
+{
+ struct connman_dhcp *dhcp;
+ const char *address;
+ struct in_addr addr;
+
+ DBG("ipconfig_table %p ipconfig %p", ipconfig_table, ipconfig);
+
+ if (!ipconfig_table)
+ return;
+
+ dhcp = g_hash_table_lookup(ipconfig_table, ipconfig);
+ if (dhcp) {
+ address = __connman_ipconfig_get_local(ipconfig);
+ if (!address)
+ return;
+
+ if (inet_pton(AF_INET, address, &addr) != 1)
+ connman_error("Could not convert address %s", address);
+
+ g_dhcp_client_decline(dhcp->dhcp_client, htonl(addr.s_addr));
+ }
+}
+
int __connman_dhcp_init(void)
{
DBG("");
g_hash_table_destroy(ipconfig_table);
ipconfig_table = NULL;
-
- dhcp_cleanup_random();
}
unsigned char *duid;
int duid_len;
- ident = __connman_service_get_ident(service);
+ ident = connman_service_get_identifier(service);
keyfile = connman_storage_load_service(ident);
if (!keyfile)
--- /dev/null
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2007-2017 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <gdbus.h>
+#include <glib.h>
+#include <connman/dbus.h>
+
+#include "connman.h"
+
+#define SYSTEMD_RESOLVED_SERVICE "org.freedesktop.resolve1"
+#define SYSTEMD_RESOLVED_PATH "/org/freedesktop/resolve1"
+
+struct mdns_data {
+ int index;
+ bool enabled;
+};
+
+static GHashTable *interface_hash;
+static DBusConnection *connection;
+static GDBusClient *client;
+static GDBusProxy *resolved_proxy;
+
+/* update after a full set of instructions has been received */
+static guint update_interfaces_source;
+
+struct dns_interface {
+ GList *domains;
+ GList *servers;
+ int index;
+ bool needs_domain_update;
+ bool needs_server_update;
+};
+
+static gboolean compare_index(gconstpointer a, gconstpointer b)
+{
+ gint ai = GPOINTER_TO_UINT(a);
+ gint bi = GPOINTER_TO_UINT(b);
+
+ return ai == bi;
+}
+
+static void free_dns_interface(gpointer data)
+{
+ struct dns_interface *iface = data;
+
+ if (!iface)
+ return;
+
+ g_list_free_full(iface->domains, g_free);
+ g_list_free_full(iface->servers, g_free);
+
+ g_free(iface);
+}
+
+static void setlinkdns_append(DBusMessageIter *iter, void *user_data)
+{
+ struct dns_interface *iface = user_data;
+ int result;
+ unsigned int i;
+ int type;
+ char ipv4_bytes[4];
+ char ipv6_bytes[16];
+ GList *list;
+ DBusMessageIter address_array, struct_array, byte_array;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &iface->index);
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "(iay)",
+ &address_array);
+
+ for (list = iface->servers; list; list = g_list_next(list)) {
+ char *server = list->data;
+
+ DBG("index: %d, server: %s", iface->index, server);
+
+ dbus_message_iter_open_container(&address_array,
+ DBUS_TYPE_STRUCT, NULL, &struct_array);
+
+ type = connman_inet_check_ipaddress(server);
+
+ if (type == AF_INET) {
+ result = inet_pton(type, server, ipv4_bytes);
+ if (!result) {
+ DBG("Failed to parse IPv4 address: %s",
+ server);
+ return;
+ }
+
+ dbus_message_iter_append_basic(&struct_array,
+ DBUS_TYPE_INT32, &type);
+
+ dbus_message_iter_open_container(&struct_array,
+ DBUS_TYPE_ARRAY, "y", &byte_array);
+
+ for (i = 0; i < sizeof(ipv4_bytes); i++) {
+ dbus_message_iter_append_basic(&byte_array,
+ DBUS_TYPE_BYTE,
+ &(ipv4_bytes[i]));
+ }
+
+ dbus_message_iter_close_container(&struct_array,
+ &byte_array);
+ } else if (type == AF_INET6) {
+ result = inet_pton(type, server, ipv6_bytes);
+ if (!result) {
+ DBG("Failed to parse IPv6 address: %s", server);
+ return;
+ }
+
+ dbus_message_iter_append_basic(&struct_array,
+ DBUS_TYPE_INT32, &type);
+
+ dbus_message_iter_open_container(&struct_array,
+ DBUS_TYPE_ARRAY, "y", &byte_array);
+
+ for (i = 0; i < sizeof(ipv6_bytes); i++) {
+ dbus_message_iter_append_basic(&byte_array,
+ DBUS_TYPE_BYTE,
+ &(ipv6_bytes[i]));
+ }
+
+ dbus_message_iter_close_container(&struct_array,
+ &byte_array);
+ }
+
+ dbus_message_iter_close_container(&address_array,
+ &struct_array);
+ }
+
+ dbus_message_iter_close_container(iter, &address_array);
+}
+
+static void setlinkdomains_append(DBusMessageIter *iter, void *user_data)
+{
+ struct dns_interface *iface = user_data;
+ GList *list;
+ DBusMessageIter domain_array, struct_array;
+ gboolean only_routing = FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &iface->index);
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "(sb)",
+ &domain_array);
+
+ for (list = iface->domains; list; list = g_list_next(list)) {
+ char *domain = list->data;
+
+ DBG("index: %d, domain: %s", iface->index, domain);
+
+ dbus_message_iter_open_container(&domain_array,
+ DBUS_TYPE_STRUCT, NULL, &struct_array);
+
+ dbus_message_iter_append_basic(&struct_array, DBUS_TYPE_STRING,
+ &domain);
+
+ dbus_message_iter_append_basic(&struct_array, DBUS_TYPE_BOOLEAN,
+ &only_routing);
+
+ dbus_message_iter_close_container(&domain_array, &struct_array);
+ }
+
+ dbus_message_iter_close_container(iter, &domain_array);
+}
+
+static int set_systemd_resolved_values(struct dns_interface *iface)
+{
+ if (!resolved_proxy || !iface)
+ return -ENOENT;
+
+ /* No async error processing -- just fire and forget */
+
+ if (iface->needs_server_update) {
+ if (!g_dbus_proxy_method_call(resolved_proxy, "SetLinkDNS",
+ setlinkdns_append, NULL, iface, NULL))
+ return -EINVAL;
+
+ iface->needs_server_update = FALSE;
+ }
+
+ if (iface->needs_domain_update) {
+ if (!g_dbus_proxy_method_call(resolved_proxy, "SetLinkDomains",
+ setlinkdomains_append, NULL, iface, NULL))
+ return -EINVAL;
+
+ iface->needs_domain_update = FALSE;
+ }
+
+ return 0;
+}
+
+static bool is_empty(struct dns_interface *iface)
+{
+ if (!iface)
+ return FALSE;
+
+ return (!iface->domains && !iface->servers);
+}
+
+static void update_interface(gpointer key, gpointer value, gpointer data)
+{
+ struct dns_interface *iface = value;
+ GList **removed_items = data;
+
+ set_systemd_resolved_values(iface);
+
+ if (is_empty(iface))
+ *removed_items = g_list_prepend(*removed_items, iface);
+}
+
+static int update_systemd_resolved(gpointer data)
+{
+ GList *removed_items = NULL, *list;
+
+ if (!interface_hash) {
+ DBG("no interface hash when updating");
+
+ return G_SOURCE_REMOVE;
+ }
+
+ g_hash_table_foreach(interface_hash, update_interface, &removed_items);
+
+ for (list = removed_items; list; list = g_list_next(list)) {
+ struct dns_interface *iface = list->data;
+
+ g_hash_table_remove(interface_hash,
+ GUINT_TO_POINTER(iface->index));
+ }
+
+ g_list_free(removed_items);
+
+ update_interfaces_source = 0;
+
+ return G_SOURCE_REMOVE;
+}
+
+static GList *remove_string(GList *str_list, const char *str)
+{
+ GList *match = NULL;
+
+ match = g_list_find_custom(str_list, str,
+ (GCompareFunc) g_strcmp0);
+ if (match) {
+ g_free(match->data);
+ return g_list_delete_link(str_list, match);
+ }
+
+ return str_list;
+}
+
+static void remove_values(struct dns_interface *iface, const char *domain,
+ const char *server)
+{
+ if (!iface)
+ return;
+
+ if (domain) {
+ iface->domains = remove_string(iface->domains, domain);
+ iface->needs_domain_update = TRUE;
+ }
+
+ if (server) {
+ iface->servers = remove_string(iface->servers, server);
+ iface->needs_server_update = TRUE;
+ }
+}
+
+int __connman_dnsproxy_remove(int index, const char *domain,
+ const char *server)
+{
+ struct dns_interface *iface;
+
+ DBG("%d, %s, %s", index, domain ? domain : "no domain",
+ server ? server : "no server");
+
+ if (!interface_hash || index < 0)
+ return -EINVAL;
+
+ iface = g_hash_table_lookup(interface_hash, GUINT_TO_POINTER(index));
+
+ if (!iface)
+ return -EINVAL;
+
+ remove_values(iface, domain, server);
+
+ if (!update_interfaces_source)
+ update_interfaces_source = g_idle_add(update_systemd_resolved,
+ NULL);
+
+ return 0;
+}
+
+static GList *replace_to_end(GList *str_list, const char *str)
+{
+ GList *list;
+
+ for (list = str_list; list; list = g_list_next(list)) {
+ char *orig = list->data;
+
+ if (g_strcmp0(orig, str) == 0) {
+ str_list = g_list_remove(str_list, orig);
+ g_free(orig);
+ break;
+ }
+ }
+
+ return g_list_append(str_list, g_strdup(str));
+}
+
+int __connman_dnsproxy_append(int index, const char *domain,
+ const char *server)
+{
+ struct dns_interface *iface;
+
+ DBG("%d, %s, %s", index, domain ? domain : "no domain",
+ server ? server : "no server");
+
+ if (!interface_hash || index < 0)
+ return -EINVAL;
+
+ iface = g_hash_table_lookup(interface_hash, GUINT_TO_POINTER(index));
+
+ if (!iface) {
+ iface = g_new0(struct dns_interface, 1);
+ if (!iface)
+ return -ENOMEM;
+
+ iface->index = index;
+ g_hash_table_replace(interface_hash, GUINT_TO_POINTER(index), iface);
+ }
+
+ if (domain) {
+ iface->domains = replace_to_end(iface->domains, domain);
+ iface->needs_domain_update = TRUE;
+ }
+
+ if (server) {
+ iface->servers = replace_to_end(iface->servers, server);
+ iface->needs_server_update = TRUE;
+ }
+
+ if (!update_interfaces_source)
+ update_interfaces_source = g_idle_add(update_systemd_resolved,
+ NULL);
+
+ return 0;
+}
+
+int __connman_dnsproxy_add_listener(int index)
+{
+ DBG("");
+
+ return -ENXIO;
+}
+
+void __connman_dnsproxy_remove_listener(int index)
+{
+ DBG("");
+}
+
+static int setup_resolved(void)
+{
+ connection = connman_dbus_get_connection();
+ if (!connection)
+ return -ENXIO;
+
+ client = g_dbus_client_new(connection, SYSTEMD_RESOLVED_SERVICE,
+ SYSTEMD_RESOLVED_PATH);
+
+ if (!client)
+ return -EINVAL;
+
+ resolved_proxy = g_dbus_proxy_new(client, SYSTEMD_RESOLVED_PATH,
+ "org.freedesktop.resolve1.Manager");
+
+ if (!resolved_proxy)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void setlinkmulticastdns_append(DBusMessageIter *iter, void *user_data) {
+ struct mdns_data *data = user_data;
+ char *val = "no";
+
+ if (data->enabled)
+ val = "yes";
+
+ DBG("SetLinkMulticastDNS: %d/%s", data->index, val);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &data->index);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &val);
+}
+
+int __connman_dnsproxy_set_mdns(int index, bool enabled)
+{
+ struct mdns_data data = { .index = index, .enabled = enabled };
+
+ if (!resolved_proxy)
+ return -ENOENT;
+
+ if (index < 0)
+ return -EINVAL;
+
+ if (!g_dbus_proxy_method_call(resolved_proxy, "SetLinkMulticastDNS",
+ setlinkmulticastdns_append, NULL, &data, NULL))
+ return -EINVAL;
+
+ return 0;
+}
+
+int __connman_dnsproxy_init(void)
+{
+ int ret;
+
+ DBG("");
+
+ ret = setup_resolved();
+ if (ret)
+ return ret;
+
+ interface_hash = g_hash_table_new_full(g_direct_hash,
+ compare_index,
+ NULL,
+ free_dns_interface);
+ if (!interface_hash)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void __connman_dnsproxy_cleanup(void)
+{
+ DBG("");
+
+ if (update_interfaces_source) {
+ /*
+ * It might be that we don't get to an idle loop anymore, so
+ * run the update function once more to clean up.
+ */
+ g_source_remove(update_interfaces_source);
+ update_systemd_resolved(NULL);
+ update_interfaces_source = 0;
+ }
+
+ if (interface_hash) {
+ g_hash_table_destroy(interface_hash);
+ interface_hash = NULL;
+ }
+
+ if (resolved_proxy) {
+ g_dbus_proxy_unref(resolved_proxy);
+ resolved_proxy = NULL;
+ }
+
+ if (client) {
+ g_dbus_client_unref(client);
+ client = NULL;
+ }
+
+ if (connection) {
+ dbus_connection_unref(connection);
+ connection = NULL;
+ }
+}
#error "Unknown byte order"
#endif
+struct qtype_qclass {
+ uint16_t qtype;
+ uint16_t qclass;
+} __attribute__ ((packed));
+
struct partial_reply {
uint16_t len;
uint16_t received;
err, len, dns_len);
}
-static void send_response(int sk, unsigned char *buf, int len,
+static void send_response(int sk, unsigned char *buf, size_t len,
const struct sockaddr *to, socklen_t tolen,
int protocol)
{
if (offset < 0)
return;
- if (len < 12)
+ if (len < sizeof(*hdr) + offset)
return;
hdr = (void *) (buf + offset);
+ if (offset) {
+ buf[0] = 0;
+ buf[1] = sizeof(*hdr);
+ }
debug("id 0x%04x qr %d opcode %d", hdr->id, hdr->qr, hdr->opcode);
hdr->qr = 1;
hdr->rcode = ns_r_servfail;
+ hdr->qdcount = 0;
hdr->ancount = 0;
hdr->nscount = 0;
hdr->arcount = 0;
- err = sendto(sk, buf, len, MSG_NOSIGNAL, to, tolen);
+ err = sendto(sk, buf, sizeof(*hdr) + offset, MSG_NOSIGNAL, to, tolen);
if (err < 0) {
connman_error("Failed to send DNS response to %d: %s",
sk, strerror(errno));
gpointer user_data)
{
unsigned char buf[4096];
- int sk, err, len;
+ int sk, len;
struct server_data *data = user_data;
if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
sk = g_io_channel_unix_get_fd(channel);
len = recv(sk, buf, sizeof(buf), 0);
- if (len < 12)
- return TRUE;
- err = forward_dns_reply(buf, len, IPPROTO_UDP, data);
- if (err < 0)
- return TRUE;
+ if (len >= 12)
+ forward_dns_reply(buf, len, IPPROTO_UDP, data);
return TRUE;
}
cache_refresh();
}
-static struct connman_notifier dnsproxy_notifier = {
+static const struct connman_notifier dnsproxy_notifier = {
.name = "dnsproxy",
.default_changed = dnsproxy_default_changed,
.offline_mode = dnsproxy_offline_mode,
};
-static unsigned char opt_edns0_type[2] = { 0x00, 0x29 };
+static const unsigned char opt_edns0_type[2] = { 0x00, 0x29 };
-static int parse_request(unsigned char *buf, int len,
+static int parse_request(unsigned char *buf, size_t len,
char *name, unsigned int size)
{
struct domain_hdr *hdr = (void *) buf;
uint16_t qdcount = ntohs(hdr->qdcount);
+ uint16_t ancount = ntohs(hdr->ancount);
+ uint16_t nscount = ntohs(hdr->nscount);
uint16_t arcount = ntohs(hdr->arcount);
unsigned char *ptr;
- char *last_label = NULL;
unsigned int remain, used = 0;
- if (len < 12)
+ if (len < sizeof(*hdr) + sizeof(struct qtype_qclass) ||
+ hdr->qr || qdcount != 1 || ancount || nscount) {
+ DBG("Dropped DNS request qr %d with len %zd qdcount %d "
+ "ancount %d nscount %d", hdr->qr, len, qdcount, ancount,
+ nscount);
+
+ return -EINVAL;
+ }
+
+ if (!name || !size)
return -EINVAL;
debug("id 0x%04x qr %d opcode %d qdcount %d arcount %d",
hdr->id, hdr->qr, hdr->opcode,
qdcount, arcount);
- if (hdr->qr != 0 || qdcount != 1)
- return -EINVAL;
-
name[0] = '\0';
ptr = buf + sizeof(struct domain_hdr);
uint8_t label_len = *ptr;
if (label_len == 0x00) {
- last_label = (char *) (ptr + 1);
+ uint8_t class;
+ struct qtype_qclass *q =
+ (struct qtype_qclass *)(ptr + 1);
+
+ if (remain < sizeof(*q)) {
+ DBG("Dropped malformed DNS query");
+ return -EINVAL;
+ }
+
+ class = ntohs(q->qclass);
+ if (class != 1 && class != 255) {
+ DBG("Dropped non-IN DNS class %d", class);
+ return -EINVAL;
+ }
+
+ ptr += sizeof(*q) + 1;
+ remain -= (sizeof(*q) + 1);
break;
}
remain -= label_len + 1;
}
- if (last_label && arcount && remain >= 9 && last_label[4] == 0 &&
- !memcmp(last_label + 5, opt_edns0_type, 2)) {
- uint16_t edns0_bufsize;
-
- edns0_bufsize = last_label[7] << 8 | last_label[8];
-
- debug("EDNS0 buffer size %u", edns0_bufsize);
+ if (arcount && remain >= sizeof(struct domain_rr) + 1 && !ptr[0] &&
+ ptr[1] == opt_edns0_type[0] && ptr[2] == opt_edns0_type[1]) {
+ struct domain_rr *edns0 = (struct domain_rr *)(ptr + 1);
- /* This is an evil hack until full TCP support has been
- * implemented.
- *
- * Somtimes the EDNS0 request gets send with a too-small
- * buffer size. Since glibc doesn't seem to crash when it
- * gets a response biffer then it requested, just bump
- * the buffer size up to 4KiB.
- */
- if (edns0_bufsize < 0x1000) {
- last_label[7] = 0x10;
- last_label[8] = 0x00;
- }
+ DBG("EDNS0 buffer size %u", ntohs(edns0->class));
+ } else if (!arcount && remain) {
+ DBG("DNS request with %d garbage bytes", remain);
}
debug("query %s", name);
return err;
}
+int __connman_dnsproxy_set_mdns(int index, bool enabled)
+{
+ return -ENOTSUP;
+}
+
void __connman_dnsproxy_cleanup(void)
{
DBG("");
g_hash_table_destroy(listener_table);
g_hash_table_destroy(partial_tcp_req_table);
+
+ if (ipv4_resolve)
+ g_resolv_unref(ipv4_resolve);
+ if (ipv6_resolve)
+ g_resolv_unref(ipv6_resolve);
}
managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX,
builtin_chains[id]);
- err = __connman_iptables_new_chain(table_name, managed_chain);
+ err = __connman_iptables_new_chain(AF_INET, table_name, managed_chain);
if (err < 0)
goto out;
rule = g_strdup_printf("-j %s", managed_chain);
- err = __connman_iptables_insert(table_name, builtin_chains[id], rule);
+ err = __connman_iptables_insert(AF_INET, table_name, builtin_chains[id],
+ rule);
g_free(rule);
if (err < 0) {
- __connman_iptables_delete_chain(table_name, managed_chain);
+ __connman_iptables_delete_chain(AF_INET, table_name,
+ managed_chain);
goto out;
}
builtin_chains[id]);
rule = g_strdup_printf("-j %s", managed_chain);
- err = __connman_iptables_delete(table_name, builtin_chains[id], rule);
+ err = __connman_iptables_delete(AF_INET, table_name, builtin_chains[id],
+ rule);
g_free(rule);
if (err < 0)
goto out;
- err = __connman_iptables_delete_chain(table_name, managed_chain);
+ err = __connman_iptables_delete_chain(AF_INET, table_name,
+ managed_chain);
out:
g_free(managed_chain);
chain = g_strdup_printf("%s%s", CHAIN_PREFIX, chain_name);
out:
- err = __connman_iptables_append(table_name, chain, rule_spec);
+ err = __connman_iptables_append(AF_INET, table_name, chain, rule_spec);
g_free(chain);
id = chain_to_index(chain_name);
if (id < 0) {
/* This chain is not managed */
- return __connman_iptables_delete(table_name, chain_name,
- rule_spec);
+ return __connman_iptables_delete(AF_INET, table_name,
+ chain_name, rule_spec);
}
managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX, chain_name);
- err = __connman_iptables_delete(table_name, managed_chain,
- rule_spec);
+ err = __connman_iptables_delete(AF_INET, table_name, managed_chain,
+ rule_spec);
for (list = managed_tables; list; list = list->next) {
mtable = list->data;
if (err < 0)
return err;
- err = __connman_iptables_commit(rule->table);
+ err = __connman_iptables_commit(AF_INET, rule->table);
if (err < 0)
return err;
return err;
}
- err = __connman_iptables_commit(rule->table);
+ err = __connman_iptables_commit(AF_INET, rule->table);
if (err < 0) {
connman_error("Cannot remove previously installed "
"iptables rules: %s", strerror(-err));
static void firewall_remove_rules(struct firewall_context *ctx)
{
- struct fw_rule *rule;
- GList *list;
-
- for (list = g_list_last(ctx->rules); list;
- list = g_list_previous(list)) {
- rule = list->data;
-
- ctx->rules = g_list_remove(ctx->rules, rule);
- cleanup_fw_rule(rule);
- }
+ g_list_free_full(ctx->rules, cleanup_fw_rule);
+ ctx->rules = NULL;
}
static int firewall_enable_rules(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",
+ "-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);
char *rule, *managed_chain;
int id, err;
- __connman_iptables_iterate_chains(table_name, iterate_chains_cb,
- &chains);
+ __connman_iptables_iterate_chains(AF_INET, table_name,
+ iterate_chains_cb, &chains);
for (list = chains; list; list = list->next) {
id = GPOINTER_TO_INT(list->data);
builtin_chains[id]);
rule = g_strdup_printf("-j %s", managed_chain);
- err = __connman_iptables_delete(table_name,
+ err = __connman_iptables_delete(AF_INET, table_name,
builtin_chains[id], rule);
if (err < 0) {
connman_warn("Failed to delete jump rule '%s': %s",
}
g_free(rule);
- err = __connman_iptables_flush_chain(table_name, managed_chain);
+ err = __connman_iptables_flush_chain(AF_INET, table_name,
+ managed_chain);
if (err < 0) {
connman_warn("Failed to flush chain '%s': %s",
managed_chain, strerror(-err));
}
- err = __connman_iptables_delete_chain(table_name, managed_chain);
+ err = __connman_iptables_delete_chain(AF_INET, table_name,
+ managed_chain);
if (err < 0) {
connman_warn("Failed to delete chain '%s': %s",
managed_chain, strerror(-err));
g_free(managed_chain);
}
- err = __connman_iptables_commit(table_name);
+ err = __connman_iptables_commit(AF_INET, table_name);
if (err < 0) {
connman_warn("Failed to flush table '%s': %s",
table_name, strerror(-err));
#define CONNMAN_CHAIN_NAT_POST "nat-postrouting"
#define CONNMAN_CHAIN_ROUTE_OUTPUT "route-output"
-static bool debug_enabled = true;
+static bool debug_enabled = false;
struct firewall_handle {
uint64_t handle;
uint32_t seq = 0;
int err;
+ bzero(buf, sizeof(buf));
+
batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
uint32_t seq = 0;
int err;
+ bzero(buf, sizeof(buf));
+
batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
uint32_t seq = 0;
int err;
+ bzero(buf, sizeof(buf));
+
debug_netlink_dump_rule(rule);
batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
nftnl_rule_set(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_NAT_POST);
- /* IOF */
+ /* OIF */
expr = nftnl_expr_alloc("meta");
if (!expr)
goto err;
/*
* # nft add chain connman nat-prerouting \
- * { type nat hook prerouting priortiy 0 ; }
+ * { type nat hook prerouting priority 0 ; }
*/
chain = build_chain(CONNMAN_CHAIN_NAT_PRE, CONNMAN_TABLE,
"nat", NF_INET_PRE_ROUTING, 0);
/*
* # nft add chain connman nat-postrouting \
- * { type nat hook postrouting priortiy 0 ; }
+ * { type nat hook postrouting priority 0 ; }
*/
chain = build_chain(CONNMAN_CHAIN_NAT_POST, CONNMAN_TABLE,
"nat", NF_INET_POST_ROUTING, 0);
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
return err;
}
+bool __connman_inet_is_any_addr(const char *address, int family)
+{
+ bool ret = false;
+ struct addrinfo hints;
+ struct addrinfo *result = NULL;
+ struct sockaddr_in6 *in6 = NULL;
+ struct sockaddr_in *in4 = NULL;
+
+ if (!address || !*address)
+ goto out;
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+
+ hints.ai_family = family;
+
+ if (getaddrinfo(address, NULL, &hints, &result))
+ goto out;
+
+ if (result) {
+ if (result->ai_family == AF_INET6) {
+ in6 = (struct sockaddr_in6*)result->ai_addr;
+ ret = IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr);
+ } else if (result->ai_family == AF_INET) {
+ in4 = (struct sockaddr_in*)result->ai_addr;
+ ret = in4->sin_addr.s_addr == INADDR_ANY;
+ }
+
+ freeaddrinfo(result);
+ }
+
+out:
+ return ret;
+}
+
int connman_inet_ifindex(const char *name)
{
struct ifreq ifr;
return err;
}
+bool connman_inet_is_ifup(int index)
+{
+ int sk;
+ struct ifreq ifr;
+ bool ret = false;
+
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (sk < 0) {
+ connman_warn("Failed to open socket");
+ return false;
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_ifindex = index;
+
+ if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+ connman_warn("Failed to get interface name for interface %d", index);
+ goto done;
+ }
+
+ if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
+ connman_warn("Failed to get interface flags for index %d", index);
+ goto done;
+ }
+
+ if (ifr.ifr_flags & IFF_UP)
+ ret = true;
+
+done:
+ close(sk);
+
+ return ret;
+}
+
struct in6_ifreq {
struct in6_addr ifr6_addr;
__u32 ifr6_prefixlen;
memset(&rt, 0, sizeof(rt));
rt.rt_flags = RTF_UP;
- if (gateway)
+
+ /*
+ * Set RTF_GATEWAY only when gateway is set and the gateway IP address
+ * is not IPv4 any address (0.0.0.0). If the given gateway IP address is
+ * any address adding of route will fail when RTF_GATEWAY set. Passing
+ * gateway as NULL or INADDR_ANY should have the same effect. Setting
+ * the gateway address later to the struct is not affected by this,
+ * since given IPv4 any address (0.0.0.0) equals the value set with
+ * INADDR_ANY.
+ */
+ if (gateway && !__connman_inet_is_any_addr(gateway, AF_INET))
rt.rt_flags |= RTF_GATEWAY;
if (!netmask)
rt.rt_flags |= RTF_HOST;
rt.rtmsg_flags = RTF_UP | RTF_HOST;
- if (gateway) {
+ /*
+ * Set RTF_GATEWAY only when gateway is set, the gateway IP address is
+ * not IPv6 any address (e.g., ::) and the address is valid (conversion
+ * succeeds). If the given gateway IP address is any address then
+ * adding of route will fail when RTF_GATEWAY set. Passing gateway as
+ * NULL or IPv6 any address should have the same effect.
+ */
+
+ if (gateway && !__connman_inet_is_any_addr(gateway, AF_INET6) &&
+ inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) > 0)
rt.rtmsg_flags |= RTF_GATEWAY;
- inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway);
- }
rt.rtmsg_metric = 1;
rt.rtmsg_ifindex = index;
return false;
if (inet_aton(host, &_host_addr) == 0)
- return -1;
+ return false;
host_addr = _host_addr.s_addr;
sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
return TRUE;
cleanup:
+ rtnl_data->callback(NULL, rtnl_data->user_data);
inet_rtnl_cleanup(rtnl_data);
return TRUE;
}
data->callback(addr, index, data->user_data);
g_free(data);
-
- return;
}
/*
addr = NULL;
result = getaddrinfo(host, NULL, &hints, &addr);
- if (result == 0)
+ if (result == 0) {
result = addr->ai_family;
- freeaddrinfo(addr);
+ freeaddrinfo(addr);
+ }
return result;
}
return err;
}
+int __connman_inet_get_interface_mac_address(int index, uint8_t *mac_address)
+{
+ struct ifreq ifr;
+ int sk, err;
+ int ret = -EINVAL;
+
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (sk < 0) {
+ DBG("Open socket error");
+ return ret;
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_ifindex = index;
+
+ err = ioctl(sk, SIOCGIFNAME, &ifr);
+ if (err < 0) {
+ DBG("Get interface name error");
+ goto done;
+ }
+
+ err = ioctl(sk, SIOCGIFHWADDR, &ifr);
+ if (err < 0) {
+ DBG("Get MAC address error");
+ goto done;
+ }
+
+ memcpy(mac_address, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+ ret = 0;
+
+done:
+ close(sk);
+ return ret;
+}
+
static int iprule_modify(int cmd, int family, uint32_t table_id,
uint32_t fwmark)
{
}
static int iproute_default_modify(int cmd, uint32_t table_id, int ifindex,
- const char *gateway)
+ const char *gateway, unsigned char prefixlen)
{
struct __connman_inet_rtnl_handle rth;
unsigned char buf[sizeof(struct in6_addr)];
int ret, len;
int family = connman_inet_check_ipaddress(gateway);
+ char *dst = NULL;
+
+ DBG("gateway %s/%u table %u", gateway, prefixlen, table_id);
switch (family) {
case AF_INET:
return -EINVAL;
}
- ret = inet_pton(family, gateway, buf);
+ if (prefixlen) {
+ struct in_addr ipv4_subnet_addr, ipv4_mask;
+
+ memset(&ipv4_subnet_addr, 0, sizeof(ipv4_subnet_addr));
+ ipv4_mask.s_addr = htonl((0xffffffff << (32 - prefixlen)) & 0xffffffff);
+ ipv4_subnet_addr.s_addr = inet_addr(gateway);
+ ipv4_subnet_addr.s_addr &= ipv4_mask.s_addr;
+
+ dst = g_strdup(inet_ntoa(ipv4_subnet_addr));
+ }
+
+ ret = inet_pton(family, dst ? dst : gateway, buf);
+ g_free(dst);
if (ret <= 0)
return -EINVAL;
rth.req.u.r.rt.rtm_protocol = RTPROT_BOOT;
rth.req.u.r.rt.rtm_scope = RT_SCOPE_UNIVERSE;
rth.req.u.r.rt.rtm_type = RTN_UNICAST;
+ rth.req.u.r.rt.rtm_dst_len = prefixlen;
+
+ __connman_inet_rtnl_addattr_l(&rth.req.n, sizeof(rth.req),
+ prefixlen > 0 ? RTA_DST : RTA_GATEWAY, buf, len);
- __connman_inet_rtnl_addattr_l(&rth.req.n, sizeof(rth.req), RTA_GATEWAY,
- buf, len);
if (table_id < 256) {
rth.req.u.r.rt.rtm_table = table_id;
} else {
{
/* ip route add default via 1.2.3.4 dev wlan0 table 1234 */
- return iproute_default_modify(RTM_NEWROUTE, table_id, ifindex, gateway);
+ return iproute_default_modify(RTM_NEWROUTE, table_id, ifindex, gateway, 0);
+}
+
+int __connman_inet_add_subnet_to_table(uint32_t table_id, int ifindex,
+ const char *gateway, unsigned char prefixlen)
+{
+ /* ip route add 1.2.3.4/24 dev eth0 table 1234 */
+ return iproute_default_modify(RTM_NEWROUTE, table_id, ifindex, gateway, prefixlen);
}
int __connman_inet_del_default_from_table(uint32_t table_id, int ifindex,
{
/* ip route del default via 1.2.3.4 dev wlan0 table 1234 */
- return iproute_default_modify(RTM_DELROUTE, table_id, ifindex, gateway);
+ return iproute_default_modify(RTM_DELROUTE, table_id, ifindex, gateway, 0);
+}
+
+int __connman_inet_del_subnet_from_table(uint32_t table_id, int ifindex,
+ const char *gateway, unsigned char prefixlen)
+{
+ /* ip route del 1.2.3.4/24 dev eth0 table 1234 */
+ return iproute_default_modify(RTM_DELROUTE, table_id, ifindex, gateway, prefixlen);
}
int __connman_inet_get_interface_ll_address(int index, int family,
pnp_file, error->message);
goto out;
}
- } else {
- connman_error("%s: File %s doesn't exist\n", __func__, pnp_file);
+ } else
goto out;
- }
len = strlen(cmdline);
if (len <= 1) {
if (!pnp_file)
pnp_file = "/proc/net/pnp";
+ if (!g_file_test(pnp_file, G_FILE_TEST_EXISTS))
+ goto out;
+
if (!g_file_get_contents(pnp_file, &pnp, NULL, &error)) {
connman_error("%s: Cannot read %s %s\n", __func__,
pnp_file, error->message);
#include <stdio.h>
#include <string.h>
#include <unistd.h>
-#include <sys/errno.h>
#include <sys/socket.h>
#include "connman.h"
};
struct connman_ippool {
- unsigned int refcount;
-
struct address_info *info;
char *gateway;
static uint32_t block_24_bits;
static uint32_t subnet_mask_24;
-struct connman_ippool *
-__connman_ippool_ref_debug(struct connman_ippool *pool,
- const char *file, int line, const char *caller)
-{
- DBG("%p ref %d by %s:%d:%s()", pool, pool->refcount + 1,
- file, line, caller);
-
- __sync_fetch_and_add(&pool->refcount, 1);
-
- return pool;
-}
-
-void __connman_ippool_unref_debug(struct connman_ippool *pool,
- const char *file, int line, const char *caller)
+void __connman_ippool_free(struct connman_ippool *pool)
{
if (!pool)
return;
- DBG("%p ref %d by %s:%d:%s()", pool, pool->refcount - 1,
- file, line, caller);
-
- if (__sync_fetch_and_sub(&pool->refcount, 1) != 1)
- return;
-
if (pool->info) {
allocated_blocks = g_slist_remove(allocated_blocks, pool->info);
g_free(pool->info);
info->start = block;
info->end = block + range;
- pool->refcount = 1;
pool->info = info;
pool->collision_cb = collision_cb;
pool->user_data = user_data;
#include <stdio.h>
#include <string.h>
#include <unistd.h>
-#include <sys/errno.h>
+#include <errno.h>
#include <sys/socket.h>
#include <xtables.h>
#include <inttypes.h>
+#include <setjmp.h>
#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
#include "connman.h"
#include "src/shared/util.h"
* End of CHAIN
*/
+/*
+ * Values for the index values used here are defined as equal for both IPv4
+ * and IPv6 (NF_IP_* and NF_IP6_*) in Netfilter headers.
+ */
static const char *hooknames[] = {
[NF_IP_PRE_ROUTING] = "PREROUTING",
[NF_IP_LOCAL_IN] = "INPUT",
#define XT_OPTION_OFFSET_SCALE 256
-#define MIN_ALIGN (__alignof__(struct ipt_entry))
-
-#define ALIGN(s) (((s) + ((MIN_ALIGN)-1)) & ~((MIN_ALIGN)-1))
-
-struct error_target {
- struct xt_entry_target t;
- char error[IPT_TABLE_MAXNAMELEN];
-};
-
struct connman_iptables_entry {
- int offset;
+ int type;
+ unsigned int offset;
int builtin;
int counter_idx;
struct ipt_entry *entry;
+ struct ip6t_entry *entry6;
};
struct connman_iptables {
+ int type;
char *name;
int ipt_sock;
struct ipt_getinfo *info;
struct ipt_get_entries *blob_entries;
+ struct ip6t_getinfo *info6;
+ struct ip6t_get_entries *blob_entries6;
unsigned int num_entries;
unsigned int old_entries;
};
static GHashTable *table_hash = NULL;
+static GHashTable *table_hash_ipv6 = NULL;
static bool debug_enabled = false;
-typedef int (*iterate_entries_cb_t)(struct ipt_entry *entry, int builtin,
- unsigned int hook, size_t size,
- unsigned int offset, void *user_data);
+struct iptables_ip {
+ int type;
+ struct ipt_ip *ip;
+ struct ip6t_ip6 *ip6;
+};
+
+struct iptables_replace {
+ int type;
+ struct ipt_replace *r;
+ struct ip6t_replace *r6;
+};
+
+static jmp_buf env_state;
+static bool jmp_set = false;
+
+static void enable_jmp()
+{
+ jmp_set = true;
+}
+
+static void disable_jmp()
+{
+ jmp_set = false;
+}
+
+static bool can_jmp()
+{
+ DBG("%s", jmp_set ? "true" : "false");
+ return jmp_set;
+}
+
+typedef int (*iterate_entries_cb_t)(struct connman_iptables_entry *entry,
+ int builtin, unsigned int hook,
+ size_t size, unsigned int offset,
+ void *user_data);
+
+static u_int16_t iptables_entry_get_next_offset(
+ struct connman_iptables_entry *entry)
+{
+ if (!entry)
+ return 0;
+
+ switch (entry->type) {
+ case AF_INET:
+ return entry->entry ? entry->entry->next_offset : 0;
+ case AF_INET6:
+ return entry->entry6 ? entry->entry6->next_offset : 0;
+ }
+
+ return 0;
+}
+
+static u_int16_t iptables_entry_get_target_offset(
+ struct connman_iptables_entry *entry)
+{
+ if (!entry)
+ return 0;
+
+ switch (entry->type) {
+ case AF_INET:
+ return entry->entry ? entry->entry->target_offset : 0;
+ case AF_INET6:
+ return entry->entry6 ? entry->entry6->target_offset : 0;
+ }
+
+ return 0;
+}
+
+static unsigned char *iptables_entry_get_elems(
+ struct connman_iptables_entry *entry)
+{
+ if (!entry)
+ return NULL;
+
+ switch (entry->type) {
+ case AF_INET:
+ return entry->entry ? entry->entry->elems : NULL;
+ case AF_INET6:
+ return entry->entry6 ? entry->entry6->elems : NULL;
+ }
+
+ return NULL;
+}
+
+static struct xt_entry_target *iptables_entry_get_target(
+ struct connman_iptables_entry *entry)
+{
+ if (!entry)
+ return NULL;
+
+ switch (entry->type) {
+ case AF_INET:
+ return entry->entry ? ipt_get_target(entry->entry) : NULL;
+ case AF_INET6:
+ return entry->entry6 ? ip6t_get_target(entry->entry6) : NULL;
+ }
+
+ return NULL;
+}
+
+static struct xt_counters *iptables_entry_get_counters(
+ struct connman_iptables_entry *entry)
+{
+ if (!entry)
+ return NULL;
+
+ switch (entry->type) {
+ case AF_INET:
+ return entry->entry ? &entry->entry->counters : NULL;
+ case AF_INET6:
+ return entry->entry6 ? &entry->entry6->counters : NULL;
+ }
+
+ return NULL;
+}
+
+static void iptables_entry_free(struct connman_iptables_entry *entry)
+{
+ if (!entry)
+ return;
+
+ g_free(entry->entry);
+ g_free(entry->entry6);
+ g_free(entry);
+}
+
+static const char *iptables_table_get_info_name(struct connman_iptables* table)
+{
+ if (!table)
+ return NULL;
+
+ switch (table->type) {
+ case AF_INET:
+ return table->info->name;
+ case AF_INET6:
+ return table->info6->name;
+ }
+
+ return NULL;
+}
+
+static unsigned int iptables_table_get_info_num_entries(
+ struct connman_iptables* table)
+{
+ if (!table)
+ return 0;
+
+ switch (table->type) {
+ case AF_INET:
+ return table->info->num_entries;
+ case AF_INET6:
+ return table->info6->num_entries;
+ }
+
+ return 0;
+}
+
+static unsigned int iptables_table_get_info_size(struct connman_iptables* table)
+{
+ if (!table)
+ return 0;
+
+ switch (table->type) {
+ case AF_INET:
+ return table->info->size;
+ case AF_INET6:
+ return table->info6->size;
+ }
+
+ return 0;
+}
+
+static unsigned int iptables_table_get_info_valid_hooks(
+ struct connman_iptables* table)
+{
+ if (!table)
+ return 0;
+
+ switch (table->type) {
+ case AF_INET:
+ return table->info->valid_hooks;
+ case AF_INET6:
+ return table->info6->valid_hooks;
+ }
+
+ return 0;
+}
+
+static unsigned int *iptables_table_get_info_hook_entry(
+ struct connman_iptables* table)
+{
+ if (!table)
+ return NULL;
+
+ switch (table->type) {
+ case AF_INET:
+ return table->info->hook_entry;
+ case AF_INET6:
+ return table->info6->hook_entry;
+ }
+
+ return NULL;
+}
+
+static unsigned int *iptables_table_get_info_underflow(
+ struct connman_iptables* table)
+{
+ if (!table)
+ return NULL;
+
+ switch (table->type) {
+ case AF_INET:
+ return table->info->underflow;
+ case AF_INET6:
+ return table->info6->underflow;
+ }
+
+ return NULL;
+}
+
+static unsigned int iptables_table_get_entries_size(
+ struct connman_iptables* table)
+{
+ if (!table)
+ return 0;
+
+ switch (table->type) {
+ case AF_INET:
+ return table->blob_entries->size;
+ case AF_INET6:
+ return table->blob_entries6->size;
+ }
+
+ return 0;
+}
+
+static const char *get_error_target(int type)
+{
+ switch (type) {
+ case AF_INET:
+ return IPT_ERROR_TARGET;
+ case AF_INET6:
+ return IP6T_ERROR_TARGET;
+ default:
+ return XT_ERROR_TARGET;
+ }
+}
+
+static const char *get_standard_target(int type)
+{
+ switch (type) {
+ case AF_INET:
+ return IPT_STANDARD_TARGET;
+ case AF_INET6:
+ return IP6T_STANDARD_TARGET;
+ default:
+ return XT_STANDARD_TARGET;
+ }
+}
+
+static struct connman_iptables *hash_table_lookup(int type,
+ const char *table_name) {
+
+ switch (type) {
+ case AF_INET:
+ return g_hash_table_lookup(table_hash, table_name);
+ case AF_INET6:
+ return g_hash_table_lookup(table_hash_ipv6, table_name);
+ }
+
+ return NULL;
+}
+
+static bool hash_table_replace(int type,
+ char *table_name,
+ struct connman_iptables *table) {
+
+ switch (type) {
+ case AF_INET:
+ return g_hash_table_replace(table_hash, table_name, table);
+ case AF_INET6:
+ return g_hash_table_replace(table_hash_ipv6, table_name, table);
+ }
+
+ return false;
+}
+
+static bool hash_table_remove(int type, const char *table_name)
+{
+ switch (type) {
+ case AF_INET:
+ return g_hash_table_remove(table_hash, table_name);
+ case AF_INET6:
+ return g_hash_table_remove(table_hash_ipv6, table_name);
+ }
+
+ return false;
+}
static unsigned int next_hook_entry_index(unsigned int *valid_hooks)
{
return h;
}
-static int iterate_entries(struct ipt_entry *entries,
+static int iterate_entries(struct connman_iptables_entry *entries,
unsigned int valid_hooks,
unsigned int *hook_entry,
unsigned int *underflow,
{
unsigned int offset, h, hook;
int builtin, err;
- struct ipt_entry *entry;
+ struct connman_iptables_entry entry;
+
+ if (!entries)
+ return -EINVAL;
+
+ switch (entries->type) {
+ case AF_INET:
+ if (!entries->entry)
+ return -EINVAL;
+
+ break;
+ case AF_INET6:
+ if (!entries->entry6)
+ return -EINVAL;
+
+ break;
+ default:
+ return -EINVAL;
+ }
h = next_hook_entry_index(&valid_hooks);
hook = h;
- for (offset = 0, entry = entries; offset < size;
- offset += entry->next_offset) {
+ entry.type = entries->type;
+ entry.entry = entries->entry;
+ entry.entry6 = entries->entry6;
+
+ for (offset = 0; offset < size;
+ offset += iptables_entry_get_next_offset(&entry)) {
builtin = -1;
- entry = (void *)entries + offset;
+
+ switch (entries->type) {
+ case AF_INET:
+ entry.entry = (void* )entries->entry + offset;
+ break;
+ case AF_INET6:
+ entry.entry6 = (void* )entries->entry6 + offset;
+ break;
+ }
/*
* Updating builtin, hook and h is very tricky.
if (h < NF_INET_NUMHOOKS && underflow[h] <= offset)
h = next_hook_entry_index(&valid_hooks);
- err = cb(entry, builtin, hook, size, offset, user_data);
+ err = cb(&entry, builtin, hook, size, offset, user_data);
if (err < 0)
return err;
}
return 0;
}
-static int print_entry(struct ipt_entry *entry, int builtin, unsigned int hook,
- size_t size, unsigned int offset,
- void *user_data)
+static int print_entry(struct connman_iptables_entry *entry, int builtin,
+ unsigned int hook, size_t size,
+ unsigned int offset, void *user_data)
{
- iterate_entries_cb_t cb = user_data;
+ iterate_entries_cb_t cb;
+ struct xt_counters *counters;
+
+ cb = user_data;
+ counters = iptables_entry_get_counters(entry);
- 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);
+ DBG("entry %p hook %u offset %u size %u packets %"PRIu64" "
+ "bytes %"PRIu64, entry, hook, offset,
+ iptables_entry_get_next_offset(entry),
+ (uint64_t) counters->pcnt, (uint64_t) counters->bcnt);
return cb(entry, builtin, hook, size, offset, NULL);
}
{
struct xt_entry_target *target;
- target = ipt_get_target(e->entry);
+ target = iptables_entry_get_target(e);
+
+ if (!target)
+ return false;
- if (!g_strcmp0(target->u.user.name, IPT_STANDARD_TARGET)) {
+ if (!g_strcmp0(target->u.user.name, get_standard_target(e->type))) {
struct xt_standard_target *t;
t = (struct xt_standard_target *)target;
{
struct xt_entry_target *target;
- target = ipt_get_target(e->entry);
- if (!g_strcmp0(target->u.user.name, IPT_STANDARD_TARGET)) {
+ target = iptables_entry_get_target(e);
+
+ if (!target)
+ return false;
+
+ if (!g_strcmp0(target->u.user.name, get_standard_target(e->type))) {
struct xt_standard_target *t;
t = (struct xt_standard_target *)target;
static bool is_chain(struct connman_iptables *table,
struct connman_iptables_entry *e)
{
- struct ipt_entry *entry;
struct xt_entry_target *target;
- entry = e->entry;
+ if (!e)
+ return false;
+
if (e->builtin >= 0)
return true;
- target = ipt_get_target(entry);
- if (!g_strcmp0(target->u.user.name, IPT_ERROR_TARGET))
+ target = iptables_entry_get_target(e);
+
+ if (!target)
+ return false;
+
+ if (!g_strcmp0(target->u.user.name, get_error_target(e->type)))
return true;
return false;
{
GList *list;
struct connman_iptables_entry *head;
- struct ipt_entry *entry;
struct xt_entry_target *target;
int builtin;
+
+ switch (table->type) {
+ case AF_INET:
+ case AF_INET6:
+ break;
+ default:
+ return NULL;
+ }
for (list = table->entries; list; list = list->next) {
head = list->data;
- entry = head->entry;
/* Buit-in chain */
builtin = head->builtin;
+
if (builtin >= 0 && !g_strcmp0(hooknames[builtin], chain_name))
break;
/* User defined chain */
- target = ipt_get_target(entry);
- if (!g_strcmp0(target->u.user.name, IPT_ERROR_TARGET) &&
- !g_strcmp0((char *)target->data, chain_name))
+ target = iptables_entry_get_target(head);
+
+ if (!target)
+ continue;
+
+ if (!g_strcmp0(target->u.user.name,
+ get_error_target(table->type)) &&
+ !g_strcmp0((char *)target->data, chain_name))
break;
}
return g_list_last(table->entries);
}
-
static void update_offsets(struct connman_iptables *table)
{
GList *list, *prev;
prev_entry = prev->data;
entry->offset = prev_entry->offset +
- prev_entry->entry->next_offset;
+ iptables_entry_get_next_offset(
+ prev_entry);
}
}
struct connman_iptables_entry *tmp;
struct xt_standard_target *t;
GList *list;
- int offset;
+ unsigned int offset;
- offset = modified_entry->entry->next_offset;
+ offset = iptables_entry_get_next_offset(modified_entry);
for (list = table->entries; list; list = list->next) {
tmp = list->data;
if (!is_jump(tmp))
continue;
- t = (struct xt_standard_target *)ipt_get_target(tmp->entry);
+ t = (struct xt_standard_target *)
+ iptables_entry_get_target(tmp);
+
+ if (!t)
+ continue;
if (is_removing) {
if (t->verdict >= entry_before->offset)
if (is_fallthrough(modified_entry)) {
t = (struct xt_standard_target *)
- ipt_get_target(modified_entry->entry);
+ iptables_entry_get_target(modified_entry);
+
+ if (!t)
+ return;
t->verdict = entry_before->offset +
- modified_entry->entry->target_offset +
- ALIGN(sizeof(struct xt_standard_target));
+ iptables_entry_get_target_offset(modified_entry) +
+ XT_ALIGN(sizeof(struct xt_standard_target));
t->target.u.target_size =
- ALIGN(sizeof(struct xt_standard_target));
+ XT_ALIGN(sizeof(struct xt_standard_target));
}
}
static int iptables_add_entry(struct connman_iptables *table,
- struct ipt_entry *entry, GList *before,
- int builtin, int counter_idx)
+ struct connman_iptables_entry *entry,
+ GList *before, int builtin, int counter_idx)
{
struct connman_iptables_entry *e, *entry_before;
- if (!table)
- return -1;
+ if (!table) {
+ return -EINVAL;
+ }
e = g_try_malloc0(sizeof(struct connman_iptables_entry));
if (!e)
- return -1;
+ return -ENOMEM;
+
+ switch (table->type) {
+ case AF_INET:
+ e->entry = entry->entry;
+ break;
+ case AF_INET6:
+ e->entry6 = entry->entry6;
+ break;
+ default:
+ g_free(e);
+ return -EINVAL;
+ }
- e->entry = entry;
+ e->type = entry->type;
e->builtin = builtin;
e->counter_idx = counter_idx;
table->entries = g_list_insert_before(table->entries, before, e);
table->num_entries++;
- table->size += entry->next_offset;
+ table->size += iptables_entry_get_next_offset(e);
if (!before) {
- e->offset = table->size - entry->next_offset;
-
+ e->offset = table->size -
+ iptables_entry_get_next_offset(e);
return 0;
}
struct connman_iptables_entry *entry)
{
int removed = 0;
+ u_int16_t next_offset;
+ next_offset = iptables_entry_get_next_offset(entry);
table->num_entries--;
- table->size -= entry->entry->next_offset;
- removed = entry->entry->next_offset;
+
+ table->size -= next_offset;
+ removed = next_offset;
table->entries = g_list_remove(table->entries, entry);
- g_free(entry->entry);
- g_free(entry);
+ iptables_entry_free(entry);
return removed;
}
const char *name)
{
GList *last;
- struct ipt_entry *entry_head;
- struct ipt_entry *entry_return;
- struct error_target *error;
- struct ipt_standard_target *standard;
+ struct ipt_entry *entry_head = NULL;
+ struct ipt_entry *entry_return = NULL;
+ struct ip6t_entry *entry6_head = NULL;
+ struct ip6t_entry *entry6_return = NULL;
+ struct connman_iptables_entry entry = { 0 };
+ struct xt_error_target *error = NULL;
+ struct ipt_standard_target *standard = NULL;
u_int16_t entry_head_size, entry_return_size;
+ size_t entry_struct_size = 0;
+ size_t xt_error_target_size = 0;
+ size_t standard_target_size = 0;
DBG("table %s chain %s", table->name, name);
+ entry.type = table->type;
+
+ /* Do not allow to add duplicate chains */
+ if (find_chain_head(table, name))
+ return -EEXIST;
+
last = g_list_last(table->entries);
+ xt_error_target_size = XT_ALIGN(sizeof(struct xt_error_target));
+
/*
* An empty chain is composed of:
* - A head entry, with no match and an error target.
*/
/* head entry */
- entry_head_size = ALIGN(sizeof(struct ipt_entry)) +
- ALIGN(sizeof(struct error_target));
- entry_head = g_try_malloc0(entry_head_size);
- if (!entry_head)
- goto err_head;
+ switch (entry.type) {
+ case AF_INET:
+ entry_struct_size = XT_ALIGN(sizeof(struct ipt_entry));
+ entry_head_size = entry_struct_size + xt_error_target_size;
+
+ entry_head = g_try_malloc0(entry_head_size);
+ if (!entry_head)
+ goto err_head;
+
+ entry_head->target_offset = entry_struct_size;
+ entry_head->next_offset = entry_head_size;
- entry_head->target_offset = ALIGN(sizeof(struct ipt_entry));
- entry_head->next_offset = entry_head_size;
+ error = (struct xt_error_target *) entry_head->elems;
+
+ entry.entry = entry_head;
+ break;
+ case AF_INET6:
+ entry_struct_size = XT_ALIGN(sizeof(struct ip6t_entry));
+ entry_head_size = entry_struct_size + xt_error_target_size;
+
+ entry6_head = g_try_malloc0(entry_head_size);
+ if (!entry6_head)
+ goto err_head;
+
+ entry6_head->target_offset = entry_struct_size;
+ entry6_head->next_offset = entry_head_size;
+
+ error = (struct xt_error_target *) entry6_head->elems;
+
+ entry.entry6 = entry6_head;
+ break;
+ default:
+ return -EINVAL;
+ }
- error = (struct error_target *) entry_head->elems;
- g_stpcpy(error->t.u.user.name, IPT_ERROR_TARGET);
- error->t.u.user.target_size = ALIGN(sizeof(struct error_target));
- g_stpcpy(error->error, name);
+ g_stpcpy(error->target.u.user.name, get_error_target(entry.type));
+ error->target.u.user.target_size = xt_error_target_size;
+ g_stpcpy(error->errorname, name);
- if (iptables_add_entry(table, entry_head, last, -1, -1) < 0)
+ if (iptables_add_entry(table, &entry, last, -1, -1) < 0)
goto err_head;
+ standard_target_size = XT_ALIGN(sizeof(struct ipt_standard_target));
+ entry_return_size = entry_struct_size + standard_target_size;
+
/* tail entry */
- entry_return_size = ALIGN(sizeof(struct ipt_entry))+
- ALIGN(sizeof(struct ipt_standard_target));
- entry_return = g_try_malloc0(entry_return_size);
- if (!entry_return)
- goto err;
+ switch (entry.type) {
+ case AF_INET:
+ entry_return = g_try_malloc0(entry_return_size);
+ if (!entry_return)
+ goto err;
+
+ entry_return->target_offset = entry_struct_size;
+ entry_return->next_offset = entry_return_size;
+
+ standard = (struct ipt_standard_target *) entry_return->elems;
+
+ entry.entry = entry_return;
+ break;
+ case AF_INET6:
+ entry6_return = g_try_malloc0(entry_return_size);
+ if (!entry6_return)
+ goto err;
+
+ entry6_return->target_offset = entry_struct_size;
+ entry6_return->next_offset = entry_return_size;
+
+ standard = (struct ipt_standard_target *) entry6_return->elems;
- entry_return->target_offset = ALIGN(sizeof(struct ipt_entry));
- entry_return->next_offset = entry_return_size;
+ entry.entry6 = entry6_return;
+ break;
+ }
- standard = (struct ipt_standard_target *) entry_return->elems;
- standard->target.u.user.target_size =
- ALIGN(sizeof(struct ipt_standard_target));
+ standard->target.u.user.target_size = standard_target_size;
standard->verdict = XT_RETURN;
- if (iptables_add_entry(table, entry_return, last, -1, -1) < 0)
+ if (iptables_add_entry(table, &entry, last, -1, -1) < 0)
goto err;
return 0;
err:
g_free(entry_return);
+ g_free(entry6_return);
err_head:
g_free(entry_head);
+ g_free(entry6_head);
return -ENOMEM;
}
return 0;
}
-static struct ipt_entry *new_rule(struct ipt_ip *ip,
+static struct connman_iptables_entry *new_rule(struct iptables_ip *ip,
const char *target_name, struct xtables_target *xt_t,
struct xtables_rule_match *xt_rm)
{
struct xtables_rule_match *tmp_xt_rm;
- struct ipt_entry *new_entry;
+ struct connman_iptables_entry *new_entry;
size_t match_size, target_size;
+ new_entry = g_try_malloc0(sizeof(struct connman_iptables_entry));
+
+ if (!new_entry)
+ return NULL;
+
+ new_entry->type = ip->type;
+
match_size = 0;
for (tmp_xt_rm = xt_rm; tmp_xt_rm; tmp_xt_rm = tmp_xt_rm->next)
match_size += tmp_xt_rm->match->m->u.match_size;
if (xt_t)
target_size = xt_t->t->u.target_size;
else
- target_size = ALIGN(sizeof(struct xt_standard_target));
-
- new_entry = g_try_malloc0(ALIGN(sizeof(struct ipt_entry)) +
- target_size + match_size);
- if (!new_entry)
- return NULL;
-
- memcpy(&new_entry->ip, ip, sizeof(struct ipt_ip));
-
- new_entry->target_offset = ALIGN(sizeof(struct ipt_entry)) +
- match_size;
- new_entry->next_offset = ALIGN(sizeof(struct ipt_entry)) +
+ target_size = XT_ALIGN(sizeof(struct xt_standard_target));
+
+ switch (ip->type) {
+ case AF_INET:
+ new_entry->entry = g_try_malloc0(
+ XT_ALIGN(sizeof(struct ipt_entry)) +
+ target_size + match_size);
+ if (!new_entry->entry)
+ goto err;
+
+ memcpy(&new_entry->entry->ip, ip->ip, sizeof(struct ipt_ip));
+
+ new_entry->entry->target_offset =
+ XT_ALIGN(sizeof(struct ipt_entry)) +
+ match_size;
+ new_entry->entry->next_offset =
+ XT_ALIGN(sizeof(struct ipt_entry)) +
target_size + match_size;
+ break;
+ case AF_INET6:
+ new_entry->entry6 = g_try_malloc0(
+ XT_ALIGN(sizeof(struct ip6t_entry)) +
+ target_size + match_size);
+ if (!new_entry->entry6)
+ goto err;
+
+ memcpy(&new_entry->entry6->ipv6, ip->ip6,
+ sizeof(struct ip6t_ip6));
+
+ new_entry->entry6->target_offset =
+ XT_ALIGN(sizeof(struct ip6t_entry)) +
+ match_size;
+ new_entry->entry6->next_offset =
+ XT_ALIGN(sizeof(struct ip6t_entry)) +
+ target_size + match_size;
+ break;
+ default:
+ goto err;
+ }
match_size = 0;
for (tmp_xt_rm = xt_rm; tmp_xt_rm;
tmp_xt_rm = tmp_xt_rm->next) {
- memcpy(new_entry->elems + match_size, tmp_xt_rm->match->m,
+
+ switch (new_entry->type) {
+ case AF_INET:
+ memcpy(new_entry->entry->elems + match_size,
+ tmp_xt_rm->match->m,
tmp_xt_rm->match->m->u.match_size);
+ break;
+ case AF_INET6:
+ memcpy(new_entry->entry6->elems + match_size,
+ tmp_xt_rm->match->m,
+ tmp_xt_rm->match->m->u.match_size);
+ break;
+ }
match_size += tmp_xt_rm->match->m->u.match_size;
}
if (xt_t) {
struct xt_entry_target *entry_target;
- entry_target = ipt_get_target(new_entry);
+ entry_target = iptables_entry_get_target(new_entry);
memcpy(entry_target, xt_t->t, target_size);
}
return new_entry;
+
+err:
+ g_free(new_entry);
+
+ return NULL;
}
static void update_hooks(struct connman_iptables *table, GList *chain_head,
- struct ipt_entry *entry)
+ struct connman_iptables_entry *entry)
{
GList *list;
struct connman_iptables_entry *head, *e;
int builtin;
+ u_int16_t next_offset;
- if (!chain_head)
+ if (!table || !chain_head)
return;
head = chain_head->data;
if (builtin < 0)
return;
- table->underflow[builtin] += entry->next_offset;
+ next_offset = iptables_entry_get_next_offset(entry);
+
+ table->underflow[builtin] += next_offset;
for (list = chain_head->next; list; list = list->next) {
e = list->data;
if (builtin < 0)
continue;
- table->hook_entry[builtin] += entry->next_offset;
- table->underflow[builtin] += entry->next_offset;
+ table->hook_entry[builtin] += next_offset;
+ table->underflow[builtin] += next_offset;
}
}
-static struct ipt_entry *prepare_rule_inclusion(struct connman_iptables *table,
- struct ipt_ip *ip, const char *chain_name,
+static struct connman_iptables_entry *prepare_rule_inclusion(
+ struct connman_iptables *table,
+ struct iptables_ip *ip,
+ const char *chain_name,
const char *target_name,
struct xtables_target *xt_t,
- int *builtin, struct xtables_rule_match *xt_rm,
+ int *builtin,
+ struct xtables_rule_match *xt_rm,
bool insert)
{
GList *chain_tail, *chain_head;
- struct ipt_entry *new_entry;
struct connman_iptables_entry *head;
+ struct connman_iptables_entry *new_entry;
chain_head = find_chain_head(table, chain_name);
if (!chain_head)
return NULL;
new_entry = new_rule(ip, target_name, xt_t, xt_rm);
- if (!new_entry)
- return NULL;
+
+ switch (new_entry->type) {
+ case AF_INET:
+ if (new_entry->entry)
+ break;
+ case AF_INET6:
+ if (new_entry->entry6)
+ break;
+ default:
+ goto err;
+ }
update_hooks(table, chain_head, new_entry);
}
return new_entry;
+
+err:
+ g_free(new_entry);
+
+ return NULL;
}
static int iptables_append_rule(struct connman_iptables *table,
- struct ipt_ip *ip, const char *chain_name,
+ struct iptables_ip *ip,
+ const char *chain_name,
const char *target_name,
struct xtables_target *xt_t,
struct xtables_rule_match *xt_rm)
{
- struct ipt_entry *new_entry;
+ struct connman_iptables_entry *new_entry;
int builtin = -1, ret;
GList *chain_tail;
if (!chain_tail)
return -EINVAL;
- new_entry = prepare_rule_inclusion(table, ip, chain_name,
- target_name, xt_t, &builtin, xt_rm, false);
+ new_entry = prepare_rule_inclusion(table, ip, chain_name, target_name,
+ xt_t, &builtin, xt_rm, false);
+
if (!new_entry)
return -EINVAL;
- ret = iptables_add_entry(table, new_entry, chain_tail->prev, builtin, -1);
+ switch (new_entry->type) {
+ case AF_INET:
+ if (new_entry->entry)
+ break;
+ case AF_INET6:
+ if (new_entry->entry6)
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = iptables_add_entry(table, new_entry, chain_tail->prev,
+ builtin, -1);
if (ret < 0)
- g_free(new_entry);
+ goto err;
+
+ /*
+ * Free only the container, not the content iptables_add_entry()
+ * allocates new containers for entries.
+ */
+ g_free(new_entry);
+
+ return ret;
+
+err:
+ iptables_entry_free(new_entry);
return ret;
}
static int iptables_insert_rule(struct connman_iptables *table,
- struct ipt_ip *ip, const char *chain_name,
+ struct iptables_ip *ip,
+ const char *chain_name,
const char *target_name,
struct xtables_target *xt_t,
struct xtables_rule_match *xt_rm)
{
- struct ipt_entry *new_entry;
+ struct connman_iptables_entry *new_entry;
int builtin = -1, ret;
GList *chain_head;
if (!chain_head)
return -EINVAL;
- new_entry = prepare_rule_inclusion(table, ip, chain_name,
- target_name, xt_t, &builtin, xt_rm, true);
+ new_entry = prepare_rule_inclusion(table, ip, chain_name, target_name,
+ xt_t, &builtin, xt_rm, true);
+
if (!new_entry)
return -EINVAL;
+ switch (new_entry->type) {
+ case AF_INET:
+ if (new_entry->entry)
+ break;
+ case AF_INET6:
+ if (new_entry->entry6)
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
if (builtin == -1)
chain_head = chain_head->next;
- ret = iptables_add_entry(table, new_entry, chain_head, builtin, -1);
- if (ret < 0)
- g_free(new_entry);
+ ret = iptables_add_entry(table, new_entry, chain_head, builtin, -1);
+ if (ret < 0)
+ goto err;
+
+ /*
+ * Free only the container, not the content iptables_add_entry()
+ * allocates new containers for entries.
+ */
+ g_free(new_entry);
+
+ return ret;
+
+err:
+ iptables_entry_free(new_entry);
return ret;
}
return true;
}
+/* A copy of is_same_ipt_entry with IPv6 structures */
+static bool is_same_ip6t_entry(struct ip6t_entry *i_e1,
+ struct ip6t_entry *i_e2)
+{
+ if (memcmp(&i_e1->ipv6, &i_e2->ipv6, sizeof(struct ip6t_ip6)) != 0)
+ return false;
+
+ if (i_e1->target_offset != i_e2->target_offset)
+ return false;
+
+ if (i_e1->next_offset != i_e2->next_offset)
+ return false;
+
+ return true;
+}
+
+static bool is_same_iptables_entry(struct connman_iptables_entry *e1,
+ struct connman_iptables_entry *e2)
+{
+ if (e1->type != e2->type)
+ return false;
+
+ switch (e1->type) {
+ case AF_INET:
+ return is_same_ipt_entry(e1->entry, e2->entry);
+ case AF_INET6:
+ return is_same_ip6t_entry(e1->entry6, e2->entry6);
+ }
+
+ return false;
+}
+
static bool is_same_target(struct xt_entry_target *xt_e_t1,
struct xt_entry_target *xt_e_t2)
{
g_strcmp0(xt_e_t2->u.user.name, "") == 0) {
/* fallthrough */
return true;
- } else if (g_strcmp0(xt_e_t1->u.user.name, IPT_STANDARD_TARGET) == 0) {
+
+ /*
+ * IPT_STANDARD_TARGET and IP6T_STANDARD_TARGET are defined by
+ * XT_STANDARD_TARGET
+ */
+ } else if (g_strcmp0(xt_e_t1->u.user.name, XT_STANDARD_TARGET) == 0) {
struct xt_standard_target *xt_s_t1;
struct xt_standard_target *xt_s_t2;
}
static GList *find_existing_rule(struct connman_iptables *table,
- struct ipt_ip *ip, const char *chain_name,
+ struct iptables_ip *ip,
+ const char *chain_name,
const char *target_name,
struct xtables_target *xt_t,
GList *matches,
struct xt_entry_target *xt_e_t = NULL;
struct xt_entry_match *xt_e_m = NULL;
struct connman_iptables_entry *entry;
- struct ipt_entry *entry_test;
+ struct connman_iptables_entry *entry_test;
int builtin;
chain_head = find_chain_head(table, chain_name);
return NULL;
entry_test = new_rule(ip, target_name, xt_t, xt_rm);
- if (!entry_test)
+
+ switch (entry_test->type) {
+ case AF_INET:
+ if (!entry_test->entry)
+ return NULL;
+ break;
+ case AF_INET6:
+ if (!entry_test->entry6)
+ return NULL;
+ break;
+ default:
return NULL;
+ }
if (xt_t)
- xt_e_t = ipt_get_target(entry_test);
+ xt_e_t = iptables_entry_get_target(entry_test);
if (matches)
- xt_e_m = (struct xt_entry_match *)entry_test->elems;
+ xt_e_m = (struct xt_entry_match *)
+ iptables_entry_get_elems(entry_test);
entry = chain_head->data;
builtin = entry->builtin;
for (; list != chain_tail->prev; list = list->next) {
struct connman_iptables_entry *tmp;
- struct ipt_entry *tmp_e;
tmp = list->data;
- tmp_e = tmp->entry;
- if (!is_same_ipt_entry(entry_test, tmp_e))
+ if (!is_same_iptables_entry(entry_test, tmp))
continue;
if (xt_t) {
- struct xt_entry_target *tmp_xt_e_t;
+ struct xt_entry_target *tmp_xt_e_t = NULL;
- tmp_xt_e_t = ipt_get_target(tmp_e);
+ tmp_xt_e_t = iptables_entry_get_target(tmp);
if (!is_same_target(tmp_xt_e_t, xt_e_t))
continue;
if (matches) {
struct xt_entry_match *tmp_xt_e_m;
- tmp_xt_e_m = (struct xt_entry_match *)tmp_e->elems;
+ tmp_xt_e_m = (struct xt_entry_match *)
+ iptables_entry_get_elems(tmp);
if (!is_same_match(tmp_xt_e_m, xt_e_m))
continue;
break;
}
- g_free(entry_test);
+ iptables_entry_free(entry_test);
if (list != chain_tail->prev)
return list;
}
static int iptables_delete_rule(struct connman_iptables *table,
- struct ipt_ip *ip, const char *chain_name,
+ struct iptables_ip *ip,
+ const char *chain_name,
const char *target_name,
struct xtables_target *xt_t,
GList *matches,
GList *chain_head, *chain_tail, *list;
int builtin, removed;
-
DBG("table %s chain %s", table->name, chain_name);
removed = 0;
return -EINVAL;
list = find_existing_rule(table, ip, chain_name, target_name,
- xt_t, matches, xt_rm);
+ xt_t, matches, xt_rm);
+
if (!list)
return -EINVAL;
return -EINVAL;
entry = chain_tail->prev->data;
- target = ipt_get_target(entry->entry);
+
+ target = iptables_entry_get_target(entry);
+
+ if (!target)
+ return -EINVAL;
t = (struct xt_standard_target *)target;
if (t->verdict != verdict)
return r;
}
-static void dump_ip(struct ipt_entry *entry)
+/* A copy of iptables_blob() with IPv6 structures */
+static struct ip6t_replace *ip6tables_blob(struct connman_iptables *table)
+{
+ struct ip6t_replace *r;
+ GList *list;
+ struct connman_iptables_entry *e;
+ unsigned char *entry_index;
+
+ r = g_try_malloc0(sizeof(struct ip6t_replace) + table->size);
+ if (!r)
+ return NULL;
+
+ memset(r, 0, sizeof(*r) + table->size);
+
+ r->counters = g_try_malloc0(sizeof(struct xt_counters)
+ * table->old_entries);
+ if (!r->counters) {
+ g_free(r);
+ return NULL;
+ }
+
+ g_stpcpy(r->name, table->info6->name);
+ r->num_entries = table->num_entries;
+ r->size = table->size;
+
+ r->num_counters = table->old_entries;
+ r->valid_hooks = table->info6->valid_hooks;
+
+ memcpy(r->hook_entry, table->hook_entry, sizeof(table->hook_entry));
+ memcpy(r->underflow, table->underflow, sizeof(table->underflow));
+
+ entry_index = (unsigned char *)r->entries;
+ for (list = table->entries; list; list = list->next) {
+ e = list->data;
+
+ memcpy(entry_index, e->entry6, e->entry6->next_offset);
+ entry_index += e->entry6->next_offset;
+ }
+
+ return r;
+}
+
+static void dump_ip(struct connman_iptables_entry *entry)
{
- struct ipt_ip *ip = &entry->ip;
+ char *iniface, *outiface;
char ip_string[INET6_ADDRSTRLEN];
char ip_mask[INET6_ADDRSTRLEN];
- if (strlen(ip->iniface))
- DBG("\tin %s", ip->iniface);
+ switch (entry->type) {
+ case AF_INET:
+ iniface = entry->entry->ip.iniface;
+ outiface = entry->entry->ip.outiface;
+ break;
+ case AF_INET6:
+ iniface = entry->entry6->ipv6.iniface;
+ outiface = entry->entry6->ipv6.outiface;
+ break;
+ default:
+ return;
+ }
+
+ if (strlen(iniface))
+ DBG("\tin %s", iniface);
- if (strlen(ip->outiface))
- DBG("\tout %s", ip->outiface);
+ if (strlen(outiface))
+ DBG("\tout %s", outiface);
- if (inet_ntop(AF_INET, &ip->src, ip_string, INET6_ADDRSTRLEN) &&
- inet_ntop(AF_INET, &ip->smsk, ip_mask,
+ if (entry->type == AF_INET) {
+ if (inet_ntop(entry->type, &entry->entry->ip.src, ip_string,
+ INET6_ADDRSTRLEN) && inet_ntop(entry->type,
+ &entry->entry->ip.smsk, ip_mask,
INET6_ADDRSTRLEN))
- DBG("\tsrc %s/%s", ip_string, ip_mask);
+ DBG("\tsrc %s/%s", ip_string, ip_mask);
- if (inet_ntop(AF_INET, &ip->dst, ip_string, INET6_ADDRSTRLEN) &&
- inet_ntop(AF_INET, &ip->dmsk, ip_mask,
+ if (inet_ntop(entry->type, &entry->entry->ip.dst, ip_string,
+ INET6_ADDRSTRLEN) && inet_ntop(entry->type,
+ &entry->entry->ip.dmsk, ip_mask,
INET6_ADDRSTRLEN))
- DBG("\tdst %s/%s", ip_string, ip_mask);
-}
+ DBG("\tdst %s/%s", ip_string, ip_mask);
+ }
+
+ if (entry->type == AF_INET6) {
+ if (inet_ntop(entry->type, &entry->entry6->ipv6.src, ip_string,
+ INET6_ADDRSTRLEN) && inet_ntop(entry->type,
+ &entry->entry6->ipv6.smsk, ip_mask,
+ INET6_ADDRSTRLEN))
+ DBG("\tsrc %s/%s", ip_string, ip_mask);
-static void dump_target(struct ipt_entry *entry)
+ if (inet_ntop(entry->type, &entry->entry6->ipv6.dst, ip_string,
+ INET6_ADDRSTRLEN) && inet_ntop(entry->type,
+ &entry->entry6->ipv6.dmsk, ip_mask,
+ INET6_ADDRSTRLEN))
+ DBG("\tdst %s/%s", ip_string, ip_mask);
+ }
+}
+static void dump_target(struct connman_iptables_entry *entry)
{
struct xtables_target *xt_t;
struct xt_entry_target *target;
+ int err;
+
+ target = iptables_entry_get_target(entry);
- target = ipt_get_target(entry);
+ if (!target)
+ return;
- if (!g_strcmp0(target->u.user.name, IPT_STANDARD_TARGET)) {
+ if (!g_strcmp0(target->u.user.name, get_standard_target(entry->type))) {
struct xt_standard_target *t;
t = (struct xt_standard_target *)target;
break;
}
- xt_t = xtables_find_target(IPT_STANDARD_TARGET,
+ enable_jmp();
+
+ if ((err = setjmp(env_state)) != 0) {
+ DBG("setjmp() called by longjmp() with value %d", err);
+ disable_jmp();
+ return;
+ }
+
+ xt_t = xtables_find_target(get_standard_target(entry->type),
XTF_LOAD_MUST_SUCCEED);
+ disable_jmp();
+
if (xt_t->print)
xt_t->print(NULL, target, 1);
} else {
+ enable_jmp();
+
+ if ((err = setjmp(env_state)) != 0) {
+ DBG("setjmp() called by longjmp() with value %d", err);
+ disable_jmp();
+ return;
+ }
+
xt_t = xtables_find_target(target->u.user.name, XTF_TRY_LOAD);
+
+ disable_jmp();
+
if (!xt_t) {
DBG("\ttarget %s", target->u.user.name);
return;
free(xt_t);
}
-static void dump_match(struct ipt_entry *entry)
+static void dump_match(struct connman_iptables_entry *entry)
{
struct xtables_match *xt_m;
struct xt_entry_match *match;
+ u_int16_t target_offset;
+ int err;
+
+ target_offset = iptables_entry_get_target_offset(entry);
- if (entry->elems == (unsigned char *)entry + entry->target_offset)
+ switch (entry->type) {
+ case AF_INET:
+ if (entry->entry->elems == (unsigned char *)entry->entry +
+ target_offset)
+ return;
+ break;
+ case AF_INET6:
+ if (entry->entry6->elems == (unsigned char *)entry->entry6 +
+ target_offset)
+ return;
+ break;
+ default:
return;
+ }
- match = (struct xt_entry_match *) entry->elems;
+ match = (struct xt_entry_match *) iptables_entry_get_elems(entry);
if (!strlen(match->u.user.name))
return;
+ enable_jmp();
+
+ if ((err = setjmp(env_state)) != 0) {
+ DBG("setjmp() called by longjmp() with value %d", err);
+ disable_jmp();
+ return;
+ }
+
xt_m = xtables_find_match(match->u.user.name, XTF_TRY_LOAD, NULL);
+
+ disable_jmp();
+
if (!xt_m)
goto out;
}
-static int dump_entry(struct ipt_entry *entry, int builtin,
+static int dump_entry(struct connman_iptables_entry *entry, int builtin,
unsigned int hook, size_t size, unsigned int offset,
void *user_data)
{
struct xt_entry_target *target;
+ char *char_entry;
- target = ipt_get_target(entry);
+ target = iptables_entry_get_target(entry);
- if (offset + entry->next_offset == size) {
+ if (!target)
+ return -EINVAL;
+
+ if (offset + iptables_entry_get_next_offset(entry) == size) {
DBG("\tEnd of CHAIN");
return 0;
}
+ switch (entry->type) {
+ case AF_INET:
+ char_entry = (char *)entry->entry;
+ break;
+ case AF_INET6:
+ char_entry = (char *)entry->entry6;
+ break;
+ default:
+ return 0;
+ }
+
if (!g_strcmp0(target->u.user.name, IPT_ERROR_TARGET)) {
DBG("\tUSER CHAIN (%s) match %p target %p",
- target->data, entry->elems,
- (char *)entry + entry->target_offset);
+ target->data, iptables_entry_get_elems(entry),
+ char_entry + iptables_entry_get_target_offset(entry));
return 0;
} else if (builtin >= 0) {
DBG("\tCHAIN (%s) match %p target %p",
- hooknames[builtin], entry->elems,
- (char *)entry + entry->target_offset);
+ hooknames[builtin], iptables_entry_get_elems(entry),
+ char_entry + iptables_entry_get_target_offset(entry));
} else {
DBG("\tRULE match %p target %p",
- entry->elems,
- (char *)entry + entry->target_offset);
+ iptables_entry_get_elems(entry),
+ char_entry + iptables_entry_get_target_offset(entry));
}
dump_match(entry);
static void dump_table(struct connman_iptables *table)
{
+ struct connman_iptables_entry entry = { 0 };
+ unsigned int *hook_entry;
+ unsigned int *underflow;
+ unsigned int valid_hooks;
+ unsigned int size;
+
+ hook_entry = iptables_table_get_info_hook_entry(table);
+ underflow = iptables_table_get_info_underflow(table);
+ valid_hooks = iptables_table_get_info_valid_hooks(table);
+ size = iptables_table_get_info_size(table);
+
DBG("%s valid_hooks=0x%08x, num_entries=%u, size=%u",
- table->info->name,
- table->info->valid_hooks, table->info->num_entries,
- table->info->size);
+ iptables_table_get_info_name(table),
+ valid_hooks,
+ iptables_table_get_info_num_entries(table),
+ size);
DBG("entry hook: pre/in/fwd/out/post %d/%d/%d/%d/%d",
- table->info->hook_entry[NF_IP_PRE_ROUTING],
- table->info->hook_entry[NF_IP_LOCAL_IN],
- table->info->hook_entry[NF_IP_FORWARD],
- table->info->hook_entry[NF_IP_LOCAL_OUT],
- table->info->hook_entry[NF_IP_POST_ROUTING]);
+ hook_entry[NF_IP_PRE_ROUTING],
+ hook_entry[NF_IP_LOCAL_IN],
+ hook_entry[NF_IP_FORWARD],
+ hook_entry[NF_IP_LOCAL_OUT],
+ hook_entry[NF_IP_POST_ROUTING]);
DBG("underflow: pre/in/fwd/out/post %d/%d/%d/%d/%d",
- table->info->underflow[NF_IP_PRE_ROUTING],
- table->info->underflow[NF_IP_LOCAL_IN],
- table->info->underflow[NF_IP_FORWARD],
- table->info->underflow[NF_IP_LOCAL_OUT],
- table->info->underflow[NF_IP_POST_ROUTING]);
+ underflow[NF_IP_PRE_ROUTING],
+ underflow[NF_IP_LOCAL_IN],
+ underflow[NF_IP_FORWARD],
+ underflow[NF_IP_LOCAL_OUT],
+ underflow[NF_IP_POST_ROUTING]);
+
+ entry.type = table->type;
+
+ switch (table->type) {
+ case AF_INET:
+ entry.entry = table->blob_entries->entrytable;
+ break;
+ case AF_INET6:
+ entry.entry6 = table->blob_entries6->entrytable;
+ }
+
+ iterate_entries(&entry,
+ valid_hooks,
+ hook_entry,
+ underflow,
+ size,
+ print_entry, dump_entry);
+}
- iterate_entries(table->blob_entries->entrytable,
- table->info->valid_hooks,
- table->info->hook_entry,
- table->info->underflow,
- table->blob_entries->size,
- print_entry, dump_entry);
+static const char *iptables_replace_get_name(struct iptables_replace *replace)
+{
+ if (!replace)
+ return NULL;
+
+ switch (replace->type) {
+ case AF_INET:
+ return replace->r->name;
+ case AF_INET6:
+ return replace->r6->name;
+ }
+
+ return NULL;
+}
+
+static unsigned int iptables_replace_get_valid_hooks(
+ struct iptables_replace *replace)
+{
+ if (!replace)
+ return 0;
+
+ switch (replace->type) {
+ case AF_INET:
+ return replace->r->valid_hooks;
+ case AF_INET6:
+ return replace->r6->valid_hooks;
+ }
+
+ return 0;
+}
+
+static unsigned int iptables_replace_get_num_entries(
+ struct iptables_replace *replace)
+{
+ if (!replace)
+ return 0;
+
+ switch (replace->type) {
+ case AF_INET:
+ return replace->r->num_entries;
+ case AF_INET6:
+ return replace->r6->num_entries;
+ }
+
+ return 0;
+}
+
+static unsigned int *iptables_replace_get_hook_entry(
+ struct iptables_replace *replace)
+{
+ if (!replace)
+ return NULL;
+
+ switch (replace->type) {
+ case AF_INET:
+ return replace->r->hook_entry;
+ case AF_INET6:
+ return replace->r6->hook_entry;
+ }
+
+ return NULL;
+}
+
+static unsigned int *iptables_replace_get_underflow(
+ struct iptables_replace *replace)
+{
+ if (!replace)
+ return NULL;
+
+ switch (replace->type) {
+ case AF_INET:
+ return replace->r->underflow;
+ case AF_INET6:
+ return replace->r6->underflow;
+ }
+
+ return NULL;
+}
+
+static unsigned int iptables_replace_get_size(struct iptables_replace *replace)
+{
+ if (!replace)
+ return 0;
+
+ switch (replace->type) {
+ case AF_INET:
+ return replace->r->size;
+ case AF_INET6:
+ return replace->r6->size;
+ }
+
+ return 0;
}
-static void dump_ipt_replace(struct ipt_replace *repl)
+static void dump_replace(struct iptables_replace *repl)
{
+ struct connman_iptables_entry entry = { 0 };
+ unsigned int *hook_entry;
+ unsigned int *underflow;
+ unsigned int valid_hooks;
+ unsigned int size;
+
+ hook_entry = iptables_replace_get_hook_entry(repl);
+ underflow = iptables_replace_get_underflow(repl);
+ valid_hooks = iptables_replace_get_valid_hooks(repl);
+ size = iptables_replace_get_size(repl);
+
+ switch (repl->type) {
+ case AF_INET:
+ entry.entry = repl->r->entries;
+ break;
+ case AF_INET6:
+ entry.entry6 = repl->r6->entries;
+ break;
+ default:
+ return;
+ }
+
DBG("%s valid_hooks 0x%08x num_entries %u size %u",
- repl->name, repl->valid_hooks, repl->num_entries,
- repl->size);
+ iptables_replace_get_name(repl),
+ valid_hooks,
+ iptables_replace_get_num_entries(repl), size);
DBG("entry hook: pre/in/fwd/out/post %d/%d/%d/%d/%d",
- repl->hook_entry[NF_IP_PRE_ROUTING],
- repl->hook_entry[NF_IP_LOCAL_IN],
- repl->hook_entry[NF_IP_FORWARD],
- repl->hook_entry[NF_IP_LOCAL_OUT],
- repl->hook_entry[NF_IP_POST_ROUTING]);
+ hook_entry[NF_IP_PRE_ROUTING],
+ hook_entry[NF_IP_LOCAL_IN],
+ hook_entry[NF_IP_FORWARD],
+ hook_entry[NF_IP_LOCAL_OUT],
+ hook_entry[NF_IP_POST_ROUTING]);
DBG("underflow: pre/in/fwd/out/post %d/%d/%d/%d/%d",
- repl->underflow[NF_IP_PRE_ROUTING],
- repl->underflow[NF_IP_LOCAL_IN],
- repl->underflow[NF_IP_FORWARD],
- repl->underflow[NF_IP_LOCAL_OUT],
- repl->underflow[NF_IP_POST_ROUTING]);
+ underflow[NF_IP_PRE_ROUTING],
+ underflow[NF_IP_LOCAL_IN],
+ underflow[NF_IP_FORWARD],
+ underflow[NF_IP_LOCAL_OUT],
+ underflow[NF_IP_POST_ROUTING]);
- iterate_entries(repl->entries, repl->valid_hooks,
- repl->hook_entry, repl->underflow,
- repl->size, print_entry, dump_entry);
+ iterate_entries(&entry, valid_hooks, hook_entry, underflow,
+ size, print_entry, dump_entry);
}
static int iptables_get_entries(struct connman_iptables *table)
socklen_t entry_size;
int err;
- entry_size = sizeof(struct ipt_get_entries) + table->info->size;
+ switch (table->type) {
+ case AF_INET:
+ entry_size = sizeof(struct ipt_get_entries) + table->info->size;
+
+ err = getsockopt(table->ipt_sock, IPPROTO_IP,
+ IPT_SO_GET_ENTRIES, table->blob_entries,
+ &entry_size);
+ break;
+ case AF_INET6:
+ entry_size = sizeof(struct ip6t_get_entries) +
+ table->info6->size;
+
+ err = getsockopt(table->ipt_sock, IPPROTO_IPV6,
+ IP6T_SO_GET_ENTRIES, table->blob_entries6,
+ &entry_size);
+ break;
+ default:
+ return -EINVAL;
+ }
- err = getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_ENTRIES,
- table->blob_entries, &entry_size);
if (err < 0)
return -errno;
}
static int iptables_replace(struct connman_iptables *table,
- struct ipt_replace *r)
+ struct iptables_replace *r)
{
int err;
- err = setsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_SET_REPLACE, r,
- sizeof(*r) + r->size);
+ switch (r->type) {
+ case AF_INET:
+ if (!r->r)
+ return -EINVAL;
+
+ err = setsockopt(table->ipt_sock, IPPROTO_IP,
+ IPT_SO_SET_REPLACE, r->r,
+ sizeof(*r->r) + r->r->size);
+ break;
+ case AF_INET6:
+ if (!r->r6)
+ return -EINVAL;
+
+ err = setsockopt(table->ipt_sock, IPPROTO_IPV6,
+ IP6T_SO_SET_REPLACE, r->r6,
+ sizeof(*r->r6) + r->r6->size);
+ break;
+ default:
+ return -EINVAL;
+ }
+
if (err < 0)
return -errno;
struct xt_counters_info *c)
{
int err;
+ int level;
+ int optname;
+
+ switch (table->type) {
+ case AF_INET:
+ level = IPPROTO_IP;
+ optname = IPT_SO_SET_ADD_COUNTERS;
+ break;
+ case AF_INET6:
+ level = IPPROTO_IPV6;
+ optname = IP6T_SO_SET_ADD_COUNTERS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ err = setsockopt(table->ipt_sock, level, optname, c,
+ sizeof(*c) + sizeof(struct xt_counters) * c->num_counters);
- 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)
+static int add_entry(struct connman_iptables_entry *entry, int builtin,
+ unsigned int hook, size_t size, unsigned offset,
+ void *user_data)
{
struct connman_iptables *table = user_data;
- struct ipt_entry *new_entry;
-
- new_entry = g_try_malloc0(entry->next_offset);
- if (!new_entry)
- return -ENOMEM;
-
- memcpy(new_entry, entry, entry->next_offset);
+ struct connman_iptables_entry new_entry = { 0 };
+ u_int16_t next_offset;
+
+ new_entry.type = entry->type;
+ next_offset = iptables_entry_get_next_offset(entry);
+
+ switch (entry->type) {
+ case AF_INET:
+ new_entry.entry = g_try_malloc0(next_offset);
+ if (!new_entry.entry)
+ return -ENOMEM;
+
+ memcpy(new_entry.entry, entry->entry, next_offset);
+ break;
+ case AF_INET6:
+ new_entry.entry6 = g_try_malloc0(next_offset);
+ if (!new_entry.entry6)
+ return -ENOMEM;
- return iptables_add_entry(table, new_entry, NULL, builtin,
- table->num_entries);
+ memcpy(new_entry.entry6, entry->entry6, next_offset);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return iptables_add_entry(table, &new_entry, NULL, builtin,
+ table->num_entries);
}
static void table_cleanup(struct connman_iptables *table)
for (list = table->entries; list; list = list->next) {
entry = list->data;
- g_free(entry->entry);
- g_free(entry);
+ iptables_entry_free(entry);
}
g_list_free(table->entries);
g_free(table->name);
- g_free(table->info);
- g_free(table->blob_entries);
+
+ if (table->type == AF_INET) {
+ g_free(table->info);
+ g_free(table->blob_entries);
+ }
+
+ if (table->type == AF_INET6) {
+ g_free(table->info6);
+ g_free(table->blob_entries6);
+ }
+
g_free(table);
}
-static struct connman_iptables *iptables_init(const char *table_name)
+static int setup_xtables(int type);
+static void reset_xtables();
+
+static struct connman_iptables *iptables_init(int type, const char *table_name)
{
struct connman_iptables *table = NULL;
+ struct connman_iptables_entry entry = { 0 };
+ char *iptables_mod = NULL;
char *module = NULL;
socklen_t s;
- DBG("%s", table_name);
+ switch(type) {
+ case AF_INET:
+ iptables_mod = g_strdup("ip_tables");
+ module = g_strconcat("iptable_", table_name, NULL);
+ break;
+ case AF_INET6:
+ iptables_mod = g_strdup("ip6_tables");
+ module = g_strconcat("ip6table_", table_name, NULL);
+ break;
+ default:
+ return NULL;
+ }
- if (xtables_insmod("ip_tables", NULL, TRUE) != 0)
- DBG("ip_tables module loading gives error but trying anyway");
+ DBG("%d %s", type, table_name);
- module = g_strconcat("iptable_", table_name, NULL);
- if (!module)
+ if (setup_xtables(type))
return NULL;
+ if (xtables_insmod(iptables_mod, NULL, TRUE) != 0)
+ DBG("%s module loading gives error but trying anyway",
+ iptables_mod);
+
+ g_free(iptables_mod);
+
if (xtables_insmod(module, NULL, TRUE) != 0)
DBG("%s module loading gives error but trying anyway", module);
if (!table)
return NULL;
- table->info = g_try_new0(struct ipt_getinfo, 1);
- if (!table->info)
- goto err;
+ table->type = entry.type = type;
- table->ipt_sock = socket(AF_INET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
+ table->ipt_sock = socket(type, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
if (table->ipt_sock < 0)
goto err;
- s = sizeof(*table->info);
- g_stpcpy(table->info->name, table_name);
- if (getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_INFO,
- table->info, &s) < 0) {
- connman_error("iptables support missing error %d (%s)", errno,
- strerror(errno));
- goto err;
- }
+ switch (type) {
+ case AF_INET:
+ table->info = g_try_new0(struct ipt_getinfo, 1);
+ if (!table->info)
+ goto err;
- table->blob_entries = g_try_malloc0(sizeof(struct ipt_get_entries) +
- table->info->size);
- if (!table->blob_entries)
- goto err;
+ s = sizeof(*table->info);
+ g_stpcpy(table->info->name, table_name);
+
+ if (getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_INFO,
+ table->info, &s) < 0) {
+ connman_error("iptables support missing error %d (%s)",
+ errno, strerror(errno));
+ goto err;
+ }
+
+ table->blob_entries = g_try_malloc0(
+ sizeof(struct ipt_get_entries) +
+ table->info->size);
+ if (!table->blob_entries)
+ goto err;
- g_stpcpy(table->blob_entries->name, table_name);
- table->blob_entries->size = table->info->size;
+ g_stpcpy(table->blob_entries->name, table_name);
+ table->blob_entries->size = table->info->size;
+
+ break;
+ case AF_INET6:
+ table->info6 = g_try_new0(struct ip6t_getinfo, 1);
+ if (!table->info6)
+ goto err;
+
+ s = sizeof(*table->info6);
+ g_stpcpy(table->info6->name, table_name);
+
+ if (getsockopt(table->ipt_sock, IPPROTO_IPV6, IP6T_SO_GET_INFO,
+ table->info6, &s) < 0) {
+ connman_error("ip6tables support missing error %d (%s)",
+ errno, strerror(errno));
+ goto err;
+ }
+
+ table->blob_entries6 = g_try_malloc0(
+ sizeof(struct ip6t_get_entries) +
+ table->info6->size);
+ if (!table->blob_entries6)
+ goto err;
+
+ g_stpcpy(table->blob_entries6->name, table_name);
+ table->blob_entries6->size = table->info6->size;
+
+ break;
+ }
if (iptables_get_entries(table) < 0)
goto err;
table->num_entries = 0;
- table->old_entries = table->info->num_entries;
table->size = 0;
- memcpy(table->underflow, table->info->underflow,
- sizeof(table->info->underflow));
- memcpy(table->hook_entry, table->info->hook_entry,
- sizeof(table->info->hook_entry));
+ switch (type) {
+ case AF_INET:
+ table->old_entries = table->info->num_entries;
- iterate_entries(table->blob_entries->entrytable,
- table->info->valid_hooks, table->info->hook_entry,
- table->info->underflow, table->blob_entries->size,
- add_entry, table);
+ memcpy(table->underflow, table->info->underflow,
+ sizeof(table->info->underflow));
+ memcpy(table->hook_entry, table->info->hook_entry,
+ sizeof(table->info->hook_entry));
+
+ entry.entry = table->blob_entries->entrytable;
+ break;
+ case AF_INET6:
+ table->old_entries = table->info6->num_entries;
+
+ memcpy(table->underflow, table->info6->underflow,
+ sizeof(table->info6->underflow));
+ memcpy(table->hook_entry, table->info6->hook_entry,
+ sizeof(table->info6->hook_entry));
+
+ entry.entry6 = table->blob_entries6->entrytable;
+ break;
+ }
+
+ iterate_entries(&entry,
+ iptables_table_get_info_valid_hooks(table),
+ iptables_table_get_info_hook_entry(table),
+ iptables_table_get_info_underflow(table),
+ iptables_table_get_entries_size(table),
+ add_entry,
+ table);
if (debug_enabled)
dump_table(table);
+ reset_xtables();
+
return table;
err:
table_cleanup(table);
+ reset_xtables();
return NULL;
}
{.name = "out-interface", .has_arg = 1, .val = 'o'},
{.name = "source", .has_arg = 1, .val = 's'},
{.name = "table", .has_arg = 1, .val = 't'},
+ {.name = "protocol", .has_arg = 1, .val = 'p'},
{NULL},
};
+void iptables_exit(enum xtables_exittype status, const char *msg, ...)
+ __attribute__((noreturn, format(printf,2,3)));
+
+void iptables_exit(enum xtables_exittype status, const char *msg, ...)
+{
+ va_list args;
+ gchar str[256] = { 0 };
+
+ switch (status) {
+ case OTHER_PROBLEM:
+ DBG("OTHER_PROBLEM");
+ break;
+ case PARAMETER_PROBLEM:
+ DBG("PARAMETER_PROBLEM");
+ break;
+ case VERSION_PROBLEM:
+ DBG("VERSION_PROBLEM");
+ break;
+ case RESOURCE_PROBLEM:
+ DBG("RESOURCE_PROBLEM");
+ break;
+ case XTF_ONLY_ONCE:
+ DBG("XTF_ONLY_ONCE");
+ break;
+ case XTF_NO_INVERT:
+ DBG("XTF_NO_INVERT");
+ break;
+ case XTF_BAD_VALUE:
+ DBG("XTF_BAD_VALUE");
+ break;
+ case XTF_ONE_ACTION:
+ DBG("XTF_ONE_ACTION");
+ break;
+ }
+
+ va_start(args, msg);
+ vsnprintf(str, 256, msg, args);
+ va_end(args);
+
+ connman_error("iptables rule error: %s", str);
+
+ if (can_jmp()) {
+ DBG("calling longjmp()");
+ /* enum xtables_exittype begins from 1 */
+ longjmp(env_state, status);
+ }
+
+ connman_error("exit because of iptables error");
+
+ exit(status);
+}
+
struct xtables_globals iptables_globals = {
.option_offset = 0,
.opts = iptables_opts,
.orig_opts = iptables_opts,
+ .exit_err = iptables_exit,
+#if XTABLES_VERSION_CODE > 10
+ .compat_rev = xtables_compatible_revision,
+#endif
+};
+
+struct xtables_globals ip6tables_globals = {
+ .option_offset = 0,
+ .opts = iptables_opts,
+ .orig_opts = iptables_opts,
+ .exit_err = iptables_exit,
#if XTABLES_VERSION_CODE > 10
.compat_rev = xtables_compatible_revision,
#endif
bool is_builtin, is_user_defined;
GList *chain_head = NULL;
size_t target_size;
+ int err;
is_builtin = false;
is_user_defined = false;
+
+ DBG("target %s", target_name);
+
+ if (!table)
+ return NULL;
if (is_builtin_target(target_name))
is_builtin = true;
is_user_defined = true;
}
+ enable_jmp();
+
+ if ((err = setjmp(env_state)) != 0) {
+ DBG("setjmp() called by longjmp() with value %d", err);
+ disable_jmp();
+ return NULL;
+ }
+
if (is_builtin || is_user_defined)
- xt_t = xtables_find_target(IPT_STANDARD_TARGET,
+ xt_t = xtables_find_target(get_standard_target(table->type),
XTF_LOAD_MUST_SUCCEED);
- else
+ else
xt_t = xtables_find_target(target_name, XTF_TRY_LOAD);
+ disable_jmp();
+
if (!xt_t)
return NULL;
- target_size = ALIGN(sizeof(struct ipt_entry_target)) + xt_t->size;
+ switch (table->type) {
+ case AF_INET:
+ target_size = XT_ALIGN(sizeof(struct ipt_entry_target)) +
+ xt_t->size;
+ break;
+ case AF_INET6:
+ target_size = XT_ALIGN(sizeof(struct ip6t_entry_target)) +
+ xt_t->size;
+ break;
+ default:
+ return NULL;
+ }
xt_t->t = g_try_malloc0(target_size);
if (!xt_t->t)
struct xt_standard_target *target;
target = (struct xt_standard_target *)(xt_t->t);
- g_stpcpy(target->target.u.user.name, IPT_STANDARD_TARGET);
+ g_stpcpy(target->target.u.user.name,
+ get_standard_target(table->type));
if (is_builtin)
target->verdict = target_to_verdict(target_name);
xt_t->init(xt_t->t);
}
- if (xt_t->x6_options)
- iptables_globals.opts =
- xtables_options_xfrm(
- iptables_globals.orig_opts,
- iptables_globals.opts,
- xt_t->x6_options,
- &xt_t->option_offset);
- else
- iptables_globals.opts =
- xtables_merge_options(
- iptables_globals.orig_opts,
- iptables_globals.opts,
- xt_t->extra_opts,
- &xt_t->option_offset);
+ switch (table->type) {
+ case AF_INET:
+ if (xt_t->x6_options)
+ iptables_globals.opts =
+ xtables_options_xfrm(
+ iptables_globals.orig_opts,
+ iptables_globals.opts,
+ xt_t->x6_options,
+ &xt_t->option_offset);
+ else
+ iptables_globals.opts =
+ xtables_merge_options(
+ iptables_globals.orig_opts,
+ iptables_globals.opts,
+ xt_t->extra_opts,
+ &xt_t->option_offset);
+
+ if (!iptables_globals.opts) {
+ g_free(xt_t->t);
+ xt_t = NULL;
+ }
- if (!iptables_globals.opts) {
- g_free(xt_t->t);
- xt_t = NULL;
+ break;
+ case AF_INET6:
+ if (xt_t->x6_options)
+ ip6tables_globals.opts =
+ xtables_options_xfrm(
+ ip6tables_globals.orig_opts,
+ ip6tables_globals.opts,
+ xt_t->x6_options,
+ &xt_t->option_offset);
+ else
+ ip6tables_globals.opts =
+ xtables_merge_options(
+ ip6tables_globals.orig_opts,
+ ip6tables_globals.opts,
+ xt_t->extra_opts,
+ &xt_t->option_offset);
+
+ if (!ip6tables_globals.opts) {
+ g_free(xt_t->t);
+ xt_t = NULL;
+ }
+
+ break;
}
return xt_t;
{
struct xtables_match *xt_m;
size_t match_size;
+ int err;
+
+ if (!table || !match_name)
+ return NULL;
+
+ enable_jmp();
- if (!match_name)
+ if ((err = setjmp(env_state)) != 0) {
+ DBG("setjmp() called by longjmp() with value %d", err);
+ disable_jmp();
return NULL;
+ }
xt_m = xtables_find_match(match_name, XTF_LOAD_MUST_SUCCEED, xt_rm);
- match_size = ALIGN(sizeof(struct ipt_entry_match)) + xt_m->size;
+
+ disable_jmp();
+
+ switch (table->type) {
+ case AF_INET:
+ match_size = XT_ALIGN(sizeof(struct ipt_entry_match)) +
+ xt_m->size;
+ break;
+ case AF_INET6:
+ match_size = XT_ALIGN(sizeof(struct ip6t_entry_match)) +
+ xt_m->size;
+ break;
+ default:
+ return NULL;
+ }
xt_m->m = g_try_malloc0(match_size);
if (!xt_m->m)
if (xt_m->init)
xt_m->init(xt_m->m);
- if (xt_m->x6_options)
- iptables_globals.opts =
- xtables_options_xfrm(
- iptables_globals.orig_opts,
- iptables_globals.opts,
- xt_m->x6_options,
- &xt_m->option_offset);
- else
+ switch (table->type) {
+ case AF_INET:
+ if (xt_m->x6_options)
+ iptables_globals.opts =
+ xtables_options_xfrm(
+ iptables_globals.orig_opts,
+ iptables_globals.opts,
+ xt_m->x6_options,
+ &xt_m->option_offset);
+ else
iptables_globals.opts =
- xtables_merge_options(
- iptables_globals.orig_opts,
- iptables_globals.opts,
- xt_m->extra_opts,
- &xt_m->option_offset);
+ xtables_merge_options(
+ iptables_globals.orig_opts,
+ iptables_globals.opts,
+ xt_m->extra_opts,
+ &xt_m->option_offset);
- if (!iptables_globals.opts) {
- g_free(xt_m->m);
+ if (!iptables_globals.opts) {
+ g_free(xt_m->m);
+
+ if (xt_m == xt_m->next)
+ free(xt_m);
- if (xt_m == xt_m->next)
- free(xt_m);
+ xt_m = NULL;
+ }
+
+ break;
+ case AF_INET6:
+ if (xt_m->x6_options)
+ ip6tables_globals.opts =
+ xtables_options_xfrm(
+ ip6tables_globals.orig_opts,
+ ip6tables_globals.opts,
+ xt_m->x6_options,
+ &xt_m->option_offset);
+ else
+ ip6tables_globals.opts =
+ xtables_merge_options(
+ ip6tables_globals.orig_opts,
+ ip6tables_globals.opts,
+ xt_m->extra_opts,
+ &xt_m->option_offset);
+
+ if (!ip6tables_globals.opts) {
+ g_free(xt_m->m);
+
+ if (xt_m == xt_m->next)
+ free(xt_m);
+
+ xt_m = NULL;
+ }
- xt_m = NULL;
+ break;
}
return xt_m;
if (tokens[1]) {
prefixlength = strtol(tokens[1], NULL, 10);
- if (prefixlength > 31) {
+ if (prefixlength > 32) {
err = -1;
goto out;
+ } else if (prefixlength == 32) {
+ tmp = 0xffffffff;
+ } else {
+ tmp = ~(0xffffffff >> prefixlength);
}
-
- tmp = ~(0xffffffff >> prefixlength);
} else {
tmp = 0xffffffff;
}
return err;
}
-static struct connman_iptables *get_table(const char *table_name)
+static int parse_ipv6_and_mask(const char *str, struct in6_addr *ip,
+ struct in6_addr *mask)
{
- struct connman_iptables *table;
+ char **tokens;
+ uint32_t prefixlength;
+ struct in6_addr in6;
+ int i, j;
+ int err;
+
+ tokens = g_strsplit(str, "/", 2);
+ if (!tokens)
+ return -1;
+
+ if (!inet_pton(AF_INET6, tokens[0], ip)) {
+ err = -1;
+ goto out;
+ }
+
+ if (tokens[1]) {
+ prefixlength = strtol(tokens[1], NULL, 10);
+ if (prefixlength > 128) {
+ err = -1;
+ goto out;
+ }
+ } else {
+ prefixlength = 128;
+ }
+
+ /*
+ * This part was adapted from (no need to re-invent the wheel):
+ * https://gitlab.com/ipcalc/ipcalc/blob/master/ipcalc.c#L733
+ */
+ memset(&in6, 0, sizeof(struct in6_addr));
+
+ for (i = prefixlength, j = 0; i > 0; i -= 8, j++) {
+ if (i >= 8)
+ in6.s6_addr[j] = 0xff;
+ else
+ in6.s6_addr[j] = (unsigned long)(0xffU << (8 - i));
+ }
+
+ memcpy(mask, &in6, sizeof(struct in6_addr));
+
+ for (i = 0; i < 16 ; i++)
+ ip->s6_addr[i] = ip->s6_addr[i] & mask->s6_addr[i];
+
+ err = 0;
+out:
+ g_strfreev(tokens);
+
+ return err;
+}
+
+static struct connman_iptables *get_table(int type, const char *table_name)
+{
+ struct connman_iptables *table = NULL;
if (!table_name)
table_name = "filter";
- table = g_hash_table_lookup(table_hash, table_name);
+ table = hash_table_lookup(type, table_name);
+
if (table)
return table;
- table = iptables_init(table_name);
+ table = iptables_init(type, table_name);
+
if (!table)
return NULL;
+ if (table->name)
+ g_free(table->name);
+
table->name = g_strdup(table_name);
- g_hash_table_replace(table_hash, table->name, table);
+
+ hash_table_replace(type, table->name, table);
return table;
}
struct parse_context {
+ int type;
int argc;
char **argv;
struct ipt_ip *ip;
+ struct ip6t_ip6 *ipv6;
struct xtables_target *xt_t;
GList *xt_m;
struct xtables_rule_match *xt_rm;
- int proto;
+ uint16_t proto;
};
static int prepare_getopt_args(const char *str, struct parse_context *ctx)
struct xtables_match *m;
struct xtables_rule_match *rm;
struct ipt_entry fw;
+ struct ip6t_entry fw6;
+ int err;
+
+ switch (ctx->type) {
+ case AF_INET:
+ 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;
+ break;
+ case AF_INET6:
+ memset(&fw6, 0, sizeof(fw6));
- memset(&fw, 0, sizeof(fw));
+ if (ctx->proto == 0)
+ ctx->proto = IPPROTO_IPV6;
- /* The SNAT parser wants to know the protocol. */
- if (ctx->proto == 0)
- ctx->proto = IPPROTO_IP;
- fw.ip.proto = ctx->proto;
+ fw6.ipv6.proto = ctx->proto;
+
+ /* Flags must be set for IPv6 if protocol is set. */
+ fw6.ipv6.flags |= IP6T_F_PROTO;
+
+ break;
+ default:
+ return 0;
+ }
for (rm = ctx->xt_rm; rm; rm = rm->next) {
if (rm->completed != 0)
+ XT_OPTION_OFFSET_SCALE)
continue;
- xtables_option_mpcall(c, ctx->argv, invert, m, &fw);
+ enable_jmp();
+
+ if ((err = setjmp(env_state)) != 0) {
+ DBG("setjmp() called by longjmp() with value %d", err);
+ disable_jmp();
+ return -EINVAL;
+ }
+
+ switch (ctx->type) {
+ case AF_INET:
+ xtables_option_mpcall(c, ctx->argv, invert, m, &fw);
+ break;
+ case AF_INET6:
+ xtables_option_mpcall(c, ctx->argv, invert, m, &fw6);
+ break;
+ }
+
+ disable_jmp();
}
if (!ctx->xt_t)
+ XT_OPTION_OFFSET_SCALE)
return 0;
- xtables_option_tpcall(c, ctx->argv, invert, ctx->xt_t, &fw);
+ enable_jmp();
+
+ if ((err = setjmp(env_state)) != 0) {
+ DBG("setjmp() called by longjmp() with value %d", err);
+ disable_jmp();
+ return -EINVAL;
+ }
+
+ switch (ctx->type) {
+ case AF_INET:
+ xtables_option_tpcall(c, ctx->argv, invert, ctx->xt_t, &fw);
+ break;
+ case AF_INET6:
+ xtables_option_tpcall(c, ctx->argv, invert, ctx->xt_t, &fw6);
+ break;
+ }
+
+ disable_jmp();
return 0;
}
static int final_check_xt_modules(struct parse_context *ctx)
{
struct xtables_rule_match *rm;
+ int err;
+
+ for (rm = ctx->xt_rm; rm; rm = rm->next) {
+ enable_jmp();
+
+ if ((err = setjmp(env_state)) != 0) {
+ DBG("setjmp() called by longjmp() with value %d", err);
+ disable_jmp();
+ return -EINVAL;
+ }
- for (rm = ctx->xt_rm; rm; rm = rm->next)
xtables_option_mfcall(rm->match);
+ disable_jmp();
+ }
+
+ enable_jmp();
+
+ if ((err = setjmp(env_state)) != 0) {
+ DBG("setjmp() called by longjmp() with value %d", err);
+ disable_jmp();
+ return -EINVAL;
+ }
+
if (ctx->xt_t)
xtables_option_tfcall(ctx->xt_t);
+ disable_jmp();
+
return 0;
}
bool invert = false;
int len, c, err;
- ctx->ip = g_try_new0(struct ipt_ip, 1);
- if (!ctx->ip)
- return -ENOMEM;
+ if (ctx->type != table->type) {
+ DBG("ctx->type %d does not match table->type %d", ctx->type,
+ table->type);
+ return -EINVAL;
+ }
+
+ switch (ctx->type) {
+ case AF_INET:
+ ctx->ip = g_try_new0(struct ipt_ip, 1);
+ if (!ctx->ip)
+ return -ENOMEM;
+
+ break;
+ case AF_INET6:
+ ctx->ipv6 = g_try_new0(struct ip6t_ip6, 1);
+ if (!ctx->ipv6)
+ return -ENOMEM;
+
+ break;
+ default:
+ return -EINVAL;
+ }
/*
* Tell getopt_long not to generate error messages for unknown
optind = 0;
while ((c = getopt_long(ctx->argc, ctx->argv,
- "-:d:i:o:s:m:j:",
- iptables_globals.opts, NULL)) != -1) {
+ "-:d:i:o:s:m:j:p:",
+ ctx->type == AF_INET ?
+ iptables_globals.opts :
+ ip6tables_globals.opts,
+ NULL)) != -1) {
switch (c) {
case 's':
- /* Source specification */
- if (!parse_ip_and_mask(optarg,
- &ctx->ip->src,
- &ctx->ip->smsk))
- break;
+ if (ctx->type == AF_INET) {
+ /* Source specification */
+ if (!parse_ip_and_mask(optarg,
+ &ctx->ip->src,
+ &ctx->ip->smsk))
+ break;
+
+ if (invert)
+ ctx->ip->invflags |= IPT_INV_SRCIP;
+ }
- if (invert)
- ctx->ip->invflags |= IPT_INV_SRCIP;
+ if (ctx->type == AF_INET6) {
+ if (!parse_ipv6_and_mask(optarg,
+ &ctx->ipv6->src,
+ &ctx->ipv6->smsk))
+ break;
+
+ if (invert)
+ ctx->ipv6->invflags |= IP6T_INV_SRCIP;
+ }
break;
case 'd':
- /* Destination specification */
- if (!parse_ip_and_mask(optarg,
- &ctx->ip->dst,
- &ctx->ip->dmsk))
- break;
+ if (ctx->type == AF_INET) {
+ /* Destination specification */
+ if (!parse_ip_and_mask(optarg,
+ &ctx->ip->dst,
+ &ctx->ip->dmsk))
+ break;
+
+ if (invert)
+ ctx->ip->invflags |= IPT_INV_DSTIP;
+ }
- if (invert)
- ctx->ip->invflags |= IPT_INV_DSTIP;
+ if (ctx->type == AF_INET6) {
+ /* Destination specification */
+ if (!parse_ipv6_and_mask(optarg,
+ &ctx->ipv6->dst,
+ &ctx->ipv6->dmsk))
+ break;
+
+ if (invert)
+ ctx->ip->invflags |= IP6T_INV_DSTIP;
+ }
+
break;
case 'i':
/* In interface specification */
if (len + 1 > IFNAMSIZ)
break;
- g_stpcpy(ctx->ip->iniface, optarg);
- memset(ctx->ip->iniface_mask, 0xff, len + 1);
+ if (ctx->type == AF_INET) {
+ g_stpcpy(ctx->ip->iniface, optarg);
+ memset(ctx->ip->iniface_mask, 0xff, len + 1);
- if (invert)
- ctx->ip->invflags |= IPT_INV_VIA_IN;
+ if (invert)
+ ctx->ip->invflags |= IPT_INV_VIA_IN;
+ }
+
+ if (ctx->type == AF_INET6) {
+ g_stpcpy(ctx->ipv6->iniface, optarg);
+ memset(ctx->ipv6->iniface_mask, 0xff, len + 1);
+
+ if (invert)
+ ctx->ipv6->invflags |= IP6T_INV_VIA_IN;
+ }
break;
case 'o':
if (len + 1 > IFNAMSIZ)
break;
- g_stpcpy(ctx->ip->outiface, optarg);
- memset(ctx->ip->outiface_mask, 0xff, len + 1);
+ if (ctx->type == AF_INET) {
+ g_stpcpy(ctx->ip->outiface, optarg);
+ memset(ctx->ip->outiface_mask, 0xff, len + 1);
+
+ if (invert)
+ ctx->ip->invflags |= IPT_INV_VIA_OUT;
+ }
+
+ if (ctx->type == AF_INET6) {
+ g_stpcpy(ctx->ipv6->outiface, optarg);
+ memset(ctx->ipv6->outiface_mask, 0xff, len + 1);
- if (invert)
- ctx->ip->invflags |= IPT_INV_VIA_OUT;
+ if (invert)
+ ctx->ipv6->invflags |= IP6T_INV_VIA_OUT;
+ }
break;
case 'm':
break;
case 'p':
+ enable_jmp();
+
+ if ((err = setjmp(env_state)) != 0) {
+ DBG("setjmp() called by longjmp() with value "
+ "%d", err);
+ disable_jmp();
+
+ /* Errors from parse_rule_spec are negative */
+ err = -EINVAL;
+ goto out;
+ }
+
ctx->proto = xtables_parse_protocol(optarg);
+
+ disable_jmp();
+
+ /*
+ * If protocol was set add it to ipt_ip.
+ * xtables_parse_protocol() returns 0 or
+ * UINT16_MAX (-1) on error
+ */
+ if (ctx->proto > 0 && ctx->proto < UINT16_MAX) {
+ if (ctx->type == AF_INET)
+ ctx->ip->proto = ctx->proto;
+
+ if (ctx->type == AF_INET6) {
+ ctx->ipv6->proto = ctx->proto;
+
+ /*
+ * Flags must be set for IPv6 if
+ * protocol is set.
+ */
+ ctx->ipv6->flags |= IP6T_F_PROTO;
+ }
+ }
break;
case 'j':
/* Target */
err = parse_xt_modules(c, invert, ctx);
if (err == 1)
continue;
+ else if (err == -EINVAL)
+ goto out;
break;
}
return err;
}
+static int current_type = -1;
+
+static int setup_xtables(int type)
+{
+ int err;
+
+ DBG("%d", type);
+
+ if (type == current_type)
+ return 0;
+
+ if (current_type != -1)
+ reset_xtables();
+
+ switch (type) {
+ case AF_INET:
+ err = xtables_init_all(&iptables_globals, NFPROTO_IPV4);
+ break;
+ case AF_INET6:
+ err = xtables_init_all(&ip6tables_globals, NFPROTO_IPV6);
+ break;
+ default:
+ return -1;
+ }
+
+ if (!err) {
+ current_type = type;
+ } else {
+ connman_error("error initializing xtables");
+ current_type = -1;
+ reset_xtables();
+ }
+
+ return err;
+}
+
static void reset_xtables(void)
{
struct xtables_match *xt_m;
GList *list;
g_strfreev(ctx->argv);
+
g_free(ctx->ip);
+ g_free(ctx->ipv6);
+
if (ctx->xt_t) {
g_free(ctx->xt_t->t);
ctx->xt_t->t = NULL;
g_free(ctx);
}
-int __connman_iptables_dump(const char *table_name)
+int __connman_iptables_dump(int type, const char *table_name)
{
struct connman_iptables *table;
- DBG("-t %s -L", table_name);
+ DBG("%d -t %s -L", type, table_name);
- table = get_table(table_name);
+ table = get_table(type, table_name);
if (!table)
return -EINVAL;
return 0;
}
-int __connman_iptables_new_chain(const char *table_name,
+int __connman_iptables_new_chain(int type,
+ const char *table_name,
const char *chain)
{
struct connman_iptables *table;
- DBG("-t %s -N %s", table_name, chain);
+ DBG("%d -t %s -N %s", type, table_name, chain);
- table = get_table(table_name);
- if (!table)
+ table = get_table(type, table_name);
+ if (!table) {
return -EINVAL;
+ }
- return iptables_add_chain(table, chain);
+ switch (type) {
+ case AF_INET:
+ case AF_INET6:
+ return iptables_add_chain(table, chain);
+ }
+
+ return -EINVAL;
}
-int __connman_iptables_delete_chain(const char *table_name,
+int __connman_iptables_delete_chain(int type,
+ const char *table_name,
const char *chain)
{
struct connman_iptables *table;
- DBG("-t %s -X %s", table_name, chain);
+ DBG("%d -t %s -X %s", type, table_name, chain);
- table = get_table(table_name);
+ table = get_table(type, table_name);
if (!table)
return -EINVAL;
return iptables_delete_chain(table, chain);
}
-int __connman_iptables_flush_chain(const char *table_name,
+int __connman_iptables_flush_chain(int type,
+ const char *table_name,
const char *chain)
{
struct connman_iptables *table;
- DBG("-t %s -F %s", table_name, chain);
+ DBG("%d -t %s -F %s", type, table_name, chain);
- table = get_table(table_name);
+ table = get_table(type, table_name);
if (!table)
return -EINVAL;
return iptables_flush_chain(table, chain);
}
-int __connman_iptables_change_policy(const char *table_name,
+int __connman_iptables_find_chain(int type,
+ const char *table_name,
+ const char *chain)
+{
+ struct connman_iptables *table;
+
+ DBG("%d -t %s -F %s", type, table_name, chain);
+
+ table = get_table(type, table_name);
+ if (!table)
+ return -EINVAL;
+
+ if(!find_chain_head(table, chain))
+ return -ENOENT; // Not Found
+
+ return 0; // Found
+}
+
+int __connman_iptables_change_policy(int type,
+ const char *table_name,
const char *chain,
const char *policy)
{
struct connman_iptables *table;
- DBG("-t %s -F %s", table_name, chain);
+ DBG("%d -t %s -F %s", type, table_name, chain);
- table = get_table(table_name);
+ table = get_table(type, table_name);
if (!table)
return -EINVAL;
return iptables_change_policy(table, chain, policy);
}
-int __connman_iptables_append(const char *table_name,
+static void iptables_ip_setup(struct iptables_ip *ip, struct parse_context *ctx)
+{
+ if (!ip || !ctx)
+ return;
+
+ ip->type = ctx->type;
+ ip->ip = ctx->ip;
+ ip->ip6 = ctx->ipv6;
+}
+
+int __connman_iptables_append(int type,
+ const char *table_name,
const char *chain,
const char *rule_spec)
{
struct connman_iptables *table;
struct parse_context *ctx;
+ struct iptables_ip ip = { 0 };
const char *target_name;
int err;
+ err = setup_xtables(type);
+
+ if (err < 0)
+ return err;
+
ctx = g_try_new0(struct parse_context, 1);
if (!ctx)
return -ENOMEM;
- DBG("-t %s -A %s %s", table_name, chain, rule_spec);
+ ctx->type = type;
+
+ DBG("%d -t %s -A %s %s", type, table_name, chain, rule_spec);
err = prepare_getopt_args(rule_spec, ctx);
if (err < 0)
goto out;
- table = get_table(table_name);
+ table = get_table(type, table_name);
if (!table) {
err = -EINVAL;
goto out;
else
target_name = ctx->xt_t->name;
- err = iptables_append_rule(table, ctx->ip, chain,
- target_name, ctx->xt_t, ctx->xt_rm);
+ iptables_ip_setup(&ip, ctx);
+
+ err = iptables_append_rule(table, &ip, chain, target_name, ctx->xt_t,
+ ctx->xt_rm);
out:
cleanup_parse_context(ctx);
reset_xtables();
return err;
}
-int __connman_iptables_insert(const char *table_name,
+int __connman_iptables_insert(int type,
+ const char *table_name,
const char *chain,
const char *rule_spec)
{
struct connman_iptables *table;
struct parse_context *ctx;
+ struct iptables_ip ip = { 0 };
const char *target_name;
int err;
+ err = setup_xtables(type);
+
+ if (err < 0)
+ return err;
+
ctx = g_try_new0(struct parse_context, 1);
if (!ctx)
return -ENOMEM;
+
+ ctx->type = type;
- DBG("-t %s -I %s %s", table_name, chain, rule_spec);
+ DBG("%d -t %s -I %s %s", type, table_name, chain, rule_spec);
err = prepare_getopt_args(rule_spec, ctx);
if (err < 0)
goto out;
- table = get_table(table_name);
+ table = get_table(type, table_name);
if (!table) {
err = -EINVAL;
goto out;
else
target_name = ctx->xt_t->name;
- err = iptables_insert_rule(table, ctx->ip, chain,
- target_name, ctx->xt_t, ctx->xt_rm);
+ iptables_ip_setup(&ip, ctx);
+
+ err = iptables_insert_rule(table, &ip, chain, target_name, ctx->xt_t,
+ ctx->xt_rm);
out:
cleanup_parse_context(ctx);
reset_xtables();
return err;
}
-int __connman_iptables_delete(const char *table_name,
+int __connman_iptables_delete(int type,
+ const char *table_name,
const char *chain,
const char *rule_spec)
{
struct connman_iptables *table;
struct parse_context *ctx;
+ struct iptables_ip ip = { 0 };
const char *target_name;
int err;
+ err = setup_xtables(type);
+
+ if (err < 0)
+ return err;
+
ctx = g_try_new0(struct parse_context, 1);
if (!ctx)
return -ENOMEM;
+
+ ctx->type = type;
- DBG("-t %s -D %s %s", table_name, chain, rule_spec);
+ DBG("%d -t %s -D %s %s", type, table_name, chain, rule_spec);
err = prepare_getopt_args(rule_spec, ctx);
if (err < 0)
goto out;
- table = get_table(table_name);
+ table = get_table(type, table_name);
if (!table) {
err = -EINVAL;
goto out;
else
target_name = ctx->xt_t->name;
- err = iptables_delete_rule(table, ctx->ip, chain,
- target_name, ctx->xt_t, ctx->xt_m,
- ctx->xt_rm);
+ iptables_ip_setup(&ip, ctx);
+
+ err = iptables_delete_rule(table, &ip, chain, target_name, ctx->xt_t,
+ ctx->xt_m, ctx->xt_rm);
out:
cleanup_parse_context(ctx);
reset_xtables();
return err;
}
-int __connman_iptables_commit(const char *table_name)
+int __connman_iptables_commit(int type, const char *table_name)
{
struct connman_iptables *table;
- struct ipt_replace *repl;
+ struct iptables_replace repl = { 0 };
int err;
struct xt_counters_info *counters;
struct connman_iptables_entry *e;
GList *list;
unsigned int cnt;
- DBG("%s", table_name);
+ err = setup_xtables(type);
+
+ if (err < 0)
+ return err;
+
+ DBG("%d %s", type, table_name);
+
+ repl.type = type;
- table = g_hash_table_lookup(table_hash, table_name);
+ table = hash_table_lookup(type, table_name);
if (!table)
return -EINVAL;
- repl = iptables_blob(table);
- if (!repl)
- return -ENOMEM;
+ switch (type) {
+ case AF_INET:
+ repl.r = iptables_blob(table);
+ if (!repl.r)
+ return -ENOMEM;
+
+ break;
+ case AF_INET6:
+ repl.r6 = ip6tables_blob(table);
+ if (!repl.r6)
+ return -ENOMEM;
+ }
if (debug_enabled)
- dump_ipt_replace(repl);
+ dump_replace(&repl);
- err = iptables_replace(table, repl);
+ err = iptables_replace(table, &repl);
if (err < 0)
goto out_free;
err = -ENOMEM;
goto out_hash_remove;
}
- g_stpcpy(counters->name, table->info->name);
+ g_stpcpy(counters->name, iptables_table_get_info_name(table));
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];
+ if (e->counter_idx >= 0) {
+
+ switch (type) {
+ case AF_INET:
+ counters->counters[cnt] =
+ repl.r->counters[e->counter_idx];
+ break;
+ case AF_INET6:
+ counters->counters[cnt] =
+ repl.r6->counters[e->counter_idx];
+ break;
+ }
+ }
}
err = iptables_add_counters(table, counters);
g_free(counters);
err = 0;
out_hash_remove:
- g_hash_table_remove(table_hash, table_name);
+ hash_table_remove(type, table_name);
out_free:
- g_free(repl->counters);
- g_free(repl);
+ if (type == AF_INET && repl.r)
+ g_free(repl.r->counters);
+
+ if (type == AF_INET6 && repl.r6)
+ g_free(repl.r6->counters);
+
+ g_free(repl.r);
+ g_free(repl.r6);
+
+ reset_xtables();
+
return err;
}
table_cleanup(table);
}
-static int iterate_chains_cb(struct ipt_entry *entry, int builtin,
+static int iterate_chains_cb(struct connman_iptables_entry *entry, int builtin,
unsigned int hook, size_t size,
unsigned int offset, void *user_data)
{
connman_iptables_iterate_chains_cb_t cb = cbd->cb;
struct xt_entry_target *target;
- if (offset + entry->next_offset == size)
+ if (offset + iptables_entry_get_next_offset(entry) == size)
return 0;
- target = ipt_get_target(entry);
+ target = iptables_entry_get_target(entry);
- if (!g_strcmp0(target->u.user.name, IPT_ERROR_TARGET))
+ if (!g_strcmp0(target->u.user.name, get_error_target(entry->type))) {
(*cb)((const char *)target->data, cbd->user_data);
- else if (builtin >= 0)
+ } else if (builtin >= 0) {
(*cb)(hooknames[builtin], cbd->user_data);
+ }
return 0;
}
-int __connman_iptables_iterate_chains(const char *table_name,
+int __connman_iptables_iterate_chains(int type, const char *table_name,
connman_iptables_iterate_chains_cb_t cb,
void *user_data)
{
struct cb_data *cbd = cb_data_new(cb, user_data);
struct connman_iptables *table;
+ struct connman_iptables_entry entry = { 0 };
+ int err;
- table = get_table(table_name);
+ err = setup_xtables(type);
+
+ if (err < 0)
+ return err;
+
+ table = get_table(type, table_name);
if (!table) {
g_free(cbd);
return -EINVAL;
}
- iterate_entries(table->blob_entries->entrytable,
- table->info->valid_hooks,
- table->info->hook_entry,
- table->info->underflow,
- table->blob_entries->size,
+ entry.type = type;
+
+ if (type == AF_INET)
+ entry.entry = table->blob_entries->entrytable;
+
+ if (type == AF_INET6)
+ entry.entry6 = table->blob_entries6->entrytable;
+
+ iterate_entries(&entry,
+ iptables_table_get_info_valid_hooks(table),
+ iptables_table_get_info_hook_entry(table),
+ iptables_table_get_info_underflow(table),
+ iptables_table_get_entries_size(table),
iterate_chains_cb, cbd);
g_free(cbd);
+
+ reset_xtables();
return 0;
}
table_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
NULL, remove_table);
- xtables_init_all(&iptables_globals, NFPROTO_IPV4);
+ table_hash_ipv6 = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, remove_table);
return 0;
}
DBG("");
g_hash_table_destroy(table_hash);
+ g_hash_table_destroy(table_hash_ipv6);
}
+
static guint timer_ra;
static char *default_interface;
static GSList *prefixes;
-static GHashTable *timer_hash;
static void *rs_context;
static int setup_prefix_delegation(struct connman_service *service);
if (!default_interface)
DBG("No uplink connection, retrying prefix delegation");
- ret = setup_prefix_delegation(__connman_service_get_default());
+ ret = setup_prefix_delegation(connman_service_get_default());
if (ret < 0 && ret != -EINPROGRESS)
return TRUE; /* delegation error, try again */
if (status == CONNMAN_DHCPV6_STATUS_SUCCEED)
dhcpv6_callback(network, status, data);
else
- setup_prefix_delegation(__connman_service_get_default());
+ setup_prefix_delegation(connman_service_get_default());
}
static void cleanup(void)
timer_uplink = 0;
}
- if (timer_hash) {
- g_hash_table_destroy(timer_hash);
- timer_hash = NULL;
- }
-
if (prefixes) {
g_slist_free_full(prefixes, g_free);
prefixes = NULL;
return err;
}
-static void cleanup_timer(gpointer user_data)
-{
- guint timer = GPOINTER_TO_UINT(user_data);
-
- g_source_remove(timer);
-}
-
static void update_default_interface(struct connman_service *service)
{
setup_prefix_delegation(service);
static void update_ipconfig(struct connman_service *service,
struct connman_ipconfig *ipconfig)
{
- if (!service || service != __connman_service_get_default())
+ if (!service || service != connman_service_get_default())
return;
if (ipconfig != __connman_service_get_ip6config(service))
}
}
-static struct connman_notifier pd_notifier = {
+static const struct connman_notifier pd_notifier = {
.name = "IPv6 prefix delegation",
.default_changed = update_default_interface,
.ipconfig_changed = update_ipconfig,
bridge_index = connman_inet_ifindex(bridge);
- timer_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
- NULL, cleanup_timer);
-
err = __connman_inet_ipv6_start_recv_rs(bridge_index, rs_received,
NULL, &rs_context);
if (err < 0)
DBG("Cannot receive router solicitation %d/%s",
err, strerror(-err));
- service = __connman_service_get_default();
+ service = connman_service_get_default();
if (service) {
/*
* We have an uplink connection already, try to use it.
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
#include <syslog.h>
#include <dlfcn.h>
+#include <signal.h>
#include "connman.h"
static const char *program_exec;
static const char *program_path;
+/* This makes sure we always have a __debug section. */
+CONNMAN_DEBUG_DEFINE(dummy);
+
/**
* connman_info:
* @format: format string
#include "connman.h"
+#define CONF_ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]) - 1)
+
#define DEFAULT_INPUT_REQUEST_TIMEOUT (120 * 1000)
#define DEFAULT_BROWSER_LAUNCH_TIMEOUT (300 * 1000)
NULL
};
+static char *default_favorite_techs[] = {
+ "ethernet",
+ NULL
+};
+
static char *default_blacklist[] = {
"vmnet",
"vboxnet",
bool bg_scan;
char **pref_timeservers;
unsigned int *auto_connect;
+ unsigned int *favorite_techs;
unsigned int *preferred_techs;
unsigned int *always_connected_techs;
char **fallback_nameservers;
unsigned int timeout_browserlaunch;
char **blacklisted_interfaces;
bool allow_hostname_updates;
+ bool allow_domainname_updates;
bool single_tech;
char **tethering_technologies;
bool persistent_tethering_mode;
bool enable_6to4;
char *vendor_class_id;
bool enable_online_check;
+ bool auto_connect_roaming_services;
+ bool acd;
+ bool use_gateways_as_timeservers;
} connman_settings = {
.bg_scan = true,
.pref_timeservers = NULL,
.auto_connect = NULL,
+ .favorite_techs = NULL,
.preferred_techs = NULL,
.always_connected_techs = NULL,
.fallback_nameservers = NULL,
.timeout_browserlaunch = DEFAULT_BROWSER_LAUNCH_TIMEOUT,
.blacklisted_interfaces = NULL,
.allow_hostname_updates = true,
+ .allow_domainname_updates = true,
.single_tech = false,
.tethering_technologies = NULL,
.persistent_tethering_mode = false,
.enable_6to4 = false,
.vendor_class_id = NULL,
.enable_online_check = true,
+ .auto_connect_roaming_services = false,
+ .acd = false,
+ .use_gateways_as_timeservers = false,
};
#define CONF_BG_SCAN "BackgroundScanning"
#define CONF_PREF_TIMESERVERS "FallbackTimeservers"
-#define CONF_AUTO_CONNECT "DefaultAutoConnectTechnologies"
+#define CONF_AUTO_CONNECT_TECHS "DefaultAutoConnectTechnologies"
+#define CONF_FAVORITE_TECHS "DefaultFavoriteTechnologies"
#define CONF_ALWAYS_CONNECTED_TECHS "AlwaysConnectedTechnologies"
#define CONF_PREFERRED_TECHS "PreferredTechnologies"
#define CONF_FALLBACK_NAMESERVERS "FallbackNameservers"
#define CONF_TIMEOUT_BROWSERLAUNCH "BrowserLaunchTimeout"
#define CONF_BLACKLISTED_INTERFACES "NetworkInterfaceBlacklist"
#define CONF_ALLOW_HOSTNAME_UPDATES "AllowHostnameUpdates"
+#define CONF_ALLOW_DOMAINNAME_UPDATES "AllowDomainnameUpdates"
#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"
+#define CONF_AUTO_CONNECT_ROAMING_SERVICES "AutoConnectRoamingServices"
+#define CONF_ACD "AddressConflictDetection"
+#define CONF_USE_GATEWAYS_AS_TIMESERVERS "UseGatewaysAsTimeservers"
static const char *supported_options[] = {
CONF_BG_SCAN,
CONF_PREF_TIMESERVERS,
- CONF_AUTO_CONNECT,
+ CONF_AUTO_CONNECT_TECHS,
CONF_ALWAYS_CONNECTED_TECHS,
CONF_PREFERRED_TECHS,
CONF_FALLBACK_NAMESERVERS,
CONF_TIMEOUT_BROWSERLAUNCH,
CONF_BLACKLISTED_INTERFACES,
CONF_ALLOW_HOSTNAME_UPDATES,
+ CONF_ALLOW_DOMAINNAME_UPDATES,
CONF_SINGLE_TECH,
CONF_TETHERING_TECHNOLOGIES,
CONF_PERSISTENT_TETHERING_MODE,
CONF_ENABLE_6TO4,
CONF_VENDOR_CLASS_ID,
CONF_ENABLE_ONLINE_CHECK,
+ CONF_AUTO_CONNECT_ROAMING_SERVICES,
+ CONF_ACD,
+ CONF_USE_GATEWAYS_AS_TIMESERVERS,
NULL
};
if (!config) {
connman_settings.auto_connect =
- parse_service_types(default_auto_connect, 3);
+ parse_service_types(default_auto_connect, CONF_ARRAY_SIZE(default_auto_connect));
+ connman_settings.favorite_techs =
+ parse_service_types(default_favorite_techs, CONF_ARRAY_SIZE(default_favorite_techs));
connman_settings.blacklisted_interfaces =
g_strdupv(default_blacklist);
return;
g_clear_error(&error);
str_list = __connman_config_get_string_list(config, "General",
- CONF_AUTO_CONNECT, &len, &error);
+ CONF_AUTO_CONNECT_TECHS, &len, &error);
if (!error)
connman_settings.auto_connect =
parse_service_types(str_list, len);
else
connman_settings.auto_connect =
- parse_service_types(default_auto_connect, 3);
+ parse_service_types(default_auto_connect, CONF_ARRAY_SIZE(default_auto_connect));
+
+ g_clear_error(&error);
+
+ str_list = __connman_config_get_string_list(config, "General",
+ CONF_FAVORITE_TECHS, &len, &error);
+
+ if (!error)
+ connman_settings.favorite_techs =
+ parse_service_types(str_list, len);
+ else
+ connman_settings.favorite_techs =
+ parse_service_types(default_favorite_techs, CONF_ARRAY_SIZE(default_favorite_techs));
g_strfreev(str_list);
g_clear_error(&error);
boolean = __connman_config_get_bool(config, "General",
+ CONF_ALLOW_DOMAINNAME_UPDATES,
+ &error);
+ if (!error)
+ connman_settings.allow_domainname_updates = boolean;
+
+ g_clear_error(&error);
+
+ boolean = __connman_config_get_bool(config, "General",
CONF_SINGLE_TECH, &error);
if (!error)
connman_settings.single_tech = boolean;
}
g_clear_error(&error);
+
+ boolean = __connman_config_get_bool(config, "General",
+ CONF_AUTO_CONNECT_ROAMING_SERVICES, &error);
+ if (!error)
+ connman_settings.auto_connect_roaming_services = boolean;
+
+ g_clear_error(&error);
+
+ boolean = __connman_config_get_bool(config, "General", CONF_ACD, &error);
+ if (!error)
+ connman_settings.acd = boolean;
+
+ g_clear_error(&error);
+
+ boolean = __connman_config_get_bool(config, "General",
+ CONF_USE_GATEWAYS_AS_TIMESERVERS, &error);
+ if (!error)
+ connman_settings.use_gateways_as_timeservers = boolean;
+
+ g_clear_error(&error);
}
static int config_init(const char *file)
G_OPTION_ARG_CALLBACK, parse_debug,
"Specify debug options to enable", "DEBUG" },
{ "device", 'i', 0, G_OPTION_ARG_STRING, &option_device,
- "Specify networking device or interface", "DEV" },
+ "Specify networking devices or interfaces", "DEV,..." },
{ "nodevice", 'I', 0, G_OPTION_ARG_STRING, &option_nodevice,
- "Specify networking interface to ignore", "DEV" },
+ "Specify networking interfaces to ignore", "DEV,..." },
{ "plugin", 'p', 0, G_OPTION_ARG_STRING, &option_plugin,
"Specify plugins to load", "NAME,..." },
{ "noplugin", 'P', 0, G_OPTION_ARG_CALLBACK, &parse_noplugin,
"Don't fork daemon to background" },
{ "nodnsproxy", 'r', G_OPTION_FLAG_REVERSE,
G_OPTION_ARG_NONE, &option_dnsproxy,
- "Don't enable DNS Proxy" },
+ "Don't support DNS resolving" },
{ "nobacktrace", 0, G_OPTION_FLAG_REVERSE,
G_OPTION_ARG_NONE, &option_backtrace,
"Don't print out backtrace information" },
if (g_str_equal(key, CONF_ALLOW_HOSTNAME_UPDATES))
return connman_settings.allow_hostname_updates;
+ if (g_str_equal(key, CONF_ALLOW_DOMAINNAME_UPDATES))
+ return connman_settings.allow_domainname_updates;
+
if (g_str_equal(key, CONF_SINGLE_TECH))
return connman_settings.single_tech;
if (g_str_equal(key, CONF_ENABLE_ONLINE_CHECK))
return connman_settings.enable_online_check;
+ if (g_str_equal(key, CONF_AUTO_CONNECT_ROAMING_SERVICES))
+ return connman_settings.auto_connect_roaming_services;
+
+ if (g_str_equal(key, CONF_ACD))
+ return connman_settings.acd;
+
+ if (g_str_equal(key, CONF_USE_GATEWAYS_AS_TIMESERVERS))
+ return connman_settings.use_gateways_as_timeservers;
+
return false;
}
unsigned int *connman_setting_get_uint_list(const char *key)
{
- if (g_str_equal(key, CONF_AUTO_CONNECT))
+ if (g_str_equal(key, CONF_AUTO_CONNECT_TECHS))
return connman_settings.auto_connect;
+ if (g_str_equal(key, CONF_FAVORITE_TECHS))
+ return connman_settings.favorite_techs;
+
if (g_str_equal(key, CONF_PREFERRED_TECHS))
return connman_settings.preferred_techs;
g_strfreev(connman_settings.pref_timeservers);
g_free(connman_settings.auto_connect);
+ g_free(connman_settings.favorite_techs);
g_free(connman_settings.preferred_techs);
g_strfreev(connman_settings.fallback_nameservers);
g_strfreev(connman_settings.blacklisted_interfaces);
# user interface designs.
# BrowserLaunchTimeout = 300
-# Enable background scanning. Default is true.
-# Background scanning will start every 5 minutes unless
-# the scan list is empty. In that case, a simple backoff
-# mechanism starting from 10s up to 5 minutes will run.
+# If wifi is disconnected, the background scanning will follow a simple
+# backoff mechanism from 3s up to 5 minutes. Then, it will stay in 5
+# minutes unless user specifically asks for scanning through a D-Bus
+# call. If so, the mechanism will start again from 3s. This feature
+# activates also the background scanning while being connected, which
+# is required for roaming on wifi.
+# When BackgroundScanning is false, ConnMan will not perform any scan
+# regardless of wifi is connected or not, unless it is requested by
+# the user through a D-Bus call.
# BackgroundScanning = true
+# Assume that service gateways also function as timeservers.
+# UseGatewaysAsTimeservers = false
+
# List of Fallback timeservers separated by ",".
# These timeservers are used for NTP sync when there are
-# no timeserver set by the user or by the service.
-# These can contain mixed combination of fully qualified
-# domain names, IPv4 and IPv6 addresses.
+# no timeservers set by the user or by the service, and
+# when UseGatewaysAsTimeservers = false. These can contain
+# mixed combination of fully qualified domain names, IPv4
+# and IPv6 addresses.
# FallbackTimeservers =
# List of fallback nameservers separated by "," used if no
# List of blacklisted network interfaces separated by ",".
# Found interfaces will be compared to the list and will
-# not be handled by connman, if their first characters
+# not be handled by ConnMan, if their first characters
# match any of the list entries. Default value is
# vmnet,vboxnet,virbr,ifb,ve-,vb-.
# NetworkInterfaceBlacklist = vmnet,vboxnet,virbr,ifb,ve-,vb-
-# Allow connman to change the system hostname. This can
+# Allow ConnMan to change the system hostname. This can
# happen for example if we receive DHCP hostname option.
# Default value is true.
# AllowHostnameUpdates = true
+# Allow ConnMan to change the system domainname. This can
+# happen for example if we receive DHCP domainname option.
+# Default value is true.
+# AllowDomainnameUpdates = true
+
# Keep only a single connected technology at any time. When a new
# service is connected by the user or a better one is found according
# to PreferredTechnologies, the new service is kept connected and all
# other which is already connected.
# This setting has no effect if SingleConnectedTechnologies is enabled.
# AlwaysConnectedTechnologies =
+
+# Enable auto connection of services in roaming.
+# If this setting is false, roaming services are not auto-connected by ConnMan.
+# Default value is false.
+# AutoConnectRoamingServices = false
+
+# Enable address conflict detection
+# If this setting is true, ConnMan will send probe ARP packets to see
+# if an IPv4 address is already in use before assigning the address
+# to an interface (in accordance with RFC 5227).
+# Default value is false.
+# AddressConflictDetection = false
return;
}
-static struct connman_notifier technology_notifier = {
+static const struct connman_notifier technology_notifier = {
.name = "manager",
.priority = CONNMAN_NOTIFIER_PRIORITY_HIGH,
.idle_state = idle_state,
return reply;
}
+static DBusMessage *get_tethering_clients(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter, array;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &array);
+
+ __connman_tethering_list_clients(&array);
+
+ dbus_message_iter_close_container(&iter, &array);
+ return reply;
+}
+
static DBusMessage *connect_provider(DBusConnection *conn,
DBusMessage *msg, void *data)
{
{ GDBUS_METHOD("GetPeers",
NULL, GDBUS_ARGS({ "peers", "a(oa{sv})" }),
get_peers) },
+ { GDBUS_METHOD("GetTetheringClients",
+ NULL, GDBUS_ARGS({ "tethering_clients", "as" }),
+ get_tethering_clients) },
{ GDBUS_DEPRECATED_ASYNC_METHOD("ConnectProvider",
GDBUS_ARGS({ "provider", "a{sv}" }),
GDBUS_ARGS({ "path", "o" }),
if (read(f, &value, sizeof(value)) < 0)
value = 0;
- if (lseek(f, 0, SEEK_SET) < 0)
+ if (lseek(f, 0, SEEK_SET) < 0) {
+ close(f);
return -errno;
+ }
}
if (enable) {
g_free(nat);
}
-static struct connman_notifier nat_notifier = {
+static const struct connman_notifier nat_notifier = {
.name = "nat",
.default_changed = update_default_interface,
};
#include <string.h>
#include "connman.h"
+#include <connman/acd.h>
+#include "src/shared/arp.h"
/*
* How many times to send RS with the purpose of
*/
#define RS_REFRESH_TIMEOUT 3
+/*
+ * As per RFC 4861, a host should transmit up to MAX_RTR_SOLICITATIONS(3)
+ * Router Solicitation messages, each separated by at least
+ * RTR_SOLICITATION_INTERVAL(4) seconds to obtain RA for IPv6 auto-configuration.
+ */
+#define RTR_SOLICITATION_INTERVAL 4
+
+#define DHCP_RETRY_TIMEOUT 10
+
static GSList *network_list = NULL;
static GSList *driver_list = NULL;
int index;
int router_solicit_count;
int router_solicit_refresh_count;
+ struct acd_host *acd_host;
+ guint ipv4ll_timeout;
+ guint dhcp_timeout;
struct connman_network_driver *driver;
void *driver_data;
char *private_key_passphrase;
char *phase2_auth;
bool wps;
+ bool wps_advertizing;
bool use_wps;
char *pin_wps;
} wifi;
type);
}
+void connman_network_append_acddbus(DBusMessageIter *dict,
+ struct connman_network *network)
+{
+ if (!network->acd_host)
+ return;
+
+ acd_host_append_dbus_property(network->acd_host, dict);
+}
+
+static int start_acd(struct connman_network *network);
+
+static void remove_ipv4ll_timeout(struct connman_network *network)
+{
+ if (network->ipv4ll_timeout > 0) {
+ g_source_remove(network->ipv4ll_timeout);
+ network->ipv4ll_timeout = 0;
+ }
+}
+
+static void acd_host_ipv4_available(struct acd_host *acd, gpointer user_data)
+{
+ struct connman_network *network = user_data;
+ struct connman_service *service;
+ struct connman_ipconfig *ipconfig_ipv4;
+ int err;
+
+ if (!network)
+ return;
+
+ service = connman_service_lookup_from_network(network);
+ if (!service)
+ return;
+
+ ipconfig_ipv4 = __connman_service_get_ip4config(service);
+ if (!ipconfig_ipv4) {
+ connman_error("Service has no IPv4 configuration");
+ return;
+ }
+
+ err = __connman_ipconfig_address_add(ipconfig_ipv4);
+ if (err < 0)
+ goto err;
+
+ err = __connman_ipconfig_gateway_add(ipconfig_ipv4);
+ if (err < 0)
+ goto err;
+
+ __connman_service_save(service);
+
+ return;
+
+err:
+ connman_network_set_error(__connman_service_get_network(service),
+ CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
+}
+
+static int start_ipv4ll(struct connman_network *network)
+{
+ struct connman_service *service;
+ struct connman_ipconfig *ipconfig_ipv4;
+ struct in_addr addr;
+ char *address;
+
+ service = connman_service_lookup_from_network(network);
+ if (!service)
+ return -EINVAL;
+
+ ipconfig_ipv4 = __connman_service_get_ip4config(service);
+ if (!ipconfig_ipv4) {
+ connman_error("Service has no IPv4 configuration");
+ return -EINVAL;
+ }
+
+ /* Apply random IPv4 address. */
+ addr.s_addr = htonl(arp_random_ip());
+ address = inet_ntoa(addr);
+ if (!address) {
+ connman_error("Could not convert IPv4LL random address %u",
+ addr.s_addr);
+ return -EINVAL;
+ }
+ __connman_ipconfig_set_local(ipconfig_ipv4, address);
+
+ connman_info("Probing IPv4LL address %s", address);
+ return start_acd(network);
+}
+
+static gboolean start_ipv4ll_ontimeout(gpointer data)
+{
+ struct connman_network *network = data;
+
+ if (!network)
+ return FALSE;
+
+ /* Start IPv4LL ACD. */
+ start_ipv4ll(network);
+
+ return FALSE;
+}
+
+static void acd_host_ipv4_lost(struct acd_host *acd, gpointer user_data)
+{
+ struct connman_network *network = user_data;
+ struct connman_service *service;
+ struct connman_ipconfig *ipconfig_ipv4;
+ enum connman_ipconfig_type type;
+ enum connman_ipconfig_method method;
+
+ if (!network)
+ return;
+
+ service = connman_service_lookup_from_network(network);
+ if (!service)
+ return;
+
+ ipconfig_ipv4 = __connman_service_get_ip4config(service);
+ if (!ipconfig_ipv4) {
+ connman_error("Service has no IPv4 configuration");
+ return;
+ }
+
+ type = __connman_ipconfig_get_config_type(ipconfig_ipv4);
+ if (type != CONNMAN_IPCONFIG_TYPE_IPV4)
+ return;
+
+ __connman_ipconfig_address_remove(ipconfig_ipv4);
+
+ method = __connman_ipconfig_get_method(ipconfig_ipv4);
+ if (method == CONNMAN_IPCONFIG_METHOD_DHCP) {
+ /*
+ * We have one more chance for DHCP. If this fails
+ * acd_host_ipv4_conflict will be called.
+ */
+ network = __connman_service_get_network(service);
+ if (network)
+ __connman_network_enable_ipconfig(network, ipconfig_ipv4);
+ } else {
+ /* Start IPv4LL ACD. */
+ start_ipv4ll(network);
+ }
+}
+
+static void acd_host_ipv4_conflict(struct acd_host *acd, gpointer user_data)
+{
+ struct connman_network *network = user_data;
+ struct connman_service *service;
+ struct connman_ipconfig *ipconfig_ipv4;
+ enum connman_ipconfig_method method;
+
+ service = connman_service_lookup_from_network(network);
+ if (!service)
+ return;
+
+ ipconfig_ipv4 = __connman_service_get_ip4config(service);
+ if (!ipconfig_ipv4) {
+ connman_error("Service has no IPv4 configuration");
+ return;
+ }
+
+ method = __connman_ipconfig_get_method(ipconfig_ipv4);
+ connman_info("%s conflict counts=%u", __FUNCTION__,
+ acd_host_get_conflicts_count(acd));
+
+ if (method == CONNMAN_IPCONFIG_METHOD_DHCP &&
+ acd_host_get_conflicts_count(acd) < 2) {
+ connman_info("%s Sending DHCP decline", __FUNCTION__);
+ __connman_dhcp_decline(ipconfig_ipv4);
+
+ connman_network_set_connected_dhcp_later(network, DHCP_RETRY_TIMEOUT);
+ __connman_ipconfig_set_local(ipconfig_ipv4, NULL);
+ } else {
+ if (method == CONNMAN_IPCONFIG_METHOD_DHCP) {
+ __connman_ipconfig_set_method(ipconfig_ipv4,
+ CONNMAN_IPCONFIG_METHOD_AUTO);
+ __connman_dhcp_decline(ipconfig_ipv4);
+ }
+ /* Start IPv4LL ACD. */
+ start_ipv4ll(network);
+ }
+}
+
+static void acd_host_ipv4_maxconflict(struct acd_host *acd, gpointer user_data)
+{
+ struct connman_network *network = user_data;
+
+ remove_ipv4ll_timeout(network);
+ connman_info("Had maximum number of conflicts. Next IPv4LL address will be "
+ "tried in %d seconds", RATE_LIMIT_INTERVAL);
+ /* Wait, then start IPv4LL ACD. */
+ network->ipv4ll_timeout =
+ g_timeout_add_seconds_full(G_PRIORITY_HIGH,
+ RATE_LIMIT_INTERVAL,
+ start_ipv4ll_ontimeout,
+ network,
+ NULL);
+}
+
+static int start_acd(struct connman_network *network)
+{
+ struct connman_service *service;
+ struct connman_ipconfig *ipconfig_ipv4;
+ const char* address;
+ struct in_addr addr;
+
+ remove_ipv4ll_timeout(network);
+
+ service = connman_service_lookup_from_network(network);
+ if (!service)
+ return -EINVAL;
+
+ ipconfig_ipv4 = __connman_service_get_ip4config(service);
+ if (!ipconfig_ipv4) {
+ connman_error("Service has no IPv4 configuration");
+ return -EINVAL;
+ }
+
+ if (!network->acd_host) {
+ int index;
+
+ index = __connman_ipconfig_get_index(ipconfig_ipv4);
+ network->acd_host = acd_host_new(index,
+ connman_service_get_dbuspath(service));
+ if (!network->acd_host) {
+ connman_error("Could not create ACD data structure");
+ return -EINVAL;
+ }
+
+ acd_host_register_event(network->acd_host,
+ ACD_HOST_EVENT_IPV4_AVAILABLE,
+ acd_host_ipv4_available, network);
+ acd_host_register_event(network->acd_host,
+ ACD_HOST_EVENT_IPV4_LOST,
+ acd_host_ipv4_lost, network);
+ acd_host_register_event(network->acd_host,
+ ACD_HOST_EVENT_IPV4_CONFLICT,
+ acd_host_ipv4_conflict, network);
+ acd_host_register_event(network->acd_host,
+ ACD_HOST_EVENT_IPV4_MAXCONFLICT,
+ acd_host_ipv4_maxconflict, network);
+ }
+
+ address = __connman_ipconfig_get_local(ipconfig_ipv4);
+ if (!address)
+ return -EINVAL;
+
+ connman_info("Starting ACD for address %s", address);
+ if (inet_pton(AF_INET, address, &addr) != 1)
+ connman_error("Could not convert address %s", address);
+
+ acd_host_start(network->acd_host, htonl(addr.s_addr));
+
+ return 0;
+}
+
static void dhcp_success(struct connman_network *network)
{
struct connman_service *service;
if (!ipconfig_ipv4)
return;
+ if (connman_setting_get_bool("AddressConflictDetection")) {
+ err = start_acd(network);
+ if (!err)
+ return;
+
+ /* On error proceed without ACD. */
+ }
+
err = __connman_ipconfig_address_add(ipconfig_ipv4);
if (err < 0)
goto err;
if (!__connman_ipconfig_get_local(ipconfig))
__connman_service_read_ip4config(service);
+ if (connman_setting_get_bool("AddressConflictDetection")) {
+ err = start_acd(network);
+ if (!err)
+ return 0;
+
+ /* On error proceed without ACD. */
+ }
+
err = __connman_ipconfig_address_add(ipconfig);
if (err < 0)
goto err;
return err;
}
+static void remove_dhcp_timeout(struct connman_network *network)
+{
+ if (network->dhcp_timeout > 0) {
+ g_source_remove(network->dhcp_timeout);
+ network->dhcp_timeout = 0;
+ }
+}
+
static int set_connected_dhcp(struct connman_network *network)
{
struct connman_service *service;
int err;
DBG("network %p", network);
+ remove_dhcp_timeout(network);
service = connman_service_lookup_from_network(network);
ipconfig_ipv4 = __connman_service_get_ip4config(service);
return 0;
}
+static gboolean set_connected_dhcp_timout(gpointer data)
+{
+ struct connman_network *network = data;
+ struct connman_service *service;
+ struct connman_ipconfig *ipconfig;
+ enum connman_ipconfig_method method;
+
+ network->dhcp_timeout = 0;
+
+ service = connman_service_lookup_from_network(network);
+ if (!service)
+ return FALSE;
+
+ ipconfig = __connman_service_get_ip4config(service);
+ if (!ipconfig)
+ return FALSE;
+
+ /* Method is still DHCP? */
+ method = __connman_ipconfig_get_method(ipconfig);
+ if (method == CONNMAN_IPCONFIG_METHOD_DHCP)
+ set_connected_dhcp(network);
+
+ return FALSE;
+}
+
+void connman_network_set_connected_dhcp_later(struct connman_network *network,
+ uint32_t sec)
+{
+ remove_dhcp_timeout(network);
+
+ network->dhcp_timeout =
+ g_timeout_add_seconds_full(G_PRIORITY_HIGH,
+ sec,
+ set_connected_dhcp_timout,
+ network,
+ NULL);
+}
+
static int manual_ipv6_set(struct connman_network *network,
struct connman_ipconfig *ipconfig_ipv6)
{
DBG("re-send router solicitation %d",
network->router_solicit_count);
network->router_solicit_count--;
- __connman_inet_ipv6_send_rs(network->index, 1,
+ __connman_inet_ipv6_send_rs(network->index, RTR_SOLICITATION_INTERVAL,
check_dhcpv6, network);
return;
}
network->router_solicit_refresh_count = 0;
connman_network_unref(network);
- return;
}
int __connman_network_refresh_rs_ipv6(struct connman_network *network,
/* Try to get stateless DHCPv6 information, RFC 3736 */
network->router_solicit_count = 3;
- __connman_inet_ipv6_send_rs(index, 1, check_dhcpv6, network);
+ __connman_inet_ipv6_send_rs(index, RTR_SOLICITATION_INTERVAL,
+ check_dhcpv6, network);
}
static void set_connected(struct connman_network *network)
__connman_service_notify_ipv4_configuration(service);
/* fall through */
case CONNMAN_IPCONFIG_METHOD_DHCP:
+ remove_dhcp_timeout(network);
__connman_dhcp_stop(ipconfig_ipv4);
break;
}
g_free(network->node);
g_free(network->name);
g_free(network->identifier);
+ acd_host_free(network->acd_host);
network->device = NULL;
network->type = type;
network->identifier = ident;
+ network->acd_host = NULL;
+ network->ipv4ll_timeout = 0;
network_list = g_slist_prepend(network_list, network);
+ network->dhcp_timeout = 0;
+
DBG("network %p identifier %s type %s", network, identifier,
type2string(type));
return network;
if (!network->device)
return -ENODEV;
- network->connecting = true;
-
__connman_device_disconnect(network->device);
+ network->connecting = true;
+
err = network->driver->connect(network);
if (err < 0) {
if (err == -EINPROGRESS)
DBG("network %p", network);
+ remove_ipv4ll_timeout(network);
+ if (network->acd_host)
+ acd_host_stop(network->acd_host);
+
if (!network->connected && !network->connecting &&
!network->associating)
return -ENOTCONN;
case CONNMAN_IPCONFIG_METHOD_OFF:
case CONNMAN_IPCONFIG_METHOD_FIXED:
return -EINVAL;
- case CONNMAN_IPCONFIG_METHOD_AUTO:
- release_dhcpv6(network);
- break;
case CONNMAN_IPCONFIG_METHOD_MANUAL:
__connman_ipconfig_address_remove(ipconfig);
break;
+ case CONNMAN_IPCONFIG_METHOD_AUTO:
+ release_dhcpv6(network);
+ if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+ break;
+ /* fall through */
case CONNMAN_IPCONFIG_METHOD_DHCP:
+ remove_dhcp_timeout(network);
__connman_dhcp_stop(ipconfig_ipv4);
break;
}
network->roaming = value;
else if (g_strcmp0(key, "WiFi.WPS") == 0)
network->wifi.wps = value;
+ else if (g_strcmp0(key, "WiFi.WPSAdvertising") == 0)
+ network->wifi.wps_advertizing = value;
else if (g_strcmp0(key, "WiFi.UseWPS") == 0)
network->wifi.use_wps = value;
return network->roaming;
else if (g_str_equal(key, "WiFi.WPS"))
return network->wifi.wps;
+ else if (g_str_equal(key, "WiFi.WPSAdvertising"))
+ return network->wifi.wps_advertizing;
else if (g_str_equal(key, "WiFi.UseWPS"))
return network->wifi.use_wps;
--- /dev/null
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2018 Chris Novakovic
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include "connman.h"
+
+int __connman_stats_service_register(struct connman_service *service)
+{
+ return -ENOTSUP;
+}
+
+void __connman_stats_service_unregister(struct connman_service *service)
+{
+}
+
+int __connman_stats_update(struct connman_service *service,
+ bool roaming,
+ struct connman_stats_data *data)
+{
+ return 0;
+}
+
+int __connman_stats_get(struct connman_service *service,
+ bool roaming,
+ struct connman_stats_data *data)
+{
+ return 0;
+}
+
+int __connman_stats_init(void)
+{
+ return 0;
+}
+
+void __connman_stats_cleanup(void)
+{
+}
*
* Returns: %0 on success
*/
-int connman_notifier_register(struct connman_notifier *notifier)
+int connman_notifier_register(const struct connman_notifier *notifier)
{
DBG("notifier %p name %s", notifier, notifier->name);
- notifier_list = g_slist_insert_sorted(notifier_list, notifier,
+ notifier_list = g_slist_insert_sorted(notifier_list, (void*)notifier,
compare_priority);
return 0;
*
* Remove a previously registered notifier module
*/
-void connman_notifier_unregister(struct connman_notifier *notifier)
+void connman_notifier_unregister(const struct connman_notifier *notifier)
{
DBG("notifier %p name %s", notifier, notifier->name);
GSList *list;
for (list = notifier_list; list; list = list->next) {
- struct connman_notifier *notifier = list->data;
+ const struct connman_notifier *notifier = list->data;
if (notifier->default_changed)
notifier->default_changed(service);
GSList *list;
for (list = notifier_list; list; list = list->next) {
- struct connman_notifier *notifier = list->data;
+ const struct connman_notifier *notifier = list->data;
if (notifier->service_add)
notifier->service_add(service, name);
}
for (list = notifier_list; list; list = list->next) {
- struct connman_notifier *notifier = list->data;
+ const struct connman_notifier *notifier = list->data;
if (notifier->service_remove)
notifier->service_remove(service);
GSList *list;
for (list = notifier_list; list; list = list->next) {
- struct connman_notifier *notifier = list->data;
+ const struct connman_notifier *notifier = list->data;
if (notifier->proxy_changed)
notifier->proxy_changed(service);
state_changed();
for (list = notifier_list; list; list = list->next) {
- struct connman_notifier *notifier = list->data;
+ const struct connman_notifier *notifier = list->data;
if (notifier->offline_mode)
notifier->offline_mode(enabled);
DBG("idle %d", idle);
for (list = notifier_list; list; list = list->next) {
- struct connman_notifier *notifier = list->data;
+ const struct connman_notifier *notifier = list->data;
if (notifier->idle_state)
notifier->idle_state(idle);
bool found;
for (list = notifier_list; list; list = list->next) {
- struct connman_notifier *notifier = list->data;
+ const struct connman_notifier *notifier = list->data;
if (notifier->service_state_changed)
notifier->service_state_changed(service, state);
GSList *list;
for (list = notifier_list; list; list = list->next) {
- struct connman_notifier *notifier = list->data;
+ const struct connman_notifier *notifier = list->data;
if (notifier->ipconfig_changed)
notifier->ipconfig_changed(service, ipconfig);
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#define NTP_PRECISION_US -19
#define NTP_PRECISION_NS -29
-static guint channel_watch = 0;
-static struct timespec mtx_time;
-static int transmit_fd = 0;
-
-static char *timeserver = NULL;
-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, struct sockaddr *server, uint32_t timeout);
-
-static void next_server(void)
+struct ntp_data {
+ char *timeserver;
+ struct sockaddr_in6 timeserver_addr;
+ struct timespec mtx_time;
+ int transmit_fd;
+ gint timeout_id;
+ guint retries;
+ guint channel_watch;
+ gint poll_id;
+ uint32_t timeout;
+ __connman_ntp_cb_t cb;
+ void *user_data;
+};
+
+static struct ntp_data *ntp_data;
+
+static void free_ntp_data(struct ntp_data *nd)
{
- if (timeserver) {
- g_free(timeserver);
- timeserver = NULL;
- }
-
- __connman_timeserver_sync_next();
+ if (nd->poll_id)
+ g_source_remove(nd->poll_id);
+ if (nd->timeout_id)
+ g_source_remove(nd->timeout_id);
+ if (nd->channel_watch)
+ g_source_remove(nd->channel_watch);
+ if (nd->timeserver)
+ g_free(nd->timeserver);
+ g_free(nd);
}
+static void send_packet(struct ntp_data *nd, struct sockaddr *server,
+ uint32_t timeout);
+
static gboolean send_timeout(gpointer user_data)
{
- uint32_t timeout = GPOINTER_TO_UINT(user_data);
+ struct ntp_data *nd = user_data;
- DBG("send timeout %u (retries %d)", timeout, retries);
+ DBG("send timeout %u (retries %d)", nd->timeout, nd->retries);
- if (retries++ == NTP_SEND_RETRIES)
- next_server();
+ if (nd->retries++ == NTP_SEND_RETRIES)
+ nd->cb(false, nd->user_data);
else
- send_packet(transmit_fd, (struct sockaddr *)×erver_addr, timeout << 1);
+ send_packet(nd, (struct sockaddr *)&nd->timeserver_addr,
+ nd->timeout << 1);
return FALSE;
}
-static void send_packet(int fd, struct sockaddr *server, uint32_t timeout)
+static void send_packet(struct ntp_data *nd, struct sockaddr *server,
+ uint32_t timeout)
{
struct ntp_msg msg;
struct timeval transmit_timeval;
if (server->sa_family == AF_INET) {
size = sizeof(struct sockaddr_in);
- addr = (void *)&(((struct sockaddr_in *)×erver_addr)->sin_addr);
+ addr = (void *)&(((struct sockaddr_in *)&nd->timeserver_addr)->sin_addr);
} else if (server->sa_family == AF_INET6) {
size = sizeof(struct sockaddr_in6);
- addr = (void *)×erver_addr.sin6_addr;
+ addr = (void *)&nd->timeserver_addr.sin6_addr;
} else {
- connman_error("Family is neither ipv4 nor ipv6");
+ DBG("Family is neither ipv4 nor ipv6");
+ nd->cb(false, nd->user_data);
return;
}
gettimeofday(&transmit_timeval, NULL);
- clock_gettime(CLOCK_MONOTONIC, &mtx_time);
+ clock_gettime(CLOCK_MONOTONIC, &nd->mtx_time);
msg.xmttime.seconds = htonl(transmit_timeval.tv_sec + OFFSET_1900_1970);
msg.xmttime.fraction = htonl(transmit_timeval.tv_usec * 1000);
- len = sendto(fd, &msg, sizeof(msg), MSG_DONTWAIT,
+ len = sendto(nd->transmit_fd, &msg, sizeof(msg), MSG_DONTWAIT,
server, size);
-
- if (len < 0) {
- connman_error("Time request for server %s failed (%d/%s)",
- inet_ntop(server->sa_family, addr, ipaddrstring, sizeof(ipaddrstring)),
- errno, strerror(errno));
-
- if (errno == ENETUNREACH)
- __connman_timeserver_sync_next();
-
- return;
- }
-
- if (len != sizeof(msg)) {
- connman_error("Broken time request for server %s",
+ if (len < 0 || len != sizeof(msg)) {
+ DBG("Time request for server %s failed",
inet_ntop(server->sa_family, addr, ipaddrstring, sizeof(ipaddrstring)));
+ nd->cb(false, nd->user_data);
return;
}
* trying another server.
*/
- timeout_id = g_timeout_add_seconds(timeout, send_timeout,
- GUINT_TO_POINTER(timeout));
+ nd->timeout = timeout;
+ nd->timeout_id = g_timeout_add_seconds(timeout, send_timeout, nd);
}
static gboolean next_poll(gpointer user_data)
{
- poll_id = 0;
+ struct ntp_data *nd = user_data;
+ nd->poll_id = 0;
- if (!timeserver || transmit_fd == 0)
+ if (!nd->timeserver || nd->transmit_fd == 0)
return FALSE;
- send_packet(transmit_fd, (struct sockaddr *)×erver_addr, NTP_SEND_TIMEOUT);
+ send_packet(nd, (struct sockaddr *)&nd->timeserver_addr, NTP_SEND_TIMEOUT);
return FALSE;
}
-static void reset_timeout(void)
+static void reset_timeout(struct ntp_data *nd)
{
- if (timeout_id > 0) {
- g_source_remove(timeout_id);
- timeout_id = 0;
+ if (nd->timeout_id > 0) {
+ g_source_remove(nd->timeout_id);
+ nd->timeout_id = 0;
}
- retries = 0;
+ nd->retries = 0;
}
-static void decode_msg(void *base, size_t len, struct timeval *tv,
- struct timespec *mrx_time)
+static void decode_msg(struct ntp_data *nd, void *base, size_t len,
+ struct timeval *tv, struct timespec *mrx_time)
{
struct ntp_msg *msg = base;
double m_delta, org, rec, xmt, dst;
uint32_t code = ntohl(msg->refid);
connman_info("Skipping server %s KoD code %c%c%c%c",
- timeserver, code >> 24, code >> 16 & 0xff,
+ nd->timeserver, code >> 24, code >> 16 & 0xff,
code >> 8 & 0xff, code & 0xff);
- next_server();
+ nd->cb(false, nd->user_data);
return;
}
if (NTP_FLAGS_LI_DECODE(msg->flags) == NTP_FLAG_LI_NOTINSYNC) {
DBG("ignoring unsynchronized peer");
+ nd->cb(false, nd->user_data);
return;
}
NTP_FLAG_VN_VER4, NTP_FLAGS_VN_DECODE(msg->flags));
} else {
DBG("unsupported version %d", NTP_FLAGS_VN_DECODE(msg->flags));
+ nd->cb(false, nd->user_data);
return;
}
}
if (NTP_FLAGS_MD_DECODE(msg->flags) != NTP_FLAG_MD_SERVER) {
DBG("unsupported mode %d", NTP_FLAGS_MD_DECODE(msg->flags));
+ nd->cb(false, nd->user_data);
return;
}
- m_delta = mrx_time->tv_sec - mtx_time.tv_sec +
- 1.0e-9 * (mrx_time->tv_nsec - mtx_time.tv_nsec);
+ m_delta = mrx_time->tv_sec - nd->mtx_time.tv_sec +
+ 1.0e-9 * (mrx_time->tv_nsec - nd->mtx_time.tv_nsec);
org = tv->tv_sec + (1.0e-6 * tv->tv_usec) - m_delta + OFFSET_1900_1970;
rec = ntohl(msg->rectime.seconds) +
/* Remove the timeout, as timeserver has responded */
- reset_timeout();
+ reset_timeout(nd);
/*
* Now poll the server every transmit_delay seconds
* for time correction.
*/
- if (poll_id > 0)
- g_source_remove(poll_id);
+ if (nd->poll_id > 0)
+ g_source_remove(nd->poll_id);
- DBG("Timeserver %s, next sync in %d seconds", timeserver, transmit_delay);
+ DBG("Timeserver %s, next sync in %d seconds", nd->timeserver,
+ transmit_delay);
- poll_id = g_timeout_add_seconds(transmit_delay, next_poll, NULL);
+ nd->poll_id = g_timeout_add_seconds(transmit_delay, next_poll, nd);
if (offset < STEPTIME_MIN_OFFSET && offset > -STEPTIME_MIN_OFFSET) {
tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_OFFSET | ADJ_TIMECONST | ADJ_MAXERROR | ADJ_ESTERROR;
tmx.status |= STA_DEL;
if (adjtimex(&tmx) < 0) {
- connman_error("Failed to adjust time");
+ connman_error("Failed to adjust time: %s (%d)", strerror(errno), errno);
+ nd->cb(false, nd->user_data);
return;
}
DBG("interval/delta/delay/drift %fs/%+.3fs/%.3fs/%+ldppm",
LOGTOD(msg->poll), offset, delay, tmx.freq / 65536);
+
+ nd->cb(true, nd->user_data);
}
static gboolean received_data(GIOChannel *channel, GIOCondition condition,
gpointer user_data)
{
+ struct ntp_data *nd = user_data;
unsigned char buf[128];
struct sockaddr_in6 sender_addr;
struct msghdr msg;
if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
connman_error("Problem with timer server channel");
- channel_watch = 0;
+ nd->channel_watch = 0;
return FALSE;
}
if (sender_addr.sin6_family == AF_INET) {
size = 4;
- addr_ptr = &((struct sockaddr_in *)×erver_addr)->sin_addr;
+ addr_ptr = &((struct sockaddr_in *)&nd->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 *)×erver_addr)->sin6_addr;
+ addr_ptr = &((struct sockaddr_in6 *)&nd->timeserver_addr)->sin6_addr;
src_ptr = &((struct sockaddr_in6 *)&sender_addr)->sin6_addr;
} else {
connman_error("Not a valid family type");
}
}
- decode_msg(iov.iov_base, iov.iov_len, tv, &mrx_time);
+ decode_msg(nd, iov.iov_base, iov.iov_len, tv, &mrx_time);
return TRUE;
}
-static void start_ntp(char *server)
+static void start_ntp(struct ntp_data *nd)
{
GIOChannel *channel;
struct addrinfo hint;
int tos = IPTOS_LOWDELAY, timestamp = 1;
int ret;
- if (!server)
- return;
-
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);
+ ret = getaddrinfo(nd->timeserver, NULL, &hint, &info);
if (ret) {
connman_error("cannot get server info");
family = info->ai_family;
- memcpy(×erver_addr, info->ai_addr, info->ai_addrlen);
+ memcpy(&ntp_data->timeserver_addr, info->ai_addr, info->ai_addrlen);
freeaddrinfo(info);
memset(&in6addr, 0, sizeof(in6addr));
if (family == AF_INET) {
- ((struct sockaddr_in *)×erver_addr)->sin_port = htons(123);
+ ((struct sockaddr_in *)&ntp_data->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);
+ ntp_data->timeserver_addr.sin6_port = htons(123);
in6addr.sin6_family = family;
addr = (struct sockaddr *)&in6addr;
size = sizeof(in6addr);
return;
}
- DBG("server %s family %d", server, family);
+ DBG("server %s family %d", nd->timeserver, family);
- if (channel_watch > 0)
+ if (nd->channel_watch > 0)
goto send;
- transmit_fd = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ nd->transmit_fd = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (transmit_fd <= 0) {
- connman_error("Failed to open time server socket");
- return;
+ if (nd->transmit_fd <= 0) {
+ if (errno != EAFNOSUPPORT)
+ connman_error("Failed to open time server socket");
}
- if (bind(transmit_fd, (struct sockaddr *) addr, size) < 0) {
+ if (bind(nd->transmit_fd, (struct sockaddr *) addr, size) < 0) {
connman_error("Failed to bind time server socket");
- close(transmit_fd);
- return;
+ goto err;
}
if (family == AF_INET) {
- if (setsockopt(transmit_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) {
+ if (setsockopt(nd->transmit_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) {
connman_error("Failed to set type of service option");
- close(transmit_fd);
- return;
+ goto err;
}
}
- if (setsockopt(transmit_fd, SOL_SOCKET, SO_TIMESTAMP, ×tamp,
+ if (setsockopt(nd->transmit_fd, SOL_SOCKET, SO_TIMESTAMP, ×tamp,
sizeof(timestamp)) < 0) {
connman_error("Failed to enable timestamp support");
- close(transmit_fd);
- return;
+ goto err;
}
- channel = g_io_channel_unix_new(transmit_fd);
- if (!channel) {
- close(transmit_fd);
- return;
- }
+ channel = g_io_channel_unix_new(nd->transmit_fd);
+ if (!channel)
+ goto err;
g_io_channel_set_encoding(channel, NULL, NULL);
g_io_channel_set_buffered(channel, FALSE);
g_io_channel_set_close_on_unref(channel, TRUE);
- channel_watch = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT,
+ nd->channel_watch = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT,
G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- received_data, NULL, NULL);
+ received_data, nd, NULL);
g_io_channel_unref(channel);
send:
- send_packet(transmit_fd, (struct sockaddr*)×erver_addr, NTP_SEND_TIMEOUT);
+ send_packet(nd, (struct sockaddr*)&ntp_data->timeserver_addr,
+ NTP_SEND_TIMEOUT);
+ return;
+
+err:
+ if (nd->transmit_fd > 0)
+ close(nd->transmit_fd);
+
+ nd->cb(false, nd->user_data);
}
-int __connman_ntp_start(char *server)
+int __connman_ntp_start(char *server, __connman_ntp_cb_t callback,
+ void *user_data)
{
- DBG("%s", server);
-
if (!server)
return -EINVAL;
- if (timeserver)
- g_free(timeserver);
+ if (ntp_data) {
+ connman_warn("ntp_data is not NULL (timerserver %s)",
+ ntp_data->timeserver);
+ free_ntp_data(ntp_data);
+ }
+
+ ntp_data = g_new0(struct ntp_data, 1);
- timeserver = g_strdup(server);
+ ntp_data->timeserver = g_strdup(server);
+ ntp_data->cb = callback;
+ ntp_data->user_data = user_data;
- start_ntp(timeserver);
+ start_ntp(ntp_data);
return 0;
}
void __connman_ntp_stop()
{
- DBG("");
-
- if (poll_id > 0) {
- g_source_remove(poll_id);
- poll_id = 0;
- }
-
- reset_timeout();
-
- if (channel_watch > 0) {
- g_source_remove(channel_watch);
- channel_watch = 0;
- transmit_fd = 0;
- }
-
- if (timeserver) {
- g_free(timeserver);
- timeserver = NULL;
+ if (ntp_data) {
+ free_ntp_data(ntp_data);
+ ntp_data = NULL;
}
}
peer->dhcp_server = NULL;
if (peer->ip_pool)
- __connman_ippool_unref(peer->ip_pool);
+ __connman_ippool_free(peer->ip_pool);
peer->ip_pool = NULL;
peer->lease_ip = 0;
}
{
int err = -ENOTSUP;
+ if (is_connected(peer))
+ return -EISCONN;
+
if (peer_driver->connect)
err = peer_driver->connect(peer,
CONNMAN_PEER_WPS_UNKNOWN, NULL);
return peer->path;
}
+static void disconnect_peer_hash_table(gpointer key,
+ gpointer value, gpointer user_data)
+{
+ struct connman_peer *peer = value;
+ peer_disconnect(peer);
+}
+
+void __connman_peer_disconnect_all(void)
+{
+ g_hash_table_foreach(peers_table, disconnect_peer_hash_table, NULL);
+}
+
int __connman_peer_init(void)
{
DBG("");
vpn_index = __connman_connection_get_vpn_index(service_index);
DBG("service %p %s state %d index %d/%d", service,
- __connman_service_get_ident(service),
+ connman_service_get_identifier(service),
state, service_index, vpn_index);
if (vpn_index < 0)
DBG("disconnect %p index %d", provider, vpn_index);
connman_provider_disconnect(provider);
-
- return;
}
-static struct connman_notifier provider_notifier = {
+static const struct connman_notifier provider_notifier = {
.name = "provider",
.offline_mode = provider_offline_mode,
.service_state_changed = provider_service_changed,
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
g_free(entry);
}
+int __connman_resolver_set_mdns(int index, bool enabled)
+{
+ if (!dnsproxy_enabled)
+ return -ENOTSUP;
+
+ return __connman_dnsproxy_set_mdns(index, enabled);
+}
+
int __connman_resolver_init(gboolean dnsproxy)
{
int i;
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
if (ether_blacklisted(name)) {
interface->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN;
interface->device_type = CONNMAN_DEVICE_TYPE_UNKNOWN;
+ goto out;
} else {
interface->service_type = CONNMAN_SERVICE_TYPE_ETHERNET;
interface->device_type = CONNMAN_DEVICE_TYPE_ETHERNET;
if (!NLMSG_OK(hdr, len))
break;
- DBG("%s len %d type %d flags 0x%04x seq %d pid %d",
+ DBG("%s len %u type %u flags 0x%04x seq %u pid %u",
type2string(hdr->nlmsg_type),
hdr->nlmsg_len, hdr->nlmsg_type,
hdr->nlmsg_flags, hdr->nlmsg_seq,
static GList *service_list = NULL;
static GHashTable *service_hash = NULL;
static GSList *counter_list = NULL;
-static unsigned int autoconnect_timeout = 0;
-static unsigned int vpn_autoconnect_timeout = 0;
+static unsigned int autoconnect_id = 0;
+static unsigned int vpn_autoconnect_id = 0;
static struct connman_service *current_default = NULL;
static bool services_dirty = false;
char **nameservers_auto;
int nameservers_timeout;
char **domains;
+ bool mdns;
+ bool mdns_config;
char *hostname;
char *domainname;
char **timeservers;
char **excludes;
char *pac;
bool wps;
- int online_check_count;
+ bool wps_advertizing;
+ guint online_timeout;
+ int online_check_interval_ipv4;
+ int online_check_interval_ipv6;
bool do_split_routing;
bool new_service;
bool hidden_service;
service->pac = str;
}
+ service->mdns_config = g_key_file_get_boolean(keyfile,
+ service->identifier, "mDNS", NULL);
+
service->hidden_service = g_key_file_get_boolean(keyfile,
service->identifier, "Hidden", NULL);
g_key_file_remove_key(keyfile, service->identifier,
"Proxy.URL", NULL);
+ if (service->mdns_config)
+ g_key_file_set_boolean(keyfile, service->identifier,
+ "mDNS", TRUE);
+ else
+ g_key_file_remove_key(keyfile, service->identifier,
+ "mDNS", NULL);
+
if (service->hidden_service)
g_key_file_set_boolean(keyfile, service->identifier, "Hidden",
TRUE);
enum connman_ipconfig_type type)
{
if (is_connected(service->state) &&
- service == __connman_service_get_default()) {
+ service == connman_service_get_default()) {
nameserver_remove_all(service, type);
nameserver_add_all(service, type);
g_timer_reset(service->stats_roaming.timer);
}
-struct connman_service *__connman_service_get_default(void)
+struct connman_service *connman_service_get_default(void)
{
struct connman_service *service;
if (index < 0)
return false;
- service = __connman_service_get_default();
+ service = connman_service_get_default();
return __connman_service_get_index(service) == index;
}
static void default_changed(void)
{
- struct connman_service *service = __connman_service_get_default();
+ struct connman_service *service = connman_service_get_default();
if (service == current_default)
return;
connman_setting_get_bool("AllowHostnameUpdates"))
__connman_utsname_set_hostname(service->hostname);
- if (service->domainname)
+ if (service->domainname &&
+ connman_setting_get_bool("AllowDomainnameUpdates"))
__connman_utsname_set_domainname(service->domainname);
}
case CONNMAN_SERVICE_SECURITY_8021X:
break;
}
+
+ if (service->wps_advertizing) {
+ str = "wps_advertising";
+ dbus_message_iter_append_basic(iter,
+ DBUS_TYPE_STRING, &str);
+ }
}
}
+static void security_changed(struct connman_service *service)
+{
+ if (!service->path)
+ return;
+
+ if (!allow_property_changed(service))
+ return;
+
+ connman_dbus_property_changed_array(service->path,
+ CONNMAN_SERVICE_INTERFACE, "Security",
+ DBUS_TYPE_STRING, append_security, service);
+}
+
static void append_ethernet(DBusMessageIter *iter, void *user_data)
{
struct connman_service *service = user_data;
proxy_changed(service);
}
+static void mdns_changed(struct connman_service *service)
+{
+ dbus_bool_t mdns = service->mdns;
+
+ if (!allow_property_changed(service))
+ return;
+
+ connman_dbus_property_changed_basic(service->path,
+ CONNMAN_SERVICE_INTERFACE, "mDNS", DBUS_TYPE_BOOLEAN,
+ &mdns);
+}
+
+static void mdns_configuration_changed(struct connman_service *service)
+{
+ dbus_bool_t mdns_config = service->mdns_config;
+
+ if (!allow_property_changed(service))
+ return;
+
+ connman_dbus_property_changed_basic(service->path,
+ CONNMAN_SERVICE_INTERFACE, "mDNS.Configuration",
+ DBUS_TYPE_BOOLEAN, &mdns_config);
+}
+
+static int set_mdns(struct connman_service *service,
+ bool enabled)
+{
+ int result;
+
+ result = __connman_resolver_set_mdns(
+ __connman_service_get_index(service), enabled);
+
+ if (result == 0) {
+ if (service->mdns != enabled) {
+ service->mdns = enabled;
+ mdns_changed(service);
+ }
+ }
+
+ return result;
+}
+
static void timeservers_configuration_changed(struct connman_service *service)
{
if (!allow_property_changed(service))
counter_list = g_slist_remove(counter_list, counter);
}
-int __connman_service_iterate_services(service_iterate_cb cb, void *user_data)
+int connman_service_iterate_services(connman_service_iterate_cb cb,
+ void *user_data)
{
GList *list;
+ int ret = 0;
- for (list = service_list; list; list = list->next) {
- struct connman_service *service = list->data;
-
- cb(service, user_data);
- }
+ for (list = service_list; list && ret == 0; list = list->next)
+ ret = cb((struct connman_service *)list->data, user_data);
- return 0;
+ return ret;
}
static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
connman_dbus_dict_append_dict(dict, "Proxy.Configuration",
append_proxyconfig, service);
+ val = service->mdns;
+ connman_dbus_dict_append_basic(dict, "mDNS", DBUS_TYPE_BOOLEAN,
+ &val);
+
+ val = service->mdns_config;
+ connman_dbus_dict_append_basic(dict, "mDNS.Configuration",
+ DBUS_TYPE_BOOLEAN, &val);
+
connman_dbus_dict_append_dict(dict, "Provider",
append_provider, service);
+
+ if (service->network)
+ connman_network_append_acddbus(dict, service->network);
}
static void append_struct_service(DBusMessageIter *iter,
return;
g_free(service->hostname);
- service->hostname = g_strdup(hostname);
+ service->hostname = NULL;
+
+ if (hostname && g_str_is_ascii(hostname))
+ service->hostname = g_strdup(hostname);
}
const char *__connman_service_get_hostname(struct connman_service *service)
return;
g_free(service->domainname);
- service->domainname = g_strdup(domainname);
+ service->domainname = NULL;
+
+ if (domainname && g_str_is_ascii(domainname))
+ service->domainname = g_strdup(domainname);
domain_changed(service);
}
return service->domainname;
}
+const char *connman_service_get_dbuspath(struct connman_service *service)
+{
+ if (!service)
+ return NULL;
+
+ return service->path;
+}
+
char **connman_service_get_nameservers(struct connman_service *service)
{
if (!service)
*new_state = service->state_ipv6;
settings_changed(service, new_ipconfig);
- address_updated(service, new_method);
+ address_updated(service, type);
__connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO);
}
return err;
}
+/*
+ * We set the timeout to 1 sec so that we have a chance to get
+ * necessary IPv6 router advertisement messages that might have
+ * DNS data etc.
+ */
+#define ONLINE_CHECK_INITIAL_INTERVAL 1
+#define ONLINE_CHECK_MAX_INTERVAL 12
+
+void __connman_service_wispr_start(struct connman_service *service,
+ enum connman_ipconfig_type type)
+{
+ DBG("service %p type %s", service, __connman_ipconfig_type2string(type));
+
+ if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+ service->online_check_interval_ipv4 =
+ ONLINE_CHECK_INITIAL_INTERVAL;
+ else
+ service->online_check_interval_ipv6 =
+ ONLINE_CHECK_INITIAL_INTERVAL;
+
+ __connman_wispr_start(service, type);
+}
+
static DBusMessage *set_property(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
if (__connman_service_is_connected_state(service,
CONNMAN_IPCONFIG_TYPE_IPV4))
- __connman_wispr_start(service,
- CONNMAN_IPCONFIG_TYPE_IPV4);
+ __connman_service_wispr_start(service, CONNMAN_IPCONFIG_TYPE_IPV4);
if (__connman_service_is_connected_state(service,
CONNMAN_IPCONFIG_TYPE_IPV6))
- __connman_wispr_start(service,
- CONNMAN_IPCONFIG_TYPE_IPV6);
+ __connman_service_wispr_start(service, CONNMAN_IPCONFIG_TYPE_IPV6);
service_save(service);
} else if (g_str_equal(name, "Timeservers.Configuration")) {
char **timeservers = g_strsplit_set(str->str, " ", 0);
timeservers = remove_empty_strings(timeservers);
service->timeservers_config = timeservers;
- } else
- service->timeservers = NULL;
+ }
g_string_free(str, TRUE);
service_save(service);
timeservers_configuration_changed(service);
- if (service == __connman_service_get_default())
+ if (service == connman_service_get_default())
__connman_timeserver_sync(service);
} else if (g_str_equal(name, "Domains.Configuration")) {
__connman_notifier_proxy_changed(service);
service_save(service);
+ } else if (g_str_equal(name, "mDNS.Configuration")) {
+ dbus_bool_t val;
+
+ if (service->immutable)
+ return __connman_error_not_supported(msg);
+
+ if (type != DBUS_TYPE_BOOLEAN)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_get_basic(&value, &val);
+ service->mdns_config = val;
+
+ mdns_configuration_changed(service);
+
+ set_mdns(service, service->mdns_config);
+
+ service_save(service);
} else if (g_str_equal(name, "IPv4.Configuration") ||
g_str_equal(name, "IPv6.Configuration")) {
if (!service->autoconnect)
return true;
- if (service->roaming)
+ if (service->roaming &&
+ !connman_setting_get_bool("AutoConnectRoamingServices"))
return true;
if (service->ignore)
bool autoconnecting = false;
GList *preferred_tech;
- autoconnect_timeout = 0;
+ autoconnect_id = 0;
DBG("");
{
DBG("");
- if (autoconnect_timeout != 0)
+ if (autoconnect_id != 0)
return;
if (!__connman_session_policy_autoconnect(reason))
return;
- autoconnect_timeout = g_idle_add(run_auto_connect,
+ autoconnect_id = g_idle_add(run_auto_connect,
GUINT_TO_POINTER(reason));
}
GList *list;
bool need_split = false;
- vpn_autoconnect_timeout = 0;
+ vpn_autoconnect_id = 0;
for (list = service_list; list; list = list->next) {
struct connman_service *service = list->data;
static void vpn_auto_connect(void)
{
- if (vpn_autoconnect_timeout)
+ if (vpn_autoconnect_id)
return;
- vpn_autoconnect_timeout =
+ vpn_autoconnect_id =
g_idle_add(run_vpn_auto_connect, NULL);
}
}
service->provider_pending = msg;
- return;
}
static void check_pending_msg(struct connman_service *service)
err = __connman_service_connect(service,
CONNMAN_SERVICE_CONNECT_REASON_USER);
- if (err == -EINPROGRESS)
- return NULL;
-
- if (service->pending) {
- dbus_message_unref(service->pending);
- service->pending = NULL;
- }
-
- if (err < 0)
- return __connman_error_failed(msg, -err);
+ if (err != -EINPROGRESS)
+ reply_pending(service, -err);
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+ return NULL;
}
static DBusMessage *disconnect_service(DBusConnection *conn,
{
struct connman_service *def_service;
- def_service = __connman_service_get_default();
+ def_service = connman_service_get_default();
if (!def_service)
return;
downgrade_state(downgrade_service);
}
+static struct _services_notify {
+ int id;
+ GHashTable *add;
+ GHashTable *remove;
+} *services_notify;
+
+
+static void service_append_added_foreach(gpointer data, gpointer user_data)
+{
+ struct connman_service *service = data;
+ DBusMessageIter *iter = user_data;
+
+ if (!service || !service->path) {
+ DBG("service %p or path is NULL", service);
+ return;
+ }
+
+ if (g_hash_table_lookup(services_notify->add, service->path)) {
+ DBG("new %s", service->path);
+
+ append_struct(service, iter);
+ g_hash_table_remove(services_notify->add, service->path);
+ } else {
+ DBG("changed %s", service->path);
+
+ append_struct_service(iter, NULL, service);
+ }
+}
+
+static void service_append_ordered(DBusMessageIter *iter, void *user_data)
+{
+ g_list_foreach(service_list, service_append_added_foreach, iter);
+}
+
+static void append_removed(gpointer key, gpointer value, gpointer user_data)
+{
+ char *objpath = key;
+ DBusMessageIter *iter = user_data;
+
+ DBG("removed %s", objpath);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &objpath);
+}
+
+static void service_append_removed(DBusMessageIter *iter, void *user_data)
+{
+ g_hash_table_foreach(services_notify->remove, append_removed, iter);
+}
+
+static gboolean service_send_changed(gpointer data)
+{
+ DBusMessage *signal;
+
+ DBG("");
+
+ services_notify->id = 0;
+
+ signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
+ CONNMAN_MANAGER_INTERFACE, "ServicesChanged");
+ if (!signal)
+ return FALSE;
+
+ __connman_dbus_append_objpath_dict_array(signal,
+ service_append_ordered, NULL);
+ __connman_dbus_append_objpath_array(signal,
+ service_append_removed, NULL);
+
+ dbus_connection_send(connection, signal, NULL);
+ dbus_message_unref(signal);
+
+ g_hash_table_remove_all(services_notify->remove);
+ g_hash_table_remove_all(services_notify->add);
+
+ return FALSE;
+}
+
+static void service_schedule_changed(void)
+{
+ if (services_notify->id != 0)
+ return;
+
+ services_notify->id = g_timeout_add(100, service_send_changed, NULL);
+}
+
static DBusMessage *move_service(DBusConnection *conn,
DBusMessage *msg, void *user_data,
bool before)
__connman_connection_update_gateway();
+ service_schedule_changed();
+
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
-static struct _services_notify {
- int id;
- GHashTable *add;
- GHashTable *remove;
-} *services_notify;
-
-static void service_append_added_foreach(gpointer data, gpointer user_data)
-{
- struct connman_service *service = data;
- DBusMessageIter *iter = user_data;
-
- if (!service || !service->path) {
- DBG("service %p or path is NULL", service);
- return;
- }
-
- if (g_hash_table_lookup(services_notify->add, service->path)) {
- DBG("new %s", service->path);
-
- append_struct(service, iter);
- g_hash_table_remove(services_notify->add, service->path);
- } else {
- DBG("changed %s", service->path);
-
- append_struct_service(iter, NULL, service);
- }
-}
-
-static void service_append_ordered(DBusMessageIter *iter, void *user_data)
-{
- g_list_foreach(service_list, service_append_added_foreach, iter);
-}
-
-static void append_removed(gpointer key, gpointer value, gpointer user_data)
-{
- char *objpath = key;
- DBusMessageIter *iter = user_data;
-
- DBG("removed %s", objpath);
- dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &objpath);
-}
-
-static void service_append_removed(DBusMessageIter *iter, void *user_data)
-{
- g_hash_table_foreach(services_notify->remove, append_removed, iter);
-}
-
-static gboolean service_send_changed(gpointer data)
-{
- DBusMessage *signal;
-
- DBG("");
-
- services_notify->id = 0;
-
- signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
- CONNMAN_MANAGER_INTERFACE, "ServicesChanged");
- if (!signal)
- return FALSE;
-
- __connman_dbus_append_objpath_dict_array(signal,
- service_append_ordered, NULL);
- __connman_dbus_append_objpath_array(signal,
- service_append_removed, NULL);
-
- dbus_connection_send(connection, signal, NULL);
- dbus_message_unref(signal);
-
- g_hash_table_remove_all(services_notify->remove);
- g_hash_table_remove_all(services_notify->add);
-
- return FALSE;
-}
-
-static void service_schedule_changed(void)
-{
- if (services_notify->id != 0)
- return;
-
- services_notify->id = g_timeout_add(100, service_send_changed, NULL);
-}
-
static void service_schedule_added(struct connman_service *service)
{
DBG("service %p", service);
service->provider = NULL;
service->wps = false;
+ service->wps_advertizing = false;
}
/**
service->favorite = favorite;
- if (!delay_ordering)
- __connman_service_get_order(service);
-
favorite_changed(service);
if (!delay_ordering) {
searchdomain_add_all(service);
}
+int __connman_service_set_mdns(struct connman_service *service,
+ bool enabled)
+{
+ service->mdns_config = enabled;
+
+ return set_mdns(service, enabled);
+}
+
static void report_error_cb(void *user_context, bool retry,
void *user_data)
{
if (g_strcmp0(error,
"net.connman.Agent.Error.Canceled") == 0) {
- err = -EINVAL;
+ err = -ECONNABORTED;
if (service->hidden)
__connman_service_return_error(service,
if (old_state == new_state)
return -EALREADY;
- def_service = __connman_service_get_default();
+ def_service = connman_service_get_default();
if (new_state == CONNMAN_SERVICE_STATE_ONLINE) {
result = service_update_preferred_order(def_service,
default_changed();
- def_service = __connman_service_get_default();
+ def_service = connman_service_get_default();
service_update_preferred_order(def_service, service, new_state);
reply_pending(service, ECONNABORTED);
- def_service = __connman_service_get_default();
-
- if (!__connman_notifier_is_connected() &&
- def_service &&
- def_service->provider)
- connman_provider_disconnect(def_service->provider);
-
default_changed();
__connman_wispr_stop(service);
break;
case CONNMAN_SERVICE_STATE_FAILURE:
-
- if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER &&
+ if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER) {
connman_agent_report_error(service, service->path,
- error2string(service->error),
- report_error_cb,
- get_dbus_sender(service),
- NULL) == -EINPROGRESS)
- return 0;
+ error2string(service->error),
+ report_error_cb,
+ get_dbus_sender(service),
+ NULL);
+ }
service_complete(service);
-
break;
}
return;
done:
- __connman_wispr_start(service, CONNMAN_IPCONFIG_TYPE_IPV4);
+ __connman_service_wispr_start(service, CONNMAN_IPCONFIG_TYPE_IPV4);
}
/*
connected_networks_count, original_rp_filter);
}
-static gboolean redo_wispr(gpointer user_data)
+static void redo_wispr(struct connman_service *service,
+ enum connman_ipconfig_type type)
+{
+ service->online_timeout = 0;
+ connman_service_unref(service);
+
+ DBG("Retrying %s WISPr for %p %s",
+ __connman_ipconfig_type2string(type),
+ service, service->name);
+
+ __connman_wispr_start(service, type);
+}
+
+static gboolean redo_wispr_ipv4(gpointer user_data)
{
struct connman_service *service = user_data;
- int refcount = service->refcount - 1;
- connman_service_unref(service);
- if (refcount == 0) {
- DBG("Service %p already removed", service);
- return FALSE;
- }
+ redo_wispr(service, CONNMAN_IPCONFIG_TYPE_IPV4);
- __connman_wispr_start(service, CONNMAN_IPCONFIG_TYPE_IPV6);
+ return FALSE;
+}
+
+static gboolean redo_wispr_ipv6(gpointer user_data)
+{
+ struct connman_service *service = user_data;
+
+ redo_wispr(service, CONNMAN_IPCONFIG_TYPE_IPV6);
return FALSE;
}
int __connman_service_online_check_failed(struct connman_service *service,
enum connman_ipconfig_type type)
{
- DBG("service %p type %d count %d", service, type,
- service->online_check_count);
+ GSourceFunc redo_func;
+ int *interval;
- /* currently we only retry IPv6 stuff */
- if (type == CONNMAN_IPCONFIG_TYPE_IPV4 ||
- service->online_check_count != 1) {
- connman_warn("Online check failed for %p %s", service,
- service->name);
- return 0;
+ if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+ interval = &service->online_check_interval_ipv4;
+ redo_func = redo_wispr_ipv4;
+ } else {
+ interval = &service->online_check_interval_ipv6;
+ redo_func = redo_wispr_ipv6;
}
- service->online_check_count = 0;
+ DBG("service %p type %s interval %d", service,
+ __connman_ipconfig_type2string(type), *interval);
- /*
- * We set the timeout to 1 sec so that we have a chance to get
- * necessary IPv6 router advertisement messages that might have
- * DNS data etc.
+ service->online_timeout = g_timeout_add_seconds(*interval * *interval,
+ redo_func, connman_service_ref(service));
+
+ /* Increment the interval for the next time, set a maximum timeout of
+ * ONLINE_CHECK_MAX_INTERVAL * ONLINE_CHECK_MAX_INTERVAL seconds.
*/
- g_timeout_add_seconds(1, redo_wispr, connman_service_ref(service));
+ if (*interval < ONLINE_CHECK_MAX_INTERVAL)
+ (*interval)++;
return EAGAIN;
}
+static void cancel_online_check(struct connman_service *service)
+{
+ if (service->online_timeout == 0)
+ return;
+
+ g_source_remove(service->online_timeout);
+ service->online_timeout = 0;
+ connman_service_unref(service);
+}
+
int __connman_service_ipconfig_indicate_state(struct connman_service *service,
enum connman_service_state new_state,
enum connman_ipconfig_type type)
if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
check_proxy_setup(service);
} else {
- service->online_check_count = 1;
- __connman_wispr_start(service, type);
+ __connman_service_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);
+ set_mdns(service, service->mdns_config);
break;
case CONNMAN_SERVICE_STATE_ONLINE:
break;
break;
}
- if (is_connected(old_state) && !is_connected(new_state))
+ if (is_connected(old_state) && !is_connected(new_state)) {
nameserver_remove_all(service, type);
+ cancel_online_check(service);
+ }
if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
service->state_ipv4 = new_state;
break;
case CONNMAN_SERVICE_SECURITY_8021X:
- if (!service->eap)
+ if (!service->eap) {
+ connman_warn("EAP type has not been found. "
+ "Most likely ConnMan is not able to "
+ "find a configuration for given "
+ "8021X network. "
+ "Check SSID or Name match with the "
+ "network name.");
return -EINVAL;
+ }
/*
* never request credentials if using EAP-TLS
return err;
}
- reply_pending(service, -err);
}
return err;
connman_agent_cancel(service);
- reply_pending(service, ECONNABORTED);
+ __connman_stats_service_unregister(service);
if (service->network) {
err = __connman_network_disconnect(service->network);
__connman_ipconfig_disable(service->ipconfig_ipv4);
__connman_ipconfig_disable(service->ipconfig_ipv6);
- __connman_stats_service_unregister(service);
-
return err;
}
struct connman_service *connman_service_lookup_from_identifier(const char* identifier)
{
- return lookup_by_identifier(identifier);
+ return identifier ? lookup_by_identifier(identifier) : NULL;
}
struct provision_user_data {
return NULL;
}
-struct connman_service *__connman_service_lookup_from_ident(const char *identifier)
-{
- return lookup_by_identifier(identifier);
-}
-
-const char *__connman_service_get_ident(struct connman_service *service)
+const char *connman_service_get_identifier(struct connman_service *service)
{
- return service->identifier;
+ return service ? service->identifier : NULL;
}
const char *__connman_service_get_path(struct connman_service *service)
return service->name;
}
-enum connman_service_state __connman_service_get_state(struct connman_service *service)
+enum connman_service_state connman_service_get_state(struct connman_service *service)
{
- return service->state;
-}
-
-unsigned int __connman_service_get_order(struct connman_service *service)
-{
- unsigned int order = 0;
-
- if (!service)
- return 0;
-
- service->order = 0;
-
- if (!service->favorite)
- return 0;
-
- if (service == service_list->data)
- order = 1;
-
- if (service->type == CONNMAN_SERVICE_TYPE_VPN &&
- !service->do_split_routing) {
- service->order = 10;
- order = 10;
- }
-
- DBG("service %p name %s order %d split %d", service, service->name,
- order, service->do_split_routing);
-
- return order;
+ return service ? service->state : CONNMAN_SERVICE_STATE_UNKNOWN;
}
static enum connman_service_type convert_network_type(struct connman_network *network)
return CONNMAN_SERVICE_SECURITY_UNKNOWN;
}
+static void update_wps_values(struct connman_service *service,
+ struct connman_network *network)
+{
+ bool wps = connman_network_get_bool(network, "WiFi.WPS");
+ bool wps_advertising = connman_network_get_bool(network,
+ "WiFi.WPSAdvertising");
+
+ if (service->wps != wps ||
+ service->wps_advertizing != wps_advertising) {
+ service->wps = wps;
+ service->wps_advertizing = wps_advertising;
+ security_changed(service);
+ }
+}
+
static void update_from_network(struct connman_service *service,
struct connman_network *network)
{
service->security = convert_wifi_security(str);
if (service->type == CONNMAN_SERVICE_TYPE_WIFI)
- service->wps = connman_network_get_bool(network, "WiFi.WPS");
+ update_wps_values(service, network);
if (service->strength > strength && service->network) {
connman_network_unref(service->network);
struct connman_device *device;
const char *ident, *group;
char *name;
- unsigned int *auto_connect_types;
+ unsigned int *auto_connect_types, *favorite_types;
int i, index;
DBG("network %p", network);
}
}
- switch (service->type) {
- case CONNMAN_SERVICE_TYPE_UNKNOWN:
- case CONNMAN_SERVICE_TYPE_SYSTEM:
- case CONNMAN_SERVICE_TYPE_BLUETOOTH:
- case CONNMAN_SERVICE_TYPE_GPS:
- case CONNMAN_SERVICE_TYPE_VPN:
- case CONNMAN_SERVICE_TYPE_GADGET:
- case CONNMAN_SERVICE_TYPE_WIFI:
- case CONNMAN_SERVICE_TYPE_CELLULAR:
- case CONNMAN_SERVICE_TYPE_P2P:
- break;
- case CONNMAN_SERVICE_TYPE_ETHERNET:
- service->favorite = true;
- break;
+ favorite_types = connman_setting_get_uint_list("DefaultFavoriteTechnologies");
+ service->favorite = false;
+ for (i = 0; favorite_types && favorite_types[i] != 0; i++) {
+ if (service->type == favorite_types[i]) {
+ service->favorite = true;
+ break;
+ }
}
service->state_ipv4 = service->state_ipv6 = CONNMAN_SERVICE_STATE_IDLE;
if (service->favorite) {
device = connman_network_get_device(service->network);
- if (device && !connman_device_get_scanning(device)) {
+ if (device && !connman_device_get_scanning(device,
+ CONNMAN_SERVICE_TYPE_UNKNOWN)) {
switch (service->type) {
case CONNMAN_SERVICE_TYPE_UNKNOWN:
}
if (service->type == CONNMAN_SERVICE_TYPE_WIFI)
- service->wps = connman_network_get_bool(network, "WiFi.WPS");
+ update_wps_values(service, network);
strength = connman_network_get_strength(service->network);
if (strength == service->strength)
return NULL;
service->type = CONNMAN_SERVICE_TYPE_VPN;
+ service->order = service->do_split_routing ? 0 : 10;
service->provider = connman_provider_ref(provider);
service->autoconnect = false;
service->favorite = true;
{
DBG("");
- if (vpn_autoconnect_timeout) {
- g_source_remove(vpn_autoconnect_timeout);
- vpn_autoconnect_timeout = 0;
+ if (vpn_autoconnect_id) {
+ g_source_remove(vpn_autoconnect_id);
+ vpn_autoconnect_id = 0;
}
- if (autoconnect_timeout != 0) {
- g_source_remove(autoconnect_timeout);
- autoconnect_timeout = 0;
+ if (autoconnect_id != 0) {
+ g_source_remove(autoconnect_id);
+ autoconnect_id = 0;
}
connman_agent_driver_unregister(&agent_driver);
#include <gdbus.h>
-#include <connman/session.h>
-
#include "connman.h"
static DBusConnection *connection;
struct firewall_context *fw;
uint32_t mark;
int index;
+ char *addr;
char *gateway;
+ unsigned char prefixlen;
bool policy_routing;
bool snat_enabled;
};
GSList *sessions;
int id;
int index;
+ char *addr;
struct firewall_context *fw;
};
return "";
}
-static struct fw_snat *fw_snat_lookup(int index)
+static struct fw_snat *fw_snat_lookup(int index, const char *addr)
{
struct fw_snat *fw_snat;
GSList *list;
for (list = fw_snat_list; list; list = list->next) {
fw_snat = list->data;
- if (fw_snat->index == index)
+ if (fw_snat->index == index) {
+ if (g_strcmp0(addr, fw_snat->addr) != 0)
+ continue;
return fw_snat;
+ }
}
return NULL;
}
fw_snat->fw = __connman_firewall_create();
fw_snat->index = index;
+ fw_snat->addr = g_strdup(addr);
fw_snat->id = __connman_firewall_enable_snat(fw_snat->fw,
index, ifname, addr);
return 0;
err:
__connman_firewall_destroy(fw_snat->fw);
+ g_free(fw_snat->addr);
g_free(fw_snat);
return err;
}
if (!session->gateway)
return;
- DBG("index %d routing table %d default gateway %s",
- session->index, session->mark, session->gateway);
+ DBG("index %d routing table %d default gateway %s/%u",
+ session->index, session->mark, session->gateway, session->prefixlen);
+
+ __connman_inet_del_subnet_from_table(session->mark,
+ session->index, session->gateway, session->prefixlen);
__connman_inet_del_default_from_table(session->mark,
session->index, session->gateway);
g_free(session->gateway);
session->gateway = NULL;
+ session->prefixlen = 0;
session->index = -1;
}
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);
+ session->prefixlen = __connman_ipconfig_get_prefixlen(ipconfig);
+
+ DBG("index %d routing table %d default gateway %s/%u",
+ session->index, session->mark, session->gateway, session->prefixlen);
err = __connman_inet_add_default_to_table(session->mark,
session->index, session->gateway);
if (err < 0)
DBG("session %p %s", session, strerror(-err));
+
+ err = __connman_inet_add_subnet_to_table(session->mark,
+ session->index, session->gateway, session->prefixlen);
+ if (err < 0)
+ DBG("session add subnet route %p %s", session, strerror(-err));
}
static void del_nat_rules(struct connman_session *session)
return;
session->snat_enabled = false;
- fw_snat = fw_snat_lookup(session->index);
+ fw_snat = fw_snat_lookup(session->index, session->addr);
if (!fw_snat)
return;
if (!addr)
return;
+ g_free(session->addr);
+ session->addr = g_strdup(addr);
+
session->snat_enabled = true;
- fw_snat = fw_snat_lookup(index);
+ fw_snat = fw_snat_lookup(index, session->addr);
if (fw_snat) {
fw_snat_ref(session, fw_snat);
return;
if (session->notify_watch > 0)
g_dbus_remove_watch(connection, session->notify_watch);
+ g_dbus_unregister_interface(connection, session->session_path,
+ CONNMAN_SESSION_INTERFACE);
+
destroy_policy_config(session);
g_slist_free(session->info->config.allowed_bearers);
g_free(session->info->config.allowed_interface);
g_free(session->info);
g_free(session->info_last);
g_free(session->gateway);
+ g_free(session->addr);
g_free(session);
}
GSList *allowed_bearers;
char *allowed_interface;
bool source_ip_rule;
+ char *context_identifier;
};
static void cleanup_creation_data(struct creation_data *creation_data)
if (creation_data->pending)
dbus_message_unref(creation_data->pending);
+ if (creation_data->context_identifier)
+ g_free(creation_data->context_identifier);
g_slist_free(creation_data->allowed_bearers);
g_free(creation_data->allowed_interface);
}
if (session->append_all ||
+ info->config.context_identifier != info_last->config.context_identifier) {
+ char *ifname = info->config.context_identifier;
+ if (!ifname)
+ ifname = "";
+ connman_dbus_dict_append_basic(dict, "ContextIdentifier",
+ DBUS_TYPE_STRING,
+ &ifname);
+ info_last->config.context_identifier = info->config.context_identifier;
+ }
+
+ 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)
connman_error("Failed to register %s", session->session_path);
g_hash_table_remove(session_hash, session->session_path);
err = -EINVAL;
- goto err;
+ goto err_notify;
}
reply = g_dbus_create_reply(creation_data->pending,
return 0;
err:
+ cleanup_session(session);
+
+err_notify:
reply = __connman_error_failed(creation_data->pending, -err);
g_dbus_send_message(connection, reply);
creation_data->pending = NULL;
- cleanup_session(session);
cleanup_creation_data(creation_data);
return err;
connman_session_parse_connection_type(val);
user_connection_type = true;
+ } else if (g_str_equal(key, "ContextIdentifier")) {
+ dbus_message_iter_get_basic(&value, &val);
+ creation_data->context_identifier = g_strdup(val);
} else if (g_str_equal(key, "AllowedInterface")) {
dbus_message_iter_get_basic(&value, &val);
creation_data->allowed_interface = g_strdup(val);
enum connman_session_state state = CONNMAN_SESSION_STATE_DISCONNECTED;
if (session->service) {
- service_state = __connman_service_get_state(session->service);
+ service_state = connman_service_get_state(session->service);
state = service_to_session_state(service_state);
session->info->state = state;
}
while (g_hash_table_iter_next(&iter, &key, &value)) {
struct connman_service_info *info = value;
- state = __connman_service_get_state(info->service);
+ state = connman_service_get_state(info->service);
if (is_session_connected(session, state))
service_list = g_slist_prepend(service_list,
struct connman_service_info *info = value;
enum connman_service_state state;
- state = __connman_service_get_state(info->service);
+ state = connman_service_get_state(info->service);
if (is_session_connected(session, state) &&
session_match_service(session, info->service)) {
}
}
-static struct connman_notifier session_notifier = {
+static const struct connman_notifier session_notifier = {
.name = "session",
.service_state_changed = service_state_changed,
.ipconfig_changed = ipconfig_changed,
--- /dev/null
+/*
+ *
+ * Connection Manager
+ *
+ * based on IPv4 Local Link library with GLib integration,
+ * Copyright (C) 2009-2010 Aldebaran Robotics. All rights reserved.
+ *
+ * Copyright (C) 2018 Commend International. 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.
+ *
+ */
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#include <netinet/if_ether.h>
+#include <net/if_arp.h>
+
+#include <arpa/inet.h>
+
+#include "src/shared/arp.h"
+#include "src/connman.h"
+
+int arp_send_packet(uint8_t* source_eth, uint32_t source_ip,
+ uint32_t target_ip, int ifindex)
+{
+ struct sockaddr_ll dest;
+ struct ether_arp p;
+ uint32_t ip_source;
+ uint32_t ip_target;
+ int fd, n;
+
+ fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return -errno;
+
+ memset(&dest, 0, sizeof(dest));
+ memset(&p, 0, sizeof(p));
+
+ dest.sll_family = AF_PACKET;
+ dest.sll_protocol = htons(ETH_P_ARP);
+ dest.sll_ifindex = ifindex;
+ dest.sll_halen = ETH_ALEN;
+ memset(dest.sll_addr, 0xFF, ETH_ALEN);
+ if (bind(fd, (struct sockaddr *)&dest, sizeof(dest)) < 0) {
+ int err = errno;
+ close(fd);
+ return -err;
+ }
+
+ ip_source = htonl(source_ip);
+ ip_target = htonl(target_ip);
+ p.arp_hrd = htons(ARPHRD_ETHER);
+ p.arp_pro = htons(ETHERTYPE_IP);
+ p.arp_hln = ETH_ALEN;
+ p.arp_pln = 4;
+ p.arp_op = htons(ARPOP_REQUEST);
+
+ memcpy(&p.arp_sha, source_eth, ETH_ALEN);
+ memcpy(&p.arp_spa, &ip_source, sizeof(p.arp_spa));
+ memcpy(&p.arp_tpa, &ip_target, sizeof(p.arp_tpa));
+
+ n = sendto(fd, &p, sizeof(p), 0,
+ (struct sockaddr*) &dest, sizeof(dest));
+ if (n < 0)
+ n = -errno;
+
+ close(fd);
+
+ return n;
+}
+
+int arp_socket(int ifindex)
+{
+ int fd;
+ struct sockaddr_ll sock;
+
+ fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return fd;
+
+ memset(&sock, 0, sizeof(sock));
+
+ sock.sll_family = AF_PACKET;
+ sock.sll_protocol = htons(ETH_P_ARP);
+ sock.sll_ifindex = ifindex;
+
+ if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) != 0) {
+ int err = errno;
+ close(fd);
+ return -err;
+ }
+
+ return fd;
+}
+
+/**
+ * Return a random link local IP (in host byte order)
+ */
+uint32_t arp_random_ip(void)
+{
+ unsigned tmp;
+
+ do {
+ uint64_t rand;
+ __connman_util_get_random(&rand);
+ tmp = rand;
+ tmp = tmp & IN_CLASSB_HOST;
+ } while (tmp > (IN_CLASSB_HOST - 0x0200));
+
+ return (LINKLOCAL_ADDR + 0x0100) + tmp;
+}
/*
*
- * IPV4 Local Link library with GLib integration
+ * Connection Manager
*
- * Copyright (C) 2009-2010 Aldebaran Robotics. All rights reserved.
+ * based on IPv4 Local Link library with GLib integration,
+ * Copyright (C) 2009-2010 Aldebaran Robotics. All rights reserved.
+ *
+ * Copyright (C) 2018 Commend International. 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
* 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
- *
*/
-#ifndef __G_IPV4LL_H
-#define __G_IPV4LL_H
-
-#include <glib.h>
+#ifndef SHARED_ARP_H
+#define SHARED_ARP_H
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include <stdint.h>
-/* 169.254.0.0 */
-#define LINKLOCAL_ADDR 0xa9fe0000
-
-/* See RFC 3927 */
+/* IPv4 Link-Local (RFC 3927), IPv4 Address Conflict Detection (RFC 5227) */
#define PROBE_WAIT 1
#define PROBE_NUM 3
#define PROBE_MIN 1
#define RATE_LIMIT_INTERVAL 60
#define DEFEND_INTERVAL 10
-uint32_t ipv4ll_random_ip(void);
-guint ipv4ll_random_delay_ms(guint secs);
-int ipv4ll_send_arp_packet(uint8_t* source_eth, uint32_t source_ip,
+/* 169.254.0.0 */
+#define LINKLOCAL_ADDR 0xa9fe0000
+
+int arp_send_packet(uint8_t* source_eth, uint32_t source_ip,
uint32_t target_ip, int ifindex);
-int ipv4ll_arp_socket(int ifindex);
+int arp_socket(int ifindex);
+
+uint32_t arp_random_ip(void);
-#ifdef __cplusplus
-}
#endif
-#endif /* !IPV4LL_H_ */
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <errno.h>
#include <sys/mman.h>
#include <sys/types.h>
DBG("service %p", service);
dir = g_strdup_printf("%s/%s", STORAGEDIR,
- __connman_service_get_ident(service));
+ connman_service_get_identifier(service));
/* If the dir doesn't exist, create it */
if (!g_file_test(dir, G_FILE_TEST_IS_DIR)) {
}
name = g_strdup_printf("%s/%s/data", STORAGEDIR,
- __connman_service_get_ident(service));
+ connman_service_get_identifier(service));
file->history_name = g_strdup_printf("%s/%s/history", STORAGEDIR,
- __connman_service_get_ident(service));
+ connman_service_get_identifier(service));
/* TODO: Use a global config file instead of hard coded value. */
file->account_period_offset = 1;
struct connman_rfkill *rfkill = value;
enum connman_service_type type = GPOINTER_TO_INT(user_data);
- /* Calling _technology_rfkill_add will update the tech. */
+ /* Calling _technology_add_rfkill will update the tech. */
if (rfkill->type == type)
__connman_technology_add_rfkill(rfkill->index, type,
rfkill->softblock, rfkill->hardblock);
__connman_storage_save_global(keyfile);
g_key_file_free(keyfile);
-
- return;
}
static void tethering_changed(struct connman_technology *technology)
return NULL;
}
+enum connman_service_type connman_technology_get_type
+ (struct connman_technology *technology)
+{
+ if (!technology)
+ return CONNMAN_SERVICE_TYPE_UNKNOWN;
+
+ return technology->type;
+}
+
bool connman_technology_get_wifi_tethering(const char **ssid,
const char **psk)
{
g_free(identifier);
g_key_file_free(keyfile);
-
- return;
}
bool __connman_technology_get_offlinemode(void)
}
g_key_file_free(keyfile);
-
- return;
}
static bool connman_technology_load_offlinemode(void)
struct connman_technology *technology = user_data;
DBusMessage *reply;
- /* Power request timedout, send ETIMEDOUT. */
+ /* Power request timed out, send ETIMEDOUT. */
if (technology->pending_reply) {
reply = __connman_error_failed(technology->pending_reply, ETIMEDOUT);
if (reply)
if (technology->type == CONNMAN_SERVICE_TYPE_P2P) {
technology->enable_persistent = false;
+ __connman_device_stop_scan(CONNMAN_SERVICE_TYPE_P2P);
+ __connman_peer_disconnect_all();
return technology_disabled(technology);
} else if (technology->type == CONNMAN_SERVICE_TYPE_WIFI) {
struct connman_technology *p2p;
if (device == other_device)
continue;
- if (__connman_device_get_service_type(other_device) != type)
- continue;
-
- if (connman_device_get_scanning(other_device))
+ if (connman_device_get_scanning(other_device, type))
count += 1;
}
technology->scan_pending =
g_slist_prepend(technology->scan_pending, msg);
- err = __connman_device_request_scan(technology->type);
+ err = __connman_device_request_scan_full(technology->type);
if (err < 0)
reply_scan_pending(technology, err);
int err = __connman_device_enable(device);
/*
* connman_technology_add_device() calls __connman_device_enable()
- * but since the device is already enabled, the calls does not
+ * but since the device is already enabled, the call does not
* propagate through to connman_technology_enabled via
* connman_device_set_powered.
*/
* resuming offlinemode from last saved profile. We need that
* information in rfkill_update, otherwise it falls back on the
* technology's persistent state. Hence we set the offline mode here
- * but save it & call the notifier only if its successful.
+ * but save it & call the notifier only if it is successful.
*/
global_offlinemode = offlinemode;
#include <stdio.h>
#include <sys/ioctl.h>
#include <net/if.h>
-#include <linux/sockios.h>
#include <string.h>
#include <fcntl.h>
-#include <linux/if_tun.h>
#include <netinet/in.h>
+#include <linux/sockios.h>
+#include <linux/if_tun.h>
#include <linux/if_bridge.h>
#include "connman.h"
static DBusConnection *connection;
static GHashTable *pn_hash;
+static GHashTable *clients_table;
+
+struct _clients_notify {
+ int id;
+ GHashTable *remove;
+} *clients_notify;
+
struct connman_private_network {
char *owner;
char *path;
__connman_tethering_set_enabled();
}
+static void unregister_client(gpointer key,
+ gpointer value, gpointer user_data)
+{
+ const char *addr = key;
+ __connman_tethering_client_unregister(addr);
+}
+
+static void unregister_all_clients(void)
+{
+ g_hash_table_foreach(clients_table, unregister_client, NULL);
+}
+
int __connman_tethering_set_enabled(void)
{
int index;
connman_ipaddress_calc_netmask_len(subnet_mask),
broadcast);
if (err < 0 && err != -EALREADY) {
- __connman_ippool_unref(dhcp_ippool);
+ __connman_ippool_free(dhcp_ippool);
+ dhcp_ippool = NULL;
__connman_bridge_remove(BRIDGE_NAME);
__sync_fetch_and_sub(&tethering_enabled, 1);
return -EADDRNOTAVAIL;
24 * 3600, dns);
if (!tethering_dhcp_server) {
__connman_bridge_disable(BRIDGE_NAME);
- __connman_ippool_unref(dhcp_ippool);
+ __connman_ippool_free(dhcp_ippool);
+ dhcp_ippool = NULL;
__connman_bridge_remove(BRIDGE_NAME);
__sync_fetch_and_sub(&tethering_enabled, 1);
return -EOPNOTSUPP;
connman_error("Cannot enable NAT %d/%s", err, strerror(-err));
dhcp_server_stop(tethering_dhcp_server);
__connman_bridge_disable(BRIDGE_NAME);
- __connman_ippool_unref(dhcp_ippool);
+ __connman_ippool_free(dhcp_ippool);
+ dhcp_ippool = NULL;
__connman_bridge_remove(BRIDGE_NAME);
__sync_fetch_and_sub(&tethering_enabled, 1);
return -EOPNOTSUPP;
if (__sync_fetch_and_sub(&tethering_enabled, 1) != 1)
return;
+ unregister_all_clients();
+
__connman_ipv6pd_cleanup();
index = connman_inet_ifindex(BRIDGE_NAME);
__connman_bridge_disable(BRIDGE_NAME);
- __connman_ippool_unref(dhcp_ippool);
+ __connman_ippool_free(dhcp_ippool);
+ dhcp_ippool = NULL;
__connman_bridge_remove(BRIDGE_NAME);
DBG("tethering stopped");
}
+static void append_client(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ const char *addr = key;
+ DBusMessageIter *array = user_data;
+
+ dbus_message_iter_append_basic(array, DBUS_TYPE_STRING,
+ &addr);
+}
+
+void __connman_tethering_list_clients(DBusMessageIter *array)
+{
+ g_hash_table_foreach(clients_table, append_client, array);
+}
+
static void setup_tun_interface(unsigned int flags, unsigned change,
void *data)
{
__connman_nat_disable(BRIDGE_NAME);
connman_rtnl_remove_watch(pn->iface_watch);
- __connman_ippool_unref(pn->pool);
+ __connman_ippool_free(pn->pool);
if (pn->watch > 0) {
g_dbus_remove_watch(connection, pn->watch);
g_hash_table_remove(pn_hash, pn->path);
}
+static gboolean client_send_changed(gpointer data)
+{
+ DBusMessage *signal;
+ DBusMessageIter iter, array;
+
+ DBG("");
+
+ clients_notify->id = 0;
+
+ signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
+ CONNMAN_MANAGER_INTERFACE, "TetheringClientsChanged");
+ if (!signal)
+ return FALSE;
+
+ dbus_message_iter_init_append(signal, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &array);
+
+ g_hash_table_foreach(clients_table, append_client, &array);
+
+ dbus_message_iter_close_container(&iter, &array);
+
+ dbus_message_iter_init_append(signal, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &array);
+
+ g_hash_table_foreach(clients_notify->remove, append_client, &array);
+
+ dbus_message_iter_close_container(&iter, &array);
+
+ dbus_connection_send(connection, signal, NULL);
+ dbus_message_unref(signal);
+
+ g_hash_table_remove_all(clients_notify->remove);
+
+ return FALSE;
+}
+
+static void client_schedule_changed(void)
+{
+ if (clients_notify->id != 0)
+ return;
+
+ clients_notify->id = g_timeout_add(100, client_send_changed, NULL);
+}
+
+static void client_added(const char *addr)
+{
+ DBG("client %s", addr);
+
+ g_hash_table_remove(clients_notify->remove, addr);
+
+ client_schedule_changed();
+}
+
+static void client_removed(const char *addr)
+{
+ DBG("client %s", addr);
+
+ g_hash_table_replace(clients_notify->remove, g_strdup(addr), NULL);
+
+ client_schedule_changed();
+}
+
int __connman_private_network_request(DBusMessage *msg, const char *owner)
{
struct connman_private_network *pn;
return 0;
}
+void __connman_tethering_client_register(const char *addr)
+{
+ g_hash_table_insert(clients_table, g_strdup(addr), NULL);
+ client_added(addr);
+}
+
+void __connman_tethering_client_unregister(const char *addr)
+{
+ g_hash_table_remove(clients_table, addr);
+ client_removed(addr);
+}
+
int __connman_tethering_init(void)
{
DBG("");
pn_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
NULL, remove_private_network);
+ clients_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, NULL);
+
+ clients_notify = g_new0(struct _clients_notify, 1);
+ clients_notify->remove = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, NULL);
return 0;
}
return;
g_hash_table_destroy(pn_hash);
+
+ g_hash_table_destroy(clients_notify->remove);
+ g_free(clients_notify);
+ clients_notify = NULL;
+
+ g_hash_table_destroy(clients_table);
+ clients_table = NULL;
+
dbus_connection_unref(connection);
}
#define TS_RECHECK_INTERVAL 7200
+static struct connman_service *ts_service;
+static GSList *timeservers_list = NULL;
static GSList *ts_list = NULL;
static char *ts_current = NULL;
static int ts_recheck_id = 0;
+static int ts_backoff_id = 0;
static GResolv *resolv = NULL;
static int resolv_id = 0;
+static void sync_next(void);
+
static void resolv_debug(const char *str, void *data)
{
connman_info("%s: %s\n", (const char *) data, str);
}
+
+static void ntp_callback(bool success, void *user_data)
+{
+ DBG("success %d", success);
+
+ if (!success)
+ sync_next();
+}
+
static void save_timeservers(char **servers)
{
GKeyFile *keyfile;
__connman_storage_save_global(keyfile);
g_key_file_free(keyfile);
-
- return;
}
static char **load_timeservers(void)
if (status == G_RESOLV_RESULT_STATUS_SUCCESS) {
if (results) {
- for (i = 0; results[i]; i++) {
+ /* prepend the results in reverse order */
+
+ for (i = 0; results[i]; i++)
+ /* count */;
+ i--;
+
+ for (; i >= 0; i--) {
DBG("result[%d]: %s", i, results[i]);
- if (i == 0)
- continue;
ts_list = __connman_timeserver_add_list(
- ts_list, results[i]);
+ ts_list, results[i]);
}
+ }
+ }
- DBG("Using timeserver %s", results[0]);
+ sync_next();
+}
- __connman_ntp_start(results[0]);
+/*
+ * Once the timeserver list (timeserver_list) is created, we start
+ * querying the servers one by one. If resolving fails on one of them,
+ * we move to the next one. The user can enter either an IP address or
+ * a URL for the timeserver. We only resolve the URLs. Once we have an
+ * IP for the NTP server, we start querying it for time corrections.
+ */
+static void timeserver_sync_start(void)
+{
+ GSList *list;
- return;
- }
+ for (list = timeservers_list; list; list = list->next) {
+ char *timeserver = list->data;
+
+ ts_list = g_slist_prepend(ts_list, g_strdup(timeserver));
}
+ ts_list = g_slist_reverse(ts_list);
+
+ sync_next();
+}
+
+static gboolean timeserver_sync_restart(gpointer user_data)
+{
+ timeserver_sync_start();
+ ts_backoff_id = 0;
- /* If resolving fails, move to the next server */
- __connman_timeserver_sync_next();
+ return FALSE;
}
/*
- * Once the timeserver list (ts_list) is created, we start querying the
- * servers one by one. If resolving fails on one of them, we move to the
- * next one. The user can enter either an IP address or a URL for the
- * timeserver. We only resolve the URLs. Once we have an IP for the NTP
- * server, we start querying it for time corrections.
+ * Select the next time server from the working list (ts_list) because
+ * for some reason the first time server in the list didn't work. If
+ * none of the server did work we start over with the first server
+ * with a backoff.
*/
-void __connman_timeserver_sync_next()
+static void sync_next(void)
{
if (ts_current) {
g_free(ts_current);
__connman_ntp_stop();
- /* Get the 1st server in the list */
- if (!ts_list)
- return;
-
- ts_current = ts_list->data;
-
- ts_list = g_slist_delete_link(ts_list, ts_list);
+ while (ts_list) {
+ ts_current = ts_list->data;
+ ts_list = g_slist_delete_link(ts_list, ts_list);
- /* if it's an IP, directly query it. */
- if (connman_inet_check_ipaddress(ts_current) > 0) {
- DBG("Using timeserver %s", ts_current);
-
- __connman_ntp_start(ts_current);
+ /* if it's an IP, directly query it. */
+ if (connman_inet_check_ipaddress(ts_current) > 0) {
+ DBG("Using timeserver %s", ts_current);
+ __connman_ntp_start(ts_current, ntp_callback, NULL);
+ return;
+ }
+ DBG("Resolving timeserver %s", ts_current);
+ resolv_id = g_resolv_lookup_hostname(resolv, ts_current,
+ resolv_result, NULL);
return;
}
- DBG("Resolving timeserver %s", ts_current);
-
- resolv_id = g_resolv_lookup_hostname(resolv, ts_current,
- resolv_result, NULL);
-
- return;
+ DBG("No timeserver could be used, restart probing in 5 seconds");
+ ts_backoff_id = g_timeout_add_seconds(5, timeserver_sync_restart, NULL);
}
GSList *__connman_timeserver_add_list(GSList *server_list,
for (i = 0; service_ts && service_ts[i]; i++)
list = __connman_timeserver_add_list(list, service_ts[i]);
- network = __connman_service_get_network(service);
- if (network) {
- index = connman_network_get_index(network);
- service_gw = __connman_ipconfig_get_gateway_from_index(index,
- CONNMAN_IPCONFIG_TYPE_ALL);
-
- /* Then add Service Gateway to the list */
- if (service_gw)
- list = __connman_timeserver_add_list(list, service_gw);
+ /*
+ * Then add Service Gateway to the list, if UseGatewaysAsTimeservers
+ * configuration option is set to true.
+ */
+ if (connman_setting_get_bool("UseGatewaysAsTimeservers")) {
+ network = __connman_service_get_network(service);
+ if (network) {
+ index = connman_network_get_index(network);
+ service_gw = __connman_ipconfig_get_gateway_from_index(index,
+ CONNMAN_IPCONFIG_TYPE_ALL);
+
+ if (service_gw)
+ list = __connman_timeserver_add_list(list, service_gw);
+ }
}
/* Then add Global Timeservers to the list */
{
GSList *ts;
- ts = __connman_timeserver_get_all(__connman_service_get_default());
+ ts = __connman_timeserver_get_all(connman_service_get_default());
if (!ts) {
DBG("timeservers disabled");
g_source_remove(ts_recheck_id);
ts_recheck_id = 0;
+ if (ts_backoff_id) {
+ g_source_remove(ts_backoff_id);
+ ts_backoff_id = 0;
+ }
+
if (ts_current) {
g_free(ts_current);
ts_current = NULL;
if (default_service)
service = default_service;
else
- service = __connman_service_get_default();
+ service = connman_service_get_default();
if (!service)
return -EINVAL;
+ if (service == ts_service)
+ return -EALREADY;
+
if (!resolv)
return 0;
/*
g_strfreev(nameservers);
- g_slist_free_full(ts_list, g_free);
+ g_slist_free_full(timeservers_list, g_free);
- ts_list = __connman_timeserver_get_all(service);
+ timeservers_list = __connman_timeserver_get_all(service);
- __connman_service_timeserver_changed(service, ts_list);
+ __connman_service_timeserver_changed(service, timeservers_list);
- if (!ts_list) {
+ if (!timeservers_list) {
DBG("No timeservers set.");
return 0;
}
ts_recheck_enable();
- __connman_timeserver_sync_next();
+ ts_service = service;
+ timeserver_sync_start();
return 0;
}
return -EINVAL;
nameservers = connman_service_get_nameservers(service);
- if (!nameservers)
- return -EINVAL;
/* Stop an already ongoing resolution, if there is one */
if (resolv && resolv_id > 0)
if (getenv("CONNMAN_RESOLV_DEBUG"))
g_resolv_set_debug(resolv, resolv_debug, "RESOLV");
- for (i = 0; nameservers[i]; i++)
- g_resolv_add_nameserver(resolv, nameservers[i], 53, 0);
+ if (nameservers) {
+ for (i = 0; nameservers[i]; i++)
+ g_resolv_add_nameserver(resolv, nameservers[i], 53, 0);
- g_strfreev(nameservers);
+ g_strfreev(nameservers);
+ }
return __connman_timeserver_sync(service);
}
{
DBG(" ");
+ ts_service = NULL;
+
if (resolv) {
g_resolv_unref(resolv);
resolv = NULL;
}
- g_slist_free_full(ts_list, g_free);
+ g_slist_free_full(timeservers_list, g_free);
+ timeservers_list = NULL;
+ g_slist_free_full(ts_list, g_free);
ts_list = NULL;
__connman_ntp_stop();
timeserver_stop();
}
-static struct connman_notifier timeserver_notifier = {
+static const struct connman_notifier timeserver_notifier = {
.name = "timeserver",
.default_changed = default_changed,
};
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
f = -1;
}
+
+/**
+ * Return a random delay in range of zero to secs*1000 milli seconds.
+ */
+unsigned int __connman_util_random_delay_ms(unsigned int secs)
+{
+ uint64_t rand;
+
+ __connman_util_get_random(&rand);
+ return rand % (secs * 1000);
+}
}
/* Restarting the test */
- __connman_wispr_start(service, wp_context->type);
+ __connman_service_wispr_start(service, wp_context->type);
}
static void wispr_portal_request_wispr_login(struct connman_service *service,
g_free(url);
- __connman_wispr_start(wpad->service,
+ __connman_service_wispr_start(wpad->service,
CONNMAN_IPCONFIG_TYPE_IPV4);
return;
connman_service_set_proxy_method(wpad->service,
CONNMAN_SERVICE_PROXY_METHOD_DIRECT);
- __connman_wispr_start(wpad->service,
+ __connman_service_wispr_start(wpad->service,
CONNMAN_IPCONFIG_TYPE_IPV4);
}
else:
if key in ["Servers", "Excludes"]:
val += extract_list(values[key])
+ elif key in ["Ethernet", "IPv4"]:
+ val += extract_values(values[key])
else:
val += str(values[key])
val += " }"
if key in ["IPv4", "IPv4.Configuration",
"IPv6", "IPv6.Configuration",
"Proxy", "Proxy.Configuration",
- "Ethernet", "Provider"]:
+ "Ethernet", "Provider",
+ "LastAddressConflict"]:
val = extract_values(properties[key])
elif key in ["Nameservers", "Nameservers.Configuration",
"Domains", "Domains.Configuration",
#include <gdhcp/gdhcp.h>
+#include "../src/connman.h"
+
static GMainLoop *main_loop;
static void sig_term(int sig)
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
+ __connman_util_init();
+
g_main_loop_run(main_loop);
+ __connman_util_cleanup();
+
g_dhcp_server_unref(dhcp_server);
g_main_loop_unref(main_loop);
#include <arpa/inet.h>
#include <net/route.h>
#include <net/ethernet.h>
-#include <linux/if_arp.h>
#include <gdhcp/gdhcp.h>
+#include "../src/connman.h"
+
static GTimer *timer;
static GMainLoop *main_loop;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
+ __connman_util_init();
+
g_main_loop_run(main_loop);
+ __connman_util_cleanup();
+
g_timer_destroy(timer);
g_dhcp_client_unref(dhcp_client);
#endif
#include <errno.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
--- /dev/null
+/*
+ * Connection Manager
+ *
+ * Copyright (C) 2007-2012 Intel Corporation. All rights reserved.
+ * Copyright (C) 2013 BMW Car IT GmbH.
+ * Copyright (C) 2018 Jolla Ltd. 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
+ *
+ */
+
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "../src/connman.h"
+
+enum iptables_command {
+ IPTABLES_COMMAND_APPEND,
+ IPTABLES_COMMAND_INSERT,
+ IPTABLES_COMMAND_DELETE,
+ IPTABLES_COMMAND_POLICY,
+ IPTABLES_COMMAND_CHAIN_INSERT,
+ IPTABLES_COMMAND_CHAIN_DELETE,
+ IPTABLES_COMMAND_CHAIN_FLUSH,
+ IPTABLES_COMMAND_DUMP,
+ IPTABLES_COMMAND_UNKNOWN,
+};
+
+int main(int argc, char *argv[])
+{
+ enum iptables_command cmd = IPTABLES_COMMAND_UNKNOWN;
+ char *table = NULL, *chain = NULL, *rule = NULL, *tmp;
+ int err, c, i;
+
+ opterr = 0;
+
+ while ((c = getopt_long(argc, argv,
+ "-A:I:D:P:N:X:F:Lt:", NULL, NULL)) != -1) {
+ switch (c) {
+ case 'A':
+ chain = optarg;
+ cmd = IPTABLES_COMMAND_APPEND;
+ break;
+ case 'I':
+ chain = optarg;
+ cmd = IPTABLES_COMMAND_INSERT;
+ break;
+ case 'D':
+ chain = optarg;
+ cmd = IPTABLES_COMMAND_DELETE;
+ break;
+ case 'P':
+ chain = optarg;
+ /* The policy will be stored in rule. */
+ cmd = IPTABLES_COMMAND_POLICY;
+ break;
+ case 'N':
+ chain = optarg;
+ cmd = IPTABLES_COMMAND_CHAIN_INSERT;
+ break;
+ case 'X':
+ chain = optarg;
+ cmd = IPTABLES_COMMAND_CHAIN_DELETE;
+ break;
+ case 'F':
+ chain = optarg;
+ cmd = IPTABLES_COMMAND_CHAIN_FLUSH;
+ break;
+ case 'L':
+ cmd = IPTABLES_COMMAND_DUMP;
+ break;
+ case 't':
+ table = optarg;
+ break;
+ default:
+ goto out;
+ }
+ }
+
+out:
+ if (!table)
+ table = "filter";
+
+ for (i = optind - 1; i < argc; i++) {
+ if (rule) {
+ tmp = rule;
+ rule = g_strdup_printf("%s %s", rule, argv[i]);
+ g_free(tmp);
+ } else
+ rule = g_strdup(argv[i]);
+ }
+
+ __connman_iptables_init();
+
+ switch (cmd) {
+ case IPTABLES_COMMAND_APPEND:
+ err = __connman_iptables_append(AF_INET6, table, chain, rule);
+ break;
+ case IPTABLES_COMMAND_INSERT:
+ err = __connman_iptables_insert(AF_INET6, table, chain, rule);
+ break;
+ case IPTABLES_COMMAND_DELETE:
+ err = __connman_iptables_delete(AF_INET6, table, chain, rule);
+ break;
+ case IPTABLES_COMMAND_POLICY:
+ err = __connman_iptables_change_policy(AF_INET6, table, chain,
+ rule);
+ break;
+ case IPTABLES_COMMAND_CHAIN_INSERT:
+ err = __connman_iptables_new_chain(AF_INET6, table, chain);
+ break;
+ case IPTABLES_COMMAND_CHAIN_DELETE:
+ err = __connman_iptables_delete_chain(AF_INET6, table, chain);
+ break;
+ case IPTABLES_COMMAND_CHAIN_FLUSH:
+ err = __connman_iptables_flush_chain(AF_INET6, table, chain);
+ break;
+ case IPTABLES_COMMAND_DUMP:
+ __connman_log_init(argv[0], "*", false, false,
+ "ip6tables-test", "1");
+ err = __connman_iptables_dump(AF_INET6, table);
+ break;
+ case IPTABLES_COMMAND_UNKNOWN:
+ printf("Missing command\n");
+ printf("usage: ip6tables-test [-t table] {-A|-I|-D} chain rule\n");
+ printf(" ip6tables-test [-t table] {-N|-X|-F} chain\n");
+ printf(" ip6tables-test [-t table] -L\n");
+ printf(" ip6tables-test [-t table] -P chain target\n");
+ exit(-EINVAL);
+ }
+
+ if (err < 0) {
+ printf("Error: %s\n", strerror(-err));
+ exit(err);
+ }
+
+ err = __connman_iptables_commit(AF_INET6, table);
+ if (err < 0) {
+ printf("Failed to commit changes: %s\n", strerror(-err));
+ exit(err);
+ }
+
+ g_free(rule);
+
+ __connman_iptables_cleanup();
+
+ return 0;
+}
switch (cmd) {
case IPTABLES_COMMAND_APPEND:
- err = __connman_iptables_append(table, chain, rule);
+ err = __connman_iptables_append(AF_INET, table, chain, rule);
break;
case IPTABLES_COMMAND_INSERT:
- err = __connman_iptables_insert(table, chain, rule);
+ err = __connman_iptables_insert(AF_INET, table, chain, rule);
break;
case IPTABLES_COMMAND_DELETE:
- err = __connman_iptables_delete(table, chain, rule);
+ err = __connman_iptables_delete(AF_INET, table, chain, rule);
break;
case IPTABLES_COMMAND_POLICY:
- err = __connman_iptables_change_policy(table, chain, rule);
+ err = __connman_iptables_change_policy(AF_INET, table, chain,
+ rule);
break;
case IPTABLES_COMMAND_CHAIN_INSERT:
- err = __connman_iptables_new_chain(table, chain);
+ err = __connman_iptables_new_chain(AF_INET, table, chain);
break;
case IPTABLES_COMMAND_CHAIN_DELETE:
- err = __connman_iptables_delete_chain(table, chain);
+ err = __connman_iptables_delete_chain(AF_INET, table, chain);
break;
case IPTABLES_COMMAND_CHAIN_FLUSH:
- err = __connman_iptables_flush_chain(table, chain);
+ err = __connman_iptables_flush_chain(AF_INET, table, chain);
break;
case IPTABLES_COMMAND_DUMP:
__connman_log_init(argv[0], "*", false, false,
"iptables-test", "1");
- err = __connman_iptables_dump(table);
+ err = __connman_iptables_dump(AF_INET, table);
break;
case IPTABLES_COMMAND_UNKNOWN:
printf("Missing command\n");
exit(err);
}
- err = __connman_iptables_commit(table);
+ err = __connman_iptables_commit(AF_INET, table);
if (err < 0) {
printf("Failed to commit changes: %s\n", strerror(-err));
exit(err);
#include "../src/connman.h"
-static bool assert_rule(const char *table_name, const char *rule)
+static bool assert_rule(int type, const char *table_name, const char *rule)
{
char *cmd, *output, **lines;
GError **error = NULL;
int i;
bool ret = true;
- cmd = g_strdup_printf(IPTABLES_SAVE " -t %s", table_name);
+ switch (type) {
+ case AF_INET:
+ cmd = g_strdup_printf(IPTABLES_SAVE " -t %s", table_name);
+ break;
+ case AF_INET6:
+ cmd = g_strdup_printf(IP6TABLES_SAVE " -t %s", table_name);
+ break;
+ default:
+ return false;
+ }
+
g_spawn_command_line_sync(cmd, &output, NULL, NULL, error);
g_free(cmd);
return ret;
}
-static void assert_rule_exists(const char *table_name, const char *rule)
+static void assert_rule_exists(int type, const char *table_name,
+ const char *rule)
{
- if (g_strcmp0(IPTABLES_SAVE, "") == 0) {
- DBG("iptables-save is missing, no assertion possible");
- return;
+ if (type == AF_INET) {
+ if (g_strcmp0(IPTABLES_SAVE, "") == 0) {
+ DBG("iptables-save is missing, no assertion possible");
+ return;
+ }
}
- g_assert(assert_rule(table_name, rule));
+ if (type == AF_INET6) {
+ if (g_strcmp0(IP6TABLES_SAVE, "") == 0) {
+ DBG("ip6tables-save is missing, no assertion possible");
+ return;
+ }
+ }
+
+ g_assert(assert_rule(type, table_name, rule));
}
-static void assert_rule_not_exists(const char *table_name, const char *rule)
+static void assert_rule_not_exists(int type, const char *table_name,
+ const char *rule)
{
- if (g_strcmp0(IPTABLES_SAVE, "") == 0) {
- DBG("iptables-save is missing, no assertion possible");
- return;
+ if (type == AF_INET) {
+ if (g_strcmp0(IPTABLES_SAVE, "") == 0) {
+ DBG("iptables-save is missing, no assertion possible");
+ return;
+ }
+ }
+
+ if (type == AF_INET6) {
+ if (g_strcmp0(IP6TABLES_SAVE, "") == 0) {
+ DBG("ip6tables-save is missing, no assertion possible");
+ return;
+ }
}
- g_assert(!assert_rule(table_name, rule));
+ g_assert(!assert_rule(type, table_name, rule));
}
static void test_iptables_chain0(void)
{
int err;
- err = __connman_iptables_new_chain("filter", "foo");
+ err = __connman_iptables_new_chain(AF_INET, "filter", "foo");
g_assert(err == 0);
- err = __connman_iptables_commit("filter");
+ err = __connman_iptables_commit(AF_INET, "filter");
g_assert(err == 0);
- assert_rule_exists("filter", ":foo - [0:0]");
+ assert_rule_exists(AF_INET, "filter", ":foo - [0:0]");
- err = __connman_iptables_delete_chain("filter", "foo");
+ err = __connman_iptables_delete_chain(AF_INET, "filter", "foo");
g_assert(err == 0);
- err = __connman_iptables_commit("filter");
+ err = __connman_iptables_commit(AF_INET, "filter");
g_assert(err == 0);
- assert_rule_not_exists("filter", ":foo - [0:0]");
+ assert_rule_not_exists(AF_INET, "filter", ":foo - [0:0]");
}
static void test_iptables_chain1(void)
{
int err;
- err = __connman_iptables_new_chain("filter", "foo");
+ err = __connman_iptables_new_chain(AF_INET, "filter", "foo");
g_assert(err == 0);
- err = __connman_iptables_commit("filter");
+ err = __connman_iptables_commit(AF_INET, "filter");
g_assert(err == 0);
- err = __connman_iptables_flush_chain("filter", "foo");
+ err = __connman_iptables_flush_chain(AF_INET, "filter", "foo");
g_assert(err == 0);
- err = __connman_iptables_commit("filter");
+ err = __connman_iptables_commit(AF_INET, "filter");
g_assert(err == 0);
- err = __connman_iptables_delete_chain("filter", "foo");
+ err = __connman_iptables_delete_chain(AF_INET, "filter", "foo");
g_assert(err == 0);
- err = __connman_iptables_commit("filter");
+ err = __connman_iptables_commit(AF_INET, "filter");
g_assert(err == 0);
}
{
int err;
- err = __connman_iptables_change_policy("filter", "INPUT", "DROP");
+ err = __connman_iptables_change_policy(AF_INET, "filter", "INPUT", "DROP");
g_assert(err == 0);
- err = __connman_iptables_commit("filter");
+ err = __connman_iptables_commit(AF_INET, "filter");
g_assert(err == 0);
- err = __connman_iptables_change_policy("filter", "INPUT", "ACCEPT");
+ err = __connman_iptables_change_policy(AF_INET, "filter", "INPUT", "ACCEPT");
g_assert(err == 0);
- err = __connman_iptables_commit("filter");
+ err = __connman_iptables_commit(AF_INET, "filter");
g_assert(err == 0);
}
{
int err;
- err = __connman_iptables_new_chain("filter", "user-chain-0");
+ err = __connman_iptables_new_chain(AF_INET, "filter", "user-chain-0");
g_assert(err == 0);
- err = __connman_iptables_commit("filter");
+ err = __connman_iptables_commit(AF_INET, "filter");
g_assert(err == 0);
- assert_rule_exists("filter", ":user-chain-0 - [0:0]");
+ assert_rule_exists(AF_INET, "filter", ":user-chain-0 - [0:0]");
- err = __connman_iptables_new_chain("filter", "user-chain-1");
+ err = __connman_iptables_new_chain(AF_INET, "filter", "user-chain-1");
g_assert(err == 0);
- err = __connman_iptables_commit("filter");
+ err = __connman_iptables_commit(AF_INET, "filter");
g_assert(err == 0);
- assert_rule_exists("filter", ":user-chain-0 - [0:0]");
- assert_rule_exists("filter", ":user-chain-1 - [0:0]");
+ assert_rule_exists(AF_INET, "filter", ":user-chain-0 - [0:0]");
+ assert_rule_exists(AF_INET, "filter", ":user-chain-1 - [0:0]");
- err = __connman_iptables_delete_chain("filter", "user-chain-1");
+ err = __connman_iptables_delete_chain(AF_INET, "filter", "user-chain-1");
g_assert(err == 0);
- err = __connman_iptables_commit("filter");
+ err = __connman_iptables_commit(AF_INET, "filter");
g_assert(err == 0);
- assert_rule_exists("filter", ":user-chain-0 - [0:0]");
- assert_rule_not_exists("filter", ":user-chain-1 - [0:0]");
+ assert_rule_exists(AF_INET, "filter", ":user-chain-0 - [0:0]");
+ assert_rule_not_exists(AF_INET, "filter", ":user-chain-1 - [0:0]");
- err = __connman_iptables_delete_chain("filter", "user-chain-0");
+ err = __connman_iptables_delete_chain(AF_INET, "filter", "user-chain-0");
g_assert(err == 0);
- err = __connman_iptables_commit("filter");
+ err = __connman_iptables_commit(AF_INET, "filter");
g_assert(err == 0);
- assert_rule_not_exists("filter", ":user-chain-0 - [0:0]");
+ assert_rule_not_exists(AF_INET, "filter", ":user-chain-0 - [0:0]");
}
static void test_iptables_rule0(void)
/* Test simple appending and removing a rule */
- err = __connman_iptables_append("filter", "INPUT",
+ err = __connman_iptables_append(AF_INET, "filter", "INPUT",
"-m mark --mark 1 -j LOG");
g_assert(err == 0);
- err = __connman_iptables_commit("filter");
+ err = __connman_iptables_commit(AF_INET, "filter");
g_assert(err == 0);
- assert_rule_exists("filter",
+ assert_rule_exists(AF_INET, "filter",
"-A INPUT -m mark --mark 0x1 -j LOG");
- err = __connman_iptables_delete("filter", "INPUT",
+ err = __connman_iptables_delete(AF_INET, "filter", "INPUT",
"-m mark --mark 1 -j LOG");
g_assert(err == 0);
- err = __connman_iptables_commit("filter");
+ err = __connman_iptables_commit(AF_INET, "filter");
g_assert(err == 0);
- assert_rule_not_exists("filter",
+ assert_rule_not_exists(AF_INET, "filter",
"-A INPUT -m mark --mark 0x1 -j LOG");
}
/* Test if we can do NAT stuff */
- err = __connman_iptables_append("nat", "POSTROUTING",
+ err = __connman_iptables_append(AF_INET, "nat", "POSTROUTING",
"-s 10.10.1.0/24 -o eth0 -j MASQUERADE");
- err = __connman_iptables_commit("nat");
+ err = __connman_iptables_commit(AF_INET, "nat");
g_assert(err == 0);
- assert_rule_exists("nat",
+ assert_rule_exists(AF_INET, "nat",
"-A POSTROUTING -s 10.10.1.0/24 -o eth0 -j MASQUERADE");
- err = __connman_iptables_delete("nat", "POSTROUTING",
+ err = __connman_iptables_delete(AF_INET, "nat", "POSTROUTING",
"-s 10.10.1.0/24 -o eth0 -j MASQUERADE");
- err = __connman_iptables_commit("nat");
+ err = __connman_iptables_commit(AF_INET, "nat");
g_assert(err == 0);
- assert_rule_not_exists("nat",
+ assert_rule_not_exists(AF_INET, "nat",
"-A POSTROUTING -s 10.10.1.0/24 -o eth0 -j MASQUERADE");
}
/* Test if the right rule is removed */
- err = __connman_iptables_append("filter", "INPUT",
+ err = __connman_iptables_append(AF_INET, "filter", "INPUT",
"-m mark --mark 1 -j LOG");
g_assert(err == 0);
- err = __connman_iptables_commit("filter");
+ err = __connman_iptables_commit(AF_INET, "filter");
g_assert(err == 0);
- assert_rule_exists("filter",
+ assert_rule_exists(AF_INET, "filter",
"-A INPUT -m mark --mark 0x1 -j LOG");
- err = __connman_iptables_append("filter", "INPUT",
+ err = __connman_iptables_append(AF_INET, "filter", "INPUT",
"-m mark --mark 2 -j LOG");
g_assert(err == 0);
- err = __connman_iptables_commit("filter");
+ err = __connman_iptables_commit(AF_INET, "filter");
g_assert(err == 0);
- assert_rule_exists("filter",
+ assert_rule_exists(AF_INET, "filter",
"-A INPUT -m mark --mark 0x1 -j LOG");
- assert_rule_exists("filter",
+ assert_rule_exists(AF_INET, "filter",
"-A INPUT -m mark --mark 0x2 -j LOG");
- err = __connman_iptables_delete("filter", "INPUT",
+ err = __connman_iptables_delete(AF_INET, "filter", "INPUT",
"-m mark --mark 2 -j LOG");
g_assert(err == 0);
- err = __connman_iptables_commit("filter");
+ err = __connman_iptables_commit(AF_INET, "filter");
g_assert(err == 0);
- assert_rule_exists("filter",
+ assert_rule_exists(AF_INET, "filter",
"-A INPUT -m mark --mark 0x1 -j LOG");
- assert_rule_not_exists("filter",
+ assert_rule_not_exists(AF_INET, "filter",
"-A INPUT -m mark --mark 0x2 -j LOG");
- err = __connman_iptables_delete("filter", "INPUT",
+ err = __connman_iptables_delete(AF_INET, "filter", "INPUT",
"-m mark --mark 1 -j LOG");
g_assert(err == 0);
- err = __connman_iptables_commit("filter");
+ err = __connman_iptables_commit(AF_INET, "filter");
g_assert(err == 0);
- assert_rule_not_exists("filter",
+ assert_rule_not_exists(AF_INET, "filter",
"-A INPUT -m mark --mark 0x1 -j LOG");
}
/* Test if 'fallthrough' targets work */
- err = __connman_iptables_append("filter", "INPUT",
+ err = __connman_iptables_append(AF_INET, "filter", "INPUT",
+ "-m mark --mark 1");
+ g_assert(err == 0);
+
+ err = __connman_iptables_append(AF_INET, "filter", "INPUT",
+ "-m mark --mark 2");
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET, "filter");
+ g_assert(err == 0);
+
+ assert_rule_exists(AF_INET, "filter", "-A INPUT -m mark --mark 0x1");
+ assert_rule_exists(AF_INET, "filter", "-A INPUT -m mark --mark 0x2");
+
+ err = __connman_iptables_delete(AF_INET, "filter", "INPUT",
+ "-m mark --mark 1");
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET, "filter");
+ g_assert(err == 0);
+
+ err = __connman_iptables_delete(AF_INET, "filter", "INPUT",
+ "-m mark --mark 2");
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET, "filter");
+ g_assert(err == 0);
+
+ assert_rule_not_exists(AF_INET, "filter",
+ "-A INPUT -m mark --mark 0x1");
+ assert_rule_not_exists(AF_INET, "filter",
+ "-A INPUT -m mark --mark 0x2");
+}
+
+static void test_ip6tables_chain0(void)
+{
+ int err;
+
+ err = __connman_iptables_new_chain(AF_INET6, "filter", "foo");
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET6, "filter");
+ g_assert(err == 0);
+
+ assert_rule_exists(AF_INET6, "filter", ":foo - [0:0]");
+
+ err = __connman_iptables_delete_chain(AF_INET6, "filter", "foo");
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET6, "filter");
+ g_assert(err == 0);
+
+ assert_rule_not_exists(AF_INET6, "filter", ":foo - [0:0]");
+}
+
+static void test_ip6tables_chain1(void)
+{
+ int err;
+
+ err = __connman_iptables_new_chain(AF_INET6, "filter", "foo");
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET6, "filter");
+ g_assert(err == 0);
+
+ err = __connman_iptables_flush_chain(AF_INET6, "filter", "foo");
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET6, "filter");
+ g_assert(err == 0);
+
+ err = __connman_iptables_delete_chain(AF_INET6, "filter", "foo");
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET6, "filter");
+ g_assert(err == 0);
+}
+
+static void test_ip6tables_chain2(void)
+{
+ int err;
+
+ err = __connman_iptables_change_policy(AF_INET6, "filter", "INPUT",
+ "DROP");
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET6, "filter");
+ g_assert(err == 0);
+
+ err = __connman_iptables_change_policy(AF_INET6, "filter", "INPUT",
+ "ACCEPT");
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET6, "filter");
+ g_assert(err == 0);
+}
+
+static void test_ip6tables_chain3(void)
+{
+ int err;
+
+ err = __connman_iptables_new_chain(AF_INET6, "filter", "user-chain-0");
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET6, "filter");
+ g_assert(err == 0);
+
+ assert_rule_exists(AF_INET6, "filter", ":user-chain-0 - [0:0]");
+
+ err = __connman_iptables_new_chain(AF_INET6, "filter", "user-chain-1");
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET6, "filter");
+ g_assert(err == 0);
+
+ assert_rule_exists(AF_INET6, "filter", ":user-chain-0 - [0:0]");
+ assert_rule_exists(AF_INET6, "filter", ":user-chain-1 - [0:0]");
+
+ err = __connman_iptables_delete_chain(AF_INET6, "filter",
+ "user-chain-1");
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET6, "filter");
+ g_assert(err == 0);
+
+ assert_rule_exists(AF_INET6, "filter", ":user-chain-0 - [0:0]");
+ assert_rule_not_exists(AF_INET6, "filter", ":user-chain-1 - [0:0]");
+
+ err = __connman_iptables_delete_chain(AF_INET6, "filter",
+ "user-chain-0");
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET6, "filter");
+ g_assert(err == 0);
+
+ assert_rule_not_exists(AF_INET6, "filter", ":user-chain-0 - [0:0]");
+}
+
+static void test_ip6tables_rule0(void)
+{
+ int err;
+
+ /* Test simple appending and removing a rule */
+
+ err = __connman_iptables_append(AF_INET6, "filter", "INPUT",
+ "-m mark --mark 1 -j LOG");
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET6, "filter");
+ g_assert(err == 0);
+
+ assert_rule_exists(AF_INET6, "filter",
+ "-A INPUT -m mark --mark 0x1 -j LOG");
+
+ err = __connman_iptables_delete(AF_INET6, "filter", "INPUT",
+ "-m mark --mark 1 -j LOG");
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET6, "filter");
+ g_assert(err == 0);
+
+ assert_rule_not_exists(AF_INET6, "filter",
+ "-A INPUT -m mark --mark 0x1 -j LOG");
+}
+
+static void test_ip6tables_rule1(void)
+{
+ int err;
+
+ /* Test if the right rule is removed */
+
+ err = __connman_iptables_append(AF_INET6, "filter", "INPUT",
+ "-m mark --mark 1 -j LOG");
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET6, "filter");
+ g_assert(err == 0);
+
+ assert_rule_exists(AF_INET6, "filter",
+ "-A INPUT -m mark --mark 0x1 -j LOG");
+
+ err = __connman_iptables_append(AF_INET6, "filter", "INPUT",
+ "-m mark --mark 2 -j LOG");
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET6, "filter");
+ g_assert(err == 0);
+
+ assert_rule_exists(AF_INET6, "filter",
+ "-A INPUT -m mark --mark 0x1 -j LOG");
+ assert_rule_exists(AF_INET6, "filter",
+ "-A INPUT -m mark --mark 0x2 -j LOG");
+
+ err = __connman_iptables_delete(AF_INET6, "filter", "INPUT",
+ "-m mark --mark 2 -j LOG");
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET6, "filter");
+ g_assert(err == 0);
+
+ assert_rule_exists(AF_INET6, "filter",
+ "-A INPUT -m mark --mark 0x1 -j LOG");
+ assert_rule_not_exists(AF_INET6, "filter",
+ "-A INPUT -m mark --mark 0x2 -j LOG");
+
+ err = __connman_iptables_delete(AF_INET6, "filter", "INPUT",
+ "-m mark --mark 1 -j LOG");
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET6, "filter");
+ g_assert(err == 0);
+
+ assert_rule_not_exists(AF_INET6, "filter",
+ "-A INPUT -m mark --mark 0x1 -j LOG");
+}
+
+static void test_ip6tables_rule2(void)
+{
+ int err;
+
+ err = __connman_iptables_append(AF_INET6, "filter", "INPUT",
+ "-p icmpv6 -m icmpv6 "
+ "--icmpv6-type 128/0 -j DROP");
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET6, "filter");
+
+ g_assert(err == 0);
+
+ assert_rule_exists(AF_INET6, "filter", "-A INPUT -p ipv6-icmp "
+ "-m icmp6 --icmpv6-type 128/0 -j DROP");
+
+ err = __connman_iptables_append(AF_INET6, "filter", "OUTPUT",
+ "-p icmpv6 -m icmpv6 "
+ "--icmpv6-type 129/0 -j DROP");
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET6, "filter");
+
+ g_assert(err == 0);
+
+ assert_rule_exists(AF_INET6, "filter", "-A OUTPUT -p ipv6-icmp "
+ "-m icmp6 --icmpv6-type 129/0 -j DROP");
+
+ err = __connman_iptables_delete(AF_INET6, "filter", "INPUT",
+ "-p icmpv6 -m icmpv6 "
+ "--icmpv6-type 128/0 -j DROP");
+
+ g_assert(err == 0);
+
+ err = __connman_iptables_delete(AF_INET6, "filter", "OUTPUT",
+ "-p icmpv6 -m icmpv6 "
+ "--icmpv6-type 129/0 -j DROP");
+
+ g_assert(err == 0);
+
+ err = __connman_iptables_commit(AF_INET6, "filter");
+
+ g_assert(err == 0);
+
+}
+
+static void test_ip6tables_target0(void)
+{
+ int err;
+
+ /* Test if 'fallthrough' targets work */
+
+ err = __connman_iptables_append(AF_INET6, "filter", "INPUT",
"-m mark --mark 1");
g_assert(err == 0);
- err = __connman_iptables_append("filter", "INPUT",
+ err = __connman_iptables_append(AF_INET6, "filter", "INPUT",
"-m mark --mark 2");
g_assert(err == 0);
- err = __connman_iptables_commit("filter");
+ err = __connman_iptables_commit(AF_INET6, "filter");
g_assert(err == 0);
- assert_rule_exists("filter", "-A INPUT -m mark --mark 0x1");
- assert_rule_exists("filter", "-A INPUT -m mark --mark 0x2");
+ assert_rule_exists(AF_INET6, "filter", "-A INPUT -m mark --mark 0x1");
+ assert_rule_exists(AF_INET6, "filter", "-A INPUT -m mark --mark 0x2");
- err = __connman_iptables_delete("filter", "INPUT",
+ err = __connman_iptables_delete(AF_INET6, "filter", "INPUT",
"-m mark --mark 1");
g_assert(err == 0);
- err = __connman_iptables_commit("filter");
+ err = __connman_iptables_commit(AF_INET6, "filter");
g_assert(err == 0);
- err = __connman_iptables_delete("filter", "INPUT",
+ err = __connman_iptables_delete(AF_INET6, "filter", "INPUT",
"-m mark --mark 2");
g_assert(err == 0);
- err = __connman_iptables_commit("filter");
+ err = __connman_iptables_commit(AF_INET6, "filter");
g_assert(err == 0);
- assert_rule_not_exists("filter", "-A INPUT -m mark --mark 0x1");
- assert_rule_not_exists("filter", "-A INPUT -m mark --mark 0x2");
+ assert_rule_not_exists(AF_INET6, "filter", "-A INPUT "
+ "-m mark --mark 0x1");
+ assert_rule_not_exists(AF_INET6, "filter", "-A INPUT "
+ "-m mark --mark 0x2");
}
-struct connman_notifier *nat_notifier;
+const struct connman_notifier *nat_notifier;
struct connman_service {
char *dummy;
return "eth0";
}
-int connman_notifier_register(struct connman_notifier *notifier)
+int connman_notifier_register(const struct connman_notifier *notifier)
{
nat_notifier = notifier;
return 0;
}
-void connman_notifier_unregister(struct connman_notifier *notifier)
+void connman_notifier_unregister(const struct connman_notifier *notifier)
{
nat_notifier = NULL;
}
g_assert(err == 0);
/* test that table is empty */
- err = __connman_iptables_append("nat", "POSTROUTING",
+ err = __connman_iptables_append(AF_INET, "nat", "POSTROUTING",
"-s 192.168.2.1/24 -o eth0 -j MASQUERADE");
g_assert(err == 0);
- err = __connman_iptables_commit("nat");
+ err = __connman_iptables_commit(AF_INET, "nat");
g_assert(err == 0);
- assert_rule_exists("nat",
+ assert_rule_exists(AF_INET, "nat",
"-A POSTROUTING -s 192.168.2.0/24 -o eth0 -j MASQUERADE");
- err = __connman_iptables_delete("nat", "POSTROUTING",
+ err = __connman_iptables_delete(AF_INET, "nat", "POSTROUTING",
"-s 192.168.2.1/24 -o eth0 -j MASQUERADE");
g_assert(err == 0);
- err = __connman_iptables_commit("nat");
+ err = __connman_iptables_commit(AF_INET, "nat");
g_assert(err == 0);
- assert_rule_not_exists("nat",
+ assert_rule_not_exists(AF_INET, "nat",
"-A POSTROUTING -s 192.168.2.0/24 -o eth0 -j MASQUERADE");
__connman_nat_disable("bridge");
g_assert(err == 0);
/* test that table is not empty */
- err = __connman_iptables_append("nat", "POSTROUTING",
+ err = __connman_iptables_append(AF_INET, "nat", "POSTROUTING",
"-s 192.168.2.1/24 -o eth0 -j MASQUERADE");
g_assert(err == 0);
- err = __connman_iptables_commit("nat");
+ err = __connman_iptables_commit(AF_INET, "nat");
g_assert(err == 0);
__connman_nat_disable("bridge");
/* test that table is empty again */
- err = __connman_iptables_delete("nat", "POSTROUTING",
+ err = __connman_iptables_delete(AF_INET, "nat", "POSTROUTING",
"-s 192.168.2.1/24 -o eth0 -j MASQUERADE");
g_assert(err == 0);
- err = __connman_iptables_commit("nat");
+ err = __connman_iptables_commit(AF_INET, "nat");
g_assert(err == 0);
g_free(service);
g_test_add_func("/iptables/rule1", test_iptables_rule1);
g_test_add_func("/iptables/rule2", test_iptables_rule2);
g_test_add_func("/iptables/target0", test_iptables_target0);
+ g_test_add_func("/ip6tables/chain0", test_ip6tables_chain0);
+ g_test_add_func("/ip6tables/chain1", test_ip6tables_chain1);
+ g_test_add_func("/ip6tables/chain2", test_ip6tables_chain2);
+ g_test_add_func("/ip6tables/chain3", test_ip6tables_chain3);
+ g_test_add_func("/ip6tables/rule0", test_ip6tables_rule0);
+ g_test_add_func("/ip6tables/rule1", test_ip6tables_rule1);
+ g_test_add_func("/ip6tables/rule2", test_ip6tables_rule2);
+ g_test_add_func("/ip6tables/target0", test_ip6tables_target0);
g_test_add_func("/nat/basic0", test_nat_basic0);
g_test_add_func("/nat/basic1", test_nat_basic1);
#include <stdlib.h>
#include <string.h>
#include <signal.h>
-#include <sys/poll.h>
+#include <poll.h>
#include <sys/signalfd.h>
#include <unistd.h>
return "not implemented";
case G_RESOLV_RESULT_STATUS_REFUSED:
return "refused";
+ case G_RESOLV_RESULT_STATUS_NO_ANSWER:
+ return "no answer";
}
return NULL;
const char *val;
dbus_message_iter_get_basic(&value, &val);
- if (info->bearer)
- g_free(info->bearer);
-
+ g_free(info->bearer);
info->bearer = g_strdup(val);
} else if (g_str_equal(key, "Name")) {
const char *val;
dbus_message_iter_get_basic(&value, &val);
- if (info->name)
- g_free(info->name);
-
+ g_free(info->name);
info->name = g_strdup(val);
} else if (g_str_equal(key, "Interface")) {
const char *val;
dbus_message_iter_get_basic(&value, &val);
- if (info->interface)
- g_free(info->interface);
-
+ g_free(info->interface);
info->interface = g_strdup(val);
- } else if (g_str_equal(key, "ConnectionType")
- ) {
+ } else if (g_str_equal(key, "ConnectionType")) {
const char *val;
dbus_message_iter_get_basic(&value, &val);
info->type = string2type(val);
+
+ } else if (g_str_equal(key, "Allowedinterface")) {
+ const char *val;
+ dbus_message_iter_get_basic(&value, &val);
+
+ g_free(info->allowed_interface);
+ info->allowed_interface = g_strdup(val);
+
+ } else if (g_str_equal(key, "ContextIdentifier")) {
+ const char *val;
+ dbus_message_iter_get_basic(&value, &val);
+
+ g_free(info->context_identifier);
+ info->context_identifier = g_strdup(val);
+
+ } else {
+ g_assert(FALSE);
+ return __connman_error_invalid_arguments(msg);
+ }
+ break;
+ case DBUS_TYPE_BOOLEAN:
+ if (g_str_equal(key, "SourceIPRule")) {
+ dbus_bool_t val;
+ dbus_message_iter_get_basic(&value, &val);
+
+ info->source_ip_rule = val;
+
} else {
g_assert(FALSE);
return __connman_error_invalid_arguments(msg);
enum connman_session_type type;
/* ipv4, ipv6 dicts */
GSList *allowed_bearers;
+ char *allowed_interface;
+ bool source_ip_rule;
+ char *context_identifier;
};
struct test_session {
util_test_func_t teardown;
};
-static void run_test_cb(gconstpointer data)
+static void run_test_cb(gpointer fixture, gconstpointer data)
{
const struct test_data_cb *cbd = data;
struct test_fix *fix;
g_test_trap_assert_passed();
#else
- util_call(fix, func, NULL);
+ util_call(fix, cbd->func, NULL);
g_main_loop_run(fix->main_loop);
#endif
cleanup_fix(fix);
}
+static void cleanup_test_cb(gpointer fixture, gconstpointer data)
+{
+ struct test_data_cb *cbd = (void *)data;
+
+ g_free(cbd);
+}
+
void util_test_add(const char *test_name, util_test_func_t test_func,
util_test_func_t setup, util_test_func_t teardown)
{
cbd->setup = setup;
cbd->teardown = teardown;
- g_test_add_vtable(test_name, 0, cbd, NULL,
- (GTestFixtureFunc) run_test_cb,
- (GTestFixtureFunc) g_free);
+ g_test_add_vtable(test_name, 0, cbd, NULL, run_test_cb, cleanup_test_cb);
}
void util_session_create(struct test_fix *fix, unsigned int max_sessions)
g_slist_foreach(session->info->allowed_bearers,
bearer_info_cleanup, NULL);
g_slist_free(session->info->allowed_bearers);
+ g_free(session->info->allowed_interface);
+ g_free(session->info->context_identifier);
session->notify = NULL;
g_free(session->notify_path);
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
-#include <sys/poll.h>
+#include <poll.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
pool = __connman_ippool_create(23, 1, 20, NULL, NULL);
g_assert(pool);
- __connman_ippool_unref(pool);
+ __connman_ippool_free(pool);
}
__connman_ippool_cleanup();
"\tgateway %s broadcast %s mask %s", start_ip, end_ip,
gateway, broadcast, subnet_mask);
- __connman_ippool_unref(pool);
+ __connman_ippool_free(pool);
}
__connman_ippool_cleanup();
for (it = list; it; it = it->next) {
pool = it->data;
- __connman_ippool_unref(pool);
+ __connman_ippool_free(pool);
}
g_slist_free(list);
g_assert(flag == 1);
- __connman_ippool_unref(pool);
+ __connman_ippool_free(pool);
flag = 0;
g_assert(flag == 1);
- __connman_ippool_unref(pool);
+ __connman_ippool_free(pool);
__connman_ippool_cleanup();
}
g_assert(flag == 0);
- __connman_ippool_unref(pool1);
- __connman_ippool_unref(pool2);
+ __connman_ippool_free(pool1);
+ __connman_ippool_free(pool2);
__connman_ippool_cleanup();
}
__connman_ippool_newaddr(25, start_ip, 24);
g_assert(flag == 1);
- __connman_ippool_unref(pool1);
- __connman_ippool_unref(pool2);
+ __connman_ippool_free(pool1);
+ __connman_ippool_free(pool2);
__connman_ippool_cleanup();
}
--- /dev/null
+/*
+ * ConnMan firewall unit tests
+ *
+ * Copyright (C) 2019 Jolla Ltd. All rights reserved.
+ * Contact: jussi.laakkonen@jolla.com
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <gdbus.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <xtables.h>
+#include <errno.h>
+#include <sys/wait.h>
+
+#include "src/connman.h"
+
+enum configtype {
+ TEST_CONFIG_PASS = 0x0001,
+ TEST_CONFIG_INIT_FAIL = 0x0002,
+ TEST_CONFIG_FIND_MATCH_FAIL = 0x0004,
+ TEST_CONFIG_FIND_TARGET_FAIL = 0x0008,
+ TEST_CONFIG_PARSE_PROTOCOL_FAIL = 0x0010,
+ TEST_CONFIG_MFCALL_FAIL = 0x0020,
+ TEST_CONFIG_TFCALL_FAIL = 0x0040,
+ TEST_CONFIG_MPCALL_FAIL = 0x0080,
+ TEST_CONFIG_TPCALL_FAIL = 0x0100,
+ TEST_CONFIG_INSMOD_FAIL = 0x0200,
+ TEST_CONFIG_COMPATIBLE_REV_FAIL = 0x0400,
+ TEST_CONFIG_OPTIONS_XFRM_FAIL = 0x0800,
+ TEST_CONFIG_MERGE_OPTIONS_FAIL = 0x1000,
+};
+
+enum configtype test_config_type = TEST_CONFIG_PASS;
+
+static void set_test_config(enum configtype type)
+{
+ test_config_type = type;
+}
+
+/* Start of dummies */
+
+/* xtables dummies */
+
+/* From /usr/include/linux/netfilter_ipv4/ip_tables.h */
+#define IPT_BASE_CTL 64
+#define IPT_SO_SET_REPLACE (IPT_BASE_CTL)
+#define IPT_SO_SET_ADD_COUNTERS (IPT_BASE_CTL + 1)
+#define IPT_SO_GET_INFO (IPT_BASE_CTL)
+#define IPT_SO_GET_ENTRIES (IPT_BASE_CTL + 1)
+
+/* From /usr/include/linux/netfilter_ipv6/ip6_tables.h */
+#define IP6T_BASE_CTL 64
+#define IP6T_SO_SET_REPLACE (IP6T_BASE_CTL)
+#define IP6T_SO_SET_ADD_COUNTERS (IP6T_BASE_CTL + 1)
+#define IP6T_SO_GET_INFO (IP6T_BASE_CTL)
+#define IP6T_SO_GET_ENTRIES (IP6T_BASE_CTL + 1)
+
+int xt_match_parse(int c, char **argv, int invert, unsigned int *flags,
+ const void *entry, struct xt_entry_match **match)
+{
+ return 0;
+}
+
+int xt_target_parse(int c, char **argv, int invert, unsigned int *flags,
+ const void *entry, struct xt_entry_target **targetinfo)
+{
+ return 0;
+}
+
+static void xt_x6_parse(struct xt_option_call *opt) {
+ return;
+}
+
+static void xt_x6_fcheck(struct xt_fcheck_call *call) {
+ return;
+}
+
+static struct xtables_match xt_match = {
+ .version = "1",
+ .next = NULL,
+ .name = "tcp",
+ .real_name = "tcp",
+ .revision = 1,
+ .ext_flags = 0,
+ .family = AF_INET,
+ .size = XT_ALIGN(sizeof(struct xtables_match)),
+ .userspacesize = XT_ALIGN(sizeof(struct xtables_match)),
+ .parse = xt_match_parse,
+ .extra_opts = NULL,
+ .x6_parse = xt_x6_parse,
+ .x6_fcheck = xt_x6_fcheck,
+ .x6_options = NULL,
+ .udata_size = XT_ALIGN(sizeof(struct xtables_match)),
+ .udata = NULL,
+ .option_offset = 32,
+ .m = NULL,
+ .mflags = 0,
+ .loaded = 1,
+};
+
+static struct xtables_target xt_target = {
+ .version = "1",
+ .next = NULL,
+ .name = "ACCEPT",
+ .real_name = "ACCEPT",
+ .revision = 1,
+ .ext_flags = 0,
+ .family = AF_INET,
+ .size = XT_ALIGN(sizeof(struct xtables_match)),
+ .userspacesize = XT_ALIGN(sizeof(struct xtables_match)),
+ .parse = xt_target_parse,
+ .extra_opts = NULL,
+ .x6_parse = xt_x6_parse,
+ .x6_fcheck = xt_x6_fcheck,
+ .x6_options = NULL,
+ .udata_size = XT_ALIGN(sizeof(struct xtables_match)),
+ .udata = NULL,
+ .option_offset = 32,
+ .t = NULL,
+ .tflags = 0,
+ .used = 0,
+ .loaded = 1,
+};
+
+struct xtables_globals *xt_params = NULL;
+
+struct xtables_match *xtables_matches = NULL;
+struct xtables_target *xtables_targets = NULL;
+
+static void call_error(const char *src)
+{
+ g_assert(xt_params);
+
+ DBG("%s", src);
+
+ xt_params->exit_err(PARAMETER_PROBLEM, "longjmp() %s", src);
+}
+
+int xtables_init_all(struct xtables_globals *xtp, uint8_t nfproto)
+{
+ DBG("%d", nfproto);
+
+ if (test_config_type & TEST_CONFIG_INIT_FAIL)
+ call_error("xtables_init_all");
+
+ xt_params = xtp;
+
+ return 0;
+}
+
+struct xtables_match *xtables_find_match(const char *name,
+ enum xtables_tryload tryload,
+ struct xtables_rule_match **matches)
+{
+ DBG("name %s type %d", name, tryload);
+
+ if (test_config_type & TEST_CONFIG_FIND_MATCH_FAIL)
+ call_error("xtables_find_match");
+
+ *matches = g_try_new0(struct xtables_rule_match, 1);
+ (*matches)->next = NULL;
+ (*matches)->match = &xt_match;
+ (*matches)->completed = 0;
+
+ return &xt_match;
+}
+
+struct xtables_target *xtables_find_target(const char *name,
+ enum xtables_tryload tryload)
+{
+ DBG("name %s type %d", name, tryload);
+
+ if (test_config_type & TEST_CONFIG_FIND_TARGET_FAIL)
+ call_error("xtables_find_target");
+
+ return &xt_target;
+}
+
+uint16_t xtables_parse_protocol(const char *s)
+{
+ DBG("protocol %s", s);
+
+ if (test_config_type & TEST_CONFIG_PARSE_PROTOCOL_FAIL)
+ call_error("xtables_parse_protocol");
+
+ if (!g_strcmp0(s, "tcp"))
+ return 6;
+
+ return 0;
+}
+
+void xtables_option_mfcall(struct xtables_match *m)
+{
+ DBG("");
+
+ if (test_config_type & TEST_CONFIG_MFCALL_FAIL)
+ call_error("xtables_option_mfcall");
+
+ m = &xt_match;
+
+ return;
+}
+
+void xtables_option_tfcall(struct xtables_target *t)
+{
+ DBG("");
+
+ if (test_config_type & TEST_CONFIG_TFCALL_FAIL)
+ call_error("xtables_option_tfcall");
+
+ t = &xt_target;
+
+ return;
+}
+
+void xtables_option_mpcall(unsigned int c, char **argv, bool invert,
+ struct xtables_match *m, void *fw)
+{
+ DBG("");
+
+ if (test_config_type & TEST_CONFIG_MPCALL_FAIL)
+ call_error("xtables_option_mpcall");
+
+ m = &xt_match;
+
+ return;
+}
+
+void xtables_option_tpcall(unsigned int c, char **argv, bool invert,
+ struct xtables_target *t, void *fw)
+{
+ DBG("");
+
+ if (test_config_type & TEST_CONFIG_TPCALL_FAIL)
+ call_error("xtables_option_tpcall");
+
+ t = &xt_target;
+
+ return;
+}
+
+int xtables_insmod(const char *modname, const char *modprobe, bool quiet)
+{
+ DBG("mod %s modprobe %s quiet %s", modname, modprobe,
+ quiet ? "true" : "false");
+
+ if (test_config_type & TEST_CONFIG_INSMOD_FAIL)
+ call_error("xtables_insmod");
+
+ return 0;
+}
+
+int xtables_compatible_revision(const char *name, uint8_t revision, int opt)
+{
+ DBG("name %s rev %d opt %d", name, revision, opt);
+
+ if (test_config_type & TEST_CONFIG_COMPATIBLE_REV_FAIL)
+ call_error("xtables_compatible_revision");
+
+ return 1;
+}
+
+struct option *xtables_options_xfrm(struct option *opt1, struct option *opt2,
+ const struct xt_option_entry *entry,
+ unsigned int *dummy)
+{
+ if (test_config_type & TEST_CONFIG_OPTIONS_XFRM_FAIL)
+ call_error("xtables_options_xfrm");
+
+ return opt1;
+}
+
+struct option *xtables_merge_options(struct option *orig_opts,
+ struct option *oldopts, const struct option *newopts,
+ unsigned int *option_offset)
+{
+ if (test_config_type & TEST_CONFIG_MERGE_OPTIONS_FAIL)
+ call_error("xtables_merge_options");
+
+ return orig_opts;
+}
+
+/* End of xtables dummies */
+
+/* socket dummies */
+
+int global_sockfd = 1000;
+
+int socket(int domain, int type, int protocol)
+{
+ DBG("domain %d type %d protocol %d", domain, type, protocol);
+
+ return global_sockfd;
+}
+
+int getsockopt(int sockfd, int level, int optname, void *optval,
+ socklen_t *optlen)
+{
+ struct ipt_getinfo *info = NULL;
+ struct ipt_get_entries *entries = NULL;
+ struct ip6t_getinfo *info6 = NULL;
+ struct ip6t_get_entries *entries6 = NULL;
+
+ DBG("");
+
+ g_assert_cmpint(global_sockfd, ==, sockfd);
+
+ switch (level) {
+ case IPPROTO_IP:
+ DBG("IPPROTO_IP");
+
+ switch (optname) {
+ case IPT_SO_GET_ENTRIES:
+ DBG("IPT_SO_GET_ENTRIES");
+ optval = entries;
+ break;
+ case IPT_SO_GET_INFO:
+ DBG("IPT_SO_GET_INFO");
+ optval = info;
+ break;
+ default:
+ DBG("optname %d", optname);
+ return -1;
+ }
+
+ break;
+ case IPPROTO_IPV6:
+ DBG("IPPROTO_IPV6");
+ switch (optname) {
+ case IP6T_SO_GET_ENTRIES:
+ DBG("IP6T_SO_GET_ENTRIES");
+ optval = entries6;
+ break;
+ case IP6T_SO_GET_INFO:
+ DBG("IP6T_SO_GET_INFO");
+ optval = info6;
+ break;
+ default:
+ DBG("optname %d", optname);
+ return -1;
+ }
+
+ break;
+ default:
+ return -1;
+ }
+
+ *optlen = 0;
+ return 0;
+}
+
+int setsockopt(int sockfd, int level, int optname, const void *optval,
+ socklen_t optlen)
+{
+ DBG("");
+
+ g_assert_cmpint(global_sockfd, ==, sockfd);
+
+ switch (level) {
+ case IPPROTO_IP:
+ DBG("IPPROTO_IP");
+ switch (optname) {
+ case IPT_SO_SET_REPLACE:
+ DBG("IPT_SO_SET_REPLACE");
+ return 0;
+ case IPT_SO_SET_ADD_COUNTERS:
+ DBG("IPT_SO_SET_ADD_COUNTERS");
+ return 0;
+ default:
+ DBG("optname %d", optname);
+ return -1;
+ }
+
+ break;
+ case IPPROTO_IPV6:
+ DBG("IPPROTO_IPV6");
+
+ switch (optname) {
+ case IP6T_SO_SET_REPLACE:
+ DBG("IP6T_SO_SET_REPLACE");
+ return 0;
+ case IP6T_SO_SET_ADD_COUNTERS:
+ DBG("IP6T_SO_SET_ADD_COUNTERS");
+ return 0;
+ default:
+ DBG("optname %d", optname);
+ return -1;
+ }
+
+ break;
+ default:
+ return -1;
+ }
+}
+
+/* End of socket dummies */
+
+/* End of dummies */
+
+static void iptables_test_basic0()
+{
+ set_test_config(TEST_CONFIG_PASS);
+
+ __connman_iptables_init();
+
+ g_assert(!__connman_iptables_new_chain(AF_INET, "filter", "INPUT"));
+ g_assert_cmpint(__connman_iptables_insert(AF_INET, "filter", "INPUT",
+ "-p tcp -m tcp --dport 42 -j ACCEPT"), ==, 0);
+
+ __connman_iptables_cleanup();
+}
+
+/*
+ * These ok0...ok6 tests test the error handling. The setjmp() position is set
+ * properly for the functions that will trigger it and as a result, depending on
+ * iptables.c, there will be an error or no error at all. Each of these should
+ * return gracefully without calling exit().
+ */
+
+static void iptables_test_jmp_ok0()
+{
+ set_test_config(TEST_CONFIG_FIND_MATCH_FAIL);
+
+ __connman_iptables_init();
+
+ g_assert(!__connman_iptables_new_chain(AF_INET, "filter", "INPUT"));
+ g_assert_cmpint(__connman_iptables_insert(AF_INET, "filter", "INPUT",
+ "-p tcp -m tcp -j ACCEPT"), ==, -EINVAL);
+
+ __connman_iptables_cleanup();
+}
+
+static void iptables_test_jmp_ok1()
+{
+ set_test_config(TEST_CONFIG_FIND_TARGET_FAIL);
+
+ __connman_iptables_init();
+
+ g_assert(!__connman_iptables_new_chain(AF_INET, "filter", "INPUT"));
+ g_assert_cmpint(__connman_iptables_insert(AF_INET, "filter", "INPUT",
+ "-p tcp -m tcp -j ACCEPT"), ==, -EINVAL);
+
+ __connman_iptables_cleanup();
+}
+
+static void iptables_test_jmp_ok2()
+{
+ set_test_config(TEST_CONFIG_PARSE_PROTOCOL_FAIL);
+
+ __connman_iptables_init();
+
+ g_assert(!__connman_iptables_new_chain(AF_INET, "filter", "INPUT"));
+ g_assert_cmpint(__connman_iptables_insert(AF_INET, "filter", "INPUT",
+ "-p tcp -m tcp --dport 42 -j ACCEPT"), ==,
+ -EINVAL);
+
+ __connman_iptables_cleanup();
+}
+
+static void iptables_test_jmp_ok3()
+{
+ set_test_config(TEST_CONFIG_TFCALL_FAIL);
+
+ __connman_iptables_init();
+
+ g_assert(!__connman_iptables_new_chain(AF_INET, "filter", "INPUT"));
+ g_assert_cmpint(__connman_iptables_insert(AF_INET, "filter", "INPUT",
+ "-p tcp -m tcp --dport 42 -j ACCEPT"), ==,
+ -EINVAL);
+
+ __connman_iptables_cleanup();
+}
+
+static void iptables_test_jmp_ok4()
+{
+ set_test_config(TEST_CONFIG_MFCALL_FAIL);
+
+ __connman_iptables_init();
+
+ g_assert(!__connman_iptables_new_chain(AF_INET, "filter", "INPUT"));
+
+ g_assert_cmpint(__connman_iptables_insert(AF_INET, "filter", "INPUT",
+ "-p tcp -m tcp --dport 42 -j ACCEPT"), ==,
+ -EINVAL);
+
+ __connman_iptables_cleanup();
+}
+
+static void iptables_test_jmp_ok5()
+{
+ set_test_config(TEST_CONFIG_TPCALL_FAIL);
+
+ __connman_iptables_init();
+
+ g_assert(!__connman_iptables_new_chain(AF_INET, "filter", "INPUT"));
+
+ g_assert_cmpint(__connman_iptables_insert(AF_INET, "filter", "INPUT",
+ "-p tcp -m tcp --dport 42 -j ACCEPT "
+ "--comment test"), ==, -EINVAL);
+
+ __connman_iptables_cleanup();
+}
+
+static void iptables_test_jmp_ok6()
+{
+ set_test_config(TEST_CONFIG_MPCALL_FAIL);
+
+ __connman_iptables_init();
+
+ g_assert(!__connman_iptables_new_chain(AF_INET, "filter", "INPUT"));
+
+ g_assert_cmpint(__connman_iptables_insert(AF_INET, "filter", "INPUT",
+ "-p tcp -m tcp --dport 42 -j ACCEPT"), ==,
+ -EINVAL);
+
+ __connman_iptables_cleanup();
+}
+
+/*
+ * These exit0...exit2 tests invoke longjmp() via xtables exit_err() without
+ * having env saved with setjmp(). All of these will result calling exit(), thus
+ * forking is required.
+ */
+
+static void iptables_test_jmp_exit0()
+{
+ pid_t cpid = 0;
+ int cstatus = 0;
+
+ /*
+ * Should work as normal but fork() is needed as exit() is called
+ * when longjmp() is not allowed. At xtables_init_all() exit_err() is
+ * not normally called.
+ */
+ set_test_config(TEST_CONFIG_INIT_FAIL);
+
+ /* Child, run iptables test */
+ if (fork() == 0) {
+ __connman_iptables_init();
+
+ /*
+ * Address family must be different from previous use because
+ * otherwise xtables_init_all() is not called.
+ */
+ g_assert(!__connman_iptables_new_chain(AF_INET6, "filter",
+ "INPUT"));
+
+ __connman_iptables_cleanup();
+ exit(0);
+ } else {
+ cpid = wait(&cstatus); /* Wait for child */
+ }
+
+ DBG("parent %d child %d exit %d", getpid(), cpid, WEXITSTATUS(cstatus));
+
+ g_assert(WIFEXITED(cstatus));
+ g_assert_cmpint(WEXITSTATUS(cstatus), ==, PARAMETER_PROBLEM);
+}
+
+static void iptables_test_jmp_exit1()
+{
+ pid_t cpid = 0;
+ int cstatus = 0;
+
+ /*
+ * Should work as normal but fork() is needed as exit() is called
+ * when longjmp() is not allowed. At xtables_insmod() exit_err() is not
+ * normally called.
+ */
+ set_test_config(TEST_CONFIG_INSMOD_FAIL);
+
+ if (fork() == 0) {
+ __connman_iptables_init();
+
+ g_assert(!__connman_iptables_new_chain(AF_INET, "filter",
+ "INPUT"));
+
+ __connman_iptables_cleanup();
+ exit(0);
+ } else {
+ cpid = wait(&cstatus);
+ }
+
+ DBG("parent %d child %d exit %d", getpid(), cpid, WEXITSTATUS(cstatus));
+
+ g_assert(WIFEXITED(cstatus));
+ g_assert_cmpint(WEXITSTATUS(cstatus), ==, PARAMETER_PROBLEM);
+}
+
+static void iptables_test_jmp_exit2()
+{
+ pid_t cpid = 0;
+ int cstatus = 0;
+
+ set_test_config(TEST_CONFIG_OPTIONS_XFRM_FAIL|
+ TEST_CONFIG_MERGE_OPTIONS_FAIL|
+ TEST_CONFIG_COMPATIBLE_REV_FAIL);
+
+ if (fork() == 0) {
+ __connman_iptables_init();
+
+ g_assert(!__connman_iptables_new_chain(AF_INET, "filter",
+ "INPUT"));
+ g_assert_cmpint(__connman_iptables_insert(AF_INET, "filter",
+ "INPUT", "-p tcp -m tcp --dport 42 "
+ "-j ACCEPT --comment test"), ==, 0);
+
+ __connman_iptables_cleanup();
+ exit(0);
+ } else {
+ cpid = wait(&cstatus);
+ }
+
+ DBG("parent %d child %d exit %d", getpid(), cpid, WEXITSTATUS(cstatus));
+
+ g_assert(WIFEXITED(cstatus));
+ g_assert_cmpint(WEXITSTATUS(cstatus), ==, PARAMETER_PROBLEM);
+}
+
+static gchar *option_debug = NULL;
+
+static bool parse_debug(const char *key, const char *value,
+ gpointer user_data, GError **error)
+{
+ if (value)
+ option_debug = g_strdup(value);
+ else
+ option_debug = g_strdup("*");
+
+ return true;
+}
+
+static GOptionEntry options[] = {
+ { "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG,
+ G_OPTION_ARG_CALLBACK, parse_debug,
+ "Specify debug options to enable", "DEBUG" },
+ { NULL },
+};
+
+int main (int argc, char *argv[])
+{
+ GOptionContext *context;
+ GError *error = NULL;
+
+ g_test_init(&argc, &argv, NULL);
+
+ context = g_option_context_new(NULL);
+ g_option_context_add_main_entries(context, options, NULL);
+
+ if (!g_option_context_parse(context, &argc, &argv, &error)) {
+ if (error) {
+ g_printerr("%s\n", error->message);
+ g_error_free(error);
+ } else
+ g_printerr("An unknown error occurred\n");
+ return 1;
+ }
+
+ g_option_context_free(context);
+
+ __connman_log_init(argv[0], option_debug, false, false,
+ "Unit Tests Connection Manager", VERSION);
+
+ g_test_add_func("/iptables/test_basic0", iptables_test_basic0);
+ g_test_add_func("/iptables/test_jmp_ok0", iptables_test_jmp_ok0);
+ g_test_add_func("/iptables/test_jmp_ok1", iptables_test_jmp_ok1);
+ g_test_add_func("/iptables/test_jmp_ok2", iptables_test_jmp_ok2);
+ g_test_add_func("/iptables/test_jmp_ok3", iptables_test_jmp_ok3);
+ g_test_add_func("/iptables/test_jmp_ok4", iptables_test_jmp_ok4);
+ g_test_add_func("/iptables/test_jmp_ok5", iptables_test_jmp_ok5);
+ g_test_add_func("/iptables/test_jmp_ok6", iptables_test_jmp_ok6);
+ g_test_add_func("/iptables/test_jmp_exit0", iptables_test_jmp_exit0);
+ g_test_add_func("/iptables/test_jmp_exit1", iptables_test_jmp_exit1);
+ g_test_add_func("/iptables/test_jmp_exit2", iptables_test_jmp_exit2);
+
+ return g_test_run();
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
l2tp_write_option(fd, "nodetach", NULL);
l2tp_write_option(fd, "lock", NULL);
+ l2tp_write_option(fd, "logfd", "2");
l2tp_write_option(fd, "usepeerdns", NULL);
l2tp_write_option(fd, "noipdefault", NULL);
l2tp_write_option(fd, "noauth", NULL);
}
}
+static int oc_route_env_parse(struct vpn_provider *provider, const char *key,
+ int *family, unsigned long *idx, enum vpn_provider_route_type *type)
+{
+ char *end;
+ const char *start;
+
+ if (g_str_has_prefix(key, "CISCO_SPLIT_INC_")) {
+ *family = AF_INET;
+ start = key + strlen("CISCO_SPLIT_INC_");
+ } else if (g_str_has_prefix(key, "CISCO_IPV6_SPLIT_INC_")) {
+ *family = AF_INET6;
+ start = key + strlen("CISCO_IPV6_SPLIT_INC_");
+ } else
+ return -EINVAL;
+
+ *idx = g_ascii_strtoull(start, &end, 10);
+
+ if (strncmp(end, "_ADDR", 5) == 0)
+ *type = VPN_PROVIDER_ROUTE_TYPE_ADDR;
+ else if (strncmp(end, "_MASK", 5) == 0)
+ *type = VPN_PROVIDER_ROUTE_TYPE_MASK;
+ else if (strncmp(end, "_MASKLEN", 8) == 0 && *family == AF_INET6)
+ *type = VPN_PROVIDER_ROUTE_TYPE_MASK;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
static struct vpn_driver vpn_driver = {
.notify = oc_notify,
.connect = oc_connect,
.error_code = oc_error_code,
.save = oc_save,
+ .route_env_parse = oc_route_env_parse,
};
static int openconnect_init(void)
return IFF_TUN;
}
+static int ov_route_env_parse(struct vpn_provider *provider, const char *key,
+ int *family, unsigned long *idx, enum vpn_provider_route_type *type)
+{
+ char *end;
+ const char *start;
+
+ if (g_str_has_prefix(key, "route_network_")) {
+ start = key + strlen("route_network_");
+ *type = VPN_PROVIDER_ROUTE_TYPE_ADDR;
+ } else if (g_str_has_prefix(key, "route_netmask_")) {
+ start = key + strlen("route_netmask_");
+ *type = VPN_PROVIDER_ROUTE_TYPE_MASK;
+ } else if (g_str_has_prefix(key, "route_gateway_")) {
+ start = key + strlen("route_gateway_");
+ *type = VPN_PROVIDER_ROUTE_TYPE_GW;
+ } else
+ return -EINVAL;
+
+ *family = AF_INET;
+ *idx = g_ascii_strtoull(start, &end, 10);
+
+ return 0;
+}
+
static struct vpn_driver vpn_driver = {
.notify = ov_notify,
.connect = ov_connect,
.save = ov_save,
.device_flags = ov_device_flags,
+ .route_env_parse = ov_route_env_parse,
};
static int openvpn_init(void)
connman_task_add_argument(task, "nodetach", NULL);
connman_task_add_argument(task, "lock", NULL);
+ connman_task_add_argument(task, "logfd", "2");
connman_task_add_argument(task, "usepeerdns", NULL);
connman_task_add_argument(task, "noipdefault", NULL);
connman_task_add_argument(task, "noauth", NULL);
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
if (!data)
goto vpn_exit;
+ /* The task may die after we have already started the new one */
+ if (data->task != task)
+ goto done;
+
state = data->state;
stop_vpn(provider);
g_free(data);
}
+done:
connman_task_destroy(task);
}
vpn_newlink, provider);
err = connman_inet_ifup(index);
if (err < 0) {
- if (err == -EALREADY)
+ if (err == -EALREADY) {
/*
* So the interface is up already, that is just
* great. Unfortunately in this case the
* newlink watch might not have been called at
* all. We must manually call it here so that
* the provider can go to ready state and the
- * routes are setup properly.
+ * routes are setup properly. Also reset flags
+ * so vpn_newlink() can handle the change.
*/
+ data->flags = 0;
vpn_newlink(IFF_UP, 0, provider);
- else
+ } else {
DBG("Cannot take interface %d up err %d/%s",
index, -err, strerror(-err));
+ }
}
break;
}
data->tun_flags = flags;
+ g_free(data->if_name);
data->if_name = (char *)g_strdup(ifr.ifr_name);
if (!data->if_name) {
connman_error("Failed to allocate memory");
static int vpn_remove(struct vpn_provider *provider)
{
struct vpn_data *data;
+ struct vpn_driver_data *driver_data;
+ const char *name;
+ int err = 0;
data = vpn_provider_get_data(provider);
+ name = vpn_provider_get_driver_name(provider);
+
if (!data)
- return 0;
+ goto call_remove;
if (data->watch != 0) {
vpn_provider_unref(provider);
g_usleep(G_USEC_PER_SEC);
stop_vpn(provider);
- return 0;
+
+call_remove:
+ if (!name)
+ return 0;
+
+ driver_data = g_hash_table_lookup(driver_hash, name);
+
+ if (driver_data && driver_data->vpn_driver->remove)
+ err = driver_data->vpn_driver->remove(provider);
+
+ if (err)
+ DBG("%p vpn_driver->remove() returned %d", provider, err);
+
+ return err;
}
static int vpn_save(struct vpn_provider *provider, GKeyFile *keyfile)
return 0;
}
+static int vpn_route_env_parse(struct vpn_provider *provider, const char *key,
+ int *family, unsigned long *idx,
+ enum vpn_provider_route_type *type)
+{
+ struct vpn_driver_data *vpn_driver_data = NULL;
+ const char *name = NULL;
+
+ if (!provider)
+ return -EINVAL;
+
+ name = vpn_provider_get_driver_name(provider);
+ vpn_driver_data = g_hash_table_lookup(driver_hash, name);
+
+ if (vpn_driver_data && vpn_driver_data->vpn_driver->route_env_parse)
+ return vpn_driver_data->vpn_driver->route_env_parse(provider, key,
+ family, idx, type);
+
+ return 0;
+}
+
int vpn_register(const char *name, struct vpn_driver *vpn_driver,
const char *program)
{
data->provider_driver.remove = vpn_remove;
data->provider_driver.save = vpn_save;
data->provider_driver.set_state = vpn_set_state;
+ data->provider_driver.route_env_parse = vpn_route_env_parse;
if (!driver_hash)
driver_hash = g_hash_table_new_full(g_str_hash,
vpn_provider_connect_cb_t cb, const char *dbus_sender,
void *user_data);
void (*disconnect) (struct vpn_provider *provider);
+ int (*remove) (struct vpn_provider *provider);
int (*error_code) (struct vpn_provider *provider, int exit_code);
int (*save) (struct vpn_provider *provider, GKeyFile *keyfile);
int (*device_flags) (struct vpn_provider *provider);
+ int (*route_env_parse) (struct vpn_provider *provider, const char *key,
+ int *family, unsigned long *idx,
+ enum vpn_provider_route_type *type);
};
int vpn_register(const char *name, struct vpn_driver *driver,
#include "connman/vpn-dbus.h"
#include "vpn-provider.h"
#include "vpn.h"
+#include "plugins/vpn.h"
static DBusConnection *connection;
static GHashTable *provider_hash;
return 0;
}
-enum provider_route_type {
- PROVIDER_ROUTE_TYPE_NONE = 0,
- PROVIDER_ROUTE_TYPE_MASK = 1,
- PROVIDER_ROUTE_TYPE_ADDR = 2,
- PROVIDER_ROUTE_TYPE_GW = 3,
-};
-
static int route_env_parse(struct vpn_provider *provider, const char *key,
int *family, unsigned long *idx,
- enum provider_route_type *type)
+ enum vpn_provider_route_type *type)
{
- char *end;
- const char *start;
+ if (!provider)
+ return -EINVAL;
DBG("name %s", provider->name);
- if (!strcmp(provider->type, "openvpn")) {
- if (g_str_has_prefix(key, "route_network_")) {
- start = key + strlen("route_network_");
- *type = PROVIDER_ROUTE_TYPE_ADDR;
- } else if (g_str_has_prefix(key, "route_netmask_")) {
- start = key + strlen("route_netmask_");
- *type = PROVIDER_ROUTE_TYPE_MASK;
- } else if (g_str_has_prefix(key, "route_gateway_")) {
- start = key + strlen("route_gateway_");
- *type = PROVIDER_ROUTE_TYPE_GW;
- } else
- return -EINVAL;
-
- *family = AF_INET;
- *idx = g_ascii_strtoull(start, &end, 10);
-
- } else if (!strcmp(provider->type, "openconnect")) {
- if (g_str_has_prefix(key, "CISCO_SPLIT_INC_")) {
- *family = AF_INET;
- start = key + strlen("CISCO_SPLIT_INC_");
- } else if (g_str_has_prefix(key,
- "CISCO_IPV6_SPLIT_INC_")) {
- *family = AF_INET6;
- start = key + strlen("CISCO_IPV6_SPLIT_INC_");
- } else
- return -EINVAL;
-
- *idx = g_ascii_strtoull(start, &end, 10);
-
- if (strncmp(end, "_ADDR", 5) == 0)
- *type = PROVIDER_ROUTE_TYPE_ADDR;
- else if (strncmp(end, "_MASK", 5) == 0)
- *type = PROVIDER_ROUTE_TYPE_MASK;
- else if (strncmp(end, "_MASKLEN", 8) == 0 &&
- *family == AF_INET6) {
- *type = PROVIDER_ROUTE_TYPE_MASK;
- } else
- return -EINVAL;
- }
+ if (provider->driver && provider->driver->route_env_parse)
+ return provider->driver->route_env_parse(provider, key, family, idx,
+ type);
return 0;
}
struct vpn_route *route;
int ret, family = 0;
unsigned long idx = 0;
- enum provider_route_type type = PROVIDER_ROUTE_TYPE_NONE;
+ enum vpn_provider_route_type type = VPN_PROVIDER_ROUTE_TYPE_NONE;
DBG("key %s value %s", key, value);
}
switch (type) {
- case PROVIDER_ROUTE_TYPE_NONE:
+ case VPN_PROVIDER_ROUTE_TYPE_NONE:
break;
- case PROVIDER_ROUTE_TYPE_MASK:
+ case VPN_PROVIDER_ROUTE_TYPE_MASK:
route->netmask = g_strdup(value);
break;
- case PROVIDER_ROUTE_TYPE_ADDR:
+ case VPN_PROVIDER_ROUTE_TYPE_ADDR:
route->network = g_strdup(value);
break;
- case PROVIDER_ROUTE_TYPE_GW:
+ case VPN_PROVIDER_ROUTE_TYPE_GW:
route->gateway = g_strdup(value);
break;
}
VPN_PROVIDER_ERROR_AUTH_FAILED = 3,
};
+enum vpn_provider_route_type {
+ VPN_PROVIDER_ROUTE_TYPE_NONE = 0,
+ VPN_PROVIDER_ROUTE_TYPE_MASK = 1,
+ VPN_PROVIDER_ROUTE_TYPE_ADDR = 2,
+ VPN_PROVIDER_ROUTE_TYPE_GW = 3,
+};
+
struct vpn_provider;
struct connman_ipaddress;
int (*save) (struct vpn_provider *provider, GKeyFile *keyfile);
int (*set_state)(struct vpn_provider *provider,
enum vpn_provider_state state);
+ int (*route_env_parse) (struct vpn_provider *provider, const char *key,
+ int *family, unsigned long *idx,
+ enum vpn_provider_route_type *type);
};
int vpn_provider_driver_register(struct vpn_provider_driver *driver);
if (!NLMSG_OK(hdr, len))
break;
- debug("%s len %d type %d flags 0x%04x seq %d pid %d",
+ debug("%s len %u type %u flags 0x%04x seq %u pid %u",
type2string(hdr->nlmsg_type),
hdr->nlmsg_len, hdr->nlmsg_type,
hdr->nlmsg_flags, hdr->nlmsg_seq,