summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNishant Chaprana <n.chaprana@samsung.com>2019-07-04 17:41:09 +0530
committerNishant Chaprana <n.chaprana@samsung.com>2019-07-04 17:41:17 +0530
commit6b2381a2adabea7d8309ff158ef675ff88184305 (patch)
tree2c9b2bb6d8b214acc18b8e784e6841f468a5a040
parent9362752a471a5c892d679548fbf2828d5fc5684b (diff)
downloadconnman-6b2381a2adabea7d8309ff158ef675ff88184305.tar.gz
connman-6b2381a2adabea7d8309ff158ef675ff88184305.tar.bz2
connman-6b2381a2adabea7d8309ff158ef675ff88184305.zip
Imported Upstream version 1.37upstream/1.37
Change-Id: Ib5957e7ee3a9315ee86a331189bc3e9e71751ee8 Signed-off-by: Nishant Chaprana <n.chaprana@samsung.com>
-rw-r--r--.gitignore1
-rw-r--r--AUTHORS23
-rw-r--r--ChangeLog26
-rw-r--r--Makefile.am77
-rw-r--r--README16
-rwxr-xr-xbootstrap-configure1
-rw-r--r--client/commands.c231
-rw-r--r--client/tethering.c63
-rw-r--r--client/tethering.h39
-rw-r--r--configure.ac37
-rw-r--r--doc/agent-api.txt2
-rw-r--r--doc/config-format.txt5
-rw-r--r--doc/connman-service.config.5.in21
-rw-r--r--doc/connman-vpn-provider.config.5.in3
-rw-r--r--doc/connman-vpn.conf.5.in3
-rw-r--r--doc/connman.8.in16
-rw-r--r--doc/connman.conf.5.in53
-rw-r--r--doc/connmanctl.1.in2
-rw-r--r--doc/manager-api.txt30
-rw-r--r--doc/plugin-api.txt2
-rw-r--r--doc/service-api.txt56
-rw-r--r--doc/session-api.txt11
-rw-r--r--doc/vpn-connection-api.txt13
-rw-r--r--gdhcp/client.c177
-rw-r--r--gdhcp/common.c45
-rw-r--r--gdhcp/common.h5
-rw-r--r--gdhcp/gdhcp.h4
-rw-r--r--gdhcp/ipv4ll.c86
-rw-r--r--gdhcp/server.c4
-rw-r--r--gsupplicant/gsupplicant.h4
-rw-r--r--gsupplicant/supplicant.c161
-rw-r--r--gweb/giognutls.c4
-rw-r--r--gweb/gresolv.c8
-rw-r--r--gweb/gresolv.h1
-rw-r--r--include/acd.h65
-rw-r--r--include/dbus.h2
-rw-r--r--include/device.h23
-rw-r--r--include/inet.h1
-rw-r--r--include/network.h7
-rw-r--r--include/notifier.h4
-rw-r--r--include/service.h10
-rw-r--r--include/session.h1
-rw-r--r--include/technology.h2
-rw-r--r--include/tethering.h37
-rw-r--r--include/vpn-dbus.h1
-rw-r--r--plugins/ethernet.c4
-rw-r--r--plugins/gadget.c4
-rw-r--r--plugins/iospm.c2
-rw-r--r--plugins/iwd.c97
-rw-r--r--plugins/nmcompat.c2
-rw-r--r--plugins/ofono.c5
-rw-r--r--plugins/pacrunner.c2
-rw-r--r--plugins/session_policy_local.c6
-rw-r--r--plugins/tist.c1
-rw-r--r--plugins/vpn.c231
-rw-r--r--plugins/wifi.c287
-rw-r--r--scripts/connman_resolvconf.conf.in2
-rw-r--r--src/acd.c596
-rw-r--r--src/backtrace.c11
-rw-r--r--src/config.c30
-rw-r--r--src/connection.c18
-rw-r--r--src/connman.h100
-rw-r--r--src/connman.service.in2
-rw-r--r--src/device.c133
-rw-r--r--src/dhcp.c26
-rw-r--r--src/dhcpv6.c2
-rw-r--r--src/dns-systemd-resolved.c490
-rw-r--r--src/dnsproxy.c101
-rw-r--r--src/firewall-iptables.c62
-rw-r--r--src/firewall-nftables.c14
-rw-r--r--src/inet.c189
-rw-r--r--src/ippool.c25
-rw-r--r--src/iptables.c2448
-rw-r--r--src/ipv6pd.c26
-rw-r--r--src/log.c5
-rw-r--r--src/main.c102
-rw-r--r--src/main.conf44
-rw-r--r--src/manager.c26
-rw-r--r--src/nat.c6
-rw-r--r--src/network.c365
-rw-r--r--src/nostats.c60
-rw-r--r--src/notifier.c22
-rw-r--r--src/ntp.c255
-rw-r--r--src/peer.c17
-rw-r--r--src/provider.c6
-rw-r--r--src/resolver.c9
-rw-r--r--src/rfkill.c1
-rw-r--r--src/rtnl.c3
-rw-r--r--src/service.c622
-rw-r--r--src/session.c75
-rw-r--r--src/shared/arp.c126
-rw-r--r--src/shared/arp.h (renamed from gdhcp/ipv4ll.h)40
-rw-r--r--src/stats.c7
-rw-r--r--src/technology.c32
-rw-r--r--src/tethering.c144
-rw-r--r--src/timeserver.c169
-rw-r--r--src/timezone.c1
-rw-r--r--src/util.c11
-rw-r--r--src/wispr.c2
-rw-r--r--src/wpad.c4
-rwxr-xr-xtest/list-services5
-rw-r--r--tools/dhcp-server-test.c6
-rw-r--r--tools/dhcp-test.c7
-rw-r--r--tools/dnsproxy-test.c1
-rw-r--r--tools/ip6tables-test.c163
-rw-r--r--tools/iptables-test.c19
-rw-r--r--tools/iptables-unit.c492
-rw-r--r--tools/private-network-test.c2
-rw-r--r--tools/resolv-test.c2
-rw-r--r--tools/session-api.c42
-rw-r--r--tools/session-test.h3
-rw-r--r--tools/session-utils.c17
-rw-r--r--tools/stats-tool.c1
-rw-r--r--tools/tap-test.c3
-rw-r--r--tools/wispr.c1
-rw-r--r--unit/test-ippool.c18
-rw-r--r--unit/test-iptables.c699
-rw-r--r--vpn/plugins/l2tp.c1
-rw-r--r--vpn/plugins/openconnect.c30
-rw-r--r--vpn/plugins/openvpn.c25
-rw-r--r--vpn/plugins/pptp.c1
-rw-r--r--vpn/plugins/vpn.c59
-rw-r--r--vpn/plugins/vpn.h4
-rw-r--r--vpn/vpn-provider.c66
-rw-r--r--vpn/vpn-provider.h10
-rw-r--r--vpn/vpn-rtnl.c2
126 files changed, 8239 insertions, 1893 deletions
diff --git a/.gitignore b/.gitignore
index b7cc1348..b43336c9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -65,6 +65,7 @@ tools/session-test
tools/netlink-test
unit/test-ippool
unit/test-nat
+unit/test-iptables
doc/*.bak
doc/*.stamp
diff --git a/AUTHORS b/AUTHORS
index 44c65f88..b992c4ed 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -131,3 +131,26 @@ Bjoern Thorwirth <external.bjoern.thorwirth@de.bosch.com>
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>
diff --git a/ChangeLog b/ChangeLog
index df80251f..9418de1b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,29 @@
+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.
diff --git a/Makefile.am b/Makefile.am
index e67a7a55..745bef05 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -11,7 +11,8 @@ include_HEADERS = include/log.h include/plugin.h \
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
@@ -32,8 +33,12 @@ gdbus_libgdbus_internal_la_SOURCES = gdbus/gdbus.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
@@ -43,12 +48,15 @@ else
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
@@ -106,9 +114,9 @@ MANUAL_PAGES =
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 \
@@ -120,10 +128,17 @@ src_connmand_SOURCES = $(gdhcp_sources) $(gweb_sources) $(backtrace_sources) \
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@ \
@@ -178,7 +193,11 @@ vpn_connman_vpnd_LDFLAGS = -Wl,--export-dynamic \
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
@@ -289,6 +308,7 @@ client_connmanctl_SOURCES = client/dbus_helpers.h client/dbus_helpers.c \
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
@@ -339,11 +359,13 @@ tools_wpad_test_LDADD = @GLIB_LIBS@ -lresolv
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@
@@ -359,25 +381,40 @@ tools_session_test_LDADD = gdbus/libgdbus-internal.la \
@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
diff --git a/README b/README
index cff3131e..f16b9ec0 100644
--- a/README
+++ b/README
@@ -11,6 +11,7 @@ The following features are built-in into Connection Manager:
- 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
@@ -220,6 +221,16 @@ For a working system, certain configuration options need to be enabled:
# 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
====================
@@ -327,7 +338,10 @@ If wpa_supplicant is configured to D-Bus autostart, then ConnMan will
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
diff --git a/bootstrap-configure b/bootstrap-configure
index 070c676d..3f697987 100755
--- a/bootstrap-configure
+++ b/bootstrap-configure
@@ -11,6 +11,7 @@ fi
--mandir=/usr/share/man \
--localstatedir=/var \
--sysconfdir=/etc \
+ --with-firewall=nftables \
--disable-datafiles \
--enable-openconnect=builtin \
--enable-openvpn=builtin \
diff --git a/client/commands.c b/client/commands.c
index 583095b1..097d293a 100644
--- a/client/commands.c
+++ b/client/commands.c
@@ -39,6 +39,7 @@
#include "dbus_helpers.h"
#include "input.h"
#include "services.h"
+#include "tethering.h"
#include "peers.h"
#include "commands.h"
#include "agent.h"
@@ -245,7 +246,7 @@ static int state_print(DBusMessageIter *iter, const char *error,
DBusMessageIter entry;
if (error) {
- fprintf(stderr, "Error: %s", error);
+ fprintf(stderr, "Error: %s\n", error);
return 0;
}
@@ -272,7 +273,7 @@ static int clock_print(DBusMessageIter *iter, const char *error,
DBusMessageIter entry;
if (error) {
- fprintf(stderr, "Error: %s", error);
+ fprintf(stderr, "Error: %s\n", error);
return 0;
}
@@ -318,6 +319,18 @@ static int peers_list(DBusMessageIter *iter,
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)
{
@@ -639,6 +652,17 @@ static int cmd_tether(char *args[], int num, struct connman_option *options)
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)
{
@@ -792,8 +816,6 @@ static void move_before_append_args(DBusMessageIter *iter, 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,
@@ -852,8 +874,6 @@ static void move_after_append_args(DBusMessageIter *iter, void *user_data)
dbus_message_iter_append_basic(iter,
DBUS_TYPE_OBJECT_PATH, &path);
-
- return;
}
static int cmd_service_move_after(char *args[], int num,
@@ -901,6 +921,13 @@ struct config_append {
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)
{
@@ -1202,6 +1229,30 @@ static int cmd_config(char *args[], int num, struct connman_option *options)
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;
@@ -1735,7 +1786,7 @@ static int session_connect_cb(DBusMessageIter *iter, const char *error,
void *user_data)
{
if (error) {
- fprintf(stderr, "Error: %s", error);
+ fprintf(stderr, "Error: %s\n", error);
return 0;
}
@@ -1754,7 +1805,7 @@ static int session_disconnect_cb(DBusMessageIter *iter, const char *error,
void *user_data)
{
if (error)
- fprintf(stderr, "Error: %s", error);
+ fprintf(stderr, "Error: %s\n", error);
return 0;
}
@@ -1796,28 +1847,140 @@ static int session_create_cb(DBusMessageIter *iter, const char *error,
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,
- &notify_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);
@@ -1871,25 +2034,6 @@ static int session_config_return(DBusMessageIter *iter, const char *error,
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)
{
@@ -1959,6 +2103,18 @@ static int session_config(char *args[], int num,
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;
@@ -1994,12 +2150,13 @@ static int cmd_session(char *args[], int num, struct connman_option *options)
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();
@@ -2222,6 +2379,7 @@ static struct connman_option config_options[] = {
{"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"
@@ -2248,6 +2406,7 @@ static struct connman_option session_options[] = {
{"type", 't', "local|internet|any"},
{"ifname", 'i', "[<interface_name>]"},
{"srciprule", 's', "yes|no"},
+ {"ctxid", 'c', "<context_identifier>"},
{ NULL, }
};
@@ -2595,6 +2754,8 @@ static const struct {
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,
@@ -3012,7 +3173,7 @@ static int populate_technology_hash(DBusMessageIter *iter, const char *error,
void *user_data)
{
if (error) {
- fprintf(stderr, "Error getting technologies: %s", error);
+ fprintf(stderr, "Error getting technologies: %s\n", error);
return 0;
}
diff --git a/client/tethering.c b/client/tethering.c
new file mode 100644
index 00000000..361189be
--- /dev/null
+++ b/client/tethering.c
@@ -0,0 +1,63 @@
+/*
+ *
+ * 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);
+ }
+}
diff --git a/client/tethering.h b/client/tethering.h
new file mode 100644
index 00000000..6135e3c5
--- /dev/null
+++ b/client/tethering.h
@@ -0,0 +1,39 @@
+/*
+ *
+ * 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 */
diff --git a/configure.ac b/configure.ac
index 4baa6852..ee49a22c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
AC_PREREQ(2.60)
-AC_INIT(connman, 1.35)
+AC_INIT(connman, 1.37)
AC_CONFIG_MACRO_DIR([m4])
@@ -20,6 +20,7 @@ AC_SUBST(abs_top_srcdir)
AC_SUBST(abs_top_builddir)
AC_LANG_C
+AC_USE_SYSTEM_EXTENSIONS
AC_PROG_CC
AM_PROG_CC_C_O
@@ -185,6 +186,8 @@ AC_CHECK_LIB(resolv, ns_initparse, dummy=yes, [
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))
@@ -218,8 +221,8 @@ fi
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)
@@ -230,9 +233,9 @@ AC_SUBST(DBUS_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
@@ -380,14 +383,24 @@ AC_ARG_ENABLE(tools, AC_HELP_STRING([--disable-tools],
[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]),
@@ -433,4 +446,18 @@ AM_CONDITIONAL(VPN, test "${enable_openconnect}" != "no" -o \
"${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)
diff --git a/doc/agent-api.txt b/doc/agent-api.txt
index aa7271d4..e3c1dcde 100644
--- a/doc/agent-api.txt
+++ b/doc/agent-api.txt
@@ -54,6 +54,8 @@ Methods void Release()
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".
diff --git a/doc/config-format.txt b/doc/config-format.txt
index ed3123aa..584220f0 100644
--- a/doc/config-format.txt
+++ b/doc/config-format.txt
@@ -63,6 +63,11 @@ Allowed fields:
- 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.
diff --git a/doc/connman-service.config.5.in b/doc/connman-service.config.5.in
index 535c626c..eb63f225 100644
--- a/doc/connman-service.config.5.in
+++ b/doc/connman-service.config.5.in
@@ -18,8 +18,7 @@ eduroam, or for static IPs and so on. Each provisioning file can be
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
@@ -96,6 +95,8 @@ configured, \fBpsk\fP if a passphrase is present and \fBnone\fP otherwise.
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
@@ -118,6 +119,22 @@ Identity string for EAP.
.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
diff --git a/doc/connman-vpn-provider.config.5.in b/doc/connman-vpn-provider.config.5.in
index ef704352..51d547b3 100644
--- a/doc/connman-vpn-provider.config.5.in
+++ b/doc/connman-vpn-provider.config.5.in
@@ -17,8 +17,7 @@ must end with \fB.config\fP. Each VPN connection requires a provisioning
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
diff --git a/doc/connman-vpn.conf.5.in b/doc/connman-vpn.conf.5.in
index fcc4c692..20d30fcc 100644
--- a/doc/connman-vpn.conf.5.in
+++ b/doc/connman-vpn.conf.5.in
@@ -16,8 +16,7 @@ behavior. The location of the file may be changed through use of
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
diff --git a/doc/connman.8.in b/doc/connman.8.in
index ddfba09b..85e7c5e0 100644
--- a/doc/connman.8.in
+++ b/doc/connman.8.in
@@ -88,13 +88,15 @@ Do not daemonize. This is useful for debugging, and directs log output to
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)
diff --git a/doc/connman.conf.5.in b/doc/connman.conf.5.in
index 95b177f5..e42b22f0 100644
--- a/doc/connman.conf.5.in
+++ b/doc/connman.conf.5.in
@@ -16,8 +16,7 @@ behavior. The location of the file may be changed through use of
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
@@ -43,16 +42,27 @@ user interface designs.
.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
@@ -67,6 +77,12 @@ for this entry when empty is ethernet,wifi,cellular.
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
@@ -98,6 +114,11 @@ Allow connman to change the system hostname. This can
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
@@ -145,6 +166,21 @@ connectivity is successful. Only then the service will be
transitioned to ONLINE state.
If this setting is false, the default service will remain in READY state.
Default value is true.
+.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.
@@ -152,6 +188,7 @@ ethernet tethering.
.nf
[General]
AllowHostnameUpdates = false
+AllowDomainnameUpdates = false
TetheringTechnologies = ethernet,wifi,bluetooth,gadget
.fi
.SH "SEE ALSO"
diff --git a/doc/connmanctl.1.in b/doc/connmanctl.1.in
index 0f891bd0..47d9c303 100644
--- a/doc/connmanctl.1.in
+++ b/doc/connmanctl.1.in
@@ -180,7 +180,7 @@ Configures the IPv6 settings for the service. The argument
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
diff --git a/doc/manager-api.txt b/doc/manager-api.txt
index 31e137ca..bfb07bd3 100644
--- a/doc/manager-api.txt
+++ b/doc/manager-api.txt
@@ -46,6 +46,11 @@ Methods dict GetProperties()
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
@@ -210,11 +215,12 @@ Signals TechnologyAdded(object path, dict properties)
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.
@@ -233,11 +239,12 @@ Signals TechnologyAdded(object path, dict properties)
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.
@@ -253,6 +260,13 @@ Signals TechnologyAdded(object path, dict properties)
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
diff --git a/doc/plugin-api.txt b/doc/plugin-api.txt
index ea5ec0a1..36391e96 100644
--- a/doc/plugin-api.txt
+++ b/doc/plugin-api.txt
@@ -6,7 +6,7 @@ Plugin basics
=============
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>
diff --git a/doc/service-api.txt b/doc/service-api.txt
index 6cdb0bb5..c0d5adbb 100644
--- a/doc/service-api.txt
+++ b/doc/service-api.txt
@@ -187,8 +187,15 @@ Properties string State [readonly]
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.
@@ -500,3 +507,48 @@ Properties string State [readonly]
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.
diff --git a/doc/session-api.txt b/doc/session-api.txt
index e8da5224..46ac5f3d 100644
--- a/doc/session-api.txt
+++ b/doc/session-api.txt
@@ -205,3 +205,14 @@ Settings string State [readonly]
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.
diff --git a/doc/vpn-connection-api.txt b/doc/vpn-connection-api.txt
index a814a388..1fd3be26 100644
--- a/doc/vpn-connection-api.txt
+++ b/doc/vpn-connection-api.txt
@@ -39,6 +39,19 @@ Methods dict GetProperties() [experimental]
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
diff --git a/gdhcp/client.c b/gdhcp/client.c
index 3e67fcd5..aab74378 100644
--- a/gdhcp/client.c
+++ b/gdhcp/client.c
@@ -23,7 +23,6 @@
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
@@ -43,9 +42,10 @@
#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
@@ -65,6 +65,7 @@ typedef enum _dhcp_client_state {
REBOOTING,
REQUESTING,
BOUND,
+ DECLINED,
RENEWING,
REBINDING,
RELEASED,
@@ -109,6 +110,7 @@ struct _GDHCPClient {
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;
@@ -464,10 +466,40 @@ static int send_discover(GDHCPClient *dhcp_client, uint32_t requested)
* 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)
@@ -519,7 +551,7 @@ static int send_release(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);
@@ -542,7 +574,7 @@ static gboolean send_probe_packet(gpointer dhcp_data)
/* 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");
@@ -551,12 +583,12 @@ static gboolean send_probe_packet(gpointer dhcp_data)
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);
@@ -580,7 +612,7 @@ static gboolean send_announce_packet(gpointer dhcp_data)
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);
@@ -605,38 +637,6 @@ static gboolean send_announce_packet(gpointer dhcp_data)
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)
@@ -667,7 +667,7 @@ int g_dhcpv6_create_duid(GDHCPDuidType duid_type, int index, int type,
(*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;
@@ -686,7 +686,7 @@ int g_dhcpv6_create_duid(GDHCPDuidType duid_type, int index, int type,
(*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;
@@ -811,7 +811,7 @@ void g_dhcpv6_client_create_iaid(GDHCPClient *dhcp_client, int index,
{
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 |
@@ -1178,7 +1178,7 @@ GDHCPClient *g_dhcp_client_new(GDHCPType type,
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;
@@ -1198,6 +1198,8 @@ GDHCPClient *g_dhcp_client_new(GDHCPType type,
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;
@@ -1373,10 +1375,10 @@ static void ipv4ll_start(GDHCPClient *dhcp_client)
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,
@@ -1413,6 +1415,7 @@ static int ipv4ll_recv_arp_packet(GDHCPClient *dhcp_client)
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));
@@ -1423,6 +1426,9 @@ static int ipv4ll_recv_arp_packet(GDHCPClient *dhcp_client)
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));
@@ -1458,23 +1464,20 @@ static int ipv4ll_recv_arp_packet(GDHCPClient *dhcp_client)
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;
}
@@ -1526,6 +1529,12 @@ static gboolean request_timeout(gpointer user_data)
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);
@@ -1564,7 +1573,7 @@ static int switch_listening_mode(GDHCPClient *dhcp_client,
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;
@@ -1585,8 +1594,8 @@ static int switch_listening_mode(GDHCPClient *dhcp_client,
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;
@@ -1684,7 +1693,7 @@ static gboolean continue_rebound(gpointer user_data)
/*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,
@@ -1732,7 +1741,7 @@ static gboolean continue_renew (gpointer user_data)
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,
@@ -2373,14 +2382,28 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
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);
@@ -2716,6 +2739,7 @@ int g_dhcp_client_start(GDHCPClient *dhcp_client, const char *last_address)
int re;
uint32_t addr;
uint64_t rand;
+ ClientState oldstate = dhcp_client->state;
remove_timeouts(dhcp_client);
@@ -2826,12 +2850,13 @@ int g_dhcp_client_start(GDHCPClient *dhcp_client, const char *last_address)
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));
@@ -3031,6 +3056,7 @@ char *g_dhcp_client_get_netmask(GDHCPClient *dhcp_client)
case REBOOTING:
case REQUESTING:
case RELEASED:
+ case DECLINED:
case IPV4LL_PROBE:
case IPV4LL_ANNOUNCE:
case INFORMATION_REQ:
@@ -3228,6 +3254,7 @@ void g_dhcp_client_unref(GDHCPClient *dhcp_client)
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);
}
diff --git a/gdhcp/common.c b/gdhcp/common.c
index 6f816718..8f7a65cc 100644
--- a/gdhcp/common.c
+++ b/gdhcp/common.c
@@ -39,6 +39,7 @@
#include "gdhcp.h"
#include "common.h"
+#include "../src/connman.h"
static const DHCPOption client_options[] = {
{ OPTION_IP, 0x01 }, /* subnet-mask */
@@ -60,42 +61,6 @@ static const DHCPOption client_options[] = {
{ 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;
@@ -332,8 +297,6 @@ void dhcp_add_option_uint32(struct dhcp_packet *packet, uint8_t code,
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,
@@ -349,8 +312,6 @@ 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,
@@ -366,8 +327,6 @@ 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)
@@ -400,7 +359,7 @@ void dhcpv6_init_header(struct dhcpv6_packet *packet, uint8_t type)
packet->message = type;
- dhcp_get_random(&rand);
+ __connman_util_get_random(&rand);
id = rand;
packet->transaction_id[0] = (id >> 16) & 0xff;
diff --git a/gdhcp/common.h b/gdhcp/common.h
index 75abc183..6899499e 100644
--- a/gdhcp/common.h
+++ b/gdhcp/common.h
@@ -19,6 +19,7 @@
*
*/
+#include <config.h>
#include <netinet/udp.h>
#include <netinet/ip.h>
@@ -170,8 +171,8 @@ static const uint8_t dhcp_option_lengths[] = {
[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 */
diff --git a/gdhcp/gdhcp.h b/gdhcp/gdhcp.h
index eaf6a748..e3b01311 100644
--- a/gdhcp/gdhcp.h
+++ b/gdhcp/gdhcp.h
@@ -134,6 +134,7 @@ GDHCPClient *g_dhcp_client_new(GDHCPType type, int 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);
@@ -232,9 +233,6 @@ void g_dhcp_server_set_save_lease(GDHCPServer *dhcp_server,
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
diff --git a/gdhcp/ipv4ll.c b/gdhcp/ipv4ll.c
index d9001987..de4e0764 100644
--- a/gdhcp/ipv4ll.c
+++ b/gdhcp/ipv4ll.c
@@ -35,6 +35,7 @@
#include <glib.h>
#include "ipv4ll.h"
#include "common.h"
+#include "../src/connman.h"
/**
* Return a random link local IP (in host byte order)
@@ -45,93 +46,10 @@ uint32_t ipv4ll_random_ip(void)
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;
-}
diff --git a/gdhcp/server.c b/gdhcp/server.c
index f7795f7e..85405f19 100644
--- a/gdhcp/server.c
+++ b/gdhcp/server.c
@@ -396,7 +396,7 @@ GDHCPServer *g_dhcp_server_new(GDHCPType type,
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;
@@ -691,7 +691,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
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;
}
diff --git a/gsupplicant/gsupplicant.h b/gsupplicant/gsupplicant.h
index db61595b..bfb52db7 100644
--- a/gsupplicant/gsupplicant.h
+++ b/gsupplicant/gsupplicant.h
@@ -352,6 +352,10 @@ struct _GSupplicantCallbacks {
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,
diff --git a/gsupplicant/supplicant.c b/gsupplicant/supplicant.c
index 4f790122..6052f7b6 100644
--- a/gsupplicant/supplicant.c
+++ b/gsupplicant/supplicant.c
@@ -614,6 +614,30 @@ static void callback_network_associated(GSupplicantNetwork *network)
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)
@@ -755,6 +779,8 @@ static void remove_bss(gpointer data)
{
struct g_supplicant_bss *bss = data;
+ supplicant_dbus_property_call_cancel_all(bss);
+
g_free(bss->path);
g_free(bss);
}
@@ -1499,7 +1525,6 @@ static void interface_network_added(DBusMessageIter *iter, void *user_data)
static void interface_network_removed(DBusMessageIter *iter, void *user_data)
{
SUPPLICANT_DBG("");
- return;
}
static char *create_name(unsigned char *ssid, int ssid_len)
@@ -1575,6 +1600,7 @@ static int add_or_replace_bss_to_network(struct g_supplicant_bss *bss)
GSupplicantInterface *interface = bss->interface;
GSupplicantNetwork *network;
char *group;
+ bool is_new_network;
group = create_group(bss);
SUPPLICANT_DBG("New group created: %s", group);
@@ -1586,10 +1612,13 @@ static int add_or_replace_bss_to_network(struct g_supplicant_bss *bss)
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);
@@ -1609,6 +1638,11 @@ static int add_or_replace_bss_to_network(struct g_supplicant_bss *bss)
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,
@@ -1626,7 +1660,10 @@ done:
/* 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");
}
/*
@@ -2064,7 +2101,7 @@ static void interface_bss_added_without_keys(DBusMessageIter *iter,
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)
@@ -2169,6 +2206,7 @@ static void interface_bss_removed(DBusMessageIter *iter, void *user_data)
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)
@@ -2182,6 +2220,7 @@ static void interface_bss_removed(DBusMessageIter *iter, void *user_data)
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);
@@ -2191,8 +2230,12 @@ static void interface_bss_removed(DBusMessageIter *iter, void *user_data)
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)
@@ -2718,11 +2761,48 @@ static void signal_network_removed(const char *path, DBusMessageIter *iter)
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("");
@@ -2747,18 +2827,18 @@ static void signal_bss_changed(const char *path, DBusMessageIter *iter)
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)
@@ -2767,20 +2847,43 @@ static void signal_bss_changed(const char *path, DBusMessageIter *iter)
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;
@@ -3468,6 +3571,8 @@ static struct {
{ 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 },
@@ -4204,11 +4309,13 @@ static void interface_scan_params(DBusMessageIter *iter, void *user_data)
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
@@ -4597,7 +4704,9 @@ static void add_network_security_peap(DBusMessageIter *dict,
}
- 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
diff --git a/gweb/giognutls.c b/gweb/giognutls.c
index 09dc9e72..b5c476cb 100644
--- a/gweb/giognutls.c
+++ b/gweb/giognutls.c
@@ -304,7 +304,7 @@ static gboolean g_io_gnutls_dispatch(GSource *source, GSourceFunc callback,
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);
@@ -421,7 +421,7 @@ GIOChannel *g_io_channel_gnutls_new(int fd)
DBG("");
- gnutls_channel = g_new(GIOGnuTLSChannel, 1);
+ gnutls_channel = g_new0(GIOGnuTLSChannel, 1);
channel = (GIOChannel *) gnutls_channel;
diff --git a/gweb/gresolv.c b/gweb/gresolv.c
index 8a51a9f6..38a554e0 100644
--- a/gweb/gresolv.c
+++ b/gweb/gresolv.c
@@ -28,6 +28,7 @@
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
+#include <stdio.h>
#include <resolv.h>
#include <sys/types.h>
#include <sys/socket.h>
@@ -511,7 +512,7 @@ static void sort_and_return_results(struct resolv_lookup *lookup)
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);
@@ -679,7 +680,10 @@ static void parse_response(struct resolv_nameserver *nameserver,
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;
diff --git a/gweb/gresolv.h b/gweb/gresolv.h
index fac14f54..5e82c168 100644
--- a/gweb/gresolv.h
+++ b/gweb/gresolv.h
@@ -44,6 +44,7 @@ typedef enum {
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,
diff --git a/include/acd.h b/include/acd.h
new file mode 100644
index 00000000..0d777963
--- /dev/null
+++ b/include/acd.h
@@ -0,0 +1,65 @@
+/*
+ *
+ * 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 */
diff --git a/include/dbus.h b/include/dbus.h
index 26f94d63..bcab4189 100644
--- a/include/dbus.h
+++ b/include/dbus.h
@@ -31,7 +31,6 @@ extern "C" {
#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"
@@ -42,7 +41,6 @@ extern "C" {
#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"
diff --git a/include/device.h b/include/device.h
index 9ac800a2..0fc06bd0 100644
--- a/include/device.h
+++ b/include/device.h
@@ -50,6 +50,17 @@ enum connman_device_type {
#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,
@@ -82,7 +93,8 @@ int connman_device_set_powered(struct connman_device *device,
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,
@@ -119,11 +131,10 @@ struct connman_device_driver {
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);
};
diff --git a/include/inet.h b/include/inet.h
index 6482934a..9c1918f3 100644
--- a/include/inet.h
+++ b/include/inet.h
@@ -38,6 +38,7 @@ char *connman_inet_ifname(int index);
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);
diff --git a/include/network.h b/include/network.h
index 4fc20c1c..8f87d7c5 100644
--- a/include/network.h
+++ b/include/network.h
@@ -25,6 +25,8 @@
#include <stdbool.h>
#include <stdint.h>
+#include <dbus/dbus.h>
+
#include <connman/device.h>
#include <connman/ipconfig.h>
@@ -100,6 +102,8 @@ void connman_network_set_error(struct connman_network *network,
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);
@@ -162,6 +166,9 @@ struct connman_network_driver {
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
diff --git a/include/notifier.h b/include/notifier.h
index 50b5fb4c..9c0909c7 100644
--- a/include/notifier.h
+++ b/include/notifier.h
@@ -57,8 +57,8 @@ struct connman_notifier {
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
}
diff --git a/include/service.h b/include/service.h
index 958e7fd1..97fdf7d5 100644
--- a/include/service.h
+++ b/include/service.h
@@ -114,9 +114,12 @@ void connman_service_unref_debug(struct connman_service *service,
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);
@@ -129,6 +132,13 @@ const char *connman_service_get_proxy_autoconfig(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);
diff --git a/include/session.h b/include/session.h
index 5106e886..39f33685 100644
--- a/include/session.h
+++ b/include/session.h
@@ -74,6 +74,7 @@ struct connman_session_config {
GSList *allowed_bearers;
char *allowed_interface;
bool source_ip_rule;
+ char *context_identifier;
};
typedef int (* connman_session_config_func_t) (struct connman_session *session,
diff --git a/include/technology.h b/include/technology.h
index 97db6607..7508a9a1 100644
--- a/include/technology.h
+++ b/include/technology.h
@@ -42,6 +42,8 @@ int connman_technology_set_regdom(const char *alpha2);
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);
diff --git a/include/tethering.h b/include/tethering.h
new file mode 100644
index 00000000..827f29af
--- /dev/null
+++ b/include/tethering.h
@@ -0,0 +1,37 @@
+/*
+ *
+ * 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 */
diff --git a/include/vpn-dbus.h b/include/vpn-dbus.h
index 01780cbd..07743bd0 100644
--- a/include/vpn-dbus.h
+++ b/include/vpn-dbus.h
@@ -48,6 +48,7 @@ extern "C" {
#define PROPERTY_CHANGED "PropertyChanged"
#define GET_CONNECTIONS "GetConnections"
#define VPN_CONNECT "Connect"
+#define VPN_CONNECT2 "Connect2"
#define VPN_DISCONNECT "Disconnect"
#define VPN_REMOVE "Remove"
diff --git a/plugins/ethernet.c b/plugins/ethernet.c
index 9a4d7413..b0395c83 100644
--- a/plugins/ethernet.c
+++ b/plugins/ethernet.c
@@ -27,6 +27,7 @@
#include <net/if.h>
#include <string.h>
#include <sys/ioctl.h>
+#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
@@ -187,11 +188,12 @@ static void add_network(struct connman_device *device,
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);
diff --git a/plugins/gadget.c b/plugins/gadget.c
index 94f66487..1b44bbb5 100644
--- a/plugins/gadget.c
+++ b/plugins/gadget.c
@@ -270,6 +270,8 @@ static void gadget_tech_enable_tethering(struct connman_technology *technology,
connman_inet_ifup(index);
connman_inet_add_to_bridge(index, bridge);
+
+ gadget_tethering = true;
}
}
@@ -286,6 +288,8 @@ static void gadget_tech_disable_tethering(struct connman_technology *technology,
connman_inet_ifdown(index);
connman_technology_tethering_notify(technology, false);
+
+ gadget_tethering = false;
}
}
diff --git a/plugins/iospm.c b/plugins/iospm.c
index fcb4cea1..cded9e00 100644
--- a/plugins/iospm.c
+++ b/plugins/iospm.c
@@ -86,7 +86,7 @@ static void iospm_offline_mode(bool enabled)
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,
diff --git a/plugins/iwd.c b/plugins/iwd.c
index b5191654..ddc9201d 100644
--- a/plugins/iwd.c
+++ b/plugins/iwd.c
@@ -55,14 +55,6 @@ static bool agent_registered;
#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;
@@ -77,7 +69,6 @@ struct iwd_device {
char *adapter;
char *name;
char *address;
- enum iwd_device_state state;
bool powered;
bool scanning;
@@ -96,38 +87,6 @@ struct iwd_network {
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;
@@ -484,17 +443,53 @@ static void update_signal_strength(struct iwd_device *iwdd)
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);
@@ -504,6 +499,7 @@ static void add_network(const char *path, struct iwd_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);
@@ -514,6 +510,8 @@ static void add_network(const char *path, struct iwd_network *iwdn)
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)
@@ -625,13 +623,6 @@ static void device_property_change(GDBusProxy *proxy, const char *name,
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;
@@ -790,13 +781,11 @@ static void create_device(GDBusProxy *proxy)
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,
diff --git a/plugins/nmcompat.c b/plugins/nmcompat.c
index 883ce9bd..274baab4 100644
--- a/plugins/nmcompat.c
+++ b/plugins/nmcompat.c
@@ -173,7 +173,7 @@ static void offline_mode(bool enabled)
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,
diff --git a/plugins/ofono.c b/plugins/ofono.c
index 78f8f196..82413b6e 100644
--- a/plugins/ofono.c
+++ b/plugins/ofono.c
@@ -1301,10 +1301,13 @@ static void remove_all_contexts(struct modem_data *modem)
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;
diff --git a/plugins/pacrunner.c b/plugins/pacrunner.c
index d2464a5e..9c652f3b 100644
--- a/plugins/pacrunner.c
+++ b/plugins/pacrunner.c
@@ -277,7 +277,7 @@ static void proxy_changed(struct connman_service *service)
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,
diff --git a/plugins/session_policy_local.c b/plugins/session_policy_local.c
index f003c0e1..9beb0980 100644
--- a/plugins/session_policy_local.c
+++ b/plugins/session_policy_local.c
@@ -271,10 +271,8 @@ static void get_uid_reply(unsigned int uid, void *user_data, int err)
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) {
@@ -333,7 +331,7 @@ static void get_uid_reply(unsigned int uid, void *user_data, int err)
return;
err:
- failed_create(NULL, cb, cbd->user_data, err);
+ failed_create(policy, cb, cbd->user_data, err);
g_free(cbd);
g_free(groups);
}
diff --git a/plugins/tist.c b/plugins/tist.c
index ad5ef79e..cc2800a1 100644
--- a/plugins/tist.c
+++ b/plugins/tist.c
@@ -23,7 +23,6 @@
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
diff --git a/plugins/vpn.c b/plugins/vpn.c
index c2a332ba..11bab154 100644
--- a/plugins/vpn.c
+++ b/plugins/vpn.c
@@ -38,6 +38,7 @@
#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>
@@ -71,8 +72,10 @@ struct connection_data {
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;
@@ -249,6 +252,12 @@ static void free_config_cb_data(struct config_create_data *cb_data)
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;
@@ -256,6 +265,11 @@ static void set_provider_state(struct connection_data *data)
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;
@@ -484,19 +498,19 @@ static void connect_reply(DBusPendingCall *call, void *user_data)
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);
}
@@ -506,7 +520,6 @@ static void connect_reply(DBusPendingCall *call, void *user_data)
* state.
*/
-done:
dbus_message_unref(reply);
dbus_pending_call_unref(call);
@@ -518,24 +531,31 @@ static int connect_provider(struct connection_data *data, void *user_data,
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
@@ -544,7 +564,8 @@ static int connect_provider(struct connection_data *data, void *user_data,
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;
}
@@ -559,6 +580,15 @@ static int connect_provider(struct connection_data *data, void *user_data,
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);
@@ -891,12 +921,10 @@ static int provider_connect(struct connman_provider *provider,
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);
@@ -911,50 +939,47 @@ static void disconnect_reply(DBusPendingCall *call, void *user_data)
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)
@@ -967,8 +992,7 @@ 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;
@@ -1476,17 +1500,11 @@ static void destroy_provider(struct connection_data *data)
{
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;
}
@@ -1499,13 +1517,24 @@ static void connection_destroy(gpointer hash_data)
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);
@@ -1813,6 +1842,120 @@ static gboolean property_changed(DBusConnection *conn,
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;
@@ -1853,6 +1996,7 @@ static int vpn_init(void)
vpnd_created(connection, &provider_driver);
}
+ connman_notifier_register(&vpn_notifier);
return err;
remove:
@@ -1873,6 +2017,7 @@ static void vpn_exit(void)
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)
diff --git a/plugins/wifi.c b/plugins/wifi.c
index 34c16dfd..f8c22be3 100644
--- a/plugins/wifi.c
+++ b/plugins/wifi.c
@@ -30,9 +30,8 @@
#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
@@ -56,6 +55,7 @@
#include <connman/provision.h>
#include <connman/utsname.h>
#include <connman/machine.h>
+#include <connman/tethering.h>
#include <gsupplicant/gsupplicant.h>
@@ -64,7 +64,8 @@
#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
@@ -83,6 +84,12 @@ enum wifi_ap_capability{
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;
@@ -143,7 +150,7 @@ struct wifi_data {
* 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;
@@ -160,7 +167,7 @@ static GList *iface_list = NULL;
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,
@@ -596,10 +603,8 @@ static int peer_register_service(const unsigned char *specification,
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,
@@ -684,10 +689,8 @@ static int peer_unregister_service(const unsigned char *specification,
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)
@@ -831,13 +834,13 @@ static void reset_autoscan(struct connman_device *device)
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);
}
@@ -893,7 +896,7 @@ static void wifi_remove(struct connman_device *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);
}
@@ -1209,7 +1212,7 @@ static int throw_wifi_scan(struct connman_device *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);
@@ -1283,7 +1286,7 @@ static void scan_callback(int result, GSupplicantInterface *interface,
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,
@@ -1363,6 +1366,21 @@ static gboolean autoscan_timeout(gpointer data)
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);
@@ -1409,19 +1427,25 @@ static struct autoscan_params *parse_autoscan_params(const char *params)
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);
@@ -1440,10 +1464,37 @@ static struct autoscan_params *parse_autoscan_params(const char *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)
@@ -1457,13 +1508,13 @@ 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,
@@ -1535,7 +1586,7 @@ static int wifi_disable(struct connman_device *device)
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);
@@ -1543,7 +1594,7 @@ static int wifi_disable(struct connman_device *device)
}
/* 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);
@@ -1691,10 +1742,29 @@ static int get_latest_connections(int max_ssids,
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);
}
@@ -1714,7 +1784,7 @@ static gboolean p2p_find_stop(gpointer data)
connman_device_set_scanning(device, CONNMAN_SERVICE_TYPE_P2P, false);
connman_device_unref(device);
- reset_autoscan(device);
+ start_autoscan(device);
return FALSE;
}
@@ -1783,11 +1853,8 @@ static int p2p_find(struct connman_device *device)
* 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;
@@ -1807,14 +1874,15 @@ static int wifi_scan(enum connman_service_type type,
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;
@@ -1843,8 +1911,8 @@ static int wifi_scan(enum connman_service_type type,
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;
@@ -1860,12 +1928,12 @@ static int wifi_scan(enum connman_service_type type,
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) {
@@ -1879,7 +1947,7 @@ static int wifi_scan(enum connman_service_type type,
} 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);
@@ -1887,6 +1955,9 @@ static int wifi_scan(enum connman_service_type type,
}
}
+ /* Distinguish between devices performing passive and active scanning */
+ wifi_update_scanner_type(wifi, WIFI_SCANNING_ACTIVE);
+
connman_device_ref(device);
reset_autoscan(device);
@@ -1909,6 +1980,24 @@ static int wifi_scan(enum connman_service_type type,
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)
@@ -1948,6 +2037,7 @@ static struct connman_device_driver wifi_ng_driver = {
.enable = wifi_enable,
.disable = wifi_disable,
.scan = wifi_scan,
+ .stop_scan = wifi_stop_scan,
.set_regdom = wifi_set_regdom,
};
@@ -2138,10 +2228,9 @@ static void disconnect_callback(int result, GSupplicantInterface *interface,
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;
@@ -2370,17 +2459,20 @@ static void interface_state(GSupplicantInterface *interface)
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) {
@@ -2461,8 +2553,11 @@ static void interface_state(GSupplicantInterface *interface)
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);
@@ -2635,8 +2730,6 @@ static void ap_create_fail(GSupplicantInterface *interface)
g_free(wifi->tethering_param);
wifi->tethering_param = NULL;
}
-
- return;
}
static unsigned char calculate_strength(GSupplicantNetwork *supplicant_network)
@@ -2714,6 +2807,8 @@ static void network_added(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?
@@ -2780,6 +2875,7 @@ static void network_changed(GSupplicantNetwork *network, const char *property)
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);
@@ -2795,11 +2891,42 @@ static void network_changed(GSupplicantNetwork *network, const char *property)
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)
@@ -2819,6 +2946,10 @@ 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);
@@ -2853,6 +2984,32 @@ static void network_associated(GSupplicantNetwork *network)
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)
{
@@ -3073,6 +3230,8 @@ static const GSupplicantCallbacks callbacks = {
.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,
diff --git a/scripts/connman_resolvconf.conf.in b/scripts/connman_resolvconf.conf.in
index 2d61dfe1..a242d7bc 100644
--- a/scripts/connman_resolvconf.conf.in
+++ b/scripts/connman_resolvconf.conf.in
@@ -1,2 +1,2 @@
d @runstatedir@/connman - - - -
-L+ /etc/resolv.conf - - - - @runstatedir@/connman/resolv.conf
+L /etc/resolv.conf - - - - @runstatedir@/connman/resolv.conf
diff --git a/src/acd.c b/src/acd.c
new file mode 100644
index 00000000..7ace1554
--- /dev/null
+++ b/src/acd.c
@@ -0,0 +1,596 @@
+/*
+ *
+ * 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;
+}
diff --git a/src/backtrace.c b/src/backtrace.c
index 6a66c0ac..bede6698 100644
--- a/src/backtrace.c
+++ b/src/backtrace.c
@@ -24,7 +24,6 @@
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
@@ -111,6 +110,11 @@ void print_backtrace(const char* program_path, const char* program_exec,
buf[len] = '\0';
pos = strchr(buf, '\n');
+ if (!pos) {
+ connman_error("Error in backtrace format");
+ break;
+ }
+
*pos++ = '\0';
if (strcmp(buf, "??") == 0) {
@@ -120,6 +124,11 @@ void print_backtrace(const char* program_path, const char* program_exec,
}
ptr = strchr(pos, '\n');
+ if (!ptr) {
+ connman_error("Error in backtrace format");
+ break;
+ }
+
*ptr++ = '\0';
if (strncmp(pos, program_path, pathlen) == 0)
diff --git a/src/config.c b/src/config.c
index a8c3da89..af4f07e1 100644
--- a/src/config.c
+++ b/src/config.c
@@ -72,6 +72,7 @@ struct connman_config_service {
char *ipv6_gateway;
char *ipv6_privacy;
char *mac;
+ bool mdns;
char **nameservers;
char **search_domains;
char **timeservers;
@@ -112,6 +113,7 @@ static bool cleanup = false;
#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"
@@ -152,6 +154,7 @@ static const char *service_possible_keys[] = {
SERVICE_KEY_IPv6,
SERVICE_KEY_IPv6_PRIVACY,
SERVICE_KEY_MAC,
+ SERVICE_KEY_MDNS,
SERVICE_KEY_NAMESERVERS,
SERVICE_KEY_SEARCH_DOMAINS,
SERVICE_KEY_TIMESERVERS,
@@ -193,7 +196,7 @@ static void unregister_service(gpointer data)
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);
@@ -514,6 +517,9 @@ static bool load_service_generic(GKeyFile *keyfile,
g_strfreev(strlist);
}
+ service->mdns = __connman_config_get_bool(keyfile, group,
+ SERVICE_KEY_MDNS, NULL);
+
return true;
err:
@@ -564,8 +570,8 @@ static bool load_service(GKeyFile *keyfile, const char *group,
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;
}
@@ -802,8 +808,11 @@ static bool load_service_from_keyfile(GKeyFile *keyfile,
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;
}
@@ -1209,10 +1218,8 @@ static int try_provision_service(struct connman_config_service *config,
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;
@@ -1246,7 +1253,7 @@ static int try_provision_service(struct connman_config_service *config,
}
DBG("service %p ident %s", service,
- __connman_service_get_ident(service));
+ connman_service_get_identifier(service));
if (config->mac) {
struct connman_device *device;
@@ -1349,7 +1356,7 @@ static int try_provision_service(struct connman_config_service *config,
__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));
@@ -1377,6 +1384,8 @@ static int try_provision_service(struct connman_config_service *config,
__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);
@@ -1694,7 +1703,6 @@ void connman_config_free_entries(struct connman_config_entry **entries)
}
g_free(entries);
- return;
}
bool __connman_config_address_provisioned(const char *address,
diff --git a/src/connection.c b/src/connection.c
index 6b005e7f..7a1fbcee 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -460,6 +460,7 @@ static void set_default_gateway(struct gateway_data *data,
"0.0.0.0") == 0) {
if (connman_inet_set_gateway_interface(index) < 0)
return;
+ data->ipv4_gateway->active = true;
goto done;
}
@@ -468,6 +469,7 @@ static void set_default_gateway(struct gateway_data *data,
"::") == 0) {
if (connman_inet_set_ipv6_gateway_interface(index) < 0)
return;
+ data->ipv6_gateway->active = true;
goto done;
}
@@ -534,6 +536,7 @@ static void unset_default_gateway(struct gateway_data *data,
g_strcmp0(data->ipv4_gateway->gateway,
"0.0.0.0") == 0) {
connman_inet_clear_gateway_interface(index);
+ data->ipv4_gateway->active = false;
return;
}
@@ -541,6 +544,7 @@ static void unset_default_gateway(struct gateway_data *data,
g_strcmp0(data->ipv6_gateway->gateway,
"::") == 0) {
connman_inet_clear_ipv6_gateway_interface(index);
+ data->ipv6_gateway->active = false;
return;
}
@@ -557,7 +561,7 @@ static struct gateway_data *find_default_gateway(void)
{
struct connman_service *service;
- service = __connman_service_get_default();
+ service = connman_service_get_default();
if (!service)
return NULL;
@@ -1008,12 +1012,18 @@ bool __connman_connection_update_gateway(void)
}
}
- 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);
}
diff --git a/src/connman.h b/src/connman.h
index 21b70802..8101c7b2 100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -161,6 +161,9 @@ int __connman_inet_modify_address(int cmd, int flags, int index, int family,
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>
@@ -240,7 +243,11 @@ int __connman_inet_rtnl_addattr32(struct nlmsghdr *n, size_t maxlen,
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);
@@ -255,6 +262,7 @@ void __connman_resolver_append_fallback_nameservers(void);
int __connman_resolvfile_append(int index, const char *domain, const char *server);
int __connman_resolvfile_remove(int index, const char *domain, const char *server);
int __connman_resolver_redo_servers(int index);
+int __connman_resolver_set_mdns(int index, bool enabled);
GKeyFile *__connman_storage_open_global(void);
GKeyFile *__connman_storage_load_global(void);
@@ -443,7 +451,6 @@ GSList *__connman_timeserver_add_list(GSList *server_list,
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,
@@ -462,6 +469,7 @@ int __connman_dhcp_start(struct connman_ipconfig *ipconfig,
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);
@@ -498,7 +506,9 @@ int __connman_connection_get_vpn_index(int phy_index);
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);
@@ -556,10 +566,12 @@ void __connman_device_list(DBusMessageIter *iter, void *user_data);
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);
@@ -619,12 +631,15 @@ bool __connman_config_get_bool(GKeyFile *key_file,
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);
@@ -664,11 +679,9 @@ int __connman_service_compare(const struct connman_service *a,
const struct connman_service *b);
struct connman_service *__connman_service_lookup_from_index(int index);
-struct connman_service *__connman_service_lookup_from_ident(const char *identifier);
struct connman_service *__connman_service_create_from_network(struct connman_network *network);
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);
@@ -682,13 +695,12 @@ struct connman_ipconfig *__connman_service_get_ipconfig(
struct connman_service *service, int family);
void __connman_service_notify_ipv4_configuration(
struct connman_service *service);
+void __connman_service_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);
@@ -704,6 +716,8 @@ int __connman_service_set_ignore(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);
@@ -816,6 +830,7 @@ void __connman_peer_cleanup(void);
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);
@@ -835,11 +850,6 @@ int __connman_peer_service_unregister(const char *owner,
#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);
@@ -912,35 +922,47 @@ int __connman_stats_get(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);
@@ -948,6 +970,7 @@ int __connman_dnsproxy_add_listener(int index);
void __connman_dnsproxy_remove_listener(int index);
int __connman_dnsproxy_append(int index, const char *domain, const char *server);
int __connman_dnsproxy_remove(int index, const char *domain, const char *server);
+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);
@@ -961,15 +984,7 @@ typedef void (*ippool_collision_cb_t) (struct connman_ippool *pool,
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,
@@ -1060,5 +1075,6 @@ int __connman_machine_init(void);
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);
diff --git a/src/connman.service.in b/src/connman.service.in
index 9f5c10fe..7376346e 100644
--- a/src/connman.service.in
+++ b/src/connman.service.in
@@ -13,7 +13,7 @@ BusName=net.connman
Restart=on-failure
ExecStart=@sbindir@/connmand -n
StandardOutput=null
-CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SYS_TIME CAP_SYS_MODULE
+CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SYS_TIME CAP_SYS_MODULE CAP_SYS_ADMIN
ProtectHome=true
ProtectSystem=full
diff --git a/src/device.c b/src/device.c
index a563f464..264c5e2d 100644
--- a/src/device.c
+++ b/src/device.c
@@ -52,7 +52,7 @@ struct connman_device {
* request
*/
bool powered;
- bool scanning;
+ bool scanning[MAX_CONNMAN_SERVICE_TYPES];
char *name;
char *node;
char *address;
@@ -152,6 +152,28 @@ enum connman_service_type __connman_device_get_service_type(
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;
@@ -179,7 +201,7 @@ int __connman_device_enable(struct connman_device *device)
return -EBUSY;
if (device->powered_pending == PENDING_ENABLE)
- return -EALREADY;
+ return -EINPROGRESS;
if (device->powered_pending == PENDING_NONE && device->powered)
return -EALREADY;
@@ -230,7 +252,7 @@ int __connman_device_disable(struct connman_device *device)
return -EBUSY;
if (device->powered_pending == PENDING_DISABLE)
- return -EALREADY;
+ return -EINPROGRESS;
if (device->powered_pending == PENDING_NONE && !device->powered)
return -EALREADY;
@@ -565,7 +587,9 @@ const char *connman_device_get_ident(struct connman_device *device)
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);
@@ -587,11 +611,15 @@ int connman_device_set_powered(struct connman_device *device,
__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(&params, 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, &params);
+ }
return 0;
}
@@ -602,16 +630,22 @@ bool connman_device_get_powered(struct connman_device *device)
}
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(&params, 0, sizeof(params));
+ params.type = type;
+ params.force_full_scan = force_full_scan;
+
+ return device->driver->scan(device, &params);
}
int __connman_device_disconnect(struct connman_device *device)
@@ -682,7 +716,8 @@ static gboolean remove_unavailable_network(gpointer key, gpointer value,
{
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))
@@ -697,9 +732,19 @@ void __connman_device_cleanup_networks(struct connman_device *device)
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)
@@ -723,10 +768,13 @@ int connman_device_set_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);
@@ -1042,7 +1090,8 @@ void connman_device_regdom_notify(struct connman_device *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;
@@ -1066,18 +1115,11 @@ int __connman_device_request_scan(enum connman_service_type type)
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 {
@@ -1092,20 +1134,53 @@ int __connman_device_request_scan(enum connman_service_type type)
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, &params);
+}
+
+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)
diff --git a/src/dhcp.c b/src/dhcp.c
index 1af1eb52..42e9f417 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -745,6 +745,30 @@ void __connman_dhcp_stop(struct connman_ipconfig *ipconfig)
}
}
+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("");
@@ -761,6 +785,4 @@ void __connman_dhcp_cleanup(void)
g_hash_table_destroy(ipconfig_table);
ipconfig_table = NULL;
-
- dhcp_cleanup_random();
}
diff --git a/src/dhcpv6.c b/src/dhcpv6.c
index cbf7974f..2d5f8f6a 100644
--- a/src/dhcpv6.c
+++ b/src/dhcpv6.c
@@ -196,7 +196,7 @@ static int set_duid(struct connman_service *service,
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)
diff --git a/src/dns-systemd-resolved.c b/src/dns-systemd-resolved.c
new file mode 100644
index 00000000..5fe306c3
--- /dev/null
+++ b/src/dns-systemd-resolved.c
@@ -0,0 +1,490 @@
+/*
+ *
+ * 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;
+ }
+}
diff --git a/src/dnsproxy.c b/src/dnsproxy.c
index 40b4f159..2dc73408 100644
--- a/src/dnsproxy.c
+++ b/src/dnsproxy.c
@@ -79,6 +79,11 @@ struct domain_hdr {
#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;
@@ -470,7 +475,7 @@ static void send_cached_response(int sk, unsigned char *buf, int len,
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)
{
@@ -482,21 +487,26 @@ static void send_response(int sk, unsigned char *buf, int len,
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));
@@ -2221,7 +2231,7 @@ static gboolean udp_server_event(GIOChannel *channel, GIOCondition condition,
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)) {
@@ -2233,12 +2243,9 @@ static gboolean udp_server_event(GIOChannel *channel, GIOCondition condition,
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;
}
@@ -2905,34 +2912,41 @@ static void dnsproxy_default_changed(struct connman_service *service)
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);
@@ -2942,7 +2956,23 @@ static int parse_request(unsigned char *buf, int len,
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;
}
@@ -2958,26 +2988,13 @@ static int parse_request(unsigned char *buf, int len,
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);
@@ -3927,6 +3944,11 @@ destroy:
return err;
}
+int __connman_dnsproxy_set_mdns(int index, bool enabled)
+{
+ return -ENOTSUP;
+}
+
void __connman_dnsproxy_cleanup(void)
{
DBG("");
@@ -3948,4 +3970,9 @@ void __connman_dnsproxy_cleanup(void)
g_hash_table_destroy(listener_table);
g_hash_table_destroy(partial_tcp_req_table);
+
+ if (ipv4_resolve)
+ g_resolv_unref(ipv4_resolve);
+ if (ipv6_resolve)
+ g_resolv_unref(ipv6_resolve);
}
diff --git a/src/firewall-iptables.c b/src/firewall-iptables.c
index 45943a82..1b04648b 100644
--- a/src/firewall-iptables.c
+++ b/src/firewall-iptables.c
@@ -92,15 +92,17 @@ static int insert_managed_chain(const char *table_name, int id)
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;
}
@@ -119,13 +121,15 @@ static int delete_managed_chain(const char *table_name, int id)
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);
@@ -178,7 +182,7 @@ static int insert_managed_rule(const char *table_name,
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);
@@ -197,14 +201,14 @@ static int delete_managed_rule(const char *table_name,
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;
@@ -281,7 +285,7 @@ static int enable_rule(struct fw_rule *rule)
if (err < 0)
return err;
- err = __connman_iptables_commit(rule->table);
+ err = __connman_iptables_commit(AF_INET, rule->table);
if (err < 0)
return err;
@@ -304,7 +308,7 @@ static int disable_rule(struct fw_rule *rule)
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));
@@ -343,16 +347,8 @@ static void firewall_add_rule(struct firewall_context *ctx,
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)
@@ -399,14 +395,12 @@ int __connman_firewall_enable_nat(struct firewall_context *ctx,
char *address, unsigned char prefixlen,
char *interface)
{
- char *cmd;
int err;
- cmd = g_strdup_printf("-s %s/%d -o %s -j MASQUERADE",
- address, prefixlen, interface);
+ firewall_add_rule(ctx, "nat", "POSTROUTING",
+ "-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);
@@ -555,8 +549,8 @@ static void flush_table(const char *table_name)
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);
@@ -565,7 +559,7 @@ static void flush_table(const char *table_name)
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",
@@ -573,12 +567,14 @@ static void flush_table(const char *table_name)
}
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));
@@ -587,7 +583,7 @@ static void flush_table(const char *table_name)
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));
diff --git a/src/firewall-nftables.c b/src/firewall-nftables.c
index 1febce44..262b2a90 100644
--- a/src/firewall-nftables.c
+++ b/src/firewall-nftables.c
@@ -67,7 +67,7 @@
#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;
@@ -402,6 +402,8 @@ static int table_cmd(struct mnl_socket *nl, struct nftnl_table *t,
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);
@@ -433,6 +435,8 @@ static int chain_cmd(struct mnl_socket *nl, struct nftnl_chain *chain,
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);
@@ -465,6 +469,8 @@ static int rule_cmd(struct mnl_socket *nl, struct nftnl_rule *rule,
uint32_t seq = 0;
int err;
+ bzero(buf, sizeof(buf));
+
debug_netlink_dump_rule(rule);
batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
@@ -670,7 +676,7 @@ static int build_rule_snat(int index, const char *address,
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;
@@ -1003,7 +1009,7 @@ static int create_table_and_chains(struct nftables_info *nft_info)
/*
* # 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);
@@ -1020,7 +1026,7 @@ static int create_table_and_chains(struct nftables_info *nft_info)
/*
* # 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);
diff --git a/src/inet.c b/src/inet.c
index b887aa0b..b128e578 100644
--- a/src/inet.c
+++ b/src/inet.c
@@ -25,7 +25,6 @@
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
@@ -190,6 +189,40 @@ done:
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;
@@ -330,6 +363,40 @@ done:
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;
@@ -478,7 +545,17 @@ int connman_inet_add_network_route(int index, const char *host,
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;
@@ -641,10 +718,17 @@ int connman_inet_add_ipv6_network_route(int index, const char *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;
@@ -996,7 +1080,7 @@ bool connman_inet_compare_subnet(int index, const char *host)
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);
@@ -2285,6 +2369,7 @@ static gboolean inet_rtnl_event(GIOChannel *chan, GIOCondition cond,
return TRUE;
cleanup:
+ rtnl_data->callback(NULL, rtnl_data->user_data);
inet_rtnl_cleanup(rtnl_data);
return TRUE;
}
@@ -2437,8 +2522,6 @@ out:
data->callback(addr, index, data->user_data);
g_free(data);
-
- return;
}
/*
@@ -2530,9 +2613,10 @@ int connman_inet_check_ipaddress(const char *host)
addr = NULL;
result = getaddrinfo(host, NULL, &hints, &addr);
- if (result == 0)
+ if (result == 0) {
result = addr->ai_family;
- freeaddrinfo(addr);
+ freeaddrinfo(addr);
+ }
return result;
}
@@ -2734,6 +2818,41 @@ out:
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)
{
@@ -2796,12 +2915,15 @@ int __connman_inet_del_fwmark_rule(uint32_t table_id, int family, uint32_t fwmar
}
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:
@@ -2814,7 +2936,19 @@ static int iproute_default_modify(int cmd, uint32_t table_id, int ifindex,
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;
@@ -2829,9 +2963,11 @@ static int iproute_default_modify(int cmd, uint32_t table_id, int ifindex,
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 {
@@ -2860,7 +2996,14 @@ int __connman_inet_add_default_to_table(uint32_t table_id, int ifindex,
{
/* 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,
@@ -2868,7 +3011,14 @@ 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,
@@ -3003,10 +3153,8 @@ static int get_nfs_server_ip(const char *cmdline_file, const char *pnp_file,
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) {
@@ -3202,6 +3350,9 @@ char **__connman_inet_get_pnp_nameservers(const char *pnp_file)
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);
diff --git a/src/ippool.c b/src/ippool.c
index cea1dccd..f2e9b000 100644
--- a/src/ippool.c
+++ b/src/ippool.c
@@ -28,7 +28,6 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
-#include <sys/errno.h>
#include <sys/socket.h>
#include "connman.h"
@@ -43,8 +42,6 @@ struct address_info {
};
struct connman_ippool {
- unsigned int refcount;
-
struct address_info *info;
char *gateway;
@@ -65,30 +62,11 @@ static uint32_t block_20_bits;
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);
@@ -386,7 +364,6 @@ struct connman_ippool *__connman_ippool_create(int index,
info->start = block;
info->end = block + range;
- pool->refcount = 1;
pool->info = info;
pool->collision_cb = collision_cb;
pool->user_data = user_data;
diff --git a/src/iptables.c b/src/iptables.c
index 5ef757a3..9cfd80f8 100644
--- a/src/iptables.c
+++ b/src/iptables.c
@@ -28,12 +28,14 @@
#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"
@@ -128,6 +130,10 @@
* 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",
@@ -143,29 +149,25 @@ static const char *hooknames[] = {
#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;
@@ -178,11 +180,307 @@ struct connman_iptables {
};
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)
{
@@ -197,7 +495,7 @@ 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,
@@ -206,15 +504,45 @@ static int iterate_entries(struct ipt_entry *entries,
{
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.
@@ -239,7 +567,7 @@ static int iterate_entries(struct ipt_entry *entries,
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;
}
@@ -247,15 +575,20 @@ static int iterate_entries(struct ipt_entry *entries,
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;
- 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);
+ 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,
+ iptables_entry_get_next_offset(entry),
+ (uint64_t) counters->pcnt, (uint64_t) counters->bcnt);
return cb(entry, builtin, hook, size, offset, NULL);
}
@@ -292,9 +625,12 @@ static bool is_jump(struct connman_iptables_entry *e)
{
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;
@@ -319,8 +655,12 @@ static bool is_fallthrough(struct connman_iptables_entry *e)
{
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;
@@ -333,15 +673,20 @@ static bool is_fallthrough(struct connman_iptables_entry *e)
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;
@@ -352,23 +697,35 @@ static GList *find_chain_head(struct connman_iptables *table,
{
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;
}
@@ -397,7 +754,6 @@ static GList *find_chain_tail(struct connman_iptables *table,
return g_list_last(table->entries);
}
-
static void update_offsets(struct connman_iptables *table)
{
GList *list, *prev;
@@ -416,7 +772,8 @@ static void update_offsets(struct connman_iptables *table)
prev_entry = prev->data;
entry->offset = prev_entry->offset +
- prev_entry->entry->next_offset;
+ iptables_entry_get_next_offset(
+ prev_entry);
}
}
@@ -428,9 +785,9 @@ static void update_targets_reference(struct connman_iptables *table,
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;
@@ -438,7 +795,11 @@ static void update_targets_reference(struct connman_iptables *table,
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)
@@ -451,40 +812,56 @@ static void update_targets_reference(struct connman_iptables *table,
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;
}
@@ -505,15 +882,17 @@ static int remove_table_entry(struct connman_iptables *table,
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;
}
@@ -590,16 +969,30 @@ static int iptables_add_chain(struct connman_iptables *table,
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.
@@ -610,47 +1003,93 @@ static int iptables_add_chain(struct connman_iptables *table,
*/
/* 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;
+
+ 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;
- entry_head->target_offset = ALIGN(sizeof(struct ipt_entry));
- entry_head->next_offset = entry_head_size;
+ entry6_head = g_try_malloc0(entry_head_size);
+ if (!entry6_head)
+ goto err_head;
- 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);
+ entry6_head->target_offset = entry_struct_size;
+ entry6_head->next_offset = entry_head_size;
- if (iptables_add_entry(table, entry_head, last, -1, -1) < 0)
+ error = (struct xt_error_target *) entry6_head->elems;
+
+ entry.entry6 = entry6_head;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ 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, 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;
- entry_return->target_offset = ALIGN(sizeof(struct ipt_entry));
- entry_return->next_offset = entry_return_size;
+ entry6_return->target_offset = entry_struct_size;
+ entry6_return->next_offset = entry_return_size;
- standard = (struct ipt_standard_target *) entry_return->elems;
- standard->target.u.user.target_size =
- ALIGN(sizeof(struct ipt_standard_target));
+ standard = (struct ipt_standard_target *) entry6_return->elems;
+
+ entry.entry6 = entry6_return;
+ break;
+ }
+
+ 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;
}
@@ -691,14 +1130,21 @@ static int iptables_delete_chain(struct connman_iptables *table,
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;
@@ -706,46 +1152,89 @@ static struct ipt_entry *new_rule(struct ipt_ip *ip,
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;
@@ -754,7 +1243,9 @@ static void update_hooks(struct connman_iptables *table, GList *chain_head,
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;
@@ -763,21 +1254,24 @@ static void update_hooks(struct connman_iptables *table, GList *chain_head,
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)
@@ -788,8 +1282,17 @@ static struct ipt_entry *prepare_rule_inclusion(struct connman_iptables *table,
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);
@@ -807,15 +1310,21 @@ static struct ipt_entry *prepare_rule_inclusion(struct connman_iptables *table,
}
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;
@@ -825,25 +1334,51 @@ static int iptables_append_rule(struct connman_iptables *table,
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;
@@ -853,17 +1388,41 @@ static int iptables_insert_rule(struct connman_iptables *table,
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);
+ 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;
}
@@ -883,6 +1442,38 @@ static bool is_same_ipt_entry(struct ipt_entry *i_e1,
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)
{
@@ -895,7 +1486,12 @@ static bool is_same_target(struct xt_entry_target *xt_e_t1,
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;
@@ -948,7 +1544,8 @@ static bool is_same_match(struct xt_entry_match *xt_e_m1,
}
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,
@@ -958,7 +1555,7 @@ static GList *find_existing_rule(struct connman_iptables *table,
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);
@@ -973,13 +1570,25 @@ static GList *find_existing_rule(struct connman_iptables *table,
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;
@@ -991,18 +1600,16 @@ static GList *find_existing_rule(struct connman_iptables *table,
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;
@@ -1011,7 +1618,8 @@ static GList *find_existing_rule(struct connman_iptables *table,
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;
@@ -1020,7 +1628,7 @@ static GList *find_existing_rule(struct connman_iptables *table,
break;
}
- g_free(entry_test);
+ iptables_entry_free(entry_test);
if (list != chain_tail->prev)
return list;
@@ -1029,7 +1637,8 @@ static GList *find_existing_rule(struct connman_iptables *table,
}
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,
@@ -1039,7 +1648,6 @@ static int iptables_delete_rule(struct connman_iptables *table,
GList *chain_head, *chain_tail, *list;
int builtin, removed;
-
DBG("table %s chain %s", table->name, chain_name);
removed = 0;
@@ -1053,7 +1661,8 @@ static int iptables_delete_rule(struct connman_iptables *table,
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;
@@ -1129,7 +1738,11 @@ static int iptables_change_policy(struct connman_iptables *table,
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)
@@ -1180,38 +1793,114 @@ static struct ipt_replace *iptables_blob(struct connman_iptables *table)
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 ipt_ip *ip = &entry->ip;
+ 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)
+{
+ 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);
+ }
-static void dump_target(struct ipt_entry *entry)
+ 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);
+ 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 = ipt_get_target(entry);
+ target = iptables_entry_get_target(entry);
- if (!g_strcmp0(target->u.user.name, IPT_STANDARD_TARGET)) {
+ if (!target)
+ return;
+
+ if (!g_strcmp0(target->u.user.name, get_standard_target(entry->type))) {
struct xt_standard_target *t;
t = (struct xt_standard_target *)target;
@@ -1242,13 +1931,34 @@ static void dump_target(struct ipt_entry *entry)
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;
@@ -1264,20 +1974,47 @@ static void dump_target(struct ipt_entry *entry)
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;
@@ -1295,33 +2032,48 @@ 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 = iptables_entry_get_target(entry);
- target = ipt_get_target(entry);
+ if (!target)
+ return -EINVAL;
- if (offset + entry->next_offset == size) {
+ 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);
@@ -1333,54 +2085,192 @@ static int dump_entry(struct ipt_entry *entry, int builtin,
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);
+}
+
+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;
- 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);
+ switch (replace->type) {
+ case AF_INET:
+ return replace->r->underflow;
+ case AF_INET6:
+ return replace->r6->underflow;
+ }
+
+ return NULL;
}
-static void dump_ipt_replace(struct ipt_replace *repl)
+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_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)
@@ -1388,10 +2278,26 @@ 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;
@@ -1399,12 +2305,31 @@ static int iptables_get_entries(struct connman_iptables *table)
}
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;
@@ -1415,29 +2340,63 @@ static int iptables_add_counters(struct connman_iptables *table,
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)
@@ -1454,32 +2413,60 @@ 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);
@@ -1489,55 +2476,112 @@ static struct connman_iptables *iptables_init(const char *table_name)
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;
+
+ memcpy(table->underflow, table->info->underflow,
+ sizeof(table->info->underflow));
+ memcpy(table->hook_entry, table->info->hook_entry,
+ sizeof(table->info->hook_entry));
- iterate_entries(table->blob_entries->entrytable,
- table->info->valid_hooks, table->info->hook_entry,
- table->info->underflow, table->blob_entries->size,
- add_entry, table);
+ 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;
}
@@ -1559,13 +2603,77 @@ static struct option iptables_opts[] = {
{.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
@@ -1578,9 +2686,15 @@ static struct xtables_target *prepare_target(struct connman_iptables *table,
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;
@@ -1590,16 +2704,37 @@ static struct xtables_target *prepare_target(struct connman_iptables *table,
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)
@@ -1611,7 +2746,8 @@ static struct xtables_target *prepare_target(struct connman_iptables *table,
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);
@@ -1628,24 +2764,51 @@ static struct xtables_target *prepare_target(struct connman_iptables *table,
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;
+ }
+
+ 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;
+ }
- if (!iptables_globals.opts) {
- g_free(xt_t->t);
- xt_t = NULL;
+ break;
}
return xt_t;
@@ -1657,12 +2820,35 @@ static struct xtables_match *prepare_matches(struct connman_iptables *table,
{
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)
@@ -1675,28 +2861,59 @@ static struct xtables_match *prepare_matches(struct connman_iptables *table,
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);
+
+ xt_m = NULL;
+ }
- if (xt_m == xt_m->next)
- free(xt_m);
+ 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;
@@ -1721,12 +2938,14 @@ static int parse_ip_and_mask(const char *str, struct in_addr *ip,
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;
}
@@ -1740,35 +2959,96 @@ out:
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)
@@ -1809,13 +3089,34 @@ static int parse_xt_modules(int c, bool invert,
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));
+
+ if (ctx->proto == 0)
+ ctx->proto = IPPROTO_IPV6;
+
+ fw6.ipv6.proto = ctx->proto;
- memset(&fw, 0, sizeof(fw));
+ /* Flags must be set for IPv6 if protocol is set. */
+ fw6.ipv6.flags |= IP6T_F_PROTO;
- /* The SNAT parser wants to know the protocol. */
- if (ctx->proto == 0)
- ctx->proto = IPPROTO_IP;
- fw.ip.proto = ctx->proto;
+ break;
+ default:
+ return 0;
+ }
for (rm = ctx->xt_rm; rm; rm = rm->next) {
if (rm->completed != 0)
@@ -1831,7 +3132,24 @@ static int parse_xt_modules(int c, bool invert,
+ 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)
@@ -1845,7 +3163,24 @@ static int parse_xt_modules(int c, bool invert,
+ XT_OPTION_OFFSET_SCALE)
return 0;
- xtables_option_tpcall(c, ctx->argv, invert, ctx->xt_t, &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;
}
@@ -1853,13 +3188,35 @@ static int parse_xt_modules(int c, bool invert,
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;
}
@@ -1946,9 +3303,28 @@ static int parse_rule_spec(struct connman_iptables *table,
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
@@ -1958,29 +3334,58 @@ static int parse_rule_spec(struct connman_iptables *table,
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 (ctx->type == AF_INET6) {
+ if (!parse_ipv6_and_mask(optarg,
+ &ctx->ipv6->src,
+ &ctx->ipv6->smsk))
+ break;
- if (invert)
- ctx->ip->invflags |= IPT_INV_SRCIP;
+ 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 (ctx->type == AF_INET6) {
+ /* Destination specification */
+ if (!parse_ipv6_and_mask(optarg,
+ &ctx->ipv6->dst,
+ &ctx->ipv6->dmsk))
+ break;
- if (invert)
- ctx->ip->invflags |= IPT_INV_DSTIP;
+ if (invert)
+ ctx->ip->invflags |= IP6T_INV_DSTIP;
+ }
+
break;
case 'i':
/* In interface specification */
@@ -1989,11 +3394,21 @@ static int parse_rule_spec(struct connman_iptables *table,
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 (ctx->type == AF_INET6) {
+ g_stpcpy(ctx->ipv6->iniface, optarg);
+ memset(ctx->ipv6->iniface_mask, 0xff, len + 1);
- if (invert)
- ctx->ip->invflags |= IPT_INV_VIA_IN;
+ if (invert)
+ ctx->ipv6->invflags |= IP6T_INV_VIA_IN;
+ }
break;
case 'o':
@@ -2003,11 +3418,21 @@ static int parse_rule_spec(struct connman_iptables *table,
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 (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->ipv6->invflags |= IP6T_INV_VIA_OUT;
+ }
break;
case 'm':
@@ -2021,7 +3446,41 @@ static int parse_rule_spec(struct connman_iptables *table,
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 */
@@ -2051,6 +3510,8 @@ static int parse_rule_spec(struct connman_iptables *table,
err = parse_xt_modules(c, invert, ctx);
if (err == 1)
continue;
+ else if (err == -EINVAL)
+ goto out;
break;
}
@@ -2064,6 +3525,42 @@ out:
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;
@@ -2103,7 +3600,10 @@ static void cleanup_parse_context(struct parse_context *ctx)
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;
@@ -2131,13 +3631,13 @@ static void cleanup_parse_context(struct parse_context *ctx)
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;
@@ -2146,83 +3646,131 @@ int __connman_iptables_dump(const char *table_name)
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;
@@ -2237,8 +3785,10 @@ int __connman_iptables_append(const char *table_name,
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();
@@ -2246,26 +3796,35 @@ out:
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;
@@ -2280,8 +3839,10 @@ int __connman_iptables_insert(const char *table_name,
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();
@@ -2289,26 +3850,35 @@ out:
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;
@@ -2323,9 +3893,10 @@ int __connman_iptables_delete(const char *table_name,
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();
@@ -2333,30 +3904,46 @@ out:
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);
- table = g_hash_table_lookup(table_hash, table_name);
+ repl.type = type;
+
+ 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;
@@ -2367,12 +3954,23 @@ int __connman_iptables_commit(const char *table_name)
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);
@@ -2383,10 +3981,19 @@ int __connman_iptables_commit(const char *table_name)
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;
}
@@ -2397,7 +4004,7 @@ static void remove_table(gpointer user_data)
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)
{
@@ -2405,40 +4012,58 @@ static int iterate_chains_cb(struct ipt_entry *entry, int builtin,
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;
+
+ err = setup_xtables(type);
+
+ if (err < 0)
+ return err;
- table = get_table(table_name);
+ 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;
}
@@ -2453,7 +4078,8 @@ int __connman_iptables_init(void)
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;
}
@@ -2463,4 +4089,6 @@ void __connman_iptables_cleanup(void)
DBG("");
g_hash_table_destroy(table_hash);
+ g_hash_table_destroy(table_hash_ipv6);
}
+
diff --git a/src/ipv6pd.c b/src/ipv6pd.c
index 0d221f07..8cd2a33e 100644
--- a/src/ipv6pd.c
+++ b/src/ipv6pd.c
@@ -41,7 +41,6 @@ static guint timer_uplink;
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);
@@ -139,7 +138,7 @@ static gboolean do_setup(gpointer data)
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 */
@@ -155,7 +154,7 @@ static void dhcpv6_renew_callback(struct connman_network *network,
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)
@@ -165,11 +164,6 @@ 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;
@@ -260,13 +254,6 @@ static int setup_prefix_delegation(struct connman_service *service)
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);
@@ -275,7 +262,7 @@ static void update_default_interface(struct connman_service *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))
@@ -308,7 +295,7 @@ static void update_ipconfig(struct connman_service *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,
@@ -333,16 +320,13 @@ int __connman_ipv6pd_setup(const char *bridge)
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.
diff --git a/src/log.c b/src/log.c
index 9bae4a3d..554b046b 100644
--- a/src/log.c
+++ b/src/log.c
@@ -23,7 +23,6 @@
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
@@ -31,12 +30,16 @@
#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
diff --git a/src/main.c b/src/main.c
index b78a0461..2371771f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -39,6 +39,8 @@
#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)
@@ -52,6 +54,11 @@ static char *default_auto_connect[] = {
NULL
};
+static char *default_favorite_techs[] = {
+ "ethernet",
+ NULL
+};
+
static char *default_blacklist[] = {
"vmnet",
"vboxnet",
@@ -66,6 +73,7 @@ static struct {
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;
@@ -73,16 +81,21 @@ static struct {
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,
@@ -90,17 +103,22 @@ static struct {
.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"
@@ -108,17 +126,21 @@ static struct {
#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,
@@ -126,12 +148,16 @@ static const char *supported_options[] = {
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
};
@@ -260,7 +286,9 @@ static void parse_config(GKeyFile *config)
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;
@@ -283,14 +311,26 @@ static void parse_config(GKeyFile *config)
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);
@@ -363,6 +403,14 @@ static void parse_config(GKeyFile *config)
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;
@@ -408,6 +456,26 @@ static void parse_config(GKeyFile *config)
}
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)
@@ -557,9 +625,9 @@ static GOptionEntry options[] = {
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,
@@ -571,7 +639,7 @@ static GOptionEntry options[] = {
"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" },
@@ -603,6 +671,9 @@ bool connman_setting_get_bool(const char *key)
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;
@@ -615,6 +686,15 @@ bool connman_setting_get_bool(const char *key)
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;
}
@@ -637,9 +717,12 @@ char **connman_setting_get_string_list(const char *key)
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;
@@ -831,6 +914,7 @@ int main(int argc, char *argv[])
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);
diff --git a/src/main.conf b/src/main.conf
index 68870b28..14965e12 100644
--- a/src/main.conf
+++ b/src/main.conf
@@ -14,17 +14,26 @@
# 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
@@ -55,16 +64,21 @@
# 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
@@ -117,3 +131,15 @@
# 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
diff --git a/src/manager.c b/src/manager.c
index d15ce203..3bf8f4e4 100644
--- a/src/manager.c
+++ b/src/manager.c
@@ -169,7 +169,7 @@ static void idle_state(bool idle)
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,
@@ -214,6 +214,27 @@ static DBusMessage *get_peers(DBusConnection *conn,
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)
{
@@ -519,6 +540,9 @@ static const GDBusMethodTable manager_methods[] = {
{ 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" }),
diff --git a/src/nat.c b/src/nat.c
index fb557101..681acb21 100644
--- a/src/nat.c
+++ b/src/nat.c
@@ -55,8 +55,10 @@ static int enable_ip_forward(bool enable)
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) {
@@ -201,7 +203,7 @@ static void cleanup_nat(gpointer data)
g_free(nat);
}
-static struct connman_notifier nat_notifier = {
+static const struct connman_notifier nat_notifier = {
.name = "nat",
.default_changed = update_default_interface,
};
diff --git a/src/network.c b/src/network.c
index ed56210f..56fe24ff 100644
--- a/src/network.c
+++ b/src/network.c
@@ -27,6 +27,8 @@
#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
@@ -41,6 +43,15 @@
*/
#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;
@@ -60,6 +71,9 @@ struct connman_network {
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;
@@ -90,6 +104,7 @@ struct connman_network {
char *private_key_passphrase;
char *phase2_auth;
bool wps;
+ bool wps_advertizing;
bool use_wps;
char *pin_wps;
} wifi;
@@ -146,6 +161,260 @@ static void set_configuration(struct connman_network *network,
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;
@@ -163,6 +432,14 @@ static void dhcp_success(struct connman_network *network)
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;
@@ -229,6 +506,14 @@ static int set_connected_manual(struct connman_network *network)
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;
@@ -241,6 +526,14 @@ 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;
@@ -248,6 +541,7 @@ static int set_connected_dhcp(struct connman_network *network)
int err;
DBG("network %p", network);
+ remove_dhcp_timeout(network);
service = connman_service_lookup_from_network(network);
ipconfig_ipv4 = __connman_service_get_ip4config(service);
@@ -263,6 +557,44 @@ static int set_connected_dhcp(struct connman_network *network)
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)
{
@@ -427,7 +759,7 @@ static void check_dhcpv6(struct nd_router_advert *reply,
DBG("re-send router solicitation %d",
network->router_solicit_count);
network->router_solicit_count--;
- __connman_inet_ipv6_send_rs(network->index, 1,
+ __connman_inet_ipv6_send_rs(network->index, RTR_SOLICITATION_INTERVAL,
check_dhcpv6, network);
return;
}
@@ -512,7 +844,6 @@ static void receive_refresh_rs_reply(struct nd_router_advert *reply,
network->router_solicit_refresh_count = 0;
connman_network_unref(network);
- return;
}
int __connman_network_refresh_rs_ipv6(struct connman_network *network,
@@ -577,7 +908,8 @@ static void autoconf_ipv6_set(struct connman_network *network)
/* Try to get stateless DHCPv6 information, RFC 3736 */
network->router_solicit_count = 3;
- __connman_inet_ipv6_send_rs(index, 1, check_dhcpv6, network);
+ __connman_inet_ipv6_send_rs(index, RTR_SOLICITATION_INTERVAL,
+ check_dhcpv6, network);
}
static void set_connected(struct connman_network *network)
@@ -663,6 +995,7 @@ static void set_disconnected(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;
}
@@ -911,6 +1244,7 @@ static void network_destruct(struct connman_network *network)
g_free(network->node);
g_free(network->name);
g_free(network->identifier);
+ acd_host_free(network->acd_host);
network->device = NULL;
@@ -946,9 +1280,13 @@ struct connman_network *connman_network_create(const char *identifier,
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;
@@ -1459,10 +1797,10 @@ int __connman_network_connect(struct connman_network *network)
if (!network->device)
return -ENODEV;
- network->connecting = true;
-
__connman_device_disconnect(network->device);
+ network->connecting = true;
+
err = network->driver->connect(network);
if (err < 0) {
if (err == -EINPROGRESS)
@@ -1490,6 +1828,10 @@ int __connman_network_disconnect(struct connman_network *network)
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;
@@ -1529,13 +1871,16 @@ int __connman_network_clear_ipconfig(struct connman_network *network,
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;
}
@@ -1919,6 +2264,8 @@ int connman_network_set_bool(struct connman_network *network,
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;
@@ -1939,6 +2286,8 @@ bool connman_network_get_bool(struct connman_network *network,
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;
diff --git a/src/nostats.c b/src/nostats.c
new file mode 100644
index 00000000..aedc2c06
--- /dev/null
+++ b/src/nostats.c
@@ -0,0 +1,60 @@
+/*
+ *
+ * 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)
+{
+}
diff --git a/src/notifier.c b/src/notifier.c
index 5ba53242..47eb72f1 100644
--- a/src/notifier.c
+++ b/src/notifier.c
@@ -50,11 +50,11 @@ static gint compare_priority(gconstpointer a, gconstpointer b)
*
* 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;
@@ -66,7 +66,7 @@ int connman_notifier_register(struct connman_notifier *notifier)
*
* 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);
@@ -215,7 +215,7 @@ void __connman_notifier_default_changed(struct connman_service *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->default_changed)
notifier->default_changed(service);
@@ -228,7 +228,7 @@ void __connman_notifier_service_add(struct connman_service *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);
@@ -251,7 +251,7 @@ void __connman_notifier_service_remove(struct connman_service *service)
}
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);
@@ -263,7 +263,7 @@ void __connman_notifier_proxy_changed(struct connman_service *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);
@@ -289,7 +289,7 @@ void __connman_notifier_offlinemode(bool enabled)
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);
@@ -303,7 +303,7 @@ static void notify_idle_state(bool idle)
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);
@@ -318,7 +318,7 @@ void __connman_notifier_service_state_changed(struct connman_service *service,
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);
@@ -361,7 +361,7 @@ void __connman_notifier_ipconfig_changed(struct connman_service *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->ipconfig_changed)
notifier->ipconfig_changed(service, ipconfig);
diff --git a/src/ntp.c b/src/ntp.c
index 0e80c3e5..e7fee22a 100644
--- a/src/ntp.c
+++ b/src/ntp.c
@@ -23,7 +23,6 @@
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
@@ -118,43 +117,55 @@ struct ntp_msg {
#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 *)&timeserver_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;
@@ -177,38 +188,28 @@ static void send_packet(int fd, struct sockaddr *server, uint32_t timeout)
if (server->sa_family == AF_INET) {
size = sizeof(struct sockaddr_in);
- addr = (void *)&(((struct sockaddr_in *)&timeserver_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 *)&timeserver_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;
}
@@ -218,34 +219,35 @@ static void send_packet(int fd, struct sockaddr *server, uint32_t timeout)
* 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 *)&timeserver_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;
@@ -280,9 +282,9 @@ static void decode_msg(void *base, size_t len, struct timeval *tv,
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;
}
@@ -290,6 +292,7 @@ static void decode_msg(void *base, size_t len, struct timeval *tv,
if (NTP_FLAGS_LI_DECODE(msg->flags) == NTP_FLAG_LI_NOTINSYNC) {
DBG("ignoring unsynchronized peer");
+ nd->cb(false, nd->user_data);
return;
}
@@ -300,17 +303,19 @@ static void decode_msg(void *base, size_t len, struct timeval *tv,
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) +
@@ -328,18 +333,19 @@ static void decode_msg(void *base, size_t len, struct timeval *tv,
/* 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;
@@ -374,17 +380,21 @@ static void decode_msg(void *base, size_t len, struct timeval *tv,
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;
@@ -401,7 +411,7 @@ static gboolean received_data(GIOChannel *channel, GIOCondition condition,
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;
}
@@ -424,11 +434,11 @@ static gboolean received_data(GIOChannel *channel, GIOCondition condition,
if (sender_addr.sin6_family == AF_INET) {
size = 4;
- addr_ptr = &((struct sockaddr_in *)&timeserver_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 *)&timeserver_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");
@@ -452,12 +462,12 @@ static gboolean received_data(GIOChannel *channel, GIOCondition condition,
}
}
- 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;
@@ -470,14 +480,11 @@ static void start_ntp(char *server)
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");
@@ -486,18 +493,18 @@ static void start_ntp(char *server)
family = info->ai_family;
- memcpy(&timeserver_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 *)&timeserver_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);
@@ -506,96 +513,90 @@ static void start_ntp(char *server)
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, &timestamp,
+ if (setsockopt(nd->transmit_fd, SOL_SOCKET, SO_TIMESTAMP, &timestamp,
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*)&timeserver_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;
}
}
diff --git a/src/peer.c b/src/peer.c
index 340cbcc2..2102f119 100644
--- a/src/peer.c
+++ b/src/peer.c
@@ -82,7 +82,7 @@ static void stop_dhcp_server(struct connman_peer *peer)
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;
}
@@ -606,6 +606,9 @@ static int peer_connect(struct connman_peer *peer)
{
int err = -ENOTSUP;
+ if (is_connected(peer))
+ return -EISCONN;
+
if (peer_driver->connect)
err = peer_driver->connect(peer,
CONNMAN_PEER_WPS_UNKNOWN, NULL);
@@ -1177,6 +1180,18 @@ const char *__connman_peer_get_path(struct connman_peer *peer)
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("");
diff --git a/src/provider.c b/src/provider.c
index 9c71a200..9d9741e1 100644
--- a/src/provider.c
+++ b/src/provider.c
@@ -732,7 +732,7 @@ static void provider_service_changed(struct connman_service *service,
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)
@@ -745,11 +745,9 @@ static void provider_service_changed(struct connman_service *service,
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,
diff --git a/src/resolver.c b/src/resolver.c
index 75ea5ba6..10121aa5 100644
--- a/src/resolver.c
+++ b/src/resolver.c
@@ -23,7 +23,6 @@
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
@@ -652,6 +651,14 @@ static void free_resolvfile(gpointer data)
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;
diff --git a/src/rfkill.c b/src/rfkill.c
index d9bed4d2..b2514c41 100644
--- a/src/rfkill.c
+++ b/src/rfkill.c
@@ -23,7 +23,6 @@
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
diff --git a/src/rtnl.c b/src/rtnl.c
index a094e257..cba5ef7a 100644
--- a/src/rtnl.c
+++ b/src/rtnl.c
@@ -127,6 +127,7 @@ static void read_uevent(struct interface_data *interface)
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;
@@ -1383,7 +1384,7 @@ static void rtnl_message(void *buf, size_t len)
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,
diff --git a/src/service.c b/src/service.c
index 02cd51f2..3202f26c 100644
--- a/src/service.c
+++ b/src/service.c
@@ -44,8 +44,8 @@ static DBusConnection *connection = NULL;
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;
@@ -94,6 +94,8 @@ struct connman_service {
char **nameservers_auto;
int nameservers_timeout;
char **domains;
+ bool mdns;
+ bool mdns_config;
char *hostname;
char *domainname;
char **timeservers;
@@ -124,7 +126,10 @@ struct connman_service {
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;
@@ -594,6 +599,9 @@ static int service_load(struct connman_service *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);
@@ -774,6 +782,13 @@ static int service_save(struct connman_service *service)
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);
@@ -1377,7 +1392,7 @@ static void address_updated(struct connman_service *service,
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);
@@ -1473,7 +1488,7 @@ static void reset_stats(struct connman_service *service)
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;
@@ -1495,14 +1510,14 @@ bool __connman_service_index_is_default(int index)
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;
@@ -1520,7 +1535,8 @@ static void default_changed(void)
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);
}
@@ -1652,9 +1668,28 @@ static void append_security(DBusMessageIter *iter, void *user_data)
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;
@@ -2075,6 +2110,48 @@ static void proxy_configuration_changed(struct connman_service *service)
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))
@@ -2325,17 +2402,16 @@ void __connman_service_counter_unregister(const char *counter)
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,
@@ -2452,8 +2528,19 @@ 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,
@@ -2551,7 +2638,10 @@ void __connman_service_set_hostname(struct connman_service *service,
return;
g_free(service->hostname);
- service->hostname = g_strdup(hostname);
+ service->hostname = NULL;
+
+ if (hostname && g_str_is_ascii(hostname))
+ service->hostname = g_strdup(hostname);
}
const char *__connman_service_get_hostname(struct connman_service *service)
@@ -2569,7 +2659,10 @@ void __connman_service_set_domainname(struct connman_service *service,
return;
g_free(service->domainname);
- service->domainname = g_strdup(domainname);
+ service->domainname = NULL;
+
+ if (domainname && g_str_is_ascii(domainname))
+ service->domainname = g_strdup(domainname);
domain_changed(service);
}
@@ -2585,6 +2678,14 @@ const char *connman_service_get_domainname(struct connman_service *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)
@@ -3319,7 +3420,7 @@ int __connman_service_reset_ipconfig(struct connman_service *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);
}
@@ -3331,6 +3432,29 @@ int __connman_service_reset_ipconfig(struct connman_service *service,
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)
{
@@ -3448,13 +3572,11 @@ static DBusMessage *set_property(DBusConnection *conn,
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")) {
@@ -3494,15 +3616,14 @@ static DBusMessage *set_property(DBusConnection *conn,
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")) {
@@ -3571,6 +3692,23 @@ static DBusMessage *set_property(DBusConnection *conn,
__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")) {
@@ -3730,7 +3868,8 @@ static bool is_ignore(struct connman_service *service)
if (!service->autoconnect)
return true;
- if (service->roaming)
+ if (service->roaming &&
+ !connman_setting_get_bool("AutoConnectRoamingServices"))
return true;
if (service->ignore)
@@ -3982,7 +4121,7 @@ static gboolean run_auto_connect(gpointer data)
bool autoconnecting = false;
GList *preferred_tech;
- autoconnect_timeout = 0;
+ autoconnect_id = 0;
DBG("");
@@ -4003,13 +4142,13 @@ void __connman_service_auto_connect(enum connman_service_connect_reason reason)
{
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));
}
@@ -4017,7 +4156,7 @@ static gboolean run_vpn_auto_connect(gpointer data) {
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;
@@ -4059,10 +4198,10 @@ static gboolean run_vpn_auto_connect(gpointer 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);
}
@@ -4088,7 +4227,6 @@ void __connman_service_set_provider_pending(struct connman_service *service,
}
service->provider_pending = msg;
- return;
}
static void check_pending_msg(struct connman_service *service)
@@ -4210,18 +4348,10 @@ static DBusMessage *connect_service(DBusConnection *conn,
err = __connman_service_connect(service,
CONNMAN_SERVICE_CONNECT_REASON_USER);
- if (err == -EINPROGRESS)
- return NULL;
-
- if (service->pending) {
- dbus_message_unref(service->pending);
- service->pending = NULL;
- }
-
- if (err < 0)
- return __connman_error_failed(msg, -err);
+ if (err != -EINPROGRESS)
+ reply_pending(service, -err);
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+ return NULL;
}
static DBusMessage *disconnect_service(DBusConnection *conn,
@@ -4346,7 +4476,7 @@ static void apply_relevant_default_downgrade(struct connman_service *service)
{
struct connman_service *def_service;
- def_service = __connman_service_get_default();
+ def_service = connman_service_get_default();
if (!def_service)
return;
@@ -4379,6 +4509,89 @@ static void switch_default_service(struct connman_service *default_service,
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)
@@ -4485,6 +4698,8 @@ static DBusMessage *move_service(DBusConnection *conn,
__connman_connection_update_gateway();
+ service_schedule_changed();
+
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
@@ -4510,88 +4725,6 @@ static DBusMessage *reset_counters(DBusConnection *conn,
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);
@@ -4798,6 +4931,7 @@ static void service_initialize(struct connman_service *service)
service->provider = NULL;
service->wps = false;
+ service->wps_advertizing = false;
}
/**
@@ -5149,9 +5283,6 @@ int __connman_service_set_favorite_delayed(struct connman_service *service,
service->favorite = favorite;
- if (!delay_ordering)
- __connman_service_get_order(service);
-
favorite_changed(service);
if (!delay_ordering) {
@@ -5273,6 +5404,14 @@ void __connman_service_set_search_domains(struct connman_service *service,
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)
{
@@ -5341,7 +5480,7 @@ static void request_input_cb(struct connman_service *service,
if (g_strcmp0(error,
"net.connman.Agent.Error.Canceled") == 0) {
- err = -EINVAL;
+ err = -ECONNABORTED;
if (service->hidden)
__connman_service_return_error(service,
@@ -5528,7 +5667,7 @@ static int service_indicate_state(struct connman_service *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,
@@ -5599,7 +5738,7 @@ static int service_indicate_state(struct connman_service *service)
default_changed();
- def_service = __connman_service_get_default();
+ def_service = connman_service_get_default();
service_update_preferred_order(def_service, service, new_state);
@@ -5651,13 +5790,6 @@ static int service_indicate_state(struct connman_service *service)
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);
@@ -5678,16 +5810,14 @@ static int service_indicate_state(struct connman_service *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;
}
@@ -5822,7 +5952,7 @@ static void check_proxy_setup(struct connman_service *service)
return;
done:
- __connman_wispr_start(service, CONNMAN_IPCONFIG_TYPE_IPV4);
+ __connman_service_wispr_start(service, CONNMAN_IPCONFIG_TYPE_IPV4);
}
/*
@@ -5878,18 +6008,33 @@ static void service_rp_filter(struct connman_service *service,
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;
}
@@ -5897,29 +6042,42 @@ static gboolean redo_wispr(gpointer user_data)
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)
@@ -5993,14 +6151,14 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service,
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;
@@ -6020,8 +6178,10 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service,
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;
@@ -6164,8 +6324,15 @@ static int service_connect(struct connman_service *service)
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
@@ -6322,7 +6489,6 @@ int __connman_service_connect(struct connman_service *service,
return err;
}
- reply_pending(service, -err);
}
return err;
@@ -6339,7 +6505,7 @@ int __connman_service_disconnect(struct connman_service *service)
connman_agent_cancel(service);
- reply_pending(service, ECONNABORTED);
+ __connman_stats_service_unregister(service);
if (service->network) {
err = __connman_network_disconnect(service->network);
@@ -6370,8 +6536,6 @@ int __connman_service_disconnect(struct connman_service *service)
__connman_ipconfig_disable(service->ipconfig_ipv4);
__connman_ipconfig_disable(service->ipconfig_ipv6);
- __connman_stats_service_unregister(service);
-
return err;
}
@@ -6418,7 +6582,7 @@ static struct connman_service *lookup_by_identifier(const char *identifier)
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 {
@@ -6798,14 +6962,9 @@ struct connman_service *__connman_service_lookup_from_index(int index)
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)
@@ -6818,36 +6977,9 @@ const char *__connman_service_get_name(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)
@@ -6894,6 +7026,21 @@ static enum connman_service_security convert_wifi_security(const char *security)
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)
{
@@ -6934,7 +7081,7 @@ static void update_from_network(struct connman_service *service,
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);
@@ -6961,7 +7108,7 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne
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);
@@ -7006,20 +7153,13 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne
}
}
- 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;
@@ -7041,7 +7181,8 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne
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:
@@ -7102,7 +7243,7 @@ void __connman_service_update_from_network(struct connman_network *network)
}
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)
@@ -7183,6 +7324,7 @@ __connman_service_create_from_provider(struct connman_provider *provider)
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;
@@ -7346,14 +7488,14 @@ void __connman_service_cleanup(void)
{
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);
diff --git a/src/session.c b/src/session.c
index 9e3c5594..2a1dd9aa 100644
--- a/src/session.c
+++ b/src/session.c
@@ -28,8 +28,6 @@
#include <gdbus.h>
-#include <connman/session.h>
-
#include "connman.h"
static DBusConnection *connection;
@@ -65,7 +63,9 @@ struct connman_session {
struct firewall_context *fw;
uint32_t mark;
int index;
+ char *addr;
char *gateway;
+ unsigned char prefixlen;
bool policy_routing;
bool snat_enabled;
};
@@ -79,6 +79,7 @@ struct fw_snat {
GSList *sessions;
int id;
int index;
+ char *addr;
struct firewall_context *fw;
};
@@ -200,7 +201,7 @@ static char *service2bearer(enum connman_service_type type)
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;
@@ -208,8 +209,11 @@ static struct fw_snat *fw_snat_lookup(int index)
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;
}
@@ -224,6 +228,7 @@ static int fw_snat_create(struct connman_session *session,
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);
@@ -238,6 +243,7 @@ static int fw_snat_create(struct connman_session *session,
return 0;
err:
__connman_firewall_destroy(fw_snat->fw);
+ g_free(fw_snat->addr);
g_free(fw_snat);
return err;
}
@@ -350,13 +356,17 @@ static void del_default_route(struct connman_session *session)
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;
}
@@ -376,13 +386,20 @@ static void add_default_route(struct connman_session *session)
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)
@@ -393,7 +410,7 @@ 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;
@@ -420,8 +437,11 @@ static void add_nat_rules(struct connman_session *session)
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;
@@ -493,6 +513,9 @@ static void free_session(struct connman_session *session)
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);
@@ -502,6 +525,7 @@ static void free_session(struct connman_session *session)
g_free(session->info);
g_free(session->info_last);
g_free(session->gateway);
+ g_free(session->addr);
g_free(session);
}
@@ -548,6 +572,7 @@ struct creation_data {
GSList *allowed_bearers;
char *allowed_interface;
bool source_ip_rule;
+ char *context_identifier;
};
static void cleanup_creation_data(struct creation_data *creation_data)
@@ -557,6 +582,8 @@ 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);
@@ -927,6 +954,17 @@ static void append_notify(DBusMessageIter *dict,
}
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)
@@ -1372,7 +1410,7 @@ static int session_policy_config_cb(struct connman_session *session,
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,
@@ -1397,11 +1435,13 @@ static int session_policy_config_cb(struct connman_session *session,
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;
@@ -1474,6 +1514,9 @@ int __connman_session_create(DBusMessage *msg)
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);
@@ -1672,7 +1715,7 @@ static void update_session_state(struct connman_session *session)
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;
}
@@ -1767,7 +1810,7 @@ static void session_activate(struct connman_session *session)
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,
@@ -1796,7 +1839,7 @@ static void session_activate(struct connman_session *session)
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)) {
@@ -1958,7 +2001,7 @@ static void ipconfig_changed(struct connman_service *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,
diff --git a/src/shared/arp.c b/src/shared/arp.c
new file mode 100644
index 00000000..6cd611f2
--- /dev/null
+++ b/src/shared/arp.c
@@ -0,0 +1,126 @@
+/*
+ *
+ * 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;
+}
diff --git a/gdhcp/ipv4ll.h b/src/shared/arp.h
index bee8138a..03e2168c 100644
--- a/gdhcp/ipv4ll.h
+++ b/src/shared/arp.h
@@ -1,8 +1,11 @@
/*
*
- * 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
@@ -13,25 +16,14 @@
* 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
@@ -43,13 +35,13 @@ extern "C" {
#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_ */
diff --git a/src/stats.c b/src/stats.c
index 663bc382..6f7ce208 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -23,7 +23,6 @@
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <errno.h>
#include <sys/mman.h>
#include <sys/types.h>
@@ -678,7 +677,7 @@ int __connman_stats_service_register(struct connman_service *service)
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)) {
@@ -707,9 +706,9 @@ int __connman_stats_service_register(struct connman_service *service)
}
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;
diff --git a/src/technology.c b/src/technology.c
index d2f0ae2b..4e053fc9 100644
--- a/src/technology.c
+++ b/src/technology.c
@@ -100,7 +100,7 @@ static void rfkill_check(gpointer key, gpointer value, gpointer user_data)
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);
@@ -196,8 +196,6 @@ done:
__connman_storage_save_global(keyfile);
g_key_file_free(keyfile);
-
- return;
}
static void tethering_changed(struct connman_technology *technology)
@@ -347,6 +345,15 @@ static struct connman_technology *technology_find(enum connman_service_type type
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)
{
@@ -435,8 +442,6 @@ done:
g_free(identifier);
g_key_file_free(keyfile);
-
- return;
}
bool __connman_technology_get_offlinemode(void)
@@ -474,8 +479,6 @@ static void connman_technology_save_offlinemode(void)
}
g_key_file_free(keyfile);
-
- return;
}
static bool connman_technology_load_offlinemode(void)
@@ -620,7 +623,7 @@ static gboolean technology_pending_reply(gpointer user_data)
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)
@@ -787,6 +790,8 @@ static int technology_disable(struct connman_technology *technology)
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;
@@ -1022,10 +1027,7 @@ void __connman_technology_scan_stopped(struct connman_device *device,
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;
}
@@ -1085,7 +1087,7 @@ static DBusMessage *scan(DBusConnection *conn, DBusMessage *msg, void *data)
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);
@@ -1476,7 +1478,7 @@ int __connman_technology_add_device(struct connman_device *device)
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.
*/
@@ -1580,7 +1582,7 @@ int __connman_technology_set_offlinemode(bool offlinemode)
* 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;
diff --git a/src/tethering.c b/src/tethering.c
index c929ba71..e04756ff 100644
--- a/src/tethering.c
+++ b/src/tethering.c
@@ -31,11 +31,11 @@
#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"
@@ -61,6 +61,13 @@ static struct connman_ippool *dhcp_ippool = NULL;
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;
@@ -181,6 +188,18 @@ static void tethering_restart(struct connman_ippool *pool, void *user_data)
__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;
@@ -225,7 +244,8 @@ int __connman_tethering_set_enabled(void)
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;
@@ -261,7 +281,8 @@ int __connman_tethering_set_enabled(void)
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;
@@ -273,7 +294,8 @@ int __connman_tethering_set_enabled(void)
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;
@@ -298,6 +320,8 @@ void __connman_tethering_set_disabled(void)
if (__sync_fetch_and_sub(&tethering_enabled, 1) != 1)
return;
+ unregister_all_clients();
+
__connman_ipv6pd_cleanup();
index = connman_inet_ifindex(BRIDGE_NAME);
@@ -311,7 +335,8 @@ void __connman_tethering_set_disabled(void)
__connman_bridge_disable(BRIDGE_NAME);
- __connman_ippool_unref(dhcp_ippool);
+ __connman_ippool_free(dhcp_ippool);
+ dhcp_ippool = NULL;
__connman_bridge_remove(BRIDGE_NAME);
@@ -323,6 +348,21 @@ void __connman_tethering_set_disabled(void)
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)
{
@@ -399,7 +439,7 @@ static void remove_private_network(gpointer user_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);
@@ -436,6 +476,70 @@ static void ippool_disconnect(struct connman_ippool *pool, void *user_data)
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;
@@ -525,6 +629,18 @@ int __connman_private_network_release(const char *path)
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("");
@@ -538,6 +654,12 @@ int __connman_tethering_init(void)
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;
}
@@ -558,5 +680,13 @@ void __connman_tethering_cleanup(void)
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);
}
diff --git a/src/timeserver.c b/src/timeserver.c
index 0e555a73..9832c2a5 100644
--- a/src/timeserver.c
+++ b/src/timeserver.c
@@ -34,17 +34,31 @@
#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;
@@ -62,8 +76,6 @@ static void save_timeservers(char **servers)
__connman_storage_save_global(keyfile);
g_key_file_free(keyfile);
-
- return;
}
static char **load_timeservers(void)
@@ -92,35 +104,60 @@ static void resolv_result(GResolvResultStatus status, char **results,
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);
@@ -129,29 +166,25 @@ void __connman_timeserver_sync_next()
__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,
@@ -204,15 +237,20 @@ GSList *__connman_timeserver_get_all(struct connman_service *service)
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 */
@@ -236,7 +274,7 @@ static gboolean ts_recheck(gpointer user_data)
{
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");
@@ -269,6 +307,11 @@ static void ts_recheck_disable(void)
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;
@@ -297,11 +340,14 @@ int __connman_timeserver_sync(struct connman_service *default_service)
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;
/*
@@ -327,20 +373,21 @@ int __connman_timeserver_sync(struct connman_service *default_service)
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;
}
@@ -357,8 +404,6 @@ static int timeserver_start(struct connman_service *service)
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)
@@ -379,10 +424,12 @@ static int timeserver_start(struct connman_service *service)
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);
}
@@ -391,13 +438,17 @@ static void timeserver_stop(void)
{
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();
@@ -430,7 +481,7 @@ static void default_changed(struct connman_service *default_service)
timeserver_stop();
}
-static struct connman_notifier timeserver_notifier = {
+static const struct connman_notifier timeserver_notifier = {
.name = "timeserver",
.default_changed = default_changed,
};
diff --git a/src/timezone.c b/src/timezone.c
index e346b11a..8e912670 100644
--- a/src/timezone.c
+++ b/src/timezone.c
@@ -23,7 +23,6 @@
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
diff --git a/src/util.c b/src/util.c
index 732d4512..03b14cdc 100644
--- a/src/util.c
+++ b/src/util.c
@@ -91,3 +91,14 @@ void __connman_util_cleanup(void)
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);
+}
diff --git a/src/wispr.c b/src/wispr.c
index 03b38bb8..473c0e03 100644
--- a/src/wispr.c
+++ b/src/wispr.c
@@ -568,7 +568,7 @@ static void wispr_portal_browser_reply_cb(struct connman_service *service,
}
/* 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,
diff --git a/src/wpad.c b/src/wpad.c
index f066feee..54084ee8 100644
--- a/src/wpad.c
+++ b/src/wpad.c
@@ -87,7 +87,7 @@ static void wpad_result(GResolvResultStatus status,
g_free(url);
- __connman_wispr_start(wpad->service,
+ __connman_service_wispr_start(wpad->service,
CONNMAN_IPCONFIG_TYPE_IPV4);
return;
@@ -119,7 +119,7 @@ failed:
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);
}
diff --git a/test/list-services b/test/list-services
index 6be4945b..a2610d7e 100755
--- a/test/list-services
+++ b/test/list-services
@@ -11,6 +11,8 @@ def extract_values(values):
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 += " }"
@@ -38,7 +40,8 @@ for path, properties in manager.GetServices():
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",
diff --git a/tools/dhcp-server-test.c b/tools/dhcp-server-test.c
index b7d2e540..59f5f34d 100644
--- a/tools/dhcp-server-test.c
+++ b/tools/dhcp-server-test.c
@@ -29,6 +29,8 @@
#include <gdhcp/gdhcp.h>
+#include "../src/connman.h"
+
static GMainLoop *main_loop;
static void sig_term(int sig)
@@ -113,8 +115,12 @@ int main(int argc, char *argv[])
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);
diff --git a/tools/dhcp-test.c b/tools/dhcp-test.c
index c34e10a8..a6c3e993 100644
--- a/tools/dhcp-test.c
+++ b/tools/dhcp-test.c
@@ -33,10 +33,11 @@
#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;
@@ -177,8 +178,12 @@ int main(int argc, char *argv[])
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);
diff --git a/tools/dnsproxy-test.c b/tools/dnsproxy-test.c
index 551cae91..371e2e23 100644
--- a/tools/dnsproxy-test.c
+++ b/tools/dnsproxy-test.c
@@ -24,6 +24,7 @@
#endif
#include <errno.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
diff --git a/tools/ip6tables-test.c b/tools/ip6tables-test.c
new file mode 100644
index 00000000..41e842dd
--- /dev/null
+++ b/tools/ip6tables-test.c
@@ -0,0 +1,163 @@
+/*
+ * 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;
+}
diff --git a/tools/iptables-test.c b/tools/iptables-test.c
index 2df53ccd..e9b7cb22 100644
--- a/tools/iptables-test.c
+++ b/tools/iptables-test.c
@@ -108,30 +108,31 @@ out:
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");
@@ -147,7 +148,7 @@ out:
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);
diff --git a/tools/iptables-unit.c b/tools/iptables-unit.c
index 426631a0..b91591f2 100644
--- a/tools/iptables-unit.c
+++ b/tools/iptables-unit.c
@@ -28,14 +28,24 @@
#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);
@@ -57,67 +67,87 @@ static bool assert_rule(const char *table_name, const char *rule)
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);
}
@@ -125,16 +155,16 @@ static void test_iptables_chain2(void)
{
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);
}
@@ -142,39 +172,39 @@ static void test_iptables_chain3(void)
{
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)
@@ -183,24 +213,24 @@ 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");
}
@@ -210,22 +240,22 @@ static void test_iptables_rule1(void)
/* 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");
}
@@ -235,48 +265,48 @@ static void test_iptables_rule2(void)
/* 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");
}
@@ -286,39 +316,309 @@ static void test_iptables_target0(void)
/* 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;
@@ -329,14 +629,14 @@ char *connman_service_get_interface(struct connman_service *service)
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;
}
@@ -349,24 +649,24 @@ static void test_nat_basic0(void)
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");
@@ -386,21 +686,21 @@ static void test_nat_basic1(void)
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);
@@ -462,6 +762,14 @@ int main(int argc, char *argv[])
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);
diff --git a/tools/private-network-test.c b/tools/private-network-test.c
index 3dd115ba..2828bb30 100644
--- a/tools/private-network-test.c
+++ b/tools/private-network-test.c
@@ -32,7 +32,7 @@
#include <stdlib.h>
#include <string.h>
#include <signal.h>
-#include <sys/poll.h>
+#include <poll.h>
#include <sys/signalfd.h>
#include <unistd.h>
diff --git a/tools/resolv-test.c b/tools/resolv-test.c
index 8953acd3..1aad2841 100644
--- a/tools/resolv-test.c
+++ b/tools/resolv-test.c
@@ -63,6 +63,8 @@ static const char *status2str(GResolvResultStatus status)
return "not implemented";
case G_RESOLV_RESULT_STATUS_REFUSED:
return "refused";
+ case G_RESOLV_RESULT_STATUS_NO_ANSWER:
+ return "no answer";
}
return NULL;
diff --git a/tools/session-api.c b/tools/session-api.c
index b97cfc01..e869d190 100644
--- a/tools/session-api.c
+++ b/tools/session-api.c
@@ -158,35 +158,55 @@ static DBusMessage *notify_update(DBusConnection *conn,
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);
diff --git a/tools/session-test.h b/tools/session-test.h
index 85129337..2c068bd7 100644
--- a/tools/session-test.h
+++ b/tools/session-test.h
@@ -76,6 +76,9 @@ struct test_session_info {
enum connman_session_type type;
/* ipv4, ipv6 dicts */
GSList *allowed_bearers;
+ char *allowed_interface;
+ bool source_ip_rule;
+ char *context_identifier;
};
struct test_session {
diff --git a/tools/session-utils.c b/tools/session-utils.c
index 51cec5c3..47f0de1f 100644
--- a/tools/session-utils.c
+++ b/tools/session-utils.c
@@ -192,7 +192,7 @@ struct test_data_cb {
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;
@@ -211,7 +211,7 @@ static void run_test_cb(gconstpointer data)
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
@@ -221,6 +221,13 @@ static void run_test_cb(gconstpointer data)
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)
{
@@ -230,9 +237,7 @@ void util_test_add(const char *test_name, util_test_func_t test_func,
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)
@@ -304,6 +309,8 @@ void util_session_cleanup(struct test_session *session)
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);
diff --git a/tools/stats-tool.c b/tools/stats-tool.c
index efa39de2..5695048f 100644
--- a/tools/stats-tool.c
+++ b/tools/stats-tool.c
@@ -22,7 +22,6 @@
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
diff --git a/tools/tap-test.c b/tools/tap-test.c
index fdc098aa..cb3ee622 100644
--- a/tools/tap-test.c
+++ b/tools/tap-test.c
@@ -23,13 +23,12 @@
#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>
diff --git a/tools/wispr.c b/tools/wispr.c
index d5f9341f..e56dfc16 100644
--- a/tools/wispr.c
+++ b/tools/wispr.c
@@ -23,7 +23,6 @@
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
diff --git a/unit/test-ippool.c b/unit/test-ippool.c
index 17fac9d9..a6cae652 100644
--- a/unit/test-ippool.c
+++ b/unit/test-ippool.c
@@ -53,7 +53,7 @@ static void test_case_1(void)
pool = __connman_ippool_create(23, 1, 20, NULL, NULL);
g_assert(pool);
- __connman_ippool_unref(pool);
+ __connman_ippool_free(pool);
}
__connman_ippool_cleanup();
@@ -92,7 +92,7 @@ static void test_case_2(void)
"\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();
@@ -157,7 +157,7 @@ static void test_case_3(void)
for (it = list; it; it = it->next) {
pool = it->data;
- __connman_ippool_unref(pool);
+ __connman_ippool_free(pool);
}
g_slist_free(list);
@@ -219,7 +219,7 @@ static void test_case_4(void)
g_assert(flag == 1);
- __connman_ippool_unref(pool);
+ __connman_ippool_free(pool);
flag = 0;
@@ -246,7 +246,7 @@ static void test_case_4(void)
g_assert(flag == 1);
- __connman_ippool_unref(pool);
+ __connman_ippool_free(pool);
__connman_ippool_cleanup();
}
@@ -330,8 +330,8 @@ static void test_case_5(void)
g_assert(flag == 0);
- __connman_ippool_unref(pool1);
- __connman_ippool_unref(pool2);
+ __connman_ippool_free(pool1);
+ __connman_ippool_free(pool2);
__connman_ippool_cleanup();
}
@@ -425,8 +425,8 @@ static void test_case_6(void)
__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();
}
diff --git a/unit/test-iptables.c b/unit/test-iptables.c
new file mode 100644
index 00000000..cd261d05
--- /dev/null
+++ b/unit/test-iptables.c
@@ -0,0 +1,699 @@
+/*
+ * 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:
+ */
diff --git a/vpn/plugins/l2tp.c b/vpn/plugins/l2tp.c
index a0d22c4d..5d83eb88 100644
--- a/vpn/plugins/l2tp.c
+++ b/vpn/plugins/l2tp.c
@@ -382,6 +382,7 @@ static int write_pppd_option(struct vpn_provider *provider, int fd)
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);
diff --git a/vpn/plugins/openconnect.c b/vpn/plugins/openconnect.c
index 87679bfa..8e74479f 100644
--- a/vpn/plugins/openconnect.c
+++ b/vpn/plugins/openconnect.c
@@ -561,11 +561,41 @@ static int oc_error_code(struct vpn_provider *provider, int exit_code)
}
}
+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)
diff --git a/vpn/plugins/openvpn.c b/vpn/plugins/openvpn.c
index e3395097..f38c0c36 100644
--- a/vpn/plugins/openvpn.c
+++ b/vpn/plugins/openvpn.c
@@ -470,11 +470,36 @@ static int ov_device_flags(struct vpn_provider *provider)
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)
diff --git a/vpn/plugins/pptp.c b/vpn/plugins/pptp.c
index 27b1d508..3dc93b03 100644
--- a/vpn/plugins/pptp.c
+++ b/vpn/plugins/pptp.c
@@ -453,6 +453,7 @@ static int run_connect(struct vpn_provider *provider,
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);
diff --git a/vpn/plugins/vpn.c b/vpn/plugins/vpn.c
index 9a423850..acede747 100644
--- a/vpn/plugins/vpn.c
+++ b/vpn/plugins/vpn.c
@@ -23,7 +23,6 @@
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
@@ -133,6 +132,10 @@ void vpn_died(struct connman_task *task, int exit_code, void *user_data)
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);
@@ -172,6 +175,7 @@ vpn_exit:
g_free(data);
}
+done:
connman_task_destroy(task);
}
@@ -303,19 +307,22 @@ static DBusMessage *vpn_notify(struct connman_task *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;
@@ -373,6 +380,7 @@ static int vpn_create_tun(struct vpn_provider *provider, int flags)
}
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");
@@ -553,10 +561,15 @@ static int vpn_disconnect(struct vpn_provider *provider)
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);
@@ -568,7 +581,20 @@ static int vpn_remove(struct vpn_provider *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)
@@ -585,6 +611,26 @@ 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)
{
@@ -606,6 +652,7 @@ int vpn_register(const char *name, struct vpn_driver *vpn_driver,
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,
diff --git a/vpn/plugins/vpn.h b/vpn/plugins/vpn.h
index cb94bdcd..265fd82f 100644
--- a/vpn/plugins/vpn.h
+++ b/vpn/plugins/vpn.h
@@ -48,9 +48,13 @@ struct vpn_driver {
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,
diff --git a/vpn/vpn-provider.c b/vpn/vpn-provider.c
index d2b3e3aa..dd54ac08 100644
--- a/vpn/vpn-provider.c
+++ b/vpn/vpn-provider.c
@@ -37,6 +37,7 @@
#include "connman/vpn-dbus.h"
#include "vpn-provider.h"
#include "vpn.h"
+#include "plugins/vpn.h"
static DBusConnection *connection;
static GHashTable *provider_hash;
@@ -2423,61 +2424,18 @@ int vpn_provider_set_nameservers(struct vpn_provider *provider,
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;
}
@@ -2488,7 +2446,7 @@ int vpn_provider_append_route(struct vpn_provider *provider,
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);
@@ -2513,15 +2471,15 @@ int vpn_provider_append_route(struct vpn_provider *provider,
}
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;
}
diff --git a/vpn/vpn-provider.h b/vpn/vpn-provider.h
index bdc5f5c1..96452c11 100644
--- a/vpn/vpn-provider.h
+++ b/vpn/vpn-provider.h
@@ -57,6 +57,13 @@ enum vpn_provider_error {
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;
@@ -134,6 +141,9 @@ struct vpn_provider_driver {
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);
diff --git a/vpn/vpn-rtnl.c b/vpn/vpn-rtnl.c
index a7565dba..6ddfd832 100644
--- a/vpn/vpn-rtnl.c
+++ b/vpn/vpn-rtnl.c
@@ -879,7 +879,7 @@ static void rtnl_message(void *buf, size_t len)
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,