diff options
author | Nishant Chaprana <n.chaprana@samsung.com> | 2019-09-17 19:00:55 +0530 |
---|---|---|
committer | Nishant Chaprana <n.chaprana@samsung.com> | 2019-09-18 19:23:41 +0530 |
commit | 26cc90dfaf2ad149b702626f9552c81abbb26862 (patch) | |
tree | 2524c8994cf58358350fde67dfba5c3b8cb58f7d | |
parent | 9e3beb21876b6e63bd8acf53e751480d7a1cc16f (diff) | |
parent | 6b2381a2adabea7d8309ff158ef675ff88184305 (diff) | |
download | connman-26cc90dfaf2ad149b702626f9552c81abbb26862.tar.gz connman-26cc90dfaf2ad149b702626f9552c81abbb26862.tar.bz2 connman-26cc90dfaf2ad149b702626f9552c81abbb26862.zip |
Imported Upstream version 1.37submit/tizen/20190920.082459
Change-Id: Idb47c1ddbedc9f97181b8e9a5eeac04ddd832a2c
Signed-off-by: Nishant Chaprana <n.chaprana@samsung.com>
147 files changed, 9121 insertions, 3117 deletions
@@ -65,6 +65,7 @@ tools/session-test tools/netlink-test unit/test-ippool unit/test-nat +unit/test-iptables doc/*.bak doc/*.stamp @@ -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> @@ -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/HACKING b/HACKING new file mode 100644 index 00000000..80382ed4 --- /dev/null +++ b/HACKING @@ -0,0 +1,109 @@ +Hacking on Connection Manager +***************************** + + +Build tools requirements +======================== + +When building and testing directly from the repository it is important to +have at least automake version 1.10 or later installed. All modern +distributions should default to the latest version, but it seems that +Debian's default is still an earlier version: + + Check version + # dpkg -l '*automake*' + + Install new version + # apt-get install automake1.10 + # update-alternatives --config automake + + +Working with the source code repository +======================================= + +The repository contains two extra scripts that accomplish the bootstrap +process. One is called "bootstrap" which is the basic scripts that uses the +autotools scripts to create the needed files for building and installing. +It makes sure to call the right programs depending on the usage of shared or +static libraries or translations etc. + +The second program is called "bootstrap-configure". This program will make +sure to properly clean the repository, call the "bootstrap" script and then +call configure with proper settings for development. It will use the best +options and pass them over to configure. These options normally include +the enabling the maintainer mode and the debugging features. + +So while in a normal source project the call "./configure ..." is used to +configure the project with its settings like prefix and extra options. In +case of bare repositories call "./bootstrap-configure" and it will bootstrap +the repository and calls configure with all the correct options to make +development easier. + +In case of preparing for a release with "make distcheck", don't use +bootstrap-configure since it could export development specific settings. + +So the normal steps to checkout, build and install such a repository is +like this: + + Checkout repository + # git clone git://git.kernel.org/pub/scm/network/connman/connman.git + # cd connman + + Configure and build + # ./bootstrap-configure + # make + + Check installation + # make install DESTDIR=$PWD/x + # find x + # rm -rf x + + Check distribution + # make distcheck + + Final installation + # sudo make install + + Remove autogenerated files + # make maintainer-clean + + +Running from within the source code repository +============================================== + +When using "./configure --enable-maintainer-mode" the automake scripts will +use the plugins directly from within the repository. This removes the need +to use "make install" when testing "connmand". The "bootstrap-configure" +automatically includes this option. + + Run daemon in foreground with debugging + # sudo ./src/connmand -n -d 'plugins/*' + +The debugging option -d takes an argument. This argument can be a comma +separated list of file names like 'plugins/wifi.c,plugins/ethernet.c' to +enable debugs in these files. Simple glob style pattern matching is +supported in this list. + +For production installations or distribution packaging it is important that +the "--enable-maintainer-mode" option is NOT used. + +Some times it is important to restrict the available interfaces. For example +in cases where testing happens over a network connection. The "-i" command +line switch allows to specify a glob pattern for the interface names. + + Run daemon for wireless interfaces + # sudo ./src/connmand -n -i wlan* + + +Debugging the D-Bus interface during runtime +============================================ + +Running the daemon with debugging information in the foreground is quite +verbose and sometimes not really helpful. The "monitor-connman" script +allows to monitor "PropertyChanged" D-Bus signals from various interfaces. + +Every "PropertyChanged" signal will generate a line of output. Some of them +can get very complex. The first detail inside "{ ... }" is the interface +name (without its service name prefix). The second detail inside "[ ... ]" +is the object path. And after that it is followed by a key and value of +the property that changed. diff --git a/Makefile.am b/Makefile.am index ac196e71..5511c3f3 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 if TIZEN_EXT_WIFI_MESH include_HEADERS += include/mesh.h include/mesh-netlink.h @@ -23,7 +24,8 @@ noinst_HEADERS = include/rtnl.h include/task.h \ include/dbus.h include/option.h \ include/provider.h include/vpn-dbus.h \ include/utsname.h include/timeserver.h include/proxy.h \ - include/technology.h include/setting.h + include/technology.h include/setting.h \ + include/backtrace.h local_headers = $(foreach file,$(include_HEADERS) $(nodist_include_HEADERS) \ $(noinst_HEADERS), include/connman/$(notdir $(file))) @@ -35,6 +37,10 @@ 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 @@ -46,8 +52,15 @@ else gweb_sources += gweb/giognutls.h gweb/gionotls.c endif +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 @@ -71,9 +84,11 @@ systemdunit_DATA = src/connman.service vpn/connman-vpn.service endif service_files_sources = src/connman.service.in src/net.connman.service.in \ - vpn/net.connman.vpn.service.in vpn/connman-vpn.service.in + vpn/connman-vpn.service.in \ + vpn/net.connman.vpn.service.in service_files = src/connman.service src/net.connman.service \ - vpn/net.connman.vpn.service vpn/connman-vpn.service + vpn/connman-vpn.service \ + vpn/net.connman.vpn.service else @@ -114,9 +129,9 @@ else sbin_PROGRAMS = src/connmand src/connmand-wait-online endif -src_connmand_SOURCES = $(gdhcp_sources) $(gweb_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 \ @@ -128,11 +143,17 @@ src_connmand_SOURCES = $(gdhcp_sources) $(gweb_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 if TIZEN_EXT_WIFI_MESH src_connmand_SOURCES += src/mesh.c src/mesh-netlink.c endif @@ -180,6 +201,7 @@ sbin_PROGRAMS += vpn/connman-vpnd endif vpn_connman_vpnd_SOURCES = $(gdhcp_sources) $(builtin_vpn_sources) \ + $(shared_sources) \ $(gweb_sources) vpn/vpn.ver vpn/main.c vpn/vpn.h \ vpn/vpn-manager.c vpn/vpn-provider.c \ vpn/vpn-provider.h vpn/vpn-rtnl.h \ @@ -200,15 +222,15 @@ vpn_connman_vpnd_SOURCES = $(gdhcp_sources) $(builtin_vpn_sources) \ src/inotify.c src/firewall-iptables.c src/ipv6pd.c src/peer.c \ src/peer_service.c src/machine.c src/util.c \ vpn/vpn-agent.c vpn/vpn-agent.h \ - vpn/vpn-config.c + vpn/vpn-config.c src/acd.c if TIZEN_EXT_WIFI_MESH vpn_connman_vpnd_SOURCES += src/mesh.c src/mesh-netlink.c endif vpn_connman_vpnd_LDADD = gdbus/libgdbus-internal.la $(builtin_vpn_libadd) \ - @GLIB_LIBS@ @DBUS_LIBS@ @XTABLES_LIBS@ @GNUTLS_LIBS@ \ - @LIBSYSTEMD_LIBS@ \ + @GLIB_LIBS@ @DBUS_LIBS@ @GNUTLS_LIBS@ \ + @LIBSYSTEMD_LIBS@ @XTABLES_LIBS@ \ -lresolv -ldl if TIZEN_EXT_WIFI_MESH @@ -219,7 +241,12 @@ vpn_connman_vpnd_LDFLAGS = -Wl,--export-dynamic \ -Wl,--version-script=$(srcdir)/vpn/vpn.ver endif -BUILT_SOURCES = $(local_headers) src/builtin.h $(service_files) scripts/connman +BUILT_SOURCES = $(local_headers) src/builtin.h $(service_files) \ + scripts/connman + +if INTERNAL_DNS_BACKEND +BUILT_SOURCES += scripts/connman_resolvconf.conf +endif if VPN BUILT_SOURCES += vpn/builtin.h @@ -339,6 +366,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 @@ -352,8 +380,8 @@ endif noinst_PROGRAMS += unit/test-ippool -unit_test_ippool_SOURCES = src/log.c src/dbus.c src/error.c \ - src/ippool.c unit/test-ippool.c +unit_test_ippool_SOURCES = $(backtrace_sources) src/log.c src/dbus.c \ + src/error.c src/ippool.c unit/test-ippool.c unit_test_ippool_LDADD = gdbus/libgdbus-internal.la \ @GLIB_LIBS@ @DBUS_LIBS@ -ldl @@ -393,11 +421,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@ @@ -406,32 +436,47 @@ tools_polkit_test_LDADD = @DBUS_LIBS@ tools_private_network_test_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ -tools_session_test_SOURCES = src/log.c src/dbus.c src/error.c \ +tools_session_test_SOURCES = $(backtrace_sources) src/log.c src/dbus.c src/error.c \ tools/session-test.c tools/session-utils.c tools/manager-api.c \ tools/session-api.c tools/session-test.h 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}"\" -tools_iptables_unit_SOURCES = src/log.c \ + -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 @@ -554,7 +599,8 @@ do_subst = $(AM_V_GEN)$(SED) \ -e 's,[@]sysconfdir[@],$(sysconfdir),g' \ -e 's,[@]storagedir[@],$(storagedir),g' \ -e 's,[@]vpn_storagedir[@],$(vpn_storagedir),g' \ - -e 's,[@]localstatedir[@],$(localstatedir),g' + -e 's,[@]localstatedir[@],$(localstatedir),g' \ + -e 's,[@]runstatedir[@],$(runstatedir),g' %.1 : %.1.in $(AM_V_at)$(MKDIR_P) $(dir $@) @@ -576,6 +622,10 @@ scripts/connman: scripts/connman.in Makefile $(AM_V_at)$(MKDIR_P) $(dir $@) $(do_subst) < $< > $@ +scripts/connman_resolvconf.conf: scripts/connman_resolvconf.conf.in + $(AM_V_at)$(MKDIR_P) $(dir $@) + $(do_subst) < $< > $@ + include/connman/version.h: include/version.h $(AM_V_at)$(MKDIR_P) include/connman $(AM_V_GEN)$(LN_S) $(abs_top_builddir)/$< $@ @@ -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 ce827919..6abe7db5 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" @@ -248,7 +249,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; } @@ -275,7 +276,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; } @@ -321,6 +322,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) { @@ -1214,6 +1227,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) { @@ -1367,8 +1391,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, @@ -1427,8 +1449,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, @@ -1476,6 +1496,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) { @@ -1777,6 +1804,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; @@ -2310,7 +2361,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; } @@ -2329,7 +2380,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; } @@ -2371,28 +2422,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, - ¬ify_path); + &args_struct->notify_path); } -static int session_create(gboolean connect) +static int session_create(gboolean connect, char *args[], int num, + struct connman_option *options) { int res; char *notify_path; + struct session_options args_struct; + args_struct.args = args; + args_struct.num = num; + args_struct.options = options; notify_path = g_strdup_printf("/net/connman/connmanctl%d", getpid()); session_notify_add(notify_path); + args_struct.notify_path = notify_path; res = __connmanctl_dbus_method_call(connection, "net.connman", "/", "net.connman.Manager", "CreateSession", session_create_cb, GINT_TO_POINTER(connect), - session_create_append, notify_path); + session_create_append, &args_struct); g_free(notify_path); @@ -2446,25 +2609,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) { @@ -2534,6 +2678,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; @@ -2569,12 +2725,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(); @@ -2797,6 +2954,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" @@ -2823,6 +2981,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, } }; @@ -3237,6 +3396,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, @@ -3654,7 +3815,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 48ea09f6..a1c34e1f 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 @@ -232,6 +233,11 @@ AC_CHECK_LIB(resolv, ns_initparse, dummy=yes, [ AC_MSG_ERROR(resolver library support is required)) ]) +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)) @@ -265,8 +271,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) @@ -319,6 +325,14 @@ if (test -n "${path_systemdunit}"); then fi AM_CONDITIONAL(SYSTEMD, test -n "${path_systemdunit}") +AC_ARG_WITH([tmpfilesdir], AC_HELP_STRING([--with-tmpfilesdir=DIR], + [path to systemd tmpfiles.d directory]), [path_tmpfiles=${withval}], + [path_tmpfiles="`$PKG_CONFIG --variable=tmpfilesdir systemd`"]) +if (test -n "${path_tmpfiles}"); then + SYSTEMD_TMPFILESDIR="${path_tmpfiles}" + AC_SUBST(SYSTEMD_TMPFILESDIR) +fi + AC_ARG_WITH(firewall, AC_HELP_STRING([--with-firewall=TYPE], [specify which firewall type is used iptables or nftables [default=iptables]]), [firewall_type=${withval}], @@ -429,19 +443,34 @@ AC_ARG_ENABLE(wispr, AC_HELP_STRING([--disable-wispr], [enable_wispr=${enableval}]) AM_CONDITIONAL(WISPR, test "${enable_wispr}" != "no") +AC_ARG_ENABLE(backtrace, AC_HELP_STRING([--disable-backtrace], + [disable backtrace support]), + [enable_backtrace=${enableval}]) +AM_CONDITIONAL(BACKTRACE, test "${enable_backtrace}" != "no") + AC_ARG_ENABLE(tools, AC_HELP_STRING([--disable-tools], [disable testing 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:/bin:/usr/bin) + AC_PATH_PROGS(IP6TABLES_SAVE, [ip6tables-save], [], + $PATH:/bin:/usr/bin) 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]), @@ -488,4 +517,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 src/connman.service src/connman_tv.service vpn/connman-vpn.service) diff --git a/doc/agent-api.txt b/doc/agent-api.txt index aa7271d4..e3c1dcde 100755 --- 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 100755 --- 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 fdc8e9ec..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 @@ -138,13 +159,28 @@ be used by DHCP servers to identify specific clients without having to rely on MAC address ranges, etc .TP .BI EnableOnlineCheck=true\ \fR|\fB\ false -Enable or disable use of HTTP GET as on online status check. +Enable or disable use of HTTP GET as an online status check. When a service is in a READY state, and is selected as default, ConnMan will issue an HTTP GET request to verify that end-to-end connectivity is successful. Only then the service will be transitioned to ONLINE state. If this setting is false, the default service will remain in READY state. Default value is true. +.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 100755 --- 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/peer-api.txt b/doc/peer-api.txt index b871f4d5..cc094ff2 100755 --- a/doc/peer-api.txt +++ b/doc/peer-api.txt @@ -100,4 +100,3 @@ Properties string State [readonly] [experimental] The TLV formated byte array representing the WiFi Display Informations Elements. - diff --git a/doc/plugin-api.txt b/doc/plugin-api.txt index ea5ec0a1..36391e96 100755 --- 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 4fe4093a..f8dbb96d 100755 --- a/doc/service-api.txt +++ b/doc/service-api.txt @@ -122,13 +122,6 @@ Methods dict GetProperties() [deprecated] Possible Errors: None - boolean GetUserFavorite() [experimental] - - This function is used to check whether this service - is favorite to the current user. - - Possible Errors: None - Signals PropertyChanged(string name, variant value) This signal indicates a changed value of the given @@ -194,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. @@ -533,3 +533,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 100755 --- 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/technology-api.txt b/doc/technology-api.txt index fb6bd314..f22e9b29 100755 --- a/doc/technology-api.txt +++ b/doc/technology-api.txt @@ -45,18 +45,6 @@ Signals PropertyChanged(string name, variant value) This signal indicates a changed value of the given property. - DhcpConnected(string aptype, string ipaddr, - string macaddr, string hostname) - - This signal indicates a station information that - has connected to the AP(Access Point). - - DhcpLeaseDeleted(string aptype, string ipaddr, - string macaddr, string hostname) - - This signal indicates a station information that - has disconnected to the AP(Access Point). - Properties boolean Powered [readwrite] Boolean representing the power state of the @@ -112,8 +100,3 @@ Properties boolean Powered [readwrite] This property is only valid for the WiFi technology, and is then mapped to the WPA pre-shared key clients will have to use in order to establish a connection. - - boolean Hidden [readwrite] - - This option allows to enable or disable the support - for the hidden Wi-Fi tethering. diff --git a/doc/valgrind.suppressions b/doc/valgrind.suppressions new file mode 100644 index 00000000..c1c7bca1 --- /dev/null +++ b/doc/valgrind.suppressions @@ -0,0 +1,235 @@ +{ + <syslog error> + Memcheck:Cond + obj:/lib/libc-*.so + ... + fun:localtime_r + fun:__vsyslog_chk + fun:__syslog_chk + fun:__connman_log_init + ... +} +{ + <iconv open> + Memcheck:Addr4 + obj:/lib/libc-*.so + obj:/lib/libglib-2.0.so* + fun:g_iconv_open + ... + fun:g_convert + fun:g_locale_to_utf8 + fun:g_strerror + fun:g_key_file_load_from_file + ... +} +{ + <ioctl ADDRT/DELRT> + Memcheck:Param + ioctl(SIOCADDRT/DELRT) + obj:/lib/ld-*.so + ... +} +{ + <g_main_loop> + Memcheck:Leak + fun:memalign + ... + fun:g_slice_alloc + ... + fun:g_main_loop_new + ... +} +{ + <g_option_context_parse> + Memcheck:Leak + ... + fun:g_slice_alloc + ... + fun:g_option_context_parse + ... +} +{ + <g_key_file_load_from_data> + Memcheck:Leak + ... + fun:g_slice_alloc + ... + fun:g_key_file_load_from_data + ... +} +{ + <g_key_file_new 1> + Memcheck:Leak + ... + fun:g_slice_alloc + ... + fun:g_key_file_new + ... +} +{ + <g_key_file_new 2> + Memcheck:Leak + fun:*alloc + ... + fun:g_key_file_new + fun:main +} +{ + <connman plugin cleanup> + Memcheck:Leak + ... + fun:__connman_plugin_cleanup + ... +} +{ + <cmd line option parsing> + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_strdup + fun:g_set_prgname + fun:g_option_context_parse + fun:main +} +{ + <dbus system bus setup 1> + Memcheck:Leak + ... + fun:dbus_malloc* + ... + fun:g_dbus_setup_bus + fun:main +} +{ + <dbus system bus setup 2> + Memcheck:Leak + ... + fun:g_malloc* + ... + fun:dbus_connection_set_watch_functions + fun:setup_bus + ... +} +{ + <key file get charset> + Memcheck:Leak + ... + fun:g_*alloc* + ... + fun:g_strerror + fun:g_key_file_load_from_file + fun:main +} +{ + <dbus disconnect func set> + Memcheck:Leak + ... + fun:filter_data_get + fun:g_dbus_add_signal_watch + fun:g_dbus_set_disconnect_function + fun:main +} +{ + <plugin dlopen> + Memcheck:Leak + ... + fun:dlopen + fun:__connman_plugin_init + fun:main +} +{ + <dbus system bus setup 3> + Memcheck:Leak + ... + fun:dbus_malloc0 + ... + fun:dbus_parse_address + ... + fun:g_dbus_setup_bus + fun:main +} +{ + <libdbus internals 1> + Memcheck:Leak + fun:*malloc + ... + obj:/lib/libdbus-1.so.3.5.3 +} +{ + <dbus system bus setup 4> + Memcheck:Leak + fun:*alloc + ... + fun:dbus_*alloc* + ... + fun:g_dbus_setup_bus + fun:main +} +{ + <dbus system bus setup 5> + Memcheck:Leak + fun:calloc + fun:g_malloc0 + ... + fun:g_dbus_set_disconnect_function + fun:main +} +{ + <dbus bus remove match> + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_source_set_callback + fun:g_timeout_add_full + fun:g_timeout_add + ... + fun:dbus_pending_call_block + fun:dbus_connection_send_with_reply_and_block + ... + fun:dbus_bus_remove_match +} +{ + <g_main_loop_run/new> + Memcheck:Leak + fun:*alloc + ... + fun:g_main_loop_* + fun:main +} +{ + <g_main_context_dispatch> + Memcheck:Leak + fun:*alloc + ... + fun:g_main_context_dispatch +} +{ + <libdbus internals 2> + Memcheck:Leak + fun:realloc + fun:dbus_realloc + ... + fun:dbus_message_set_reply_serial + fun:dbus_message_new_error + ... +} +{ + <libdbus internals 3> + Memcheck:Leak + fun:realloc + fun:dbus_realloc + ... + fun:dbus_message_new_signal + ... +} +{ + <dbus_bus_register> + Memcheck:Leak + fun:malloc + fun:realloc + fun:dbus_realloc + ... + fun:dbus_pending_call_block + fun:dbus_connection_send_with_reply_and_block + fun:dbus_bus_register +} diff --git a/doc/vpn-connection-api.txt b/doc/vpn-connection-api.txt index a814a388..1fd3be26 100755 --- 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 5a455f08..22bbc8e2 100755 --- 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 @@ -86,6 +86,7 @@ typedef enum _dhcp_client_state { REBOOTING, REQUESTING, BOUND, + DECLINED, RENEWING, REBINDING, RELEASED, @@ -130,6 +131,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; @@ -489,10 +491,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) @@ -547,7 +579,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); @@ -570,7 +602,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"); @@ -579,12 +611,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); @@ -608,7 +640,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); @@ -633,38 +665,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) @@ -695,7 +695,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; @@ -714,7 +714,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; @@ -839,7 +839,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 | @@ -1206,7 +1206,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; @@ -1226,6 +1226,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; @@ -1401,10 +1403,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, @@ -1441,6 +1443,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)); @@ -1451,6 +1454,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)); @@ -1486,23 +1492,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; } @@ -1569,6 +1572,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); @@ -1607,7 +1616,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; @@ -1628,8 +1637,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; @@ -1727,7 +1736,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, @@ -1775,7 +1784,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, @@ -1860,6 +1869,71 @@ static char *get_ip(uint32_t ip) return g_strdup(inet_ntoa(addr)); } +/* get a rough idea of how long an option will be */ +static const uint8_t len_of_option_as_string[] = { + [OPTION_IP] = sizeof("255.255.255.255 "), + [OPTION_STRING] = 1, + [OPTION_U8] = sizeof("255 "), + [OPTION_U16] = sizeof("65535 "), + [OPTION_U32] = sizeof("4294967295 "), +}; + +static int sprint_nip(char *dest, const char *pre, const uint8_t *ip) +{ + return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]); +} + +/* Create "opt_value1 option_value2 ..." string */ +static char *malloc_option_value_string(uint8_t *option, GDHCPOptionType type) +{ + unsigned upper_length; + int len, optlen; + char *dest, *ret; + + len = option[OPT_LEN - OPT_DATA]; + type &= OPTION_TYPE_MASK; + optlen = dhcp_option_lengths[type]; + if (optlen == 0) + return NULL; + upper_length = len_of_option_as_string[type] * + ((unsigned)len / (unsigned)optlen); + dest = ret = g_malloc(upper_length + 1); + if (!ret) + return NULL; + + while (len >= optlen) { + switch (type) { + case OPTION_IP: + dest += sprint_nip(dest, "", option); + break; + case OPTION_U16: { + uint16_t val_u16 = get_be16(option); + dest += sprintf(dest, "%u", val_u16); + break; + } + case OPTION_U32: { + uint32_t val_u32 = get_be32(option); + dest += sprintf(dest, "%u", val_u32); + break; + } + case OPTION_STRING: + memcpy(dest, option, len); + dest[len] = '\0'; + return ret; + default: + break; + } + option += optlen; + len -= optlen; + if (len <= 0) + break; + *dest++ = ' '; + *dest = '\0'; + } + + return ret; +} + static GList *get_option_value_list(char *value, GDHCPOptionType type) { char *pos = value; @@ -2351,14 +2425,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); @@ -2715,6 +2803,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; #if defined TIZEN_EXT int discover_retry = 0; @@ -2829,9 +2918,9 @@ int g_dhcp_client_start(GDHCPClient *dhcp_client, const char *last_address) } #if defined TIZEN_EXT - if (dhcp_client->retry_times == discover_retry) { + if (dhcp_client->retry_times == discover_retry) { #else - if (dhcp_client->retry_times == DISCOVER_RETRIES) { + if (dhcp_client->retry_times == DISCOVER_RETRIES) { #endif if (dhcp_client->no_lease_cb) dhcp_client->no_lease_cb(dhcp_client, @@ -2849,12 +2938,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)); @@ -3073,6 +3163,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: @@ -3270,6 +3361,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); #if defined TIZEN_EXT diff --git a/gdhcp/common.c b/gdhcp/common.c index b8c5091a..8f7a65cc 100755 --- 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; @@ -182,71 +147,6 @@ int dhcp_end_option(uint8_t *optionptr) return i; } -/* get a rough idea of how long an option will be */ -static const uint8_t len_of_option_as_string[] = { - [OPTION_IP] = sizeof("255.255.255.255 "), - [OPTION_STRING] = 1, - [OPTION_U8] = sizeof("255 "), - [OPTION_U16] = sizeof("65535 "), - [OPTION_U32] = sizeof("4294967295 "), -}; - -static int sprint_nip(char *dest, const char *pre, const uint8_t *ip) -{ - return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]); -} - -/* Create "opt_value1 option_value2 ..." string */ -char *malloc_option_value_string(uint8_t *option, GDHCPOptionType type) -{ - unsigned upper_length; - int len, optlen; - char *dest, *ret; - - len = option[OPT_LEN - OPT_DATA]; - type &= OPTION_TYPE_MASK; - optlen = dhcp_option_lengths[type]; - if (optlen == 0) - return NULL; - upper_length = len_of_option_as_string[type] * - ((unsigned)len / (unsigned)optlen); - dest = ret = g_malloc(upper_length + 1); - if (ret == NULL) - return NULL; - - while (len >= optlen) { - switch (type) { - case OPTION_IP: - dest += sprint_nip(dest, "", option); - break; - case OPTION_U16: { - uint16_t val_u16 = get_be16(option); - dest += sprintf(dest, "%u", val_u16); - break; - } - case OPTION_U32: { - uint32_t val_u32 = get_be32(option); - dest += sprintf(dest, "%u", val_u32); - break; - } - case OPTION_STRING: - memcpy(dest, option, len); - dest[len] = '\0'; - return ret; - default: - break; - } - option += optlen; - len -= optlen; - if (len <= 0) - break; - *dest++ = ' '; - *dest = '\0'; - } - - return ret; -} - uint8_t *dhcpv6_get_option(struct dhcpv6_packet *packet, uint16_t pkt_len, int code, uint16_t *option_len, int *option_count) { @@ -397,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, @@ -414,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, @@ -431,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) @@ -465,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 7da13135..6899499e 100755 --- a/gdhcp/common.h +++ b/gdhcp/common.h @@ -19,6 +19,7 @@ * */ +#include <config.h> #include <netinet/udp.h> #include <netinet/ip.h> @@ -170,15 +171,14 @@ 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 */ }; #endif -char *malloc_option_value_string(uint8_t *option, GDHCPOptionType type); uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code); uint8_t *dhcpv6_get_option(struct dhcpv6_packet *packet, uint16_t pkt_len, int code, uint16_t *option_len, int *option_count); diff --git a/gdhcp/gdhcp.h b/gdhcp/gdhcp.h index f51a8b05..d9944882 100755 --- 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); @@ -216,9 +217,6 @@ struct _GDHCPServer; typedef struct _GDHCPServer GDHCPServer; -typedef void (*GDHCPSaveACKLeaseFunc) (char *hostname, - unsigned char *mac, unsigned int nip); - GDHCPServer *g_dhcp_server_new(GDHCPType type, int ifindex, GDHCPServerError *error); int g_dhcp_server_start(GDHCPServer *server); @@ -239,11 +237,6 @@ void g_dhcp_server_set_save_lease(GDHCPServer *dhcp_server, GDHCPSaveLeaseFunc func, gpointer user_data); void g_dhcp_server_set_lease_added_cb(GDHCPServer *dhcp_server, GDHCPLeaseAddedCb cb); -void g_dhcp_server_set_save_ack_lease(GDHCPServer *dhcp_server, - GDHCPSaveACKLeaseFunc func, gpointer user_data); - -int dhcp_get_random(uint64_t *val); -void dhcp_cleanup_random(void); #if defined TIZEN_EXT void g_dhcp_client_set_address_known(GDHCPClient *client, gboolean known); diff --git a/gdhcp/ipv4ll.c b/gdhcp/ipv4ll.c index d9001987..de4e0764 100755 --- 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 44ce771a..85405f19 100755 --- a/gdhcp/server.c +++ b/gdhcp/server.c @@ -66,7 +66,6 @@ struct _GDHCPServer { GHashTable *option_hash; /* Options send to client */ GDHCPSaveLeaseFunc save_lease_func; GDHCPLeaseAddedCb lease_added_cb; - GDHCPSaveACKLeaseFunc save_ack_lease_func; GDHCPDebugFunc debug_func; gpointer debug_data; }; @@ -397,10 +396,9 @@ 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->save_ack_lease_func = NULL; dhcp_server->debug_func = NULL; dhcp_server->debug_data = NULL; @@ -652,12 +650,9 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition, struct dhcp_packet packet; struct dhcp_lease *lease; uint32_t requested_nip = 0; - uint8_t type, *server_id_option, *request_ip_option, *host_name; + uint8_t type, *server_id_option, *request_ip_option; int re; - GDHCPOptionType option_type; - char *option_value; - if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { dhcp_server->listener_watch = 0; return FALSE; @@ -696,28 +691,15 @@ 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; } if (lease && requested_nip == lease->lease_nip) { debug(dhcp_server, "Sending ACK"); - - host_name = dhcp_get_option(&packet, DHCP_HOST_NAME); - option_type = dhcp_get_code_type(DHCP_HOST_NAME); - option_value = malloc_option_value_string(host_name, - option_type); send_ACK(dhcp_server, &packet, lease->lease_nip); - - if (dhcp_server->save_ack_lease_func) - dhcp_server->save_ack_lease_func( - option_value, - lease->lease_mac, - lease->lease_nip); - g_free(option_value); - break; } @@ -846,15 +828,6 @@ void g_dhcp_server_set_lease_added_cb(GDHCPServer *dhcp_server, dhcp_server->lease_added_cb = cb; } -void g_dhcp_server_set_save_ack_lease(GDHCPServer *dhcp_server, - GDHCPSaveACKLeaseFunc func, gpointer user_data) -{ - if (dhcp_server == NULL) - return; - - dhcp_server->save_ack_lease_func = func; -} - GDHCPServer *g_dhcp_server_ref(GDHCPServer *dhcp_server) { if (!dhcp_server) diff --git a/gsupplicant/gsupplicant.h b/gsupplicant/gsupplicant.h index 0fbb4d44..f79e6e49 100644 --- a/gsupplicant/gsupplicant.h +++ b/gsupplicant/gsupplicant.h @@ -177,12 +177,6 @@ typedef enum { G_SUPPLICANT_PEER_GROUP_FAILED, } GSupplicantPeerState; -enum GSupplicantAPHiddenSSID { - G_SUPPLICANT_AP_NO_SSID_HIDING, - G_SUPPLICANT_AP_HIDDEN_SSID_ZERO_LEN, - G_SUPPLICANT_AP_HIDDEN_SSID_ZERO_CONTENTS, -}; - struct _GSupplicantSSID { #if defined TIZEN_EXT void *ssid; @@ -213,7 +207,6 @@ struct _GSupplicantSSID { dbus_bool_t use_wps; const char *pin_wps; const char *bgscan; - int ignore_broadcast_ssid; #if defined TIZEN_EXT unsigned char *bssid; unsigned int bssid_for_connect_len; @@ -494,8 +487,10 @@ struct _GSupplicantCallbacks { void (*system_power_off) (void); void (*assoc_failed) (void *user_data); #endif - void (*add_station) (const char *mac); - void (*remove_station) (const char *mac); + 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 d27fe0db..9c12093e 100644 --- a/gsupplicant/supplicant.c +++ b/gsupplicant/supplicant.c @@ -767,6 +767,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) @@ -920,6 +944,8 @@ static void remove_bss(gpointer data) { struct g_supplicant_bss *bss = data; + supplicant_dbus_property_call_cancel_all(bss); + g_free(bss->path); #if defined TIZEN_EXT g_slist_free_full(bss->vsie_list, g_free); @@ -1948,7 +1974,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) @@ -2024,6 +2049,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); @@ -2035,10 +2061,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); @@ -2058,6 +2087,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; + } + #if defined TIZEN_EXT network->keymgmt = bss->keymgmt; @@ -2098,7 +2132,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"); } /* @@ -2346,7 +2383,6 @@ static void bss_process_ies(DBusMessageIter *iter, void *user_data) bss->wps_capabilities = 0; bss->keymgmt = 0; - memset(bss->country_code, 0, COUNTRY_CODE_LENGTH); for (ie_end = ie + ie_len; ie < ie_end && ie + ie[1] + 1 <= ie_end; ie += ie[1] + 2) { @@ -2516,8 +2552,6 @@ static void bss_compute_security(struct g_supplicant_bss *bss) bss->security = G_SUPPLICANT_SECURITY_FT_PSK; else if (bss->ft_ieee8021x == TRUE) bss->security = G_SUPPLICANT_SECURITY_IEEE8021X; -#endif -#if defined TIZEN_EXT else if (bss->sae) bss->security = G_SUPPLICANT_SECURITY_SAE; else if (bss->owe) @@ -2710,7 +2744,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) @@ -3405,11 +3439,48 @@ void *copy_vsie_list(gconstpointer src, gpointer data) #endif +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(""); @@ -3427,6 +3498,7 @@ static void signal_bss_changed(const char *path, DBusMessageIter *iter) return; supplicant_dbus_property_foreach(iter, bss_property, bss); + #if defined TIZEN_EXT network->frequency = bss->frequency; network->phy_mode = bss->phy_mode; @@ -3437,18 +3509,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) @@ -3460,20 +3532,43 @@ static void signal_bss_changed(const char *path, DBusMessageIter *iter) new_bss->vsie_list = g_slist_copy_deep(bss->vsie_list, copy_vsie_list, NULL); #endif - 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"); + } + #if defined TIZEN_EXT if ((bss->keymgmt & G_SUPPLICANT_KEYMGMT_WPS) != 0) { network->wps = TRUE; @@ -3611,7 +3706,7 @@ static void signal_wps_event(const char *path, DBusMessageIter *iter) if (g_strcmp0(name, "success") == 0) interface->wps_state = G_SUPPLICANT_WPS_STATE_SUCCESS; - else if (g_strcmp0(name, "failed") == 0) + else if (g_strcmp0(name, "fail") == 0) interface->wps_state = G_SUPPLICANT_WPS_STATE_FAIL; else interface->wps_state = G_SUPPLICANT_WPS_STATE_UNKNOWN; @@ -3647,56 +3742,6 @@ static void signal_power_off(const char *path, DBusMessageIter *iter) } #endif -static void signal_station_connected(const char *path, DBusMessageIter *iter) -{ - GSupplicantInterface *interface; - const char *sta_mac = NULL; - - SUPPLICANT_DBG("path %s %s", path, SUPPLICANT_PATH); - - if (callbacks_pointer->add_station == NULL) - return; - - if (g_strcmp0(path, "/") == 0) - return; - - interface = g_hash_table_lookup(interface_table, path); - if (interface == NULL) - return; - - dbus_message_iter_get_basic(iter, &sta_mac); - if (sta_mac == NULL) - return; - - SUPPLICANT_DBG("New station %s connected", sta_mac); - callbacks_pointer->add_station(sta_mac); -} - -static void signal_station_disconnected(const char *path, DBusMessageIter *iter) -{ - GSupplicantInterface *interface; - const char *sta_mac = NULL; - - SUPPLICANT_DBG("path %s %s", path, SUPPLICANT_PATH); - - if (callbacks_pointer->remove_station == NULL) - return; - - if (g_strcmp0(path, "/") == 0) - return; - - interface = g_hash_table_lookup(interface_table, path); - if (interface == NULL) - return; - - dbus_message_iter_get_basic(iter, &sta_mac); - if (sta_mac == NULL) - return; - - SUPPLICANT_DBG("Station %s disconnected", sta_mac); - callbacks_pointer->remove_station(sta_mac); -} - static void create_peer_identifier(GSupplicantPeer *peer) { const unsigned char test[ETH_ALEN] = {}; @@ -4464,6 +4509,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 }, @@ -4473,9 +4520,6 @@ static struct { { "org.tizen.system.deviced.PowerOff", "ChangeState", signal_power_off }, #endif - { SUPPLICANT_INTERFACE".Interface", "StaAuthorized", signal_station_connected }, - { SUPPLICANT_INTERFACE".Interface", "StaDeauthorized", signal_station_disconnected }, - { SUPPLICANT_INTERFACE ".Interface.P2PDevice", "DeviceFound", signal_peer_found }, { SUPPLICANT_INTERFACE ".Interface.P2PDevice", "DeviceLost", signal_peer_lost }, @@ -5030,7 +5074,7 @@ struct interface_mesh_peer_data { }; static void interface_mesh_change_peer_params(DBusMessageIter *iter, - void *user_data) + void *user_data) { struct interface_mesh_peer_data *data = user_data; @@ -5352,14 +5396,16 @@ static void interface_scan_params(DBusMessageIter *iter, void *user_data) DBUS_TYPE_STRING, &type); #if defined TIZEN_EXT - SUPPLICANT_DBG("[specific_scan] num_ssids %d", data->scan_params->num_ssids); - if (data->scan_params->num_ssids != 0) + SUPPLICANT_DBG("[specific_scan] num_ssids %d", + data->scan_params->num_ssids); #endif - 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 @@ -5949,7 +5995,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 @@ -6315,10 +6363,6 @@ static void interface_add_network_params(DBusMessageIter *iter, void *user_data) DBUS_TYPE_BYTE, &ssid->ssid, ssid->ssid_len); - supplicant_dbus_dict_append_basic(&dict, "ignore_broadcast_ssid", - DBUS_TYPE_INT32, - &ssid->ignore_broadcast_ssid); - #if defined TIZEN_EXT if (ssid->bssid) { char *bssid = NULL; @@ -6415,6 +6459,7 @@ static void wps_start(const char *error, DBusMessageIter *iter, void *user_data) dbus_free(data); return; } + #if defined TIZEN_EXT GSupplicantSSID *ssid = data->ssid; if (ssid->pin_wps != NULL) { @@ -6652,7 +6697,7 @@ int g_supplicant_interface_connect(GSupplicantInterface *interface, intf_data->user_data = user_data; intf_data->network_remove_in_progress = TRUE; network_remove(intf_data); - } else + } else { #if defined TIZEN_EXT if (ssid->passphrase && g_strcmp0(ssid->passphrase, "") != 0 && @@ -6665,12 +6710,13 @@ int g_supplicant_interface_connect(GSupplicantInterface *interface, SUPPLICANT_DBG("Decryption request failed %d", ret); } else #endif - ret = supplicant_dbus_method_call(interface->path, - SUPPLICANT_INTERFACE ".Interface", "AddNetwork", - interface_add_network_params, - interface_add_network_result, data, - interface); - } + ret = supplicant_dbus_method_call(interface->path, + SUPPLICANT_INTERFACE ".Interface", "AddNetwork", + interface_add_network_params, + interface_add_network_result, data, + interface); + } + } if (ret < 0) { g_free(data->path); @@ -7319,6 +7365,7 @@ int g_supplicant_set_widi_ies(GSupplicantP2PServiceParams *p2p_service_params, return -EINPROGRESS; } + static const char *g_supplicant_rule0 = "type=signal," "path=" DBUS_PATH_DBUS "," "sender=" DBUS_SERVICE_DBUS "," diff --git a/gweb/giognutls.c b/gweb/giognutls.c index 31cf9020..b5c476cb 100755 --- 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; @@ -458,6 +458,7 @@ GIOChannel *g_io_channel_gnutls_new(int fd) gnutls_priority_set_direct(gnutls_channel->session, "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.0:+VERS-SSL3.0:%COMPAT", NULL); #endif + gnutls_certificate_allocate_credentials(&gnutls_channel->cred); gnutls_credentials_set(gnutls_channel->session, GNUTLS_CRD_CERTIFICATE, gnutls_channel->cred); diff --git a/gweb/gresolv.c b/gweb/gresolv.c index cf16a48c..38a554e0 100755 --- 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; @@ -946,12 +950,10 @@ bool g_resolv_add_nameserver(GResolv *resolv, const char *address, nameserver->flags = flags; nameserver->resolv = resolv; - debug(resolv, ""); if (connect_udp_channel(nameserver) < 0) { free_nameserver(nameserver); return false; } - debug(resolv, ""); resolv->nameserver_list = g_list_append(resolv->nameserver_list, nameserver); @@ -1053,8 +1055,6 @@ guint g_resolv_lookup_hostname(GResolv *resolv, const char *hostname, lookup->result_data = user_data; lookup->id = resolv->next_lookup_id++; - debug(resolv, ""); - if (resolv->result_family != AF_INET6) { if (add_query(lookup, hostname, ns_t_a)) { g_free(lookup); @@ -1062,8 +1062,6 @@ guint g_resolv_lookup_hostname(GResolv *resolv, const char *hostname, } } - debug(resolv, ""); - if (resolv->result_family != AF_INET) { if (add_query(lookup, hostname, ns_t_aaaa)) { if (resolv->result_family != AF_INET6) { @@ -1077,8 +1075,6 @@ guint g_resolv_lookup_hostname(GResolv *resolv, const char *hostname, } } - debug(resolv, ""); - g_queue_push_tail(resolv->lookup_queue, lookup); debug(resolv, "lookup %p id %d", lookup, lookup->id); diff --git a/gweb/gresolv.h b/gweb/gresolv.h index fac14f54..5e82c168 100755 --- 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/backtrace.h b/include/backtrace.h new file mode 100644 index 00000000..65415fb6 --- /dev/null +++ b/include/backtrace.h @@ -0,0 +1,32 @@ +/* + * + * Connection Manager + * + * Copyright (C) 2016 Yann E. MORIN <yann.morin.1998@free.fr>. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __CONNMAN_BACKTRACE_H +#define __CONNMAN_BACKTRACE_H + +#if defined HAVE_EXECINFO_H && defined BACKTRACE +void print_backtrace(const char* program_path, const char* program_exec, + unsigned int offset); +#else +#define print_backtrace(P,E,O) +#endif + +#endif /* __CONNMAN_BACKTRACE_H */ diff --git a/include/dbus.h b/include/dbus.h index 19e49380..d53037b4 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" @@ -183,10 +181,6 @@ int connman_dbus_get_connection_unix_user(DBusConnection *connection, connman_dbus_get_connection_unix_user_cb_t func, void *user_data); -int connman_dbus_get_connection_unix_user_sync(DBusConnection *connection, - const char *bus_name, - unsigned int *user_id); - typedef void (* connman_dbus_get_context_cb_t) (const unsigned char *context, void *user_data, int err); diff --git a/include/device.h b/include/device.h index 852207d4..77c476a4 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); #if defined TIZEN_EXT diff --git a/include/inet.h b/include/inet.h index ef61af93..4844979b 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); #if defined TIZEN_EXT void connman_inet_update_device_ident(struct connman_device *device); diff --git a/include/network.h b/include/network.h index dc9e6e9c..14b94429 100755 --- 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> @@ -63,9 +65,10 @@ enum connman_network_error { CONNMAN_NETWORK_ERROR_CONNECT_FAIL = 4, #if defined TIZEN_EXT CONNMAN_NETWORK_ERROR_DHCP_FAIL = 5, -#endif CONNMAN_NETWORK_ERROR_BLOCKED = 6, - +#else + CONNMAN_NETWORK_ERROR_BLOCKED = 5, +#endif }; #if defined TIZEN_EXT @@ -144,6 +147,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); @@ -266,6 +271,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 100755 --- 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 f4f4ea27..6ae94021 100644 --- a/include/service.h +++ b/include/service.h @@ -129,9 +129,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); @@ -144,6 +147,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); @@ -177,9 +187,7 @@ gboolean connman_service_user_pdn_connection_unref_and_test( */ gboolean connman_service_is_no_ref_user_pdn_connection( struct connman_service *service); -#endif -#if defined TIZEN_EXT struct connman_service *connman_service_get_default_connection(void); /* diff --git a/include/session.h b/include/session.h index 5106e886..39f33685 100755 --- 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 729d7aac..3bd6414a 100755 --- a/include/technology.h +++ b/include/technology.h @@ -49,16 +49,14 @@ typedef struct { struct connman_technology; -int connman_technology_tethering_add_station(enum connman_service_type type, - const char *mac); -int connman_technology_tethering_remove_station(const char *mac); - int connman_technology_tethering_notify(struct connman_technology *technology, bool enabled); 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 100755 --- 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/packaging/connman.spec b/packaging/connman.spec index 7ce64655..af8e27f0 100644 --- a/packaging/connman.spec +++ b/packaging/connman.spec @@ -4,7 +4,7 @@ %bcond_without connman_vpnd Name: connman -Version: 1.35 +Version: 1.37 Release: 33 License: GPL-2.0+ Summary: Connection Manager @@ -150,7 +150,7 @@ VPN_CFLAGS+=" -DTIZEN_EXT -lsmack -Werror" chmod +x bootstrap ./bootstrap %configure \ - --sysconfdir=/etc \ + --sysconfdir=/etc \ --enable-client \ --enable-tizen-ext \ --enable-pacrunner \ @@ -174,7 +174,9 @@ chmod +x bootstrap --enable-ethernet \ --with-systemdunitdir=%{_libdir}/systemd/system \ --enable-pie \ - --disable-wispr + --disable-wispr \ + --disable-backtrace \ + --disable-tools make %{?_smp_mflags} diff --git a/plugins/bluetooth.c b/plugins/bluetooth.c index 76c15e59..9e19de08 100755 --- a/plugins/bluetooth.c +++ b/plugins/bluetooth.c @@ -556,7 +556,6 @@ static void device_enable_cb(const DBusError *error, void *user_data) #if !defined TIZEN_EXT enable_device(device, path); #endif - out: g_free(path); } diff --git a/plugins/ethernet.c b/plugins/ethernet.c index aadfe897..9e157467 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> @@ -190,11 +191,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 cce51e26..1b44bbb5 100755 --- a/plugins/gadget.c +++ b/plugins/gadget.c @@ -25,8 +25,6 @@ #include <errno.h> #include <net/if.h> -#include <stdio.h> -#include <string.h> #ifndef IFF_LOWER_UP #define IFF_LOWER_UP 0x10000 @@ -228,71 +226,6 @@ static struct connman_device_driver gadget_dev_driver = { }; static GList *cdc_interface_list = NULL; -static GHashTable *cdc_mac_hash = NULL; - -static void add_station(int index) -{ - char *path, line[128] = {'\0'}; - char *ifname = connman_inet_ifname(index); - char *mac; - FILE *f; - - if (ifname == NULL) - return; - - path = g_strdup_printf("/sys/class/usb_mode/%s/f_rndis/ethaddr", - ifname); - - f = fopen(path, "re"); - - g_free(ifname); - g_free(path); - - if (f == NULL) - return; - - if (fgets(line, sizeof(line), f) == NULL) { - fclose(f); - return; - } - - fclose(f); - - mac = g_ascii_strdown(line, strlen(line) - 1); - DBG("Add station %s in Technology %d", mac, - CONNMAN_SERVICE_TYPE_GADGET); - - g_hash_table_insert(cdc_mac_hash, GINT_TO_POINTER(index), - mac); - - connman_technology_tethering_add_station(CONNMAN_SERVICE_TYPE_GADGET, - mac); -} - -static void remove_station(int index) -{ - char *mac; - mac = g_hash_table_lookup(cdc_mac_hash, GINT_TO_POINTER(index)); - if (mac == NULL) - return; - - connman_technology_tethering_remove_station(mac); - - g_hash_table_remove(cdc_mac_hash, GINT_TO_POINTER(index)); -} - -static gboolean remove_all_station(gpointer key, gpointer value, gpointer user_data) -{ - char *mac; - mac = value; - if (mac == NULL) - return TRUE; - - connman_technology_tethering_remove_station(mac); - - return TRUE; -} - static void gadget_tech_add_interface(struct connman_technology *technology, int index, const char *name, const char *ident) @@ -313,8 +246,6 @@ static void gadget_tech_remove_interface(struct connman_technology *technology, cdc_interface_list = g_list_remove(cdc_interface_list, GINT_TO_POINTER((int) index)); - - remove_station(index); } static void gadget_tech_enable_tethering(struct connman_technology *technology, @@ -340,7 +271,7 @@ static void gadget_tech_enable_tethering(struct connman_technology *technology, connman_inet_add_to_bridge(index, bridge); - add_station(index); + gadget_tethering = true; } } @@ -354,11 +285,11 @@ static void gadget_tech_disable_tethering(struct connman_technology *technology, connman_inet_remove_from_bridge(index, bridge); - remove_station(index); - connman_inet_ifdown(index); connman_technology_tethering_notify(technology, false); + + gadget_tethering = false; } } @@ -378,28 +309,14 @@ static int gadget_tech_set_tethering(struct connman_technology *technology, static int gadget_tech_probe(struct connman_technology *technology) { - DBG("tech probe %p", technology); - - cdc_mac_hash = g_hash_table_new_full(g_direct_hash, - g_direct_equal, NULL, g_free); - return 0; } static void gadget_tech_remove(struct connman_technology *technology) { - DBG("tech remove %p", technology); - g_list_free(cdc_interface_list); cdc_interface_list = NULL; - - if (cdc_mac_hash) { - g_hash_table_foreach_remove(cdc_mac_hash, remove_all_station, - NULL); - g_hash_table_destroy(cdc_mac_hash); - cdc_mac_hash = NULL; - } } static struct connman_technology_driver gadget_tech_driver = { diff --git a/plugins/iospm.c b/plugins/iospm.c index fcb4cea1..cded9e00 100755 --- 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/loopback.c b/plugins/loopback.c index 44100c64..55c8a218 100755 --- a/plugins/loopback.c +++ b/plugins/loopback.c @@ -23,8 +23,6 @@ #include <config.h> #endif -#include <stdio.h> - #include <errno.h> #include <unistd.h> #include <limits.h> @@ -34,11 +32,11 @@ #include <sys/socket.h> #include <arpa/inet.h> #include <net/if.h> +#include <stdio.h> #include <glib.h> #include <glib/gprintf.h> - #define CONNMAN_API_SUBJECT_TO_CHANGE #include <connman/plugin.h> #include <connman/utsname.h> @@ -67,46 +65,47 @@ static int setup_hostname(void) memset(system_hostname, 0, sizeof(system_hostname)); #if defined TIZEN_EXT - FILE *fp = NULL; + FILE *fp = NULL; #define WIFI_MAC "/opt/etc/.mac.info" - { - char* rv = 0; - gchar* dev_id = "TIZEN"; - char wifi_mac[HOST_NAME_MAX + 1]; - - fp = fopen(WIFI_MAC, "r"); - if(!fp){ - connman_error("Failed to get current hostname"); - strncpy(system_hostname, dev_id, strlen(dev_id)); - goto host_name_end; - } - - rv = fgets(wifi_mac, HOST_NAME_MAX, fp); - if(!rv){ - connman_error("Failed to get current hostname"); - strncpy(system_hostname, dev_id, strlen(dev_id)); - fclose(fp); - goto host_name_end; - } - - dev_id = g_base64_encode((const guchar *)wifi_mac, strlen(wifi_mac)); - g_sprintf(system_hostname, "TIZEN-%s", dev_id); - g_free(dev_id); - fclose(fp); + { + char* rv = 0; + gchar* dev_id = "TIZEN"; + char wifi_mac[HOST_NAME_MAX + 1]; + + fp = fopen(WIFI_MAC, "r"); + if(!fp){ + connman_error("Failed to get current hostname"); + strncpy(system_hostname, dev_id, strlen(dev_id)); + goto host_name_end; } - host_name_end : -#else - if (gethostname(system_hostname, HOST_NAME_MAX) < 0) { + rv = fgets(wifi_mac, HOST_NAME_MAX, fp); + if(!rv){ connman_error("Failed to get current hostname"); - return -EIO; + strncpy(system_hostname, dev_id, strlen(dev_id)); + fclose(fp); + goto host_name_end; } + + dev_id = g_base64_encode((const guchar *)wifi_mac, strlen(wifi_mac)); + g_sprintf(system_hostname, "TIZEN-%s", dev_id); + g_free(dev_id); + fclose(fp); + } + +host_name_end: +#else + if (gethostname(system_hostname, HOST_NAME_MAX) < 0) { + connman_error("Failed to get current hostname"); + return -EIO; + } #endif - if (strlen(system_hostname) > 0 && - strcmp(system_hostname, "(none)") != 0) - connman_info("System hostname is %s", system_hostname); - else - create_hostname(); + + if (strlen(system_hostname) > 0 && + strcmp(system_hostname, "(none)") != 0) + connman_info("System hostname is %s", system_hostname); + else + create_hostname(); memset(name, 0, sizeof(name)); diff --git a/plugins/nmcompat.c b/plugins/nmcompat.c index 883ce9bd..274baab4 100755 --- 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 100755 --- 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 100755 --- 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 100755 --- 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 100755 --- 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 d3d75b81..11bab154 100755 --- 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(call, disconnect_reply, NULL, NULL); + dbus_pending_call_set_notify(data->disconnect_call, disconnect_reply, + data, 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); @@ -1726,7 +1755,7 @@ static gboolean property_changed(DBusConnection *conn, struct connection_data *data = NULL; DBusMessageIter iter, value; bool ip_set = false; - int err = 0; + int err; char *str; const char *key; const char *signature = DBUS_TYPE_STRING_AS_STRING @@ -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 8da1da56..f4e6d59f 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 @@ -91,6 +92,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; @@ -151,7 +158,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; @@ -193,10 +200,9 @@ 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, const char *identifier, const char *passphrase, const char *bridge, bool enabled); @@ -1252,10 +1258,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, @@ -1340,10 +1344,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) @@ -1487,13 +1489,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); } @@ -1581,7 +1583,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); } @@ -1745,7 +1747,11 @@ static int get_hidden_connections(GSupplicantScanParams *scan_data) { struct connman_config_entry **entries; GKeyFile *keyfile; +#if defined TIZEN_EXT + gchar **services = NULL; +#else gchar **services; +#endif /* defined TIZEN_EXT */ char *ssid, *name; int i, ret; bool value; @@ -2002,10 +2008,12 @@ static int throw_wifi_scan(struct connman_device *device, if (wifi->tethering) return -EBUSY; + #if defined TIZEN_EXT - if (connman_device_get_scanning(device) && !wifi->allow_full_scan) + if (connman_device_get_scanning(device, CONNMAN_SERVICE_TYPE_WIFI) + && !wifi->allow_full_scan) #else - if (connman_device_get_scanning(device)) + if (connman_device_get_scanning(device, CONNMAN_SERVICE_TYPE_WIFI)) #endif return -EALREADY; @@ -2145,10 +2153,12 @@ static void scan_callback(int result, GSupplicantInterface *interface, } #endif - scanning = connman_device_get_scanning(device); - if (scanning) + scanning = connman_device_get_scanning(device, CONNMAN_SERVICE_TYPE_WIFI); + + if (scanning) { connman_device_set_scanning(device, CONNMAN_SERVICE_TYPE_WIFI, false); + } if (result != -ENOLINK) #if defined TIZEN_EXT @@ -2214,7 +2224,6 @@ static void scan_callback_hidden(int result, scan_callback_hidden, #endif device); - if (ret == 0) return; } @@ -2252,6 +2261,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); @@ -2298,19 +2322,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); @@ -2329,10 +2359,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) @@ -2346,13 +2403,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, @@ -2424,7 +2481,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); @@ -2439,7 +2496,7 @@ static int wifi_disable(struct connman_device *device) #endif /* 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); @@ -2567,10 +2624,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); } @@ -2590,7 +2666,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; } @@ -2670,7 +2746,8 @@ static void specific_scan_callback(int result, GSupplicantInterface *interface, wifi->scan_params = NULL; } - scanning = connman_device_get_scanning(device); + scanning = connman_device_get_scanning(device, + CONNMAN_SERVICE_TYPE_WIFI); if (scanning) { connman_device_set_scanning(device, CONNMAN_SERVICE_TYPE_WIFI, false); @@ -2704,7 +2781,9 @@ static int wifi_specific_scan(enum connman_service_type type, if (wifi->tethering) return 0; - scanning = connman_device_get_scanning(device); + scanning = + connman_device_get_scanning(device, + CONNMAN_SERVICE_TYPE_WIFI); if (scanning) return -EALREADY; @@ -2841,7 +2920,8 @@ static void mesh_scan_callback(int result, GSupplicantInterface *interface, DBG("result %d wifi %p", result, wifi); - scanning = connman_device_get_scanning(device); + scanning = connman_device_get_scanning(device, + CONNMAN_SERVICE_TYPE_MESH); if (scanning) connman_device_set_scanning(device, CONNMAN_SERVICE_TYPE_MESH, false); @@ -2905,7 +2985,8 @@ static int mesh_abort_scan(enum connman_service_type type, mesh_info = wifi->mesh_info; - scanning = connman_device_get_scanning(device); + scanning = connman_device_get_scanning(device, + CONNMAN_SERVICE_TYPE_MESH); if (!scanning) return -EEXIST; @@ -2937,7 +3018,8 @@ static int mesh_specific_scan(enum connman_service_type type, mesh_info = wifi->mesh_info; - scanning = connman_device_get_scanning(device); + scanning = connman_device_get_scanning(device, + CONNMAN_SERVICE_TYPE_MESH); if (scanning) return -EALREADY; @@ -2988,11 +3070,8 @@ static int mesh_specific_scan(enum connman_service_type type, * 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; @@ -3012,19 +3091,20 @@ 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); #if defined TIZEN_EXT_WIFI_MESH - if (type == CONNMAN_SERVICE_TYPE_MESH) + if (params->type == CONNMAN_SERVICE_TYPE_MESH) return mesh_scan(device); #endif - 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; @@ -3053,8 +3133,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; @@ -3070,12 +3150,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) { @@ -3089,7 +3169,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); @@ -3097,13 +3177,15 @@ 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); ret = g_supplicant_interface_scan(wifi->interface, scan_params, scan_callback, device); - if (ret == 0) { connman_device_set_scanning(device, CONNMAN_SERVICE_TYPE_WIFI, true); @@ -3128,6 +3210,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) @@ -3167,6 +3267,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, #if defined TIZEN_EXT .specific_scan = wifi_specific_scan, @@ -3336,6 +3437,7 @@ static void ssid_init(GSupplicantSSID *ssid, struct connman_network *network) #endif ssid->passphrase = connman_network_get_string(network, "WiFi.Passphrase"); + ssid->eap = connman_network_get_string(network, "WiFi.EAP"); /* @@ -3529,10 +3631,9 @@ found: 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; @@ -4032,17 +4133,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) { @@ -4084,7 +4188,8 @@ static void interface_state(GSupplicantInterface *interface) wifi->scan_pending_network = NULL; /* should be cleared scanning flag */ - bool scanning = connman_device_get_scanning(device); + bool scanning = connman_device_get_scanning(device, + CONNMAN_SERVICE_TYPE_WIFI); if (scanning){ connman_device_set_scanning(device, CONNMAN_SERVICE_TYPE_WIFI, false); @@ -4200,8 +4305,10 @@ static void interface_state(GSupplicantInterface *interface) } #endif - 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); @@ -4427,8 +4534,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) @@ -4436,11 +4541,11 @@ static unsigned char calculate_strength(GSupplicantNetwork *supplicant_network) unsigned char strength; strength = 120 + g_supplicant_network_get_signal(supplicant_network); - #if !defined TIZEN_EXT if (strength > 100) strength = 100; #endif + return strength; } @@ -4623,22 +4728,24 @@ 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? * If so, we decide to use WPS by default */ if (wps_ready && wps_pbc && - wps_advertizing) { + wps_advertizing) #if !defined TIZEN_EXT connman_network_set_bool(network, "WiFi.UseWPS", true); #else DBG("wps is activating by ap but ignore it."); #endif - } } connman_network_set_frequency(network, g_supplicant_network_get_frequency(supplicant_network)); + #if defined TIZEN_EXT connman_network_set_bssid(network, g_supplicant_network_get_bssid(supplicant_network)); @@ -4744,6 +4851,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; #if defined TIZEN_EXT const unsigned char *bssid; @@ -4769,11 +4877,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); #if defined TIZEN_EXT bssid = g_supplicant_network_get_bssid(network); @@ -4815,6 +4954,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); @@ -4849,6 +4992,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) { @@ -4866,17 +5035,6 @@ static void apply_peer_services(GSupplicantPeer *peer, } } -static void add_station(const char *mac) -{ - connman_technology_tethering_add_station(CONNMAN_SERVICE_TYPE_WIFI, - mac); -} - -static void remove_station(const char *mac) -{ - connman_technology_tethering_remove_station(mac); -} - static void peer_found(GSupplicantPeer *peer) { GSupplicantInterface *iface = g_supplicant_peer_get_interface(peer); @@ -4884,6 +5042,7 @@ static void peer_found(GSupplicantPeer *peer) struct connman_peer *connman_peer; const char *identifier, *name; int ret; + #if defined TIZEN_EXT if (!wifi) return; @@ -5182,8 +5341,8 @@ static const GSupplicantCallbacks callbacks = { .network_removed = network_removed, .network_changed = network_changed, .network_associated = network_associated, - .add_station = add_station, - .remove_station = remove_station, + .sta_authorized = sta_authorized, + .sta_deauthorized = sta_deauthorized, .peer_found = peer_found, .peer_lost = peer_lost, .peer_changed = peer_changed, @@ -5218,8 +5377,7 @@ static void tech_remove(struct connman_technology *technology) wifi_technology = NULL; } -static GSupplicantSSID *ssid_ap_init(const char *ssid, - const char *passphrase) +static GSupplicantSSID *ssid_ap_init(const char *ssid, const char *passphrase) { GSupplicantSSID *ap; @@ -5321,7 +5479,7 @@ static void sta_remove_callback(int result, DBG("ifname %s result %d ", info->ifname, result); - if (result < 0 || (info->wifi->ap_supported != WIFI_AP_SUPPORTED)) { + if ((result < 0) || (info->wifi->ap_supported != WIFI_AP_SUPPORTED)) { info->wifi->tethering = false; connman_technology_tethering_notify(info->technology, false); @@ -5367,10 +5525,9 @@ static int enable_wifi_tethering(struct connman_technology *technology, if (!interface) continue; - if (wifi->ap_supported == WIFI_AP_NOT_SUPPORTED) - continue; - ifname = g_supplicant_interface_get_ifname(wifi->interface); + if (!ifname) + continue; if (wifi->ap_supported == WIFI_AP_NOT_SUPPORTED) { DBG("%s does not support AP mode (detected)", ifname); @@ -5405,8 +5562,6 @@ static int enable_wifi_tethering(struct connman_technology *technology, goto failed; info->ifname = g_strdup(ifname); - if (!info->ifname) - goto failed; wifi->tethering_param->technology = technology; wifi->tethering_param->ssid = ssid_ap_init(identifier, passphrase); diff --git a/resources/usr/share/dbus-1/system-services/net.connman.service b/resources/usr/share/dbus-1/system-services/net.connman.service index 7d0eab58..990eb66b 100644 --- a/resources/usr/share/dbus-1/system-services/net.connman.service +++ b/resources/usr/share/dbus-1/system-services/net.connman.service @@ -1,4 +1,6 @@ [D-BUS Service] Name=net.connman Exec=/bin/false +User=network_fw +Group=network_fw SystemdService=connman.service diff --git a/scripts/connman_resolvconf.conf.in b/scripts/connman_resolvconf.conf.in new file mode 100644 index 00000000..a242d7bc --- /dev/null +++ b/scripts/connman_resolvconf.conf.in @@ -0,0 +1,2 @@ +d @runstatedir@/connman - - - - +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 new file mode 100644 index 00000000..bede6698 --- /dev/null +++ b/src/backtrace.c @@ -0,0 +1,147 @@ +/* + * + * Connection Manager + * + * Copyright (C) 2007-2013 Intel Corporation. All rights reserved. + * Copyright (C) 2016 Yann E. MORIN <yann.morin.1998@free.fr>. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <execinfo.h> +#include <dlfcn.h> + +#include "connman.h" + +void print_backtrace(const char* program_path, const char* program_exec, + unsigned int offset) +{ + void *frames[99]; + size_t n_ptrs; + unsigned int i; + int outfd[2], infd[2]; + int pathlen; + pid_t pid; + + if (!program_exec) + return; + + pathlen = strlen(program_path); + + n_ptrs = backtrace(frames, G_N_ELEMENTS(frames)); + if (n_ptrs < offset) + return; + + if (pipe(outfd) < 0) + return; + + if (pipe(infd) < 0) { + close(outfd[0]); + close(outfd[1]); + return; + } + + pid = fork(); + if (pid < 0) { + close(outfd[0]); + close(outfd[1]); + close(infd[0]); + close(infd[1]); + return; + } + + if (pid == 0) { + close(outfd[1]); + close(infd[0]); + + dup2(outfd[0], STDIN_FILENO); + dup2(infd[1], STDOUT_FILENO); + + execlp("addr2line", "-C", "-f", "-e", program_exec, NULL); + + exit(EXIT_FAILURE); + } + + close(outfd[0]); + close(infd[1]); + + connman_error("++++++++ backtrace ++++++++"); + + for (i = offset; i < n_ptrs - 1; i++) { + Dl_info info; + char addr[20], buf[PATH_MAX * 2]; + int len, written; + char *ptr, *pos; + + dladdr(frames[i], &info); + + len = snprintf(addr, sizeof(addr), "%p\n", frames[i]); + if (len < 0) + break; + + written = write(outfd[1], addr, len); + if (written < 0) + break; + + len = read(infd[0], buf, sizeof(buf) - 1); + if (len < 0) + break; + + buf[len] = '\0'; + + pos = strchr(buf, '\n'); + if (!pos) { + connman_error("Error in backtrace format"); + break; + } + + *pos++ = '\0'; + + if (strcmp(buf, "??") == 0) { + connman_error("#%-2u %p in %s", i - offset, + frames[i], info.dli_fname); + continue; + } + + ptr = strchr(pos, '\n'); + if (!ptr) { + connman_error("Error in backtrace format"); + break; + } + + *ptr++ = '\0'; + + if (strncmp(pos, program_path, pathlen) == 0) + pos += pathlen + 1; + + connman_error("#%-2u %p in %s() at %s", i - offset, + frames[i], buf, pos); + } + + connman_error("+++++++++++++++++++++++++++"); + + kill(pid, SIGTERM); + + close(outfd[1]); + close(infd[0]); +} diff --git a/src/config.c b/src/config.c index d4ba0b37..ca5957d6 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; } @@ -759,14 +765,14 @@ static bool load_service(GKeyFile *keyfile, const char *group, service->security = CONNMAN_SERVICE_SECURITY_PSK; } else if (str) { - if (security != CONNMAN_SERVICE_SECURITY_NONE) { + if (security != CONNMAN_SERVICE_SECURITY_NONE) connman_info("Mismatch no security and " "setting %s = %s", SERVICE_KEY_SECURITY, str); - } - service->security = CONNMAN_SERVICE_SECURITY_NONE; + + service->security = CONNMAN_SERVICE_SECURITY_NONE; } else - service->security = CONNMAN_SERVICE_SECURITY_NONE; + service->security = CONNMAN_SERVICE_SECURITY_NONE; g_free(str); @@ -806,8 +812,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; } @@ -1211,6 +1220,7 @@ static int try_provision_service(struct connman_config_service *config, enum connman_service_type type; const void *ssid; unsigned int ssid_len; + const char *str; network = __connman_service_get_network(service); if (!network) { @@ -1230,10 +1240,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; @@ -1241,6 +1249,10 @@ static int try_provision_service(struct connman_config_service *config, if (memcmp(config->ssid, ssid, ssid_len)) return -ENOENT; + str = connman_network_get_string(network, "WiFi.Security"); + if (config->security != __connman_service_string2security(str)) + return -ENOENT; + break; case CONNMAN_SERVICE_TYPE_ETHERNET: @@ -1266,7 +1278,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; @@ -1387,7 +1399,7 @@ ipv4_out: #endif __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)); @@ -1426,6 +1438,8 @@ ipv4_out: __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); @@ -1752,7 +1766,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 64d48b7d..f8194a64 100755 --- 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; @@ -1078,12 +1082,18 @@ bool __connman_connection_update_gateway(void) old_default = default_gateway; } #endif - 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-wait-online.service.in b/src/connman-wait-online.service.in new file mode 100644 index 00000000..c2ad5cc9 --- /dev/null +++ b/src/connman-wait-online.service.in @@ -0,0 +1,15 @@ +[Unit] +Description=Wait for network to be configured by ConnMan +Requisite=connman.service +After=connman.service +Before=network-online.target +DefaultDependencies=no +Conflicts=shutdown.target + +[Service] +Type=oneshot +ExecStart=@sbindir@/connmand-wait-online +RemainAfterExit=yes + +[Install] +WantedBy=network-online.target diff --git a/src/connman.h b/src/connman.h index bb4c0e59..57c30508 100644 --- a/src/connman.h +++ b/src/connman.h @@ -133,6 +133,7 @@ int __connman_agent_request_peer_authorization(struct connman_peer *peer, bool wps_requested, const char *dbus_sender, void *user_data); + #include <connman/log.h> int __connman_log_init(const char *program, const char *debug, @@ -142,6 +143,8 @@ void __connman_log_cleanup(gboolean backtrace); void __connman_log_enable(struct connman_debug_desc *start, struct connman_debug_desc *stop); +#include <connman/backtrace.h> + #include <connman/option.h> #include <connman/setting.h> @@ -166,6 +169,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> @@ -248,7 +254,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); @@ -263,6 +273,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); @@ -464,7 +475,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, @@ -488,6 +498,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); @@ -524,7 +535,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); @@ -593,10 +606,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); #if defined TIZEN_EXT int __connman_device_request_specific_scan(enum connman_service_type type, int scan_type, GSList *specific_scan_list); @@ -665,12 +680,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); @@ -719,11 +737,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); #if defined TIZEN_EXT void __connman_service_notify_strength_changed(struct connman_network *network); #endif @@ -740,13 +756,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); @@ -766,6 +781,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); @@ -898,9 +915,6 @@ void __connman_service_notify(struct connman_service *service, unsigned int rx_error, unsigned int tx_error, unsigned int rx_dropped, unsigned int tx_dropped); -bool __connman_service_is_user_allowed(enum connman_service_type type, - uid_t uid); - int __connman_service_counter_register(const char *counter); void __connman_service_counter_unregister(const char *counter); @@ -911,6 +925,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); @@ -954,11 +969,6 @@ void __connman_mesh_auto_connect(void); #include <connman/session.h> -typedef void (* service_iterate_cb) (struct connman_service *service, - void *user_data); - -int __connman_service_iterate_services(service_iterate_cb cb, void *user_data); - void __connman_service_mark_dirty(); void __connman_service_save(struct connman_service *service); @@ -1040,35 +1050,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); @@ -1076,6 +1098,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); @@ -1089,15 +1112,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, @@ -1188,6 +1203,7 @@ 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 31febbde..a1ddddc7 100755 --- a/src/connman.service.in +++ b/src/connman.service.in @@ -1,6 +1,11 @@ [Unit] Description=Connection service -After=net-config.service +DefaultDependencies=false +Conflicts=shutdown.target +RequiresMountsFor=@localstatedir@/lib/connman +After=dbus.service network-pre.target systemd-sysusers.service net-config.service +Before=network.target multi-user.target shutdown.target +Wants=network.target [Service] Type=dbus diff --git a/src/connman.socket b/src/connman.socket index bcf8638e..3e1e64d5 100644 --- a/src/connman.socket +++ b/src/connman.socket @@ -1,5 +1,6 @@ [Unit] Description=DNS Proxy Socket +Before=connman.service [Socket] ListenStream=127.0.0.1:53 diff --git a/src/connman_tv.service.in b/src/connman_tv.service.in index e5faac27..9eb75b24 100644 --- a/src/connman_tv.service.in +++ b/src/connman_tv.service.in @@ -1,6 +1,7 @@ [Unit] Description=Connection service After=net-config.service +DefaultDependencies=no [Service] Type=dbus @@ -524,35 +524,6 @@ err: return err; } -int connman_dbus_get_connection_unix_user_sync(DBusConnection *connection, - const char *bus_name, - unsigned int *user_id) -{ -#if defined TIZEN_EXT - *user_id = 0; -#else - unsigned long uid; - DBusError err; - - dbus_error_init(&err); - - uid = dbus_bus_get_unix_user(connection, bus_name, &err); - - if (uid == (unsigned long)-1) { - DBG("Can not get unix user ID!"); - if (dbus_error_is_set(&err)) { - DBG("%s", err.message); - dbus_error_free(&err); - } - return -1; - } - - *user_id = (unsigned int)uid; -#endif - - return 0; -} - static unsigned char *parse_context(DBusMessage *msg) { DBusMessageIter iter, array; diff --git a/src/device.c b/src/device.c index 8b77021a..df7b2bbc 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,33 @@ 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 defined TIZEN_EXT_WIFI_MESH + if (device_service_type == CONNMAN_SERVICE_TYPE_MESH) + return service_type != CONNMAN_SERVICE_TYPE_MESH; +#endif + + 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 +206,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 +257,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 +592,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 +616,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(¶ms, 0, sizeof(params)); + params.type = CONNMAN_SERVICE_TYPE_UNKNOWN; - if (device->driver && device->driver->scan) - device->driver->scan(CONNMAN_SERVICE_TYPE_UNKNOWN, device, - NULL, 0, NULL, NULL, NULL, NULL); + device->driver->scan(device, ¶ms); + } return 0; } @@ -602,16 +635,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(¶ms, 0, sizeof(params)); + params.type = type; + params.force_full_scan = force_full_scan; + + return device->driver->scan(device, ¶ms); } int __connman_device_disconnect(struct connman_device *device) @@ -682,7 +721,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 +737,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 +773,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); @@ -1211,7 +1264,8 @@ int __connman_device_request_mesh_specific_scan(enum connman_service_type type, #endif /* TIZEN_EXT_WIFI_MESH */ #endif -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; @@ -1238,23 +1292,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; -#if defined TIZEN_EXT_WIFI_MESH - } else if (type == CONNMAN_SERVICE_TYPE_MESH) { - if (service_type != CONNMAN_SERVICE_TYPE_WIFI) - continue; -#endif - } 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 defined TIZEN_EXT /* When Scan is already in progress then return Error so that * wifi-manager can block the scan-done signal to be sent to @@ -1277,20 +1319,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, ¶ms); +} + +void __connman_device_stop_scan(enum connman_service_type type) +{ + GSList *list; + + for (list = device_list; list; list = list->next) { + struct connman_device *device = list->data; + + if (!device_has_service_type(device, type)) + continue; + + if (device->driver && device->driver->stop_scan) + device->driver->stop_scan(type, device); + } } #if defined TIZEN_EXT @@ -134,7 +134,7 @@ static bool apply_dhcp_invalidate_on_network(struct connman_dhcp *dhcp) CONNMAN_IPCONFIG_TYPE_IPV4); #else __connman_service_nameserver_remove(service, - dhcp->nameservers[i], false); + dhcp->nameservers[i], false); #endif } g_strfreev(dhcp->nameservers); @@ -265,6 +265,7 @@ static gboolean dhcp_retry_cb(gpointer user_data) struct connman_dhcp *dhcp = user_data; dhcp->timeout = 0; + #if defined TIZEN_EXT DBG("dhcp %p", dhcp); DBG("dhcp->timeout %d", dhcp->timeout); @@ -849,6 +850,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(""); @@ -865,6 +890,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 c624cb00..4c07c769 100755 --- 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); #if defined TIZEN_EXT if(ident != NULL) DBG("ident : %s", ident); @@ -342,8 +342,8 @@ static void info_req_cb(GDHCPClient *dhcp_client, gpointer user_data) CONNMAN_IPCONFIG_TYPE_IPV6); #else __connman_service_nameserver_remove(service, - dhcp->nameservers[i], - false); + dhcp->nameservers[i], + false); #endif #if defined TIZEN_EXT } @@ -579,8 +579,8 @@ static int set_other_addresses(GDHCPClient *dhcp_client, false, CONNMAN_IPCONFIG_TYPE_IPV6); #else __connman_service_nameserver_append(service, - dhcp->nameservers[i], - false); + dhcp->nameservers[i], + false); #endif #if defined TIZEN_EXT } 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 72c0d8f1..cb583251 100755 --- a/src/dnsproxy.c +++ b/src/dnsproxy.c @@ -84,6 +84,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; @@ -489,7 +494,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) { @@ -501,21 +506,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)); @@ -2265,7 +2275,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)) { @@ -2277,12 +2287,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); #if defined TIZEN_EXT GSList *list; @@ -3137,34 +3144,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); @@ -3174,7 +3188,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; } @@ -3190,26 +3220,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]; + 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); - debug("EDNS0 buffer size %u", edns0_bufsize); - - /* 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); @@ -3902,9 +3919,6 @@ static GIOChannel *get_listener(int family, int protocol, int index) return NULL; } - /* ConnMan listens DNS from multiple interfaces - * E.g. various technology based and tethering interfaces - */ interface = connman_inet_ifname(index); if (!interface || setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, interface, @@ -3941,6 +3955,7 @@ static GIOChannel *get_listener(int family, int protocol, int index) s.sin.sin_family = AF_INET; s.sin.sin_port = htons(53); slen = sizeof(s.sin); + if (__connman_inet_get_interface_address(index, AF_INET, &s.sin.sin_addr) < 0) { @@ -3952,6 +3967,7 @@ static GIOChannel *get_listener(int family, int protocol, int index) return NULL; } #endif + #if defined TIZEN_EXT /* When ConnMan crashed, * probably DNS listener cannot bind existing address */ @@ -3971,6 +3987,7 @@ static GIOChannel *get_listener(int family, int protocol, int index) #endif if (protocol == IPPROTO_TCP) { + #if !defined TIZEN_EXT if (listen(sk, 10) < 0) { connman_error("Failed to listen on TCP socket %d/%s", @@ -3978,6 +3995,7 @@ static GIOChannel *get_listener(int family, int protocol, int index) close(sk); return NULL; } + #endif fcntl(sk, F_SETFL, O_NONBLOCK); } @@ -4275,6 +4293,11 @@ destroy: return err; } +int __connman_dnsproxy_set_mdns(int index, bool enabled) +{ + return -ENOTSUP; +} + void __connman_dnsproxy_cleanup(void) { DBG(""); @@ -4296,4 +4319,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); @@ -25,7 +25,6 @@ #include <config.h> #endif -#define _GNU_SOURCE #include <stdio.h> #include <errno.h> #include <unistd.h> @@ -262,6 +261,40 @@ char *connman_inet_ifname2addr(const char *name) } #endif +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; @@ -442,6 +475,40 @@ void connman_inet_update_device_ident(struct connman_device *device) } #endif +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; @@ -590,7 +657,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; @@ -753,10 +830,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; @@ -2433,6 +2517,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; } @@ -2585,8 +2670,6 @@ out: data->callback(addr, index, data->user_data); g_free(data); - - return; } /* @@ -2678,9 +2761,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; } @@ -2799,8 +2883,7 @@ char **__connman_inet_get_running_interfaces(void) g_free(ifr); - if (count < numif) - { + if (count < numif) { char **prev_result = result; result = g_try_realloc(result, (count + 1) * sizeof(char *)); if (!result) { @@ -2883,6 +2966,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) { @@ -2945,12 +3063,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: @@ -2963,7 +3084,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; @@ -2978,9 +3111,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 { @@ -3009,7 +3144,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, @@ -3017,7 +3159,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, @@ -3152,10 +3301,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) { @@ -3351,6 +3498,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/ipconfig.c b/src/ipconfig.c index d94b8734..fb39f64d 100755 --- a/src/ipconfig.c +++ b/src/ipconfig.c @@ -1157,6 +1157,7 @@ static struct connman_ipconfig *create_ipv6config(int index) #else ipv6config->ipv6_privacy_config = ipdevice->ipv6_privacy = 2; #endif + ipv6config->address = connman_ipaddress_alloc(AF_INET6); if (!ipv6config->address) { g_free(ipv6config); @@ -2104,6 +2105,7 @@ int __connman_ipconfig_set_config(struct connman_ipconfig *ipconfig, if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6) disable_ipv6(ipconfig); #endif + break; case CONNMAN_IPCONFIG_METHOD_AUTO: @@ -2116,6 +2118,7 @@ int __connman_ipconfig_set_config(struct connman_ipconfig *ipconfig, #if defined TIZEN_EXT enable_ipv6(ipconfig); #endif + break; case CONNMAN_IPCONFIG_METHOD_MANUAL: diff --git a/src/ippool.c b/src/ippool.c index cea1dccd..f2e9b000 100755 --- 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 aaddf9d6..9cfd80f8 100755 --- 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 100755 --- 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. @@ -23,15 +23,14 @@ #include <config.h> #endif -#define _GNU_SOURCE #include <stdio.h> #include <unistd.h> #include <stdarg.h> #include <stdlib.h> #include <string.h> #include <syslog.h> -#include <execinfo.h> #include <dlfcn.h> +#include <signal.h> #include "connman.h" @@ -175,6 +174,9 @@ void __connman_log_s(int log_priority, const char *format, ...) } #endif +/* This makes sure we always have a __debug section. */ +CONNMAN_DEBUG_DEFINE(dummy); + /** * connman_info: * @format: format string @@ -227,7 +229,6 @@ void connman_error(const char *format, ...) vsyslog(LOG_ERR, format, ap); va_end(ap); - fflush(log_file); } /** @@ -246,122 +247,13 @@ void connman_debug(const char *format, ...) vsyslog(LOG_DEBUG, format, ap); va_end(ap); - fflush(log_file); -} - -static void print_backtrace(unsigned int offset) -{ - void *frames[99]; - size_t n_ptrs; - unsigned int i; - int outfd[2], infd[2]; - int pathlen; - pid_t pid; - - if (!program_exec) - return; - - pathlen = strlen(program_path); - - n_ptrs = backtrace(frames, G_N_ELEMENTS(frames)); - if (n_ptrs < offset) - return; - - if (pipe(outfd) < 0) - return; - - if (pipe(infd) < 0) { - close(outfd[0]); - close(outfd[1]); - return; - } - - pid = fork(); - if (pid < 0) { - close(outfd[0]); - close(outfd[1]); - close(infd[0]); - close(infd[1]); - return; - } - - if (pid == 0) { - close(outfd[1]); - close(infd[0]); - - dup2(outfd[0], STDIN_FILENO); - dup2(infd[1], STDOUT_FILENO); - - execlp("addr2line", "-C", "-f", "-e", program_exec, NULL); - - exit(EXIT_FAILURE); - } - - close(outfd[0]); - close(infd[1]); - - connman_error("++++++++ backtrace ++++++++"); - - for (i = offset; i < n_ptrs - 1; i++) { - Dl_info info; - char addr[20], buf[PATH_MAX * 2]; - int len, written; - char *ptr, *pos; - - dladdr(frames[i], &info); - - len = snprintf(addr, sizeof(addr), "%p\n", frames[i]); - if (len < 0) - break; - - written = write(outfd[1], addr, len); - if (written < 0) - break; - - len = read(infd[0], buf, sizeof(buf) - 1); - if (len < 0) - break; - - buf[len] = '\0'; - - pos = strchr(buf, '\n'); -#if defined TIZEN_EXT - if (pos) { -#endif - *pos++ = '\0'; - - if (strcmp(buf, "??") == 0) { - connman_error("#%-2u %p in %s", i - offset, - frames[i], info.dli_fname); - continue; - } - - ptr = strchr(pos, '\n'); - *ptr++ = '\0'; - - if (strncmp(pos, program_path, pathlen) == 0) - pos += pathlen + 1; - - connman_error("#%-2u %p in %s() at %s", i - offset, - frames[i], buf, pos); -#if defined TIZEN_EXT - } -#endif - } - - connman_error("+++++++++++++++++++++++++++"); - - kill(pid, SIGTERM); - - close(outfd[1]); - close(infd[0]); } static void signal_handler(int signo) { connman_error("Aborting (signal %d) [%s]", signo, program_exec); - print_backtrace(2); + print_backtrace(program_path, program_exec, 2); exit(EXIT_FAILURE); } @@ -30,17 +30,19 @@ #include <string.h> #include <signal.h> #include <sys/signalfd.h> -#include <sys/types.h> -#include <sys/resource.h> #include <getopt.h> #include <sys/stat.h> #include <net/if.h> #include <netdb.h> +#include <sys/time.h> +#include <sys/resource.h> #include <gdbus.h> #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) @@ -54,6 +56,11 @@ static char *default_auto_connect[] = { NULL }; +static char *default_favorite_techs[] = { + "ethernet", + NULL +}; + static char *default_blacklist[] = { "vmnet", "vboxnet", @@ -68,6 +75,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; @@ -75,21 +83,25 @@ 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; #if defined TIZEN_EXT char **cellular_interfaces; bool tizen_tv_extension; - bool use_gateway_timeserver; #endif } connman_settings = { .bg_scan = true, .pref_timeservers = NULL, .auto_connect = NULL, + .favorite_techs = NULL, .preferred_techs = NULL, .always_connected_techs = NULL, .fallback_nameservers = NULL, @@ -97,22 +109,26 @@ 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, #if defined TIZEN_EXT .cellular_interfaces = NULL, .tizen_tv_extension = false, - .use_gateway_timeserver = false, #endif }; #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" @@ -120,22 +136,25 @@ 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" #if defined TIZEN_EXT #define CONF_CELLULAR_INTERFACE "NetworkCellularInterfaceList" #define CONF_TIZEN_TV_EXT "TizenTVExtension" -#define CONF_USE_GATEWAY_TIMESERVER "UseGatewayTimeserver" #endif 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, @@ -143,15 +162,19 @@ 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, #if defined TIZEN_EXT CONF_CELLULAR_INTERFACE, CONF_TIZEN_TV_EXT, - CONF_USE_GATEWAY_TIMESERVER, #endif NULL }; @@ -289,13 +312,6 @@ static void check_Tizen_configuration(GKeyFile *config) connman_settings.tizen_tv_extension = boolean; g_clear_error(&error); - - boolean = __connman_config_get_bool(config, "General", - CONF_USE_GATEWAY_TIMESERVER, &error); - if (!error) - connman_settings.use_gateway_timeserver = boolean; - - g_clear_error(&error); } static void set_nofile_inc(void) @@ -328,7 +344,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; @@ -351,14 +369,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); @@ -431,6 +461,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; @@ -477,6 +515,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); + #if defined TIZEN_EXT check_Tizen_configuration(config); #endif @@ -632,9 +690,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, @@ -646,7 +704,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" }, @@ -678,6 +736,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; @@ -690,10 +751,14 @@ bool connman_setting_get_bool(const char *key) if (g_str_equal(key, CONF_ENABLE_ONLINE_CHECK)) return connman_settings.enable_online_check; -#if defined TIZEN_EXT - if (g_str_equal(key, CONF_USE_GATEWAY_TIMESERVER)) - return connman_settings.use_gateway_timeserver; -#endif + 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; } @@ -722,9 +787,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; @@ -926,6 +994,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 80724d52..c3190ca3 100755 --- a/src/main.conf +++ b/src/main.conf @@ -14,18 +14,27 @@ # 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 BackgroundScanning = false +# 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 = #FallbackTimeservers = pool.ntp.org @@ -58,17 +67,22 @@ PreferredTechnologies = wifi, ethernet # 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- -NetworkInterfaceBlacklist = veth, vmnet,vboxnet,virbr,usb,rndis,rmnet,rev_rmnet,dummy,seth_td,seth_w +NetworkInterfaceBlacklist = veth,vmnet,vboxnet,virbr,usb,rndis,rmnet,rev_rmnet,dummy,seth_td,seth_w -# 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 @@ -123,8 +137,20 @@ SingleConnectedTechnology = true # This setting has no effect if SingleConnectedTechnologies is enabled. # AlwaysConnectedTechnologies = -NetworkCellularInterfaceList = pdp,rmnet,seth_td,seth_w - # Allow connman to add service gateway to the time server list. # Default value is false. -# UseGatewayTimeserver = false +# UseGatewaysAsTimeservers = false + +# 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 + +NetworkCellularInterfaceList = pdp,rmnet,seth_td,seth_w diff --git a/src/main_disable_eth.conf b/src/main_disable_eth.conf index ea4a9ddb..4cc22f1d 100755 --- a/src/main_disable_eth.conf +++ b/src/main_disable_eth.conf @@ -111,4 +111,4 @@ NetworkCellularInterfaceList = pdp,rmnet,seth_td,seth_w # Allow connman to add service gateway to the time server list. # Default value is false. -# UseGatewayTimeserver = false +# UseGatewaysAsTimeservers = false diff --git a/src/main_ivi.conf b/src/main_ivi.conf index c7e65c48..8ac0d589 100755 --- a/src/main_ivi.conf +++ b/src/main_ivi.conf @@ -111,4 +111,4 @@ NetworkCellularInterfaceList = pdp,rmnet,seth_td,seth_w # Allow connman to add service gateway to the time server list. # Default value is false. -# UseGatewayTimeserver = false +# UseGatewaysAsTimeservers = false diff --git a/src/main_tv.conf b/src/main_tv.conf index 44d1b025..7a72dbe1 100755 --- a/src/main_tv.conf +++ b/src/main_tv.conf @@ -111,7 +111,7 @@ NetworkCellularInterfaceList = pdp,rmnet,seth_td,seth_w # Allow connman to add service gateway to the time server list. # Default value is false. -# UseGatewayTimeserver = false +# UseGatewaysAsTimeservers = false # Enable Tizen TV Profile Features TizenTVExtension = true diff --git a/src/manager.c b/src/manager.c index bd44fea4..583b2ad1 100644 --- a/src/manager.c +++ b/src/manager.c @@ -111,20 +111,6 @@ static DBusMessage *set_property(DBusConnection *conn, dbus_message_iter_get_basic(&value, &offlinemode); - if (offlinemode) { - uid_t uid; - if (connman_dbus_get_connection_unix_user_sync(conn, - dbus_message_get_sender(msg), - &uid) < 0) { - DBG("Can not get unix user id!"); - return __connman_error_permission_denied(msg); - } - - if (!__connman_service_is_user_allowed(CONNMAN_SERVICE_TYPE_WIFI, uid)) { - DBG("Not allow this user to turn on offlinemode now!"); - return __connman_error_permission_denied(msg); - } - } __connman_technology_set_offlinemode(offlinemode); } else if (g_str_equal(name, "SessionMode")) { @@ -132,9 +118,8 @@ static DBusMessage *set_property(DBusConnection *conn, return __connman_error_invalid_arguments(msg); dbus_message_iter_get_basic(&value, &sessionmode); - } #if defined TIZEN_EXT - else if (g_str_equal(name, "AutoConnectMode") == TRUE) { + } else if (g_str_equal(name, "AutoConnectMode") == TRUE) { bool automode; if (type != DBUS_TYPE_BOOLEAN) @@ -143,9 +128,8 @@ static DBusMessage *set_property(DBusConnection *conn, dbus_message_iter_get_basic(&value, &automode); __connman_service_set_auto_connect_mode(automode); - } #endif - else + } else return __connman_error_invalid_property(msg); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); @@ -206,7 +190,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, @@ -251,6 +235,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) { @@ -664,6 +669,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" }), @@ -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 fc57dcc8..a6d635ec 100755 --- 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 @@ -48,6 +50,8 @@ */ #define RTR_SOLICITATION_INTERVAL 4 +#define DHCP_RETRY_TIMEOUT 10 + static GSList *network_list = NULL; static GSList *driver_list = NULL; @@ -67,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; @@ -97,6 +104,7 @@ struct connman_network { char *private_key_passphrase; char *phase2_auth; bool wps; + bool wps_advertizing; bool use_wps; char *pin_wps; #if defined TIZEN_EXT @@ -179,6 +187,264 @@ 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; + +#if defined TIZEN_EXT + err = __connman_ipconfig_gateway_add(ipconfig_ipv4, service); +#else + err = __connman_ipconfig_gateway_add(ipconfig_ipv4); +#endif + 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; @@ -196,6 +462,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; @@ -266,6 +540,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; @@ -282,6 +564,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; @@ -289,6 +579,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); @@ -304,6 +595,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) { @@ -573,7 +902,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, @@ -730,6 +1058,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; } @@ -770,6 +1099,7 @@ static void set_disconnected(struct connman_network *network) __connman_ipconfig_address_unset(ipconfig_ipv4); __connman_ipconfig_address_unset(ipconfig_ipv6); + #if defined TIZEN_EXT } #endif @@ -982,6 +1312,7 @@ static void network_destruct(struct connman_network *network) g_free(network->wifi.private_key_passphrase); g_free(network->wifi.phase2_auth); g_free(network->wifi.pin_wps); + #if defined TIZEN_EXT g_slist_free_full(network->wifi.vsie_list, g_free); g_slist_free_full(network->wifi.bssid_list, g_free); @@ -991,6 +1322,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; @@ -1026,9 +1358,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; @@ -1559,7 +1895,6 @@ void connman_network_set_error(struct connman_network *network, case CONNMAN_NETWORK_ERROR_BLOCKED: set_blocked_error(network); break; - } __connman_network_disconnect(network); @@ -1697,17 +2032,17 @@ int __connman_network_connect(struct connman_network *network) if (!network->device) return -ENODEV; - network->connecting = true; - #if defined TIZEN_EXT if (network->type != CONNMAN_NETWORK_TYPE_CELLULAR) #endif __connman_device_disconnect(network->device); + + network->connecting = true; + #if defined TIZEN_EXT DBG("ConnMan, Connect Request [%s]", network->name); - struct connman_service *service = connman_service_lookup_from_network(network); - connman_service_set_disconnection_requested(service, false); #endif + err = network->driver->connect(network); if (err < 0) { if (err == -EINPROGRESS) { @@ -1738,6 +2073,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; @@ -1746,6 +2085,7 @@ int __connman_network_disconnect(struct connman_network *network) return -EUNATCH; network->connecting = false; + #if defined TIZEN_EXT DBG("ConnMan, Disconnect request"); struct connman_service *service = connman_service_lookup_from_network(network); @@ -1781,13 +2121,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; } @@ -2443,6 +2786,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; #if defined TIZEN_EXT @@ -2469,6 +2814,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; #if defined TIZEN_EXT 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 7c3d0311..d1be47f6 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); @@ -221,7 +221,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); @@ -234,7 +234,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); @@ -257,7 +257,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); @@ -269,7 +269,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); @@ -295,7 +295,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); @@ -309,7 +309,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); @@ -324,7 +324,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); @@ -367,7 +367,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); @@ -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 *)×erver_addr, timeout << 1); + send_packet(nd, (struct sockaddr *)&nd->timeserver_addr, + nd->timeout << 1); return FALSE; } -static void send_packet(int fd, struct sockaddr *server, uint32_t timeout) +static void send_packet(struct ntp_data *nd, struct sockaddr *server, + uint32_t timeout) { struct ntp_msg msg; struct timeval transmit_timeval; @@ -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 *)×erver_addr)->sin_addr); + addr = (void *)&(((struct sockaddr_in *)&nd->timeserver_addr)->sin_addr); } else if (server->sa_family == AF_INET6) { size = sizeof(struct sockaddr_in6); - addr = (void *)×erver_addr.sin6_addr; + addr = (void *)&nd->timeserver_addr.sin6_addr; } else { - connman_error("Family is neither ipv4 nor ipv6"); + DBG("Family is neither ipv4 nor ipv6"); + nd->cb(false, nd->user_data); return; } gettimeofday(&transmit_timeval, NULL); - clock_gettime(CLOCK_MONOTONIC, &mtx_time); + clock_gettime(CLOCK_MONOTONIC, &nd->mtx_time); msg.xmttime.seconds = htonl(transmit_timeval.tv_sec + OFFSET_1900_1970); msg.xmttime.fraction = htonl(transmit_timeval.tv_usec * 1000); - len = sendto(fd, &msg, sizeof(msg), MSG_DONTWAIT, + len = sendto(nd->transmit_fd, &msg, sizeof(msg), MSG_DONTWAIT, server, size); - - if (len < 0) { - connman_error("Time request for server %s failed (%d/%s)", - inet_ntop(server->sa_family, addr, ipaddrstring, sizeof(ipaddrstring)), - errno, strerror(errno)); - - if (errno == ENETUNREACH) - __connman_timeserver_sync_next(); - - return; - } - - if (len != sizeof(msg)) { - connman_error("Broken time request for server %s", + if (len < 0 || len != sizeof(msg)) { + DBG("Time request for server %s failed", inet_ntop(server->sa_family, addr, ipaddrstring, sizeof(ipaddrstring))); + nd->cb(false, nd->user_data); return; } @@ -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 *)×erver_addr, NTP_SEND_TIMEOUT); + send_packet(nd, (struct sockaddr *)&nd->timeserver_addr, NTP_SEND_TIMEOUT); return FALSE; } -static void reset_timeout(void) +static void reset_timeout(struct ntp_data *nd) { - if (timeout_id > 0) { - g_source_remove(timeout_id); - timeout_id = 0; + if (nd->timeout_id > 0) { + g_source_remove(nd->timeout_id); + nd->timeout_id = 0; } - retries = 0; + nd->retries = 0; } -static void decode_msg(void *base, size_t len, struct timeval *tv, - struct timespec *mrx_time) +static void decode_msg(struct ntp_data *nd, void *base, size_t len, + struct timeval *tv, struct timespec *mrx_time) { struct ntp_msg *msg = base; double m_delta, org, rec, xmt, dst; @@ -254,6 +256,7 @@ static void decode_msg(void *base, size_t len, struct timeval *tv, #if !defined TIZEN_EXT struct timex tmx = {}; #endif + if (len < sizeof(*msg)) { connman_error("Invalid response from time server"); return; @@ -281,9 +284,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; } @@ -291,6 +294,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; } @@ -301,17 +305,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) + @@ -329,18 +335,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 defined TIZEN_EXT //send the dbus message to alram-manager @@ -435,18 +442,22 @@ 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); + LOGTOD(msg->poll), offset, delay, tmx.freq / 65536); + + nd->cb(true, nd->user_data); #endif } 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; @@ -463,7 +474,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; } @@ -486,11 +497,11 @@ static gboolean received_data(GIOChannel *channel, GIOCondition condition, if (sender_addr.sin6_family == AF_INET) { size = 4; - addr_ptr = &((struct sockaddr_in *)×erver_addr)->sin_addr; + addr_ptr = &((struct sockaddr_in *)&nd->timeserver_addr)->sin_addr; src_ptr = &((struct sockaddr_in *)&sender_addr)->sin_addr; } else if (sender_addr.sin6_family == AF_INET6) { size = 16; - addr_ptr = &((struct sockaddr_in6 *)×erver_addr)->sin6_addr; + addr_ptr = &((struct sockaddr_in6 *)&nd->timeserver_addr)->sin6_addr; src_ptr = &((struct sockaddr_in6 *)&sender_addr)->sin6_addr; } else { connman_error("Not a valid family type"); @@ -514,12 +525,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; @@ -532,14 +543,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"); @@ -548,18 +556,18 @@ static void start_ntp(char *server) family = info->ai_family; - memcpy(×erver_addr, info->ai_addr, info->ai_addrlen); + memcpy(&ntp_data->timeserver_addr, info->ai_addr, info->ai_addrlen); freeaddrinfo(info); memset(&in6addr, 0, sizeof(in6addr)); if (family == AF_INET) { - ((struct sockaddr_in *)×erver_addr)->sin_port = htons(123); + ((struct sockaddr_in *)&ntp_data->timeserver_addr)->sin_port = htons(123); in4addr = (struct sockaddr_in *)&in6addr; in4addr->sin_family = family; addr = (struct sockaddr *)in4addr; size = sizeof(struct sockaddr_in); } else if (family == AF_INET6) { - timeserver_addr.sin6_port = htons(123); + ntp_data->timeserver_addr.sin6_port = htons(123); in6addr.sin6_family = family; addr = (struct sockaddr *)&in6addr; size = sizeof(in6addr); @@ -568,96 +576,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, ×tamp, + if (setsockopt(nd->transmit_fd, SOL_SOCKET, SO_TIMESTAMP, ×tamp, sizeof(timestamp)) < 0) { connman_error("Failed to enable timestamp support"); - close(transmit_fd); - return; + goto err; } - channel = g_io_channel_unix_new(transmit_fd); - if (!channel) { - close(transmit_fd); - return; - } + channel = g_io_channel_unix_new(nd->transmit_fd); + if (!channel) + goto err; g_io_channel_set_encoding(channel, NULL, NULL); g_io_channel_set_buffered(channel, FALSE); g_io_channel_set_close_on_unref(channel, TRUE); - channel_watch = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, + nd->channel_watch = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - received_data, NULL, NULL); + received_data, nd, NULL); g_io_channel_unref(channel); send: - send_packet(transmit_fd, (struct sockaddr*)×erver_addr, NTP_SEND_TIMEOUT); + send_packet(nd, (struct sockaddr*)&ntp_data->timeserver_addr, + NTP_SEND_TIMEOUT); + return; + +err: + if (nd->transmit_fd > 0) + close(nd->transmit_fd); + + nd->cb(false, nd->user_data); } -int __connman_ntp_start(char *server) +int __connman_ntp_start(char *server, __connman_ntp_cb_t callback, + void *user_data) { - DBG("%s", server); - if (!server) return -EINVAL; - if (timeserver) - g_free(timeserver); + if (ntp_data) { + connman_warn("ntp_data is not NULL (timerserver %s)", + ntp_data->timeserver); + free_ntp_data(ntp_data); + } + + ntp_data = g_new0(struct ntp_data, 1); - timeserver = g_strdup(server); + ntp_data->timeserver = g_strdup(server); + ntp_data->cb = callback; + ntp_data->user_data = user_data; - start_ntp(timeserver); + start_ntp(ntp_data); return 0; } void __connman_ntp_stop() { - DBG(""); - - if (poll_id > 0) { - g_source_remove(poll_id); - poll_id = 0; - } - - reset_timeout(); - - if (channel_watch > 0) { - g_source_remove(channel_watch); - channel_watch = 0; - transmit_fd = 0; - } - - if (timeserver) { - g_free(timeserver); - timeserver = NULL; + if (ntp_data) { + free_ntp_data(ntp_data); + ntp_data = NULL; } } @@ -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 c0d69e49..f1e4a067 100755 --- a/src/provider.c +++ b/src/provider.c @@ -742,7 +742,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) @@ -755,11 +755,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 d6c20cdd..7ec2150b 100755 --- 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> @@ -35,6 +34,19 @@ #include "connman.h" +/* + * Just to avoid build failure due to missing STATEDIR + */ +#if defined TIZEN_EXT +#ifdef STATEDIR +#undef STATEDIR +#endif +#define STATEDIR "/etc" +#endif + +#define RESOLV_CONF_STATEDIR STATEDIR"/resolv.conf" +#define RESOLV_CONF_ETC "/etc/resolv.conf" + #define RESOLVER_FLAG_PUBLIC (1 << 0) /* @@ -130,11 +142,19 @@ static int resolvfile_export(void) old_umask = umask(022); - fd = open("/etc/resolv.conf", O_RDWR | O_CREAT | O_CLOEXEC, + fd = open(RESOLV_CONF_STATEDIR, O_RDWR | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (fd < 0) { - err = -errno; - goto done; + connman_warn_once("Cannot create "RESOLV_CONF_STATEDIR" " + "falling back to "RESOLV_CONF_ETC); + + fd = open(RESOLV_CONF_ETC, O_RDWR | O_CREAT | O_CLOEXEC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + if (fd < 0) { + err = -errno; + goto done; + } } if (ftruncate(fd, 0) < 0) { @@ -658,6 +678,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 fce9d720..99b337d2 100755 --- 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> @@ -167,6 +167,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; @@ -1381,10 +1382,10 @@ static void rtnl_newnduseropt(struct nlmsghdr *hdr) DBG("service: %p\n",service); #endif servers = rtnl_nd_opt_rdnss(opt, &lifetime, - &nr_servers); + &nr_servers); for (i = 0; i < nr_servers; i++) { if (!inet_ntop(AF_INET6, servers + i, buf, - sizeof(buf))) + sizeof(buf))) continue; #if defined TIZEN_EXT @@ -1396,7 +1397,7 @@ static void rtnl_newnduseropt(struct nlmsghdr *hdr) CONNMAN_IPCONFIG_TYPE_IPV6); #endif connman_resolver_append_lifetime(index, - NULL, buf, lifetime); + NULL, buf, lifetime); } } else if (opt->nd_opt_type == 31) { /* ND_OPT_DNSSL */ @@ -1531,7 +1532,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 47c828d8..0c6e6235 100644 --- a/src/service.c +++ b/src/service.c @@ -30,8 +30,6 @@ #include <gdbus.h> #include <ctype.h> #include <stdint.h> -#include <pwd.h> -#include <utmpx.h> #include <connman/storage.h> #include <connman/setting.h> @@ -41,9 +39,6 @@ #define CONNECT_TIMEOUT 120 -#define USER_ROOT 0 -#define USER_NONE (uid_t)-1 - #if defined TIZEN_EXT #define WIFI_BSSID_STR_LEN 18 #endif @@ -53,8 +48,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; @@ -76,11 +71,6 @@ struct connman_stats_counter { struct connman_stats stats_roaming; }; -struct connman_service_user { - uid_t favorite_user; - uid_t current_user; -}; - struct connman_service { int refcount; char *identifier; @@ -103,8 +93,6 @@ struct connman_service { char *name; char *passphrase; bool roaming; - bool request_passphrase_input; - struct connman_service_user user; struct connman_ipconfig *ipconfig_ipv4; struct connman_ipconfig *ipconfig_ipv6; struct connman_network *network; @@ -114,6 +102,8 @@ struct connman_service { char **nameservers_auto; int nameservers_timeout; char **domains; + bool mdns; + bool mdns_config; char *hostname; char *domainname; char **timeservers; @@ -144,7 +134,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; @@ -497,89 +490,6 @@ static enum connman_dnsconfig_method __connman_dnsconfig_string2method( } #endif -static bool -connman_service_is_user_allowed(struct connman_service *service, uid_t uid) -{ - uid_t favorite_user = service->user.favorite_user; - uid_t current_user = uid; - - DBG("Service favorite UID: %d, current UID: %d", favorite_user, current_user); - if (favorite_user == USER_NONE || current_user == USER_ROOT) - return true; - - if (favorite_user != current_user || current_user == USER_NONE) { - DBG("Current user is not a favorite user to this service!"); - return false; - } - - return true; -} - -#if !defined TIZEN_EXT -static GList *connman_service_get_login_users() -{ - struct utmpx *utmp; - struct passwd *pwd; - GList *user_list = NULL; - - setutxent(); - - while ((utmp = getutxent()) != NULL) { - DBG("User Name: %s", utmp->ut_user); - - pwd = getpwnam(utmp->ut_user); - if (pwd) { - if (!g_list_find(user_list, GUINT_TO_POINTER(pwd->pw_uid))) - user_list = g_list_append(user_list, - GUINT_TO_POINTER(pwd->pw_uid)); - - DBG("User Name: %s, UID: %d", utmp->ut_user, pwd->pw_uid); - } - } - - endutxent(); - - return user_list; -} -#endif - -static bool is_service_owner_user_login(struct connman_service *service) -{ -#if defined TIZEN_EXT - return true; -#else - GList *list, *user_list; - bool ret = false; - - /* Here we only care about wifi service */ - if (service->type != CONNMAN_SERVICE_TYPE_WIFI) - return true; - - DBG("service favorite user id is: %d", service->user.favorite_user); - - user_list = connman_service_get_login_users(); - if (user_list == NULL) { - DBG("Can not get any logged in user info."); - return true; - } - - for (list = user_list; list; list = list->next) { - uid_t uid = GPOINTER_TO_UINT(list->data); - - DBG("login user id is %d", uid); - - if (service->user.favorite_user == uid) { - ret = true; - break; - } - } - - g_list_free(user_list); - - return ret; -#endif -} - static void set_split_routing(struct connman_service *service, bool value) { if (service->type != CONNMAN_SERVICE_TYPE_VPN) @@ -646,25 +556,6 @@ int __connman_service_load_modifiable(struct connman_service *service) return 0; } -static int service_load_passphrase(struct connman_service *service) -{ - GKeyFile *keyfile; - gchar *str; - - keyfile = connman_storage_load_service(service->identifier); - if (!keyfile) - return -EIO; - - str = g_key_file_get_string(keyfile, - service->identifier, "Passphrase", NULL); - if (str) - service->passphrase = str; - - g_key_file_free(keyfile); - - return 0; -} - static int service_load(struct connman_service *service) { GKeyFile *keyfile; @@ -862,6 +753,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); @@ -919,9 +813,6 @@ static int service_load(struct connman_service *service) } #endif - if (g_key_file_has_key(keyfile, service->identifier, "UID", NULL)) - service->user.favorite_user = g_key_file_get_integer(keyfile, - service->identifier, "UID", NULL); done: g_key_file_free(keyfile); @@ -970,13 +861,6 @@ static int service_save(struct connman_service *service) const unsigned char *ssid; unsigned int ssid_len = 0; - if (service->user.favorite_user == USER_NONE) - g_key_file_remove_key(keyfile, service->identifier, - "UID", NULL); - else - g_key_file_set_integer(keyfile, service->identifier, - "UID", service->user.favorite_user); - ssid = connman_network_get_blob(service->network, "WiFi.SSID", &ssid_len); @@ -1032,14 +916,12 @@ static int service_save(struct connman_service *service) g_free(str); } - if (service->user.current_user == service->user.favorite_user) { - if (service->passphrase && strlen(service->passphrase) > 0) - g_key_file_set_string(keyfile, service->identifier, + if (service->passphrase && strlen(service->passphrase) > 0) + g_key_file_set_string(keyfile, service->identifier, "Passphrase", service->passphrase); - else - g_key_file_remove_key(keyfile, service->identifier, - "Passphrase", NULL); - } + else + g_key_file_remove_key(keyfile, service->identifier, + "Passphrase", NULL); if (service->ipconfig_ipv4) __connman_ipconfig_save(service->ipconfig_ipv4, keyfile, @@ -1133,6 +1015,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); @@ -1646,6 +1535,7 @@ static int nameserver_remove_all(struct connman_service *service, return -ENXIO; while (service->nameservers_config && service->nameservers_config[i]) { + #if defined TIZEN_EXT DBG("type %d Remove service->nameservers_config[%d]: %s", type, i, service->nameservers_config[i]); @@ -1789,7 +1679,7 @@ int __connman_service_nameserver_append(struct connman_service *service, char **nameservers; int len, i; - DBG("service %p nameserver %s auto %d", service, nameserver, is_auto); + DBG("service %p nameserver %s auto %d", service, nameserver, is_auto); if (!nameserver) return -EINVAL; @@ -2050,7 +1940,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); @@ -2160,19 +2050,6 @@ static gboolean __connman_service_is_internet_profile( return FALSE; } -static gboolean __connman_service_is_tethering_profile( - struct connman_service *cellular) -{ - const char tethering_suffix[] = "_5"; - - DBG("Service path: %s", cellular->path); - - if (g_str_has_suffix(cellular->path, tethering_suffix) == TRUE) - return TRUE; - - return FALSE; -} - struct connman_service *connman_service_get_default_connection(void) { GList *list; @@ -2216,7 +2093,7 @@ struct connman_service *connman_service_get_default_connection(void) } #endif -struct connman_service *__connman_service_get_default(void) +struct connman_service *connman_service_get_default(void) { #if defined TIZEN_MAINTAIN_ONLINE return connman_service_get_default_connection(); @@ -2242,14 +2119,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; @@ -2273,7 +2150,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); } @@ -2293,6 +2171,7 @@ static void state_changed(struct connman_service *service) #if !defined TIZEN_EXT if (!allow_property_changed(service)) return; + #endif #if defined TIZEN_EXT DBG(" %s, %s", str, service->path); @@ -2451,15 +2330,37 @@ static void append_security(DBusMessageIter *iter, void *user_data) dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str); break; +#if defined TIZEN_EXT + case CONNMAN_SERVICE_SECURITY_OWE: +#endif case CONNMAN_SERVICE_SECURITY_UNKNOWN: case CONNMAN_SERVICE_SECURITY_NONE: case CONNMAN_SERVICE_SECURITY_WEP: 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; @@ -3039,6 +2940,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)) @@ -3289,17 +3232,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; + for (list = service_list; list && ret == 0; list = list->next) + ret = cb((struct connman_service *)list->data, user_data); - cb(service, user_data); - } - - return 0; + return ret; } #if defined TIZEN_EXT @@ -3602,8 +3544,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, @@ -3701,7 +3654,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) @@ -3719,7 +3675,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); } @@ -3735,6 +3694,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) @@ -4235,6 +4202,9 @@ int __connman_service_check_passphrase(enum connman_service_security security, break; case CONNMAN_SERVICE_SECURITY_8021X: +#if defined TIZEN_EXT + case CONNMAN_SERVICE_SECURITY_OWE: +#endif break; } @@ -4252,6 +4222,7 @@ int __connman_service_set_passphrase(struct connman_service *service, if (service->immutable && service->security != CONNMAN_SERVICE_SECURITY_8021X) return -EINVAL; + #if defined TIZEN_EXT /* The encrypted passphrase is used here * and validation is done by net-config before being encrypted. @@ -4561,7 +4532,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); } @@ -4573,6 +4544,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) { @@ -4589,21 +4583,6 @@ static DBusMessage *set_property(DBusConnection *conn, if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __connman_error_invalid_arguments(msg); - if (service->type == CONNMAN_SERVICE_TYPE_WIFI && is_connected(service->state)) { - uid_t uid; - if (connman_dbus_get_connection_unix_user_sync(conn, - dbus_message_get_sender(msg), - &uid) < 0) { - DBG("Can not get unix user id!"); - return __connman_error_permission_denied(msg); - } - - if (!connman_service_is_user_allowed(service, uid)) { - DBG("Not allow this user to operate this wifi service now!"); - return __connman_error_permission_denied(msg); - } - } - dbus_message_iter_get_basic(&iter, &name); dbus_message_iter_next(&iter); @@ -4665,6 +4644,7 @@ static DBusMessage *set_property(DBusConnection *conn, if (gw && strlen(gw)) __connman_service_nameserver_del_routes(service, CONNMAN_IPCONFIG_TYPE_ALL); + #endif dbus_message_iter_recurse(&value, &entry); @@ -4775,13 +4755,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")) { @@ -4821,15 +4799,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")) { @@ -4898,6 +4875,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")) { @@ -5036,14 +5030,6 @@ static void service_complete(struct connman_service *service) service_save(service); } -static void set_idle(struct connman_service *service) -{ - service->state = service->state_ipv4 = service->state_ipv6 = - CONNMAN_SERVICE_STATE_IDLE; - set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN); - state_changed(service); -} - static DBusMessage *clear_property(DBusConnection *conn, DBusMessage *msg, void *user_data) { @@ -5080,7 +5066,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) @@ -5358,11 +5345,6 @@ static bool auto_connect_service(GList *services, continue; } - if (!is_service_owner_user_login(service)) { - DBG("favorite user not login, wifi auto connect denied"); - continue; - } - DBG("service %p %s %s", service, service->name, (preferred) ? "preferred" : reason2string(reason)); @@ -5383,7 +5365,7 @@ static gboolean run_auto_connect(gpointer data) bool autoconnecting = false; GList *preferred_tech; - autoconnect_timeout = 0; + autoconnect_id = 0; DBG(""); @@ -5419,7 +5401,7 @@ void __connman_service_auto_connect(enum connman_service_connect_reason reason) { DBG(""); - if (autoconnect_timeout != 0) + if (autoconnect_id != 0) return; #if defined TIZEN_EXT @@ -5445,9 +5427,9 @@ void __connman_service_auto_connect(enum connman_service_connect_reason reason) * FAILURE state due to this when connection with AP2 is cancelled * then autoconnect with AP1 doesn't works because its autoconnection * is ignored as its last state was FAILURE rather than IDLE */ - autoconnect_timeout = g_timeout_add(500, run_auto_connect, + autoconnect_id = g_timeout_add(500, run_auto_connect, #else - autoconnect_timeout = g_idle_add(run_auto_connect, + autoconnect_id = g_idle_add(run_auto_connect, #endif GUINT_TO_POINTER(reason)); } @@ -5456,7 +5438,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; @@ -5498,10 +5480,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); } @@ -5527,7 +5509,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) @@ -5641,31 +5622,6 @@ static DBusMessage *connect_service(DBusConnection *conn, if (service->pending) return __connman_error_in_progress(msg); - if (service->type == CONNMAN_SERVICE_TYPE_WIFI) { - uid_t uid; - if (connman_dbus_get_connection_unix_user_sync(conn, - dbus_message_get_sender(msg), - &uid) < 0) { - DBG("Can not get unix user id!"); - return __connman_error_permission_denied(msg); - } - - if (!__connman_service_is_user_allowed(CONNMAN_SERVICE_TYPE_WIFI, uid)) { - DBG("Not allow this user to connect this wifi service now!"); - return __connman_error_permission_denied(msg); - } - - if (uid != USER_ROOT && uid != service->user.favorite_user) - service->request_passphrase_input = true; - - service->user.current_user = uid; - - if (!service->passphrase && uid == service->user.favorite_user) { - DBG("Now load this favorite user's passphrase."); - service_load_passphrase(service); - } - } - #if !defined TIZEN_EXT index = __connman_service_get_index(service); @@ -5701,18 +5657,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, @@ -5737,21 +5685,6 @@ static DBusMessage *disconnect_service(DBusConnection *conn, } #endif - if (service->type == CONNMAN_SERVICE_TYPE_WIFI) { - uid_t uid; - if (connman_dbus_get_connection_unix_user_sync(conn, - dbus_message_get_sender(msg), - &uid) < 0) { - DBG("Can not get unix user id!"); - return __connman_error_permission_denied(msg); - } - - if (!connman_service_is_user_allowed(service, uid)) { - DBG("Not allow this user to disconnect this wifi service now!"); - return __connman_error_permission_denied(msg); - } - } - service->ignore = true; err = __connman_service_disconnect(service); @@ -5853,15 +5786,8 @@ bool __connman_service_remove(struct connman_service *service) #endif -#if defined TIZEN_EXT - if (service->security != CONNMAN_SERVICE_SECURITY_8021X) -#endif - set_idle(service); - service->error = CONNMAN_SERVICE_ERROR_UNKNOWN; - service->user.favorite_user = USER_NONE; - __connman_service_set_favorite(service, false); __connman_ipconfig_ipv6_reset_privacy(service->ipconfig_ipv6); @@ -5891,23 +5817,6 @@ static DBusMessage *remove_service(DBusConnection *conn, DBG("service %p", service); - if (service->type == CONNMAN_SERVICE_TYPE_WIFI) { - uid_t uid; - if (connman_dbus_get_connection_unix_user_sync(conn, - dbus_message_get_sender(msg), - &uid) < 0) { - DBG("Can not get unix user id!"); - return __connman_error_permission_denied(msg); - } - -#if !defined TIZEN_EXT - if (!connman_service_is_user_allowed(service, uid)) { - DBG("Not allow this user to remove this wifi service now!"); - return __connman_error_permission_denied(msg); - } -#endif - } - if (!__connman_service_remove(service)) return __connman_error_not_supported(msg); @@ -5953,7 +5862,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; @@ -5986,6 +5895,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) @@ -6092,6 +6084,8 @@ static DBusMessage *move_service(DBusConnection *conn, __connman_connection_update_gateway(); + service_schedule_changed(); + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } @@ -6117,30 +6111,6 @@ static DBusMessage *reset_counters(DBusConnection *conn, return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } -static DBusMessage *get_user_favorite(DBusConnection *conn, - DBusMessage *msg, void *user_data) -{ - DBusMessage *reply; - uid_t uid = USER_NONE; - dbus_bool_t user_favorite = false; - struct connman_service *service = user_data; - - connman_dbus_get_connection_unix_user_sync(conn, - dbus_message_get_sender(msg), - &uid); - if (uid == USER_ROOT) - user_favorite = service->favorite; - else if (uid != USER_NONE && uid == service->user.favorite_user) { - DBG("The service is favorite to this user!"); - user_favorite = true; - } - - reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID); - dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, - &user_favorite, DBUS_TYPE_INVALID); - return reply; -} - #if defined TIZEN_MAINTAIN_ONLINE static DBusMessage *downgrade_service(DBusConnection *conn, DBusMessage *msg, void *user_data) @@ -6154,94 +6124,6 @@ static DBusMessage *downgrade_service(DBusConnection *conn, } #endif -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) { -#if !defined TIZEN_EXT - DBG("service %p or path is NULL", service); -#endif - return; - } - - if (g_hash_table_lookup(services_notify->add, service->path)) { -#if !defined TIZEN_EXT - DBG("new %s", service->path); -#endif - - append_struct(service, iter); - g_hash_table_remove(services_notify->add, service->path); - } else { -#if !defined TIZEN_EXT - DBG("changed %s", service->path); -#endif - - 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); @@ -6303,9 +6185,6 @@ static const GDBusMethodTable service_methods[] = { GDBUS_ARGS({ "service", "o" }), NULL, move_after) }, { GDBUS_METHOD("ResetCounters", NULL, NULL, reset_counters) }, - { GDBUS_METHOD("GetUserFavorite", - NULL, GDBUS_ARGS({ "value", "v" }), - get_user_favorite) }, #if defined TIZEN_MAINTAIN_ONLINE { GDBUS_METHOD("Downgrade", NULL, NULL, downgrade_service) }, #endif @@ -6449,11 +6328,6 @@ static void service_initialize(struct connman_service *service) service->ignore = false; - service->user.favorite_user = USER_NONE; - service->user.current_user = USER_NONE; - - service->request_passphrase_input = false; - service->connect_reason = CONNMAN_SERVICE_CONNECT_REASON_NONE; service->order = 0; @@ -6463,6 +6337,7 @@ static void service_initialize(struct connman_service *service) service->provider = NULL; service->wps = false; + service->wps_advertizing = false; #if defined TIZEN_EXT service->disconnection_requested = false; service->storage_reload = false; @@ -6709,40 +6584,6 @@ char *connman_service_get_interface(struct connman_service *service) } /** - * __connman_service_is_user_allowed: - * @type: service type - * @uid: user id - * - * Check a user is allowed to operate a type of service - */ -bool __connman_service_is_user_allowed(enum connman_service_type type, - uid_t uid) -{ - GList *list; - uid_t owner_user = USER_NONE; - - for (list = service_list; list; list = list->next) { - struct connman_service *service = list->data; - - if (service->type != type) - continue; - - if (is_connected(service->state)) { - owner_user = service->user.favorite_user; - break; - } - } - - if (uid == USER_NONE || - (uid != USER_ROOT && - owner_user != USER_NONE && - owner_user != uid)) - return false; - - return true; -} - -/** * connman_service_get_network: * @service: service structure * @@ -6908,9 +6749,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) { @@ -7032,6 +6870,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) { @@ -7107,7 +6953,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, @@ -7389,6 +7235,7 @@ static void single_connected_tech(struct connman_service *allowed) if (service == allowed) continue; #endif + services = g_slist_prepend(services, service); } @@ -7465,7 +7312,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, @@ -7536,7 +7383,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); @@ -7607,15 +7454,6 @@ static int service_indicate_state(struct connman_service *service) reply_pending(service, ECONNABORTED); - def_service = __connman_service_get_default(); - service->disconnect_reason = connman_network_get_disconnect_reason(service->network); - service->assoc_status_code = connman_network_get_assoc_status_code(service->network); - - if (!__connman_notifier_is_connected() && - def_service && - def_service->provider) - connman_provider_disconnect(def_service->provider); - default_changed(); __connman_wispr_stop(service); @@ -7624,9 +7462,9 @@ static int service_indicate_state(struct connman_service *service) #if defined TIZEN_EXT /** - * Skip the functions if there is any connected profiles - * that use same interface - */ + * Skip the functions if there is any connected profiles + * that use same interface + */ if (service->type != CONNMAN_SERVICE_TYPE_CELLULAR || __connman_service_get_connected_count_of_iface( service) <= 0) { @@ -7653,15 +7491,14 @@ static int service_indicate_state(struct connman_service *service) service->order = 5; __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO); #endif - 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; } @@ -7689,20 +7526,6 @@ static int service_indicate_state(struct connman_service *service) default_changed(); } - if (service->type == CONNMAN_SERVICE_TYPE_WIFI && - service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER && - (new_state == CONNMAN_SERVICE_STATE_READY || - new_state == CONNMAN_SERVICE_STATE_ONLINE)) { - if (service->user.favorite_user != service->user.current_user) { - DBG("Now set service favorite user id from %d to %d", - service->user.favorite_user, service->user.current_user); - - service->user.favorite_user = service->user.current_user; - - service_save(service); - } - } - return 0; } @@ -7835,7 +7658,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); } #if defined TIZEN_EXT @@ -7900,18 +7723,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; } @@ -7932,40 +7770,42 @@ static gboolean redo_wispr_ipv4(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; -#if defined TIZEN_MAINTAIN_ONLINE - /* Retry IPv4 stuff also */ if (type == CONNMAN_IPCONFIG_TYPE_IPV4) { - connman_warn("Online check failed for %p %s", service, - service->name); - - g_timeout_add_seconds(1, redo_wispr_ipv4, service); - return 0; - } -#else - /* 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; + interval = &service->online_check_interval_ipv4; + redo_func = redo_wispr_ipv4; + } else { + interval = &service->online_check_interval_ipv6; + redo_func = redo_wispr_ipv6; } -#endif - 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) @@ -8060,24 +7900,24 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service, break; } #endif - if (connman_setting_get_bool("EnableOnlineCheck")) { + if (connman_setting_get_bool("EnableOnlineCheck")) if (type == CONNMAN_IPCONFIG_TYPE_IPV4) { #if !defined TIZEN_EXT check_proxy_setup(service); #endif #if defined TIZEN_MAINTAIN_ONLINE -/* if (old_state == CONNMAN_SERVICE_STATE_ONLINE) */ - check_proxy_setup(service); +/* if (old_state == CONNMAN_SERVICE_STATE_ONLINE) */ + check_proxy_setup(service); #endif } else { - service->online_check_count = 1; - __connman_wispr_start(service, type); + __connman_service_wispr_start(service, type); } - } else + 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; @@ -8097,8 +7937,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; @@ -8290,11 +8132,7 @@ static int service_connect(struct connman_service *service) if (service->error == CONNMAN_SERVICE_ERROR_INVALID_KEY) return -ENOKEY; - if (service->request_passphrase_input) { - DBG("Now try to connect other user's favorite service"); - service->request_passphrase_input = false; - return -ENOKEY; - } else if (!service->passphrase) { + if (!service->passphrase) { if (!service->network) return -EOPNOTSUPP; @@ -8305,8 +8143,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; + } #if defined TIZEN_EXT /* @@ -8329,6 +8174,7 @@ static int service_connect(struct connman_service *service) */ if (g_str_equal(service->eap, "tls")) break; + #endif /* * Return -ENOKEY if either identity or passphrase is @@ -8445,6 +8291,7 @@ int __connman_service_connect(struct connman_service *service, #if defined TIZEN_EXT connect_reason_changed(service); #endif + if (err >= 0) return 0; @@ -8487,7 +8334,6 @@ int __connman_service_connect(struct connman_service *service, return err; } - reply_pending(service, -err); } return err; @@ -8504,7 +8350,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); @@ -8546,8 +8392,6 @@ int __connman_service_disconnect(struct connman_service *service) } #endif - __connman_stats_service_unregister(service); - return err; } @@ -8594,7 +8438,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 { @@ -8993,14 +8837,9 @@ struct connman_service *__connman_service_lookup_from_index(int index) return NULL; } -struct connman_service *__connman_service_lookup_from_ident(const char *identifier) +const char *connman_service_get_identifier(struct connman_service *service) { - return lookup_by_identifier(identifier); -} - -const char *__connman_service_get_ident(struct connman_service *service) -{ - return service->identifier; + return service ? service->identifier : NULL; } const char *__connman_service_get_path(struct connman_service *service) @@ -9013,65 +8852,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) -{ - return service->state; -} - -unsigned int __connman_service_get_order(struct connman_service *service) +enum connman_service_state connman_service_get_state(struct connman_service *service) { - unsigned int order = 0; - - if (!service) - return 0; - - service->order = 0; - - if (!service->favorite) - return 0; - -#if defined TIZEN_EXT - if (service->type == CONNMAN_SERVICE_TYPE_VPN && - service->do_split_routing == FALSE) - order = 10; - else if (service->type == CONNMAN_SERVICE_TYPE_WIFI) { -#if defined TIZEN_MAINTAIN_ONLINE - if (service->state != CONNMAN_SERVICE_STATE_ONLINE) - service->order = 0; - else if (service->order < 5) - service->order = 5; -#else - if (service->order < 5) - order = 5; -#endif - } else if (service->type == CONNMAN_SERVICE_TYPE_ETHERNET) - order = 4; - else if (service->type == CONNMAN_SERVICE_TYPE_BLUETOOTH) - order = 3; - else if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR && - __connman_service_is_internet_profile(service) == TRUE) - order = 1; - else if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR && - __connman_service_is_tethering_profile(service) == TRUE) - order = 0; - else if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR) - order = 0; - else - order = 2; -#else - if (service == service_list->data) - order = 1; - - if (service->type == CONNMAN_SERVICE_TYPE_VPN && - !service->do_split_routing) { - service->order = 10; - order = 10; - } -#endif - 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) @@ -9142,6 +8925,21 @@ int check_passphrase_ext(struct connman_network *network, } #endif +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) { @@ -9182,7 +8980,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); @@ -9209,7 +9007,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); @@ -9254,23 +9052,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: -#if defined TIZEN_EXT_WIFI_MESH - case CONNMAN_SERVICE_TYPE_MESH: -#endif - 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; @@ -9292,7 +9080,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: @@ -9400,7 +9189,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) @@ -9481,6 +9270,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; @@ -9644,14 +9434,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 26cbf878..808931a8 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; }; @@ -203,7 +204,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; @@ -211,8 +212,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; } @@ -227,6 +231,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); @@ -241,6 +246,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; } @@ -353,13 +359,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; } @@ -379,13 +389,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) @@ -396,7 +413,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; @@ -423,8 +440,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; @@ -496,6 +516,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); @@ -505,6 +528,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); } @@ -551,6 +575,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) @@ -560,6 +585,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); @@ -930,6 +957,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) @@ -1375,7 +1413,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, @@ -1400,11 +1438,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; @@ -1477,6 +1517,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); @@ -1675,7 +1718,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; } @@ -1770,7 +1813,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, @@ -1799,7 +1842,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)) { @@ -1961,7 +2004,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/src/shared/arp.h b/src/shared/arp.h new file mode 100755 index 00000000..03e2168c --- /dev/null +++ b/src/shared/arp.h @@ -0,0 +1,47 @@ +/* + * + * 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. + * + */ + +#ifndef SHARED_ARP_H +#define SHARED_ARP_H + +#include <stdint.h> + +/* IPv4 Link-Local (RFC 3927), IPv4 Address Conflict Detection (RFC 5227) */ +#define PROBE_WAIT 1 +#define PROBE_NUM 3 +#define PROBE_MIN 1 +#define PROBE_MAX 2 +#define ANNOUNCE_WAIT 2 +#define ANNOUNCE_NUM 2 +#define ANNOUNCE_INTERVAL 2 +#define MAX_CONFLICTS 10 +#define RATE_LIMIT_INTERVAL 60 +#define DEFEND_INTERVAL 10 + +/* 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 arp_socket(int ifindex); + +uint32_t arp_random_ip(void); + +#endif diff --git a/src/stats.c b/src/stats.c index 663bc382..6f7ce208 100755 --- 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/storage.c b/src/storage.c index 50c8e955..6ca600b2 100755 --- a/src/storage.c +++ b/src/storage.c @@ -23,11 +23,11 @@ #include <config.h> #endif -#include <stdio.h> #include <errno.h> #include <unistd.h> #include <sys/stat.h> #include <dirent.h> +#include <stdio.h> #include <connman/storage.h> diff --git a/src/technology.c b/src/technology.c index 2f14d57d..782ce15c 100644 --- a/src/technology.c +++ b/src/technology.c @@ -80,7 +80,6 @@ struct connman_technology { */ char *tethering_ident; char *tethering_passphrase; - bool tethering_hidden; bool enable_persistent; /* Save the tech state */ @@ -122,7 +121,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); @@ -206,9 +205,6 @@ static void technology_save(struct connman_technology *technology) g_key_file_set_boolean(keyfile, identifier, "Tethering", technology->tethering_persistent); - g_key_file_set_boolean(keyfile, identifier, "Hidden", - technology->tethering_hidden); - if (technology->tethering_ident) g_key_file_set_string(keyfile, identifier, "Tethering.Identifier", @@ -225,8 +221,6 @@ done: __connman_storage_save_global(keyfile); g_key_file_free(keyfile); - - return; } static void tethering_changed(struct connman_technology *technology) @@ -282,7 +276,8 @@ static int set_tethering(struct connman_technology *technology, if (!bridge) return -EOPNOTSUPP; - if (technology->type == CONNMAN_SERVICE_TYPE_WIFI && (!ident)) + if (technology->type == CONNMAN_SERVICE_TYPE_WIFI && + (!ident || !passphrase)) return -EINVAL; for (tech_drivers = technology->driver_list; tech_drivers; @@ -375,6 +370,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) { @@ -473,8 +477,6 @@ done: g_free(identifier); g_key_file_free(keyfile); - - return; } bool __connman_technology_get_offlinemode(void) @@ -512,8 +514,6 @@ static void connman_technology_save_offlinemode(void) } g_key_file_free(keyfile); - - return; } static bool connman_technology_load_offlinemode(void) @@ -584,11 +584,6 @@ static void append_properties(DBusMessageIter *iter, DBUS_TYPE_STRING, &technology->tethering_passphrase); - val = technology->tethering_hidden; - connman_dbus_dict_append_basic(&dict, "Hidden", - DBUS_TYPE_BOOLEAN, - &val); - connman_dbus_dict_close(iter, &dict); } @@ -663,7 +658,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) @@ -839,6 +834,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; @@ -977,21 +974,6 @@ static DBusMessage *set_property(DBusConnection *conn, DBG("property %s", name); - if (technology->type == CONNMAN_SERVICE_TYPE_WIFI && technology->connected) { - uid_t uid; - if (connman_dbus_get_connection_unix_user_sync(conn, - dbus_message_get_sender(msg), - &uid) < 0) { - DBG("Can not get unix user id!"); - return __connman_error_permission_denied(msg); - } - - if (!__connman_service_is_user_allowed(CONNMAN_SERVICE_TYPE_WIFI, uid)) { - DBG("Not allow this user to operate wifi technology now!"); - return __connman_error_permission_denied(msg); - } - } - if (g_str_equal(name, "Tethering")) { dbus_bool_t tethering; int err; @@ -1068,25 +1050,6 @@ static DBusMessage *set_property(DBusConnection *conn, DBUS_TYPE_STRING, &technology->tethering_passphrase); } - } else if (g_str_equal(name, "Hidden")) { - dbus_bool_t hidden; - - if (type != DBUS_TYPE_BOOLEAN) - return __connman_error_invalid_arguments(msg); - - dbus_message_iter_get_basic(&value, &hidden); - - if (technology->type != CONNMAN_SERVICE_TYPE_WIFI) - return __connman_error_not_supported(msg); - - technology->tethering_hidden = hidden; - technology_save(technology); - - connman_dbus_property_changed_basic(technology->path, - CONNMAN_TECHNOLOGY_INTERFACE, - "Hidden", - DBUS_TYPE_BOOLEAN, - &hidden); } else if (g_str_equal(name, "Powered")) { dbus_bool_t enable; @@ -1191,10 +1154,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; } @@ -1280,12 +1240,11 @@ static DBusMessage *scan(DBusConnection *conn, DBusMessage *msg, void *data) g_slist_prepend(technology->scan_pending, msg); #endif - err = __connman_device_request_scan(technology->type); -#if defined TIZEN_EXT + err = __connman_device_request_scan_full(technology->type); if (err < 0) +#if defined TIZEN_EXT return __connman_error_failed(msg, -err); #else - if (err < 0) reply_scan_pending(technology, err); #endif @@ -1463,7 +1422,8 @@ static DBusMessage *get_scan_state(DBusConnection *conn, DBusMessage *msg, void for (list = technology->device_list; list; list = list->next) { struct connman_device *device = list->data; - scanning = connman_device_get_scanning(device); + scanning = connman_device_get_scanning(device, + connman_device_get_type(device)); if(scanning) break; } @@ -2012,16 +1972,6 @@ static const GDBusMethodTable technology_methods[] = { static const GDBusSignalTable technology_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, - { GDBUS_SIGNAL("DhcpConnected", - GDBUS_ARGS({ "aptype", "s" }, - { "ipaddr", "s" }, - { "macaddr", "s" }, - { "hostname", "s" })) }, - { GDBUS_SIGNAL("DhcpLeaseDeleted", - GDBUS_ARGS({ "aptype", "s" }, - { "ipaddr", "s" }, - { "macaddr", "s" }, - { "hostname", "s" })) }, { }, }; @@ -2148,7 +2098,6 @@ static struct connman_technology *technology_get(enum connman_service_type type) technology->refcount = 1; technology->type = type; - technology->tethering_hidden = FALSE; technology->path = g_strdup_printf("%s/technology/%s", CONNMAN_PATH, str); @@ -2428,7 +2377,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. */ @@ -2484,6 +2433,7 @@ int __connman_technology_enabled(enum connman_service_type type) DBG("technology %p type %s rfkill %d enabled %d", technology, get_name(type), technology->rfkill_driven, technology->enabled); + #if !defined TIZEN_EXT if (technology->rfkill_driven) { if (technology->tethering_persistent) @@ -2503,9 +2453,11 @@ int __connman_technology_disabled(enum connman_service_type type) technology = technology_find(type); if (!technology) return -ENXIO; + #if !defined TIZEN_EXT if (technology->rfkill_driven) return 0; + #endif for (list = technology->device_list; list; list = list->next) { struct connman_device *device = list->data; @@ -2533,7 +2485,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; @@ -2716,6 +2668,7 @@ done: softblock, hardblock, true)) return 0; #endif + if (global_offlinemode) return 0; diff --git a/src/tethering.c b/src/tethering.c index 891ee51f..e04756ff 100755 --- 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" @@ -52,9 +52,6 @@ #define DEFAULT_MTU 1500 -#define CONNMAN_STATION_STR_INFO_LEN 64 -#define CONNMAN_STATION_MAC_INFO_LEN 32 - static char *private_network_primary_dns = NULL; static char *private_network_secondary_dns = NULL; @@ -63,7 +60,13 @@ static GDHCPServer *tethering_dhcp_server = NULL; static struct connman_ippool *dhcp_ippool = NULL; static DBusConnection *connection; static GHashTable *pn_hash; -static GHashTable *sta_hash; + +static GHashTable *clients_table; + +struct _clients_notify { + int id; + GHashTable *remove; +} *clients_notify; struct connman_private_network { char *owner; @@ -80,164 +83,6 @@ struct connman_private_network { char *secondary_dns; }; -struct connman_station_info { - bool is_connected; - char *path; - char *type; - char ip[CONNMAN_STATION_STR_INFO_LEN]; - char mac[CONNMAN_STATION_MAC_INFO_LEN]; - char hostname[CONNMAN_STATION_STR_INFO_LEN]; -}; - -static void emit_station_signal(char *action_str, - const struct connman_station_info *station_info) -{ - char *ip, *mac, *hostname; - - if (station_info->path == NULL || station_info->type == NULL - || station_info->ip == NULL || station_info->mac == NULL - || station_info->hostname == NULL) - return; - - ip = g_strdup(station_info->ip); - mac = g_strdup(station_info->mac); - hostname = g_strdup(station_info->hostname); - - g_dbus_emit_signal(connection, station_info->path, - CONNMAN_TECHNOLOGY_INTERFACE, action_str, - DBUS_TYPE_STRING, &station_info->type, - DBUS_TYPE_STRING, &ip, - DBUS_TYPE_STRING, &mac, - DBUS_TYPE_STRING, &hostname, - DBUS_TYPE_INVALID); - - g_free(ip); - g_free(mac); - g_free(hostname); -} -static void destroy_station(gpointer key, gpointer value, gpointer user_data) -{ - struct connman_station_info *station_info; - - __sync_synchronize(); - - station_info = value; - - if (station_info->is_connected) { - station_info->is_connected = FALSE; - emit_station_signal("DhcpLeaseDeleted", station_info); - } - - g_free(station_info->path); - g_free(station_info->type); - g_free(station_info); -} - -static void save_dhcp_ack_lease_info(char *hostname, - unsigned char *mac, unsigned int nip) -{ - char *lower_mac; - const char *ip; - char sta_mac[CONNMAN_STATION_MAC_INFO_LEN]; - struct connman_station_info *info_found; - struct in_addr addr; - int str_len; - - __sync_synchronize(); - - snprintf(sta_mac, CONNMAN_STATION_MAC_INFO_LEN, - "%02x:%02x:%02x:%02x:%02x:%02x", - mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - lower_mac = g_ascii_strdown(sta_mac, -1); - - info_found = g_hash_table_lookup(sta_hash, lower_mac); - if (info_found == NULL) { - g_free(lower_mac); - return; - } - - /* get the ip */ - addr.s_addr = nip; - ip = inet_ntoa(addr); - str_len = strlen(ip) + 1; - if (str_len > CONNMAN_STATION_STR_INFO_LEN) - str_len = CONNMAN_STATION_STR_INFO_LEN - 1; - memcpy(info_found->ip, ip, str_len); - - /* get hostname */ - str_len = strlen(hostname) + 1; - if (str_len > CONNMAN_STATION_STR_INFO_LEN) - str_len = CONNMAN_STATION_STR_INFO_LEN - 1; - memcpy(info_found->hostname, hostname, str_len); - - /* emit a signal */ - info_found->is_connected = TRUE; - emit_station_signal("DhcpConnected", info_found); - g_free(lower_mac); -} - -int connman_technology_tethering_add_station(enum connman_service_type type, - const char *mac) -{ - const char *str_type; - char *lower_mac; - char *path; - struct connman_station_info *station_info; - - __sync_synchronize(); - - DBG("type %d", type); - - str_type = __connman_service_type2string(type); - if (str_type == NULL) - return 0; - - path = g_strdup_printf("%s/technology/%s", CONNMAN_PATH, str_type); - - station_info = g_try_new0(struct connman_station_info, 1); - if (station_info == NULL) - return -ENOMEM; - - lower_mac = g_ascii_strdown(mac, -1); - - memcpy(station_info->mac, lower_mac, strlen(lower_mac) + 1); - station_info->path = path; - station_info->type = g_strdup(str_type); - - g_hash_table_insert(sta_hash, station_info->mac, station_info); - - g_free(lower_mac); - return 0; -} - -int connman_technology_tethering_remove_station(const char *mac) -{ - char *lower_mac; - struct connman_station_info *info_found; - - __sync_synchronize(); - - lower_mac = g_ascii_strdown(mac, -1); - - info_found = g_hash_table_lookup(sta_hash, lower_mac); - if (info_found == NULL) { - g_free(lower_mac); - return -EACCES; - } - - if (info_found->is_connected) { - info_found->is_connected = FALSE; - emit_station_signal("DhcpLeaseDeleted", info_found); - } - g_free(lower_mac); - g_hash_table_remove(sta_hash, info_found->mac); - g_free(info_found->path); - g_free(info_found->type); - g_free(info_found); - - return 0; -} - const char *__connman_tethering_get_bridge(void) { int sk, err; @@ -323,9 +168,6 @@ static GDHCPServer *dhcp_server_start(const char *bridge, g_dhcp_server_set_option(dhcp_server, G_DHCP_DNS_SERVER, dns); g_dhcp_server_set_ip_range(dhcp_server, start_ip, end_ip); - g_dhcp_server_set_save_ack_lease(dhcp_server, - save_dhcp_ack_lease_info, NULL); - g_dhcp_server_start(dhcp_server); return dhcp_server; @@ -346,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; @@ -390,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; @@ -426,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; @@ -438,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; @@ -463,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); @@ -476,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); @@ -488,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) { @@ -564,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); @@ -601,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; @@ -690,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(""); @@ -703,9 +654,12 @@ int __connman_tethering_init(void) pn_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, remove_private_network); - sta_hash = g_hash_table_new_full(g_str_hash, - g_str_equal, NULL, NULL); + 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; } @@ -726,7 +680,13 @@ void __connman_tethering_cleanup(void) return; g_hash_table_destroy(pn_hash); - g_hash_table_foreach(sta_hash, destroy_station, NULL); - g_hash_table_destroy(sta_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 e4cfc062..c6103466 100755 --- 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) @@ -101,35 +113,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); @@ -138,34 +175,30 @@ 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); - - /* if it's an IP, directly query it. */ - if (connman_inet_check_ipaddress(ts_current) > 0) { - DBG("Using timeserver %s", ts_current); + while (ts_list) { + ts_current = ts_list->data; + ts_list = g_slist_delete_link(ts_list, ts_list); - __connman_ntp_start(ts_current); - - return; - } - - DBG("Resolving timeserver %s", 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); #if defined TIZEN_EXT - resolv_id = g_resolv_lookup_hostname(resolv, ts_current, + resolv_id = g_resolv_lookup_hostname(resolv, ts_current, resolv_result, ts_current); #else - resolv_id = g_resolv_lookup_hostname(resolv, ts_current, + resolv_id = g_resolv_lookup_hostname(resolv, ts_current, resolv_result, NULL); #endif + return; + } - 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, @@ -218,22 +251,22 @@ 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]); -#if defined TIZEN_EXT - if (connman_setting_get_bool("UseGatewayTimeserver")) { -#endif - 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); - } -#if defined TIZEN_EXT + /* + * 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); + } } -#endif + /* Then add Global Timeservers to the list */ timeservers = load_timeservers(); @@ -255,7 +288,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"); @@ -288,6 +321,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; @@ -316,11 +354,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; /* @@ -346,20 +387,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; } @@ -376,8 +418,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) @@ -398,10 +438,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); } @@ -410,13 +452,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(); @@ -449,7 +495,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 100755 --- 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> @@ -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 f5fe36b4..57abdf1e 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, @@ -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/enable-tethering b/test/enable-tethering index 674b3c91..cbcd4e72 100755 --- a/test/enable-tethering +++ b/test/enable-tethering @@ -3,17 +3,8 @@ import sys import dbus -if (len(sys.argv) >= 2 and len(sys.argv) < 4 and sys.argv[1] == "wifi"): - print "Usage: %s wifi [SSID] [passphrase] [hidden]" % (sys.argv[0]) - print "Example:" - print "Create the open system access point:" - print "%s wifi abcd \"\"" % (sys.argv[0]) - print "Create the security access point:" - print "%s wifi abcd 123456789" % (sys.argv[0]) - print "Create the hidden access point:" - print "%s wifi abcd 123456789 hidden" % (sys.argv[0]) - print "Create the open and hidden access point:" - print "%s wifi abcd \"\" hidden" % (sys.argv[0]) +if (len(sys.argv) >= 3 and len(sys.argv) != 4 and sys.argv[1] == "wifi"): + print "Usage: %s wifi [SSID] [passphrase]" % (sys.argv[0]) sys.exit(1) elif (len(sys.argv) < 2): print "Usage: %s type" % (sys.argv[0]) @@ -24,7 +15,7 @@ bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('net.connman', "/"), 'net.connman.Manager') -def technology_enable_tethering(path, tech_type, ssid, psk, hidden): +def technology_enable_tethering(path, tech_type, ssid, psk): tech = dbus.Interface(bus.get_object("net.connman", path), "net.connman.Technology") @@ -36,17 +27,9 @@ def technology_enable_tethering(path, tech_type, ssid, psk, hidden): if len(ssid) > 0: tech.SetProperty("TetheringIdentifier", ssid) + if len(psk) > 0: tech.SetProperty("TetheringPassphrase", psk) - if tech_type == "wifi": - if len(hidden) > 0: - if hidden == "hidden": - tech.SetProperty("Hidden",dbus.Boolean(1)) - else: - tech.SetProperty("Hidden",dbus.Boolean(0)) - else: - tech.SetProperty("Hidden",dbus.Boolean(0)) - print "Enabling %s tethering" % tech_type tech.SetProperty("Tethering", dbus.Boolean(1)) @@ -58,14 +41,11 @@ technologies = manager.GetTechnologies() tech = None for path,_ in technologies: - if (len(sys.argv) == 5): - tech = technology_enable_tethering(path, - sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]) - elif (len(sys.argv) == 4): + if (len(sys.argv) == 4): tech = technology_enable_tethering(path, - sys.argv[1], sys.argv[2], sys.argv[3], "") + sys.argv[1], sys.argv[2], sys.argv[3]) else: - tech = technology_enable_tethering(path, sys.argv[1], "", "", "") + tech = technology_enable_tethering(path, sys.argv[1], "", "") if tech != None: break; 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 100755 --- 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 100755 --- 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 100755 --- 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 100755 --- 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 100755 --- 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 100755 --- 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 100755 --- 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 100755 --- 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 100755 --- 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 100755 --- 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 100755 --- 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 100755 --- 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 100755 --- 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 100755 --- 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/connman-vpn.service.in b/vpn/connman-vpn.service.in index 9399eb96..a8f2948f 100755 --- a/vpn/connman-vpn.service.in +++ b/vpn/connman-vpn.service.in @@ -1,5 +1,7 @@ [Unit] Description=ConnMan VPN service +Requires=dbus.socket +After=dbus.socket [Service] Type=dbus diff --git a/vpn/plugins/l2tp.c b/vpn/plugins/l2tp.c index a0d22c4d..5d83eb88 100755 --- 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 100755 --- 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 d115df6e..6b090e45 100755 --- a/vpn/plugins/openvpn.c +++ b/vpn/plugins/openvpn.c @@ -472,11 +472,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 100755 --- 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 f6e24c4c..d9c6dbbb 100755 --- 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; @@ -423,6 +430,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"); @@ -609,10 +617,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); @@ -624,7 +637,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) @@ -641,6 +667,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) { @@ -662,6 +708,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 1888d5ff..318a10c5 100755 --- a/vpn/plugins/vpn.h +++ b/vpn/plugins/vpn.h @@ -55,9 +55,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-dbus.conf b/vpn/vpn-dbus.conf index 476be1c1..8ebfc6b6 100755 --- a/vpn/vpn-dbus.conf +++ b/vpn/vpn-dbus.conf @@ -4,13 +4,19 @@ <policy user="root"> <allow own="net.connman.vpn"/> <allow send_destination="net.connman.vpn"/> + <allow send_interface="net.connman.vpn.Agent"/> </policy> <policy user="network_fw"> <allow own="net.connman.vpn"/> <allow send_destination="net.connman.vpn"/> + <allow send_interface="net.connman.vpn.Agent"/> + </policy> + <policy at_console="true"> + <allow send_destination="net.connman.vpn"/> </policy> <policy context="default"> <deny own="net.connman.vpn"/> <deny send_destination="net.connman.vpn"/> + <deny send_interface="net.connman.vpn.Agent"/> </policy> </busconfig> diff --git a/vpn/vpn-provider.c b/vpn/vpn-provider.c index 67239e40..bb1a103a 100755 --- 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; @@ -2446,61 +2447,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; } @@ -2511,7 +2469,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); @@ -2536,15 +2494,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 100755 --- 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 100755 --- 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, |