summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS6
-rw-r--r--ChangeLog23
-rw-r--r--Makefile.am10
-rw-r--r--Makefile.in71
-rw-r--r--Makefile.plugins7
-rw-r--r--README42
-rw-r--r--client/agent.c196
-rw-r--r--client/commands.c322
-rw-r--r--client/dbus_helpers.c5
-rwxr-xr-xconfigure20
-rw-r--r--configure.ac2
-rw-r--r--doc/agent-api.txt49
-rw-r--r--doc/config-format.txt4
-rw-r--r--doc/manager-api.txt38
-rw-r--r--doc/session-overview.txt2
-rw-r--r--doc/session-policy-format.txt8
-rw-r--r--gdbus/client.c14
-rw-r--r--gdbus/mainloop.c10
-rw-r--r--gdbus/object.c8
-rw-r--r--gdbus/watch.c15
-rw-r--r--gdhcp/client.c76
-rw-r--r--gdhcp/common.c8
-rw-r--r--gdhcp/common.h3
-rw-r--r--gdhcp/server.c4
-rw-r--r--gsupplicant/dbus.c147
-rw-r--r--gsupplicant/dbus.h12
-rw-r--r--gsupplicant/gsupplicant.h86
-rw-r--r--gsupplicant/supplicant.c1239
-rw-r--r--include/agent.h4
-rw-r--r--include/dbus.h3
-rw-r--r--include/inet.h4
-rw-r--r--include/inotify.h2
-rw-r--r--include/ipaddress.h4
-rw-r--r--include/machine.h35
-rw-r--r--include/network.h1
-rw-r--r--include/peer.h75
-rw-r--r--include/session.h2
-rw-r--r--plugins/dundee.c2
-rw-r--r--plugins/ofono.c2
-rw-r--r--plugins/session_policy_local.c2
-rw-r--r--plugins/vpn.c2
-rw-r--r--plugins/wifi.c784
-rw-r--r--scripts/openvpn-script.c6
-rw-r--r--src/agent-connman.c173
-rw-r--r--src/agent.c21
-rw-r--r--src/bridge.c2
-rw-r--r--src/config.c146
-rw-r--r--src/connection.c2
-rw-r--r--src/connman.h54
-rw-r--r--src/dbus.c32
-rw-r--r--src/device.c6
-rw-r--r--src/dhcp.c413
-rw-r--r--src/dhcpv6.c197
-rw-r--r--src/dnsproxy.c124
-rw-r--r--src/inet.c63
-rw-r--r--src/inotify.c2
-rw-r--r--src/ipaddress.c84
-rw-r--r--src/ipconfig.c39
-rw-r--r--src/ippool.c2
-rw-r--r--src/machine.c125
-rw-r--r--src/main.c6
-rw-r--r--src/manager.c127
-rw-r--r--src/nat.c2
-rw-r--r--src/net.connman.service.in2
-rw-r--r--src/network.c60
-rw-r--r--src/peer.c913
-rw-r--r--src/peer_service.c429
-rw-r--r--src/service.c417
-rw-r--r--src/session.c2
-rw-r--r--src/shared/netlink.c2
-rw-r--r--src/stats.c2
-rw-r--r--src/tethering.c7
-rw-r--r--src/timeserver.c10
-rw-r--r--src/wispr.c10
-rwxr-xr-xtest/p2p-on-supplicant33
-rw-r--r--tools/iptables-unit.c2
-rw-r--r--tools/manager-api.c2
-rw-r--r--tools/netlink-test.c2
-rw-r--r--tools/session-api.c2
-rw-r--r--tools/session-test.c2
-rw-r--r--tools/session-utils.c2
-rw-r--r--tools/stats-tool.c2
-rw-r--r--unit/test-ippool.c2
-rw-r--r--vpn/plugins/l2tp.c2
-rw-r--r--vpn/plugins/openconnect.c1
-rw-r--r--vpn/plugins/openvpn.c2
-rw-r--r--vpn/plugins/pptp.c2
-rw-r--r--vpn/plugins/vpn.c29
-rw-r--r--vpn/plugins/vpn.h2
-rw-r--r--vpn/plugins/vpnc.c2
-rw-r--r--vpn/vpn-ipconfig.c24
-rw-r--r--vpn/vpn-provider.c117
-rw-r--r--vpn/vpn-provider.h2
-rw-r--r--vpn/vpn.h2
94 files changed, 5859 insertions, 1204 deletions
diff --git a/AUTHORS b/AUTHORS
index df0b8f1e..aad802d5 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -74,3 +74,9 @@ Maneesh Jain <maneesh.jain@samsung.com>
Eduardo Abinader <eduardo.abinader@openbossa.org>
Guoqiang Liu <guoqiang.liu@archermind.com>
Eric Bouxirot <eric.bouxirot@azimut-monitoring.com>
+Alexandru Costache <alexandrux.costache@intel.com>
+Pasi Sjöholm <pasi.sjoholm@jolla.com>
+Mario Schuknecht <mario.schuknecht@dresearch-fe.de>
+Slava Monich <slava.monich@jolla.com>
+Aaron McCarthy <aaron.mccarthy@jolla.com>
+Saurav Babu <saurav.babu@samsung.com>
diff --git a/ChangeLog b/ChangeLog
index d622a84c..6e5c6893 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+ver 1.26:
+ Fix issue with missing WiFi security provisioning support.
+ Fix issue with immutable setting and provisioned services.
+ Fix issue with scheduling DNS cache cleanup procedure.
+ Fix issue with IPv6 Privacy setting on service removal.
+ Fix issue with DHCPv6 CONFIRM message sending procedure.
+ Fix issue with DHCPv6 lease expiration handling support.
+ Fix issue with DHCPv4 networks and broadcast flag handling.
+ Fix issue with DHCPv4 networks without gateway configuration.
+ Fix issue with P2P Peer authorization handling.
+ Fix issue with P2P Peer service registration.
+ Add support for WiFi Display information elements.
+ Add support for systemd-hostnamed integration.
+
+ver 1.25:
+ Fix issue with handling rebind timer for DHCPv6.
+ Fix issue with handling DHCP renew transaction.
+ Fix issue with user supplied proxy settings and DHCP.
+ Fix issue with extra status codes from captive portals.
+ Fix issue with service idle state reset on failure.
+ Fix issue with DNS label compression handling.
+ Add support for experimental P2P Peer service.
+
ver 1.24:
Fix issue with handling slave interfaces.
Fix issue with handling DHCPv4 broadcast flag.
diff --git a/Makefile.am b/Makefile.am
index 53bf799e..a5741701 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -11,7 +11,7 @@ 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/inotify.h include/peer.h include/machine.h
nodist_include_HEADERS = include/version.h
@@ -105,7 +105,8 @@ src_connmand_SOURCES = $(gdhcp_sources) $(gweb_sources) \
src/session.c src/tethering.c src/wpad.c src/wispr.c \
src/stats.c src/iptables.c src/dnsproxy.c src/6to4.c \
src/ippool.c src/bridge.c src/nat.c src/ipaddress.c \
- src/inotify.c src/firewall.c src/ipv6pd.c src/peer.c
+ src/inotify.c src/firewall.c src/ipv6pd.c src/peer.c \
+ src/peer_service.c src/machine.c
src_connmand_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \
@GLIB_LIBS@ @DBUS_LIBS@ @XTABLES_LIBS@ @GNUTLS_LIBS@ \
@@ -263,7 +264,8 @@ unit_test_prf_sha1_SOURCES = unit/test-prf-sha1.c \
src/shared/sha1.h src/shared/sha1.c
unit_test_prf_sha1_LDADD = @GLIB_LIBS@
-unit_test_ippool_SOURCES = src/log.c src/dbus.c src/ippool.c unit/test-ippool.c
+unit_test_ippool_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
@@ -319,7 +321,7 @@ tools_iptables_test_LDADD = @GLIB_LIBS@ @XTABLES_LIBS@ -ldl
tools_private_network_test_LDADD = @GLIB_LIBS@ @DBUS_LIBS@
-tools_session_test_SOURCES = src/log.c src/dbus.c \
+tools_session_test_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 \
diff --git a/Makefile.in b/Makefile.in
index f65924d1..3fe3485d 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -409,7 +409,7 @@ am__src_connmand_SOURCES_DIST = gdhcp/gdhcp.h gdhcp/common.h \
src/wpad.c src/wispr.c src/stats.c src/iptables.c \
src/dnsproxy.c src/6to4.c src/ippool.c src/bridge.c src/nat.c \
src/ipaddress.c src/inotify.c src/firewall.c src/ipv6pd.c \
- src/peer.c
+ src/peer.c src/peer_service.c src/machine.c
am__objects_1 = gdhcp/src_connmand-common.$(OBJEXT) \
gdhcp/src_connmand-client.$(OBJEXT) \
gdhcp/src_connmand-server.$(OBJEXT) \
@@ -498,7 +498,9 @@ am_src_connmand_OBJECTS = $(am__objects_1) $(am__objects_4) \
src/src_connmand-inotify.$(OBJEXT) \
src/src_connmand-firewall.$(OBJEXT) \
src/src_connmand-ipv6pd.$(OBJEXT) \
- src/src_connmand-peer.$(OBJEXT)
+ src/src_connmand-peer.$(OBJEXT) \
+ src/src_connmand-peer_service.$(OBJEXT) \
+ src/src_connmand-machine.$(OBJEXT)
src_connmand_OBJECTS = $(am_src_connmand_OBJECTS)
am__DEPENDENCIES_1 =
src_connmand_DEPENDENCIES = gdbus/libgdbus-internal.la \
@@ -578,11 +580,12 @@ am__tools_resolv_test_SOURCES_DIST = gweb/gresolv.h gweb/gresolv.c \
@TOOLS_TRUE@ tools/resolv-test.$(OBJEXT)
tools_resolv_test_OBJECTS = $(am_tools_resolv_test_OBJECTS)
tools_resolv_test_DEPENDENCIES =
-am__tools_session_test_SOURCES_DIST = src/log.c src/dbus.c \
+am__tools_session_test_SOURCES_DIST = 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_TRUE@am_tools_session_test_OBJECTS = src/log.$(OBJEXT) \
-@TOOLS_TRUE@ src/dbus.$(OBJEXT) tools/session-test.$(OBJEXT) \
+@TOOLS_TRUE@ src/dbus.$(OBJEXT) src/error.$(OBJEXT) \
+@TOOLS_TRUE@ tools/session-test.$(OBJEXT) \
@TOOLS_TRUE@ tools/session-utils.$(OBJEXT) \
@TOOLS_TRUE@ tools/manager-api.$(OBJEXT) \
@TOOLS_TRUE@ tools/session-api.$(OBJEXT)
@@ -630,7 +633,8 @@ am__tools_wpad_test_SOURCES_DIST = gweb/gresolv.h gweb/gresolv.c \
tools_wpad_test_OBJECTS = $(am_tools_wpad_test_OBJECTS)
tools_wpad_test_DEPENDENCIES =
am_unit_test_ippool_OBJECTS = src/log.$(OBJEXT) src/dbus.$(OBJEXT) \
- src/ippool.$(OBJEXT) unit/test-ippool.$(OBJEXT)
+ src/error.$(OBJEXT) src/ippool.$(OBJEXT) \
+ unit/test-ippool.$(OBJEXT)
unit_test_ippool_OBJECTS = $(am_unit_test_ippool_OBJECTS)
unit_test_ippool_DEPENDENCIES = gdbus/libgdbus-internal.la
am_unit_test_pbkdf2_sha1_OBJECTS = unit/test-pbkdf2-sha1.$(OBJEXT) \
@@ -991,7 +995,7 @@ 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/inotify.h include/peer.h include/machine.h
nodist_include_HEADERS = include/version.h
noinst_HEADERS = include/rtnl.h include/task.h \
@@ -1058,7 +1062,8 @@ src_connmand_SOURCES = $(gdhcp_sources) $(gweb_sources) \
src/session.c src/tethering.c src/wpad.c src/wispr.c \
src/stats.c src/iptables.c src/dnsproxy.c src/6to4.c \
src/ippool.c src/bridge.c src/nat.c src/ipaddress.c \
- src/inotify.c src/firewall.c src/ipv6pd.c src/peer.c
+ src/inotify.c src/firewall.c src/ipv6pd.c src/peer.c \
+ src/peer_service.c src/machine.c
src_connmand_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \
@GLIB_LIBS@ @DBUS_LIBS@ @XTABLES_LIBS@ @GNUTLS_LIBS@ \
@@ -1166,9 +1171,6 @@ plugin_cflags = -fvisibility=hidden -I$(srcdir)/gdbus \
@DBUS_CFLAGS@ @GLIB_CFLAGS@
plugin_ldflags = -no-undefined -module -avoid-version
-script_cflags = -fvisibility=hidden -I$(srcdir)/gdbus \
- @DBUS_CFLAGS@
-
gsupplicant_sources = gsupplicant/gsupplicant.h gsupplicant/dbus.h \
gsupplicant/supplicant.c gsupplicant/dbus.c
@@ -1221,8 +1223,8 @@ gsupplicant_sources = gsupplicant/gsupplicant.h gsupplicant/dbus.h \
@PPTP_BUILTIN_FALSE@@PPTP_TRUE@@VPN_TRUE@ -DSCRIPTDIR=\""$(build_scriptdir)"\"
@PPTP_BUILTIN_FALSE@@PPTP_TRUE@@VPN_TRUE@vpn_plugins_pptp_la_LDFLAGS = $(plugin_ldflags)
-@L2TP_TRUE@@PPTP_FALSE@@VPN_TRUE@scripts_libppp_plugin_la_LDFLAGS = $(script_cflags) @DBUS_CFLAGS@
-@PPTP_TRUE@@VPN_TRUE@scripts_libppp_plugin_la_LDFLAGS = $(script_cflags) @DBUS_CFLAGS@
+@L2TP_TRUE@@PPTP_FALSE@@VPN_TRUE@scripts_libppp_plugin_la_LDFLAGS = $(plugin_ldflags)
+@PPTP_TRUE@@VPN_TRUE@scripts_libppp_plugin_la_LDFLAGS = $(plugin_ldflags)
@L2TP_TRUE@@PPTP_FALSE@@VPN_TRUE@scripts_libppp_plugin_la_LIBADD = @DBUS_LIBS@
@PPTP_TRUE@@VPN_TRUE@scripts_libppp_plugin_la_LIBADD = @DBUS_LIBS@
@DATAFILES_TRUE@@POLKIT_TRUE@policydir = @POLKIT_DATADIR@
@@ -1260,7 +1262,9 @@ unit_test_prf_sha1_SOURCES = unit/test-prf-sha1.c \
src/shared/sha1.h src/shared/sha1.c
unit_test_prf_sha1_LDADD = @GLIB_LIBS@
-unit_test_ippool_SOURCES = src/log.c src/dbus.c src/ippool.c unit/test-ippool.c
+unit_test_ippool_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
@@ -1290,7 +1294,7 @@ unit_test_ippool_LDADD = gdbus/libgdbus-internal.la \
@TOOLS_TRUE@tools_iptables_test_SOURCES = src/log.c src/iptables.c tools/iptables-test.c
@TOOLS_TRUE@tools_iptables_test_LDADD = @GLIB_LIBS@ @XTABLES_LIBS@ -ldl
@TOOLS_TRUE@tools_private_network_test_LDADD = @GLIB_LIBS@ @DBUS_LIBS@
-@TOOLS_TRUE@tools_session_test_SOURCES = src/log.c src/dbus.c \
+@TOOLS_TRUE@tools_session_test_SOURCES = src/log.c src/dbus.c src/error.c \
@TOOLS_TRUE@ tools/session-test.c tools/session-utils.c tools/manager-api.c \
@TOOLS_TRUE@ tools/session-api.c tools/session-test.h
@@ -1924,6 +1928,10 @@ src/src_connmand-ipv6pd.$(OBJEXT): src/$(am__dirstamp) \
src/$(DEPDIR)/$(am__dirstamp)
src/src_connmand-peer.$(OBJEXT): src/$(am__dirstamp) \
src/$(DEPDIR)/$(am__dirstamp)
+src/src_connmand-peer_service.$(OBJEXT): src/$(am__dirstamp) \
+ src/$(DEPDIR)/$(am__dirstamp)
+src/src_connmand-machine.$(OBJEXT): src/$(am__dirstamp) \
+ src/$(DEPDIR)/$(am__dirstamp)
src/connmand$(EXEEXT): $(src_connmand_OBJECTS) $(src_connmand_DEPENDENCIES) $(EXTRA_src_connmand_DEPENDENCIES) src/$(am__dirstamp)
@rm -f src/connmand$(EXEEXT)
$(AM_V_CCLD)$(src_connmand_LINK) $(src_connmand_OBJECTS) $(src_connmand_LDADD) $(LIBS)
@@ -2014,6 +2022,7 @@ tools/resolv-test$(EXEEXT): $(tools_resolv_test_OBJECTS) $(tools_resolv_test_DEP
@rm -f tools/resolv-test$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(tools_resolv_test_OBJECTS) $(tools_resolv_test_LDADD) $(LIBS)
src/dbus.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/error.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
tools/session-test.$(OBJEXT): tools/$(am__dirstamp) \
tools/$(DEPDIR)/$(am__dirstamp)
tools/session-utils.$(OBJEXT): tools/$(am__dirstamp) \
@@ -2266,6 +2275,7 @@ mostlyclean-compile:
-rm -f scripts/openconnect-script.$(OBJEXT)
-rm -f scripts/openvpn-script.$(OBJEXT)
-rm -f src/dbus.$(OBJEXT)
+ -rm -f src/error.$(OBJEXT)
-rm -f src/ippool.$(OBJEXT)
-rm -f src/iptables.$(OBJEXT)
-rm -f src/log.$(OBJEXT)
@@ -2298,6 +2308,7 @@ mostlyclean-compile:
-rm -f src/src_connmand-iptables.$(OBJEXT)
-rm -f src/src_connmand-ipv6pd.$(OBJEXT)
-rm -f src/src_connmand-log.$(OBJEXT)
+ -rm -f src/src_connmand-machine.$(OBJEXT)
-rm -f src/src_connmand-main.$(OBJEXT)
-rm -f src/src_connmand-manager.$(OBJEXT)
-rm -f src/src_connmand-nat.$(OBJEXT)
@@ -2305,6 +2316,7 @@ mostlyclean-compile:
-rm -f src/src_connmand-notifier.$(OBJEXT)
-rm -f src/src_connmand-ntp.$(OBJEXT)
-rm -f src/src_connmand-peer.$(OBJEXT)
+ -rm -f src/src_connmand-peer_service.$(OBJEXT)
-rm -f src/src_connmand-plugin.$(OBJEXT)
-rm -f src/src_connmand-provider.$(OBJEXT)
-rm -f src/src_connmand-proxy.$(OBJEXT)
@@ -2459,6 +2471,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@scripts/$(DEPDIR)/openconnect-script.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@scripts/$(DEPDIR)/openvpn-script.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/dbus.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/error.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/ippool.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/iptables.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/log.Po@am__quote@
@@ -2486,6 +2499,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/src_connmand-iptables.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/src_connmand-ipv6pd.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/src_connmand-log.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/src_connmand-machine.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/src_connmand-main.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/src_connmand-manager.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/src_connmand-nat.Po@am__quote@
@@ -2493,6 +2507,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/src_connmand-notifier.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/src_connmand-ntp.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/src_connmand-peer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/src_connmand-peer_service.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/src_connmand-plugin.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/src_connmand-provider.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/src_connmand-proxy.Po@am__quote@
@@ -3780,6 +3795,34 @@ src/src_connmand-peer.obj: src/peer.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_connmand_CFLAGS) $(CFLAGS) -c -o src/src_connmand-peer.obj `if test -f 'src/peer.c'; then $(CYGPATH_W) 'src/peer.c'; else $(CYGPATH_W) '$(srcdir)/src/peer.c'; fi`
+src/src_connmand-peer_service.o: src/peer_service.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_connmand_CFLAGS) $(CFLAGS) -MT src/src_connmand-peer_service.o -MD -MP -MF src/$(DEPDIR)/src_connmand-peer_service.Tpo -c -o src/src_connmand-peer_service.o `test -f 'src/peer_service.c' || echo '$(srcdir)/'`src/peer_service.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/src_connmand-peer_service.Tpo src/$(DEPDIR)/src_connmand-peer_service.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/peer_service.c' object='src/src_connmand-peer_service.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_connmand_CFLAGS) $(CFLAGS) -c -o src/src_connmand-peer_service.o `test -f 'src/peer_service.c' || echo '$(srcdir)/'`src/peer_service.c
+
+src/src_connmand-peer_service.obj: src/peer_service.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_connmand_CFLAGS) $(CFLAGS) -MT src/src_connmand-peer_service.obj -MD -MP -MF src/$(DEPDIR)/src_connmand-peer_service.Tpo -c -o src/src_connmand-peer_service.obj `if test -f 'src/peer_service.c'; then $(CYGPATH_W) 'src/peer_service.c'; else $(CYGPATH_W) '$(srcdir)/src/peer_service.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/src_connmand-peer_service.Tpo src/$(DEPDIR)/src_connmand-peer_service.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/peer_service.c' object='src/src_connmand-peer_service.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_connmand_CFLAGS) $(CFLAGS) -c -o src/src_connmand-peer_service.obj `if test -f 'src/peer_service.c'; then $(CYGPATH_W) 'src/peer_service.c'; else $(CYGPATH_W) '$(srcdir)/src/peer_service.c'; fi`
+
+src/src_connmand-machine.o: src/machine.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_connmand_CFLAGS) $(CFLAGS) -MT src/src_connmand-machine.o -MD -MP -MF src/$(DEPDIR)/src_connmand-machine.Tpo -c -o src/src_connmand-machine.o `test -f 'src/machine.c' || echo '$(srcdir)/'`src/machine.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/src_connmand-machine.Tpo src/$(DEPDIR)/src_connmand-machine.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/machine.c' object='src/src_connmand-machine.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_connmand_CFLAGS) $(CFLAGS) -c -o src/src_connmand-machine.o `test -f 'src/machine.c' || echo '$(srcdir)/'`src/machine.c
+
+src/src_connmand-machine.obj: src/machine.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_connmand_CFLAGS) $(CFLAGS) -MT src/src_connmand-machine.obj -MD -MP -MF src/$(DEPDIR)/src_connmand-machine.Tpo -c -o src/src_connmand-machine.obj `if test -f 'src/machine.c'; then $(CYGPATH_W) 'src/machine.c'; else $(CYGPATH_W) '$(srcdir)/src/machine.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/src_connmand-machine.Tpo src/$(DEPDIR)/src_connmand-machine.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/machine.c' object='src/src_connmand-machine.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_connmand_CFLAGS) $(CFLAGS) -c -o src/src_connmand-machine.obj `if test -f 'src/machine.c'; then $(CYGPATH_W) 'src/machine.c'; else $(CYGPATH_W) '$(srcdir)/src/machine.c'; fi`
+
src/tools_iptables_unit-log.o: src/log.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tools_iptables_unit_CFLAGS) $(CFLAGS) -MT src/tools_iptables_unit-log.o -MD -MP -MF src/$(DEPDIR)/tools_iptables_unit-log.Tpo -c -o src/tools_iptables_unit-log.o `test -f 'src/log.c' || echo '$(srcdir)/'`src/log.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/tools_iptables_unit-log.Tpo src/$(DEPDIR)/tools_iptables_unit-log.Po
diff --git a/Makefile.plugins b/Makefile.plugins
index 294cae04..e90ad198 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -3,9 +3,6 @@ plugin_cflags = -fvisibility=hidden -I$(srcdir)/gdbus \
@DBUS_CFLAGS@ @GLIB_CFLAGS@
plugin_ldflags = -no-undefined -module -avoid-version
-script_cflags = -fvisibility=hidden -I$(srcdir)/gdbus \
- @DBUS_CFLAGS@
-
if LOOPBACK
builtin_modules += loopback
builtin_sources += plugins/loopback.c
@@ -156,12 +153,12 @@ endif
if PPTP
script_LTLIBRARIES += scripts/libppp-plugin.la
-scripts_libppp_plugin_la_LDFLAGS = $(script_cflags) @DBUS_CFLAGS@
+scripts_libppp_plugin_la_LDFLAGS = $(plugin_ldflags)
scripts_libppp_plugin_la_LIBADD = @DBUS_LIBS@
else
if L2TP
script_LTLIBRARIES += scripts/libppp-plugin.la
-scripts_libppp_plugin_la_LDFLAGS = $(script_cflags) @DBUS_CFLAGS@
+scripts_libppp_plugin_la_LDFLAGS = $(plugin_ldflags)
scripts_libppp_plugin_la_LIBADD = @DBUS_LIBS@
endif
endif
diff --git a/README b/README
index 9361287c..b1367dd9 100644
--- a/README
+++ b/README
@@ -44,7 +44,7 @@ In order to compile Connection Manager you need following software packages:
- GCC compiler
- GLib library
- D-Bus library
- - IP-Tables library
+ - IP-Tables library (for tethering support)
- GnuTLS library (optional)
- PolicyKit (optional)
- readline (command line client)
@@ -353,6 +353,46 @@ network. While the same setup works well for a WiFi or ethernet
uplink.
+Online check
+============
+
+ConnMan tries to detect if it has Internet connection or not when
+a service is connected. If the online check succeeds the service
+enters Online state, if not it stays in Ready state. The online
+check is also used to detect whether ConnMan is behind a captive
+portal like when you are in hotel and need to pay for connectivity.
+
+The online check is done by trying to fetch status.html document
+from ipv4.connman.net (for IPv4 connectivity) and ipv6.connman.net
+(for IPv6 connectivity). The used URL looks like this
+http://ipv{4|6}.connman.net/online/status.html
+
+During the online check procedure, ConnMan will temporarily install
+a host route to both the ipv4.connman.net and ipv6.connman.net so that
+the online check query can be directed via the correct network
+interface which the connected service is using. This host route is
+automatically removed when the online check is done.
+
+ConnMan sends this very minimal information in http header when doing
+the online check request (example):
+ Host: ipv4.connman.net
+ User-Agent: ConnMan/1.23 wispr
+ Connection: close
+
+Currently following information is returned from connman.net if
+the connection is successfull (200 OK http response code is returned):
+ Server: nginx
+ Date: Mon, 09 Jun 2014 09:25:42 GMT
+ Content-Type: text/html
+ Connection: close
+ X-ConnMan-Status: online
+
+The X-ConnMan-Status field is used in portal detection, if it is missing
+ConnMan will call RequestBrowser method in net.connman.Agent dbus
+interface to handle the portal login if the portal does not support WISPr.
+See doc/agent-api.txt for more details.
+
+
Information
===========
diff --git a/client/agent.c b/client/agent.c
index baa0a87b..d0208892 100644
--- a/client/agent.c
+++ b/client/agent.c
@@ -368,6 +368,36 @@ static DBusMessage *agent_report_error(DBusConnection *connection,
return NULL;
}
+static DBusMessage *agent_report_peer_error(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ struct agent_data *request = user_data;
+ char *path, *peer, *error;
+ DBusMessageIter iter;
+
+ if (handle_message(message, request,
+ agent_report_peer_error) == false)
+ return NULL;
+
+ dbus_message_iter_init(message, &iter);
+
+ dbus_message_iter_get_basic(&iter, &path);
+ peer = strip_path(path);
+
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_get_basic(&iter, &error);
+
+ __connmanctl_save_rl();
+ fprintf(stdout, "Agent ReportPeerError %s\n", peer);
+ fprintf(stdout, " %s\n", error);
+ __connmanctl_redraw_rl();
+
+ request->message = dbus_message_ref(message);
+ __connmanctl_agent_mode("Retry (yes/no)? ",
+ report_error_return, request);
+ return NULL;
+}
+
static void request_input_next(struct agent_data *request)
{
int i;
@@ -462,37 +492,16 @@ static void request_input_string_return(char *input, void *user_data)
request_input_next(request);
}
-static DBusMessage *agent_request_input(DBusConnection *connection,
- DBusMessage *message, void *user_data)
+static void parse_agent_request(struct agent_data *request,
+ DBusMessageIter *iter)
{
- struct agent_data *request = user_data;
- DBusMessageIter iter, dict, entry, variant;
- char *service, *str, *field;
- DBusMessageIter dict_entry, field_entry, field_value;
- char *argument, *value, *attr_type = NULL;
-
+ DBusMessageIter dict, entry, variant, dict_entry;
+ DBusMessageIter field_entry, field_value;
+ char *field, *argument, *value;
+ char *attr_type = NULL;
int i;
- if (handle_message(message, request, agent_request_input) == false)
- return NULL;
-
- dbus_message_iter_init(message, &iter);
-
- dbus_message_iter_get_basic(&iter, &str);
- service = strip_path(str);
-
- dbus_message_iter_next(&iter);
- dbus_message_iter_recurse(&iter, &dict);
-
- __connmanctl_save_rl();
- if (strcmp(request->interface, AGENT_INTERFACE) == 0)
- fprintf(stdout, "Agent RequestInput %s\n", service);
- else
- fprintf(stdout, "VPN Agent RequestInput %s\n", service);
- __connmanctl_dbus_print(&dict, " ", " = ", "\n");
- fprintf(stdout, "\n");
-
- dbus_message_iter_recurse(&iter, &dict);
+ dbus_message_iter_recurse(iter, &dict);
while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
@@ -536,6 +545,128 @@ static DBusMessage *agent_request_input(DBusConnection *connection,
dbus_message_iter_next(&dict);
}
+}
+
+static DBusMessage *agent_request_input(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ struct agent_data *request = user_data;
+ DBusMessageIter iter, dict;
+ char *service, *str;
+
+ if (handle_message(message, request, agent_request_input) == false)
+ return NULL;
+
+ dbus_message_iter_init(message, &iter);
+
+ dbus_message_iter_get_basic(&iter, &str);
+ service = strip_path(str);
+
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &dict);
+
+ __connmanctl_save_rl();
+ if (strcmp(request->interface, AGENT_INTERFACE) == 0)
+ fprintf(stdout, "Agent RequestInput %s\n", service);
+ else
+ fprintf(stdout, "VPN Agent RequestInput %s\n", service);
+ __connmanctl_dbus_print(&dict, " ", " = ", "\n");
+ fprintf(stdout, "\n");
+
+ parse_agent_request(request, &iter);
+
+ request->reply = dbus_message_new_method_return(message);
+ dbus_message_iter_init_append(request->reply, &request->iter);
+
+ dbus_message_iter_open_container(&request->iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &request->dict);
+
+ request_input_next(request);
+
+ return NULL;
+}
+
+static void request_authorization_return(char *input, void *user_data)
+{
+ struct agent_data *request = user_data;
+
+ switch (confirm_input(input)) {
+ case 1:
+ request->reply = dbus_message_new_method_return(
+ request->message);
+ dbus_message_iter_init_append(request->reply, &request->iter);
+
+ dbus_message_iter_open_container(&request->iter,
+ DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &request->dict);
+ dbus_message_iter_close_container(&request->iter,
+ &request->dict);
+ g_dbus_send_message(agent_connection, request->reply);
+ request->reply = NULL;
+ break;
+ case 0:
+ g_dbus_send_error(agent_connection, request->message,
+ "net.connman.Agent.Error.Rejected", NULL);
+ break;
+ default:
+ g_dbus_send_error(agent_connection, request->message,
+ "net.connman.Agent.Error.Canceled", NULL);
+ break;
+ }
+
+ pending_message_remove(request);
+ pending_command_complete("");
+}
+
+static DBusMessage *
+agent_request_peer_authorization(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ struct agent_data *request = user_data;
+ DBusMessageIter iter, dict;
+ char *peer, *str;
+ bool input;
+ int i;
+
+ if (handle_message(message, request, agent_request_peer_authorization)
+ == false)
+ return NULL;
+
+ dbus_message_iter_init(message, &iter);
+
+ dbus_message_iter_get_basic(&iter, &str);
+ peer = strip_path(str);
+
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &dict);
+
+ __connmanctl_save_rl();
+ fprintf(stdout, "Agent RequestPeerAuthorization %s\n", peer);
+ __connmanctl_dbus_print(&dict, " ", " = ", "\n");
+ fprintf(stdout, "\n");
+
+ parse_agent_request(request, &iter);
+
+ for (input = false, i = 0; request->input[i].attribute; i++) {
+ if (request->input[i].requested == true) {
+ input = true;
+ break;
+ }
+ }
+
+ if (!input) {
+ request->message = dbus_message_ref(message);
+ __connmanctl_agent_mode("Accept connection (yes/no)? ",
+ request_authorization_return, request);
+ return NULL;
+ }
request->reply = dbus_message_new_method_return(message);
dbus_message_iter_init_append(request->reply, &request->iter);
@@ -562,11 +693,20 @@ static const GDBusMethodTable agent_methods[] = {
GDBUS_ARGS({ "service", "o" },
{ "error", "s" }),
NULL, agent_report_error) },
+ { GDBUS_ASYNC_METHOD("ReportPeerError",
+ GDBUS_ARGS({ "peer", "o" },
+ { "error", "s" }),
+ NULL, agent_report_peer_error) },
{ GDBUS_ASYNC_METHOD("RequestInput",
GDBUS_ARGS({ "service", "o" },
{ "fields", "a{sv}" }),
GDBUS_ARGS({ "fields", "a{sv}" }),
agent_request_input) },
+ { GDBUS_ASYNC_METHOD("RequestPeerAuthorization",
+ GDBUS_ARGS({ "peer", "o" },
+ { "fields", "a{sv}" }),
+ GDBUS_ARGS({ "fields", "a{sv}" }),
+ agent_request_peer_authorization) },
{ },
};
diff --git a/client/commands.c b/client/commands.c
index 9c01fd53..9208016b 100644
--- a/client/commands.c
+++ b/client/commands.c
@@ -31,6 +31,7 @@
#include <stdbool.h>
#include <sys/types.h>
#include <unistd.h>
+#include <ctype.h>
#include <glib.h>
#include <gdbus.h>
@@ -660,6 +661,7 @@ static int connect_return(DBusMessageIter *iter, const char *error,
static int cmd_connect(char *args[], int num, struct connman_option *options)
{
+ const char *iface = "net.connman.Service";
char *path;
if (num > 2)
@@ -671,10 +673,14 @@ static int cmd_connect(char *args[], int num, struct connman_option *options)
if (check_dbus_name(args[1]) == false)
return -EINVAL;
- path = g_strdup_printf("/net/connman/service/%s", args[1]);
+ if (g_strstr_len(args[1], 5, "peer_") == args[1]) {
+ iface = "net.connman.Peer";
+ path = g_strdup_printf("/net/connman/peer/%s", args[1]);
+ } else
+ path = g_strdup_printf("/net/connman/service/%s", args[1]);
+
return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, path,
- "net.connman.Service", "Connect",
- connect_return, path, NULL, NULL);
+ iface, "Connect", connect_return, path, NULL, NULL);
}
static int disconnect_return(DBusMessageIter *iter, const char *error,
@@ -696,6 +702,7 @@ static int disconnect_return(DBusMessageIter *iter, const char *error,
static int cmd_disconnect(char *args[], int num, struct connman_option *options)
{
+ const char *iface = "net.connman.Service";
char *path;
if (num > 2)
@@ -707,10 +714,15 @@ static int cmd_disconnect(char *args[], int num, struct connman_option *options)
if (check_dbus_name(args[1]) == false)
return -EINVAL;
- path = g_strdup_printf("/net/connman/service/%s", args[1]);
- return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, path,
- "net.connman.Service", "Disconnect",
- disconnect_return, path, NULL, NULL);
+ if (g_strstr_len(args[1], 5, "peer_") == args[1]) {
+ iface = "net.connman.Peer";
+ path = g_strdup_printf("/net/connman/peer/%s", args[1]);
+ } else
+ path = g_strdup_printf("/net/connman/service/%s", args[1]);
+
+ return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
+ path, iface, "Disconnect",
+ disconnect_return, path, NULL, NULL);
}
static int config_return(DBusMessageIter *iter, const char *error,
@@ -2085,6 +2097,273 @@ static char *lookup_session(const char *text, int state)
return lookup_options(session_options, text, state);
}
+static int peer_service_cb(DBusMessageIter *iter, const char *error,
+ void *user_data)
+{
+ bool registration = GPOINTER_TO_INT(user_data);
+
+ if (error)
+ fprintf(stderr, "Error %s peer service: %s\n",
+ registration ? "registering" : "unregistering", error);
+ else
+ fprintf(stdout, "Peer service %s\n",
+ registration ? "registered" : "unregistered");
+
+ return 0;
+}
+
+struct _peer_service {
+ unsigned char *bjr_query;
+ int bjr_query_len;
+ unsigned char *bjr_response;
+ int bjr_response_len;
+ unsigned char *wfd_ies;
+ int wfd_ies_len;
+ char *upnp_service;
+ int version;
+ int master;
+};
+
+static void append_dict_entry_fixed_array(DBusMessageIter *iter,
+ const char *property, void *value, int length)
+{
+ DBusMessageIter dict_entry, variant, array;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY,
+ NULL, &dict_entry);
+ dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING,
+ &property);
+ dbus_message_iter_open_container(&dict_entry, DBUS_TYPE_VARIANT,
+ DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING,
+ &variant);
+ dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING, &array);
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ value, length);
+ dbus_message_iter_close_container(&variant, &array);
+ dbus_message_iter_close_container(&dict_entry, &variant);
+ dbus_message_iter_close_container(iter, &dict_entry);
+}
+
+static void append_peer_service_dict(DBusMessageIter *iter, void *user_data)
+{
+ struct _peer_service *service = user_data;
+
+ if (service->bjr_query && service->bjr_response) {
+ append_dict_entry_fixed_array(iter, "BonjourQuery",
+ &service->bjr_query, service->bjr_query_len);
+ append_dict_entry_fixed_array(iter, "BonjourResponse",
+ &service->bjr_response, service->bjr_response_len);
+ } else if (service->upnp_service && service->version) {
+ __connmanctl_dbus_append_dict_entry(iter, "UpnpVersion",
+ DBUS_TYPE_INT32, &service->version);
+ __connmanctl_dbus_append_dict_entry(iter, "UpnpService",
+ DBUS_TYPE_STRING, &service->upnp_service);
+ } else if (service->wfd_ies) {
+ append_dict_entry_fixed_array(iter, "WiFiDisplayIEs",
+ &service->wfd_ies, service->wfd_ies_len);
+ }
+}
+
+static void peer_service_append(DBusMessageIter *iter, void *user_data)
+{
+ struct _peer_service *service = user_data;
+ dbus_bool_t master;
+
+ __connmanctl_dbus_append_dict(iter, append_peer_service_dict, service);
+
+ if (service->master < 0)
+ return;
+
+ master = service->master == 1 ? TRUE : FALSE;
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &master);
+}
+
+static struct _peer_service *fill_in_peer_service(unsigned char *bjr_query,
+ int bjr_query_len, unsigned char *bjr_response,
+ int bjr_response_len, char *upnp_service,
+ int version, unsigned char *wfd_ies,
+ int wfd_ies_len)
+{
+ struct _peer_service *service;
+
+ service = dbus_malloc0(sizeof(*service));
+
+ if (bjr_query_len && bjr_response_len) {
+ service->bjr_query = dbus_malloc0(bjr_query_len);
+ memcpy(service->bjr_query, bjr_query, bjr_query_len);
+ service->bjr_query_len = bjr_query_len;
+
+ service->bjr_response = dbus_malloc0(bjr_response_len);
+ memcpy(service->bjr_response, bjr_response, bjr_response_len);
+ service->bjr_response_len = bjr_response_len;
+ } else if (upnp_service && version) {
+ service->upnp_service = strdup(upnp_service);
+ service->version = version;
+ } else if (wfd_ies && wfd_ies_len) {
+ service->wfd_ies = dbus_malloc0(wfd_ies_len);
+ memcpy(service->wfd_ies, wfd_ies, wfd_ies_len);
+ service->wfd_ies_len = wfd_ies_len;
+ } else {
+ dbus_free(service);
+ service = NULL;
+ }
+
+ return service;
+}
+
+static void free_peer_service(struct _peer_service *service)
+{
+ dbus_free(service->bjr_query);
+ dbus_free(service->bjr_response);
+ dbus_free(service->wfd_ies);
+ free(service->upnp_service);
+ dbus_free(service);
+}
+
+static int peer_service_register(unsigned char *bjr_query, int bjr_query_len,
+ unsigned char *bjr_response, int bjr_response_len,
+ char *upnp_service, int version,
+ unsigned char *wfd_ies, int wfd_ies_len, int master)
+{
+ struct _peer_service *service;
+ bool registration = true;
+ int ret;
+
+ service = fill_in_peer_service(bjr_query, bjr_query_len, bjr_response,
+ bjr_response_len, upnp_service, version,
+ wfd_ies, wfd_ies_len);
+ if (!service)
+ return -EINVAL;
+
+ service->master = master;
+
+ ret = __connmanctl_dbus_method_call(connection, "net.connman", "/",
+ "net.connman.Manager", "RegisterPeerService",
+ peer_service_cb, GINT_TO_POINTER(registration),
+ peer_service_append, service);
+
+ free_peer_service(service);
+
+ return ret;
+}
+
+static int peer_service_unregister(unsigned char *bjr_query, int bjr_query_len,
+ unsigned char *bjr_response, int bjr_response_len,
+ char *upnp_service, int version,
+ unsigned char *wfd_ies, int wfd_ies_len)
+{
+ struct _peer_service *service;
+ bool registration = false;
+ int ret;
+
+ service = fill_in_peer_service(bjr_query, bjr_query_len, bjr_response,
+ bjr_response_len, upnp_service, version,
+ wfd_ies, wfd_ies_len);
+ if (!service)
+ return -EINVAL;
+
+ service->master = -1;
+
+ ret = __connmanctl_dbus_method_call(connection, "net.connman", "/",
+ "net.connman.Manager", "UnregisterPeerService",
+ peer_service_cb, GINT_TO_POINTER(registration),
+ peer_service_append, service);
+
+ free_peer_service(service);
+
+ return ret;
+}
+
+static int parse_spec_array(char *command, unsigned char spec[1024])
+{
+ int length, pos, end;
+ char b[3] = {};
+ char *e;
+
+ end = strlen(command);
+ for (e = NULL, length = pos = 0; command[pos] != '\0'; length++) {
+ if (pos+2 > end)
+ return -EINVAL;
+
+ b[0] = command[pos];
+ b[1] = command[pos+1];
+
+ spec[length] = strtol(b, &e, 16);
+ if (e && *e != '\0')
+ return -EINVAL;
+
+ pos += 2;
+ }
+
+ return length;
+}
+
+static int cmd_peer_service(char *args[], int num,
+ struct connman_option *options)
+{
+ unsigned char bjr_query[1024] = {};
+ unsigned char bjr_response[1024] = {};
+ unsigned char wfd_ies[1024] = {};
+ char *upnp_service = NULL;
+ int bjr_query_len = 0, bjr_response_len = 0;
+ int version = 0, master = 0, wfd_ies_len = 0;
+ int limit;
+
+ if (num < 4)
+ return -EINVAL;
+
+ if (!strcmp(args[2], "wfd_ies")) {
+ wfd_ies_len = parse_spec_array(args[3], wfd_ies);
+ if (wfd_ies_len == -EINVAL)
+ return -EINVAL;
+ limit = 5;
+ goto master;
+ }
+
+ if (num < 6)
+ return -EINVAL;
+
+ limit = 7;
+ if (!strcmp(args[2], "bjr_query")) {
+ if (strcmp(args[4], "bjr_response"))
+ return -EINVAL;
+ bjr_query_len = parse_spec_array(args[3], bjr_query);
+ bjr_response_len = parse_spec_array(args[5], bjr_response);
+
+ if (bjr_query_len == -EINVAL || bjr_response_len == -EINVAL)
+ return -EINVAL;
+ } else if (!strcmp(args[2], "upnp_service")) {
+ char *e = NULL;
+
+ if (strcmp(args[4], "upnp_version"))
+ return -EINVAL;
+ upnp_service = args[3];
+ version = strtol(args[5], &e, 10);
+ if (*e != '\0')
+ return -EINVAL;
+ }
+
+master:
+ if (num == limit) {
+ master = parse_boolean(args[6]);
+ if (master < 0)
+ return -EINVAL;
+ }
+
+ if (!strcmp(args[1], "register")) {
+ return peer_service_register(bjr_query, bjr_query_len,
+ bjr_response, bjr_response_len, upnp_service,
+ version, wfd_ies, wfd_ies_len, master);
+ } else if (!strcmp(args[1], "unregister")) {
+ return peer_service_unregister(bjr_query, bjr_query_len,
+ bjr_response, bjr_response_len, upnp_service,
+ version, wfd_ies, wfd_ies_len);
+ }
+
+ return -EINVAL;
+}
+
static const struct {
const char *cmd;
const char *argument;
@@ -2115,10 +2394,10 @@ static const struct {
{ "scan", "<technology>", NULL, cmd_scan,
"Scans for new services for given technology",
lookup_technology_arg },
- { "connect", "<service>", NULL, cmd_connect,
- "Connect a given service", lookup_service_arg },
- { "disconnect", "<service>", NULL, cmd_disconnect,
- "Disconnect a given service", lookup_service_arg },
+ { "connect", "<service/peer>", NULL, cmd_connect,
+ "Connect a given service or peer", lookup_service_arg },
+ { "disconnect", "<service/peer>", NULL, cmd_disconnect,
+ "Disconnect a given service or peer", lookup_service_arg },
{ "config", "<service>", config_options, cmd_config,
"Set service configuration options", lookup_config },
{ "monitor", "[off]", monitor_options, cmd_monitor,
@@ -2131,6 +2410,12 @@ static const struct {
"VPN Agent mode", lookup_agent },
{ "session", "on|off|connect|disconnect|config", session_options,
cmd_session, "Enable or disable a session", lookup_session },
+ { "peer_service", "register|unregister <specs> <master>\n"
+ "Where specs are:\n"
+ "\tbjr_query <query> bjr_response <response>\n"
+ "\tupnp_service <service> upnp_version <version>\n"
+ "\twfd_ies <ies>\n", NULL,
+ cmd_peer_service, "(Un)Register a Peer Service", NULL },
{ "help", NULL, NULL, cmd_help,
"Show help", NULL },
{ "exit", NULL, NULL, cmd_exit,
@@ -2315,6 +2600,11 @@ static void update_services(DBusMessageIter *iter)
static int populate_service_hash(DBusMessageIter *iter, const char *error,
void *user_data)
{
+ if (error) {
+ fprintf(stderr, "Error getting services: %s", error);
+ return 0;
+ }
+
update_services(iter);
return 0;
}
@@ -2376,6 +2666,11 @@ static void update_peers(DBusMessageIter *iter)
static int populate_peer_hash(DBusMessageIter *iter,
const char *error, void *user_data)
{
+ if (error) {
+ fprintf(stderr, "Error getting peers: %s", error);
+ return 0;
+ }
+
update_peers(iter);
return 0;
}
@@ -2436,6 +2731,11 @@ static void update_technologies(DBusMessageIter *iter)
static int populate_technology_hash(DBusMessageIter *iter, const char *error,
void *user_data)
{
+ if (error) {
+ fprintf(stderr, "Error getting technologies: %s", error);
+ return 0;
+ }
+
update_technologies(iter);
return 0;
diff --git a/client/dbus_helpers.c b/client/dbus_helpers.c
index 826111f2..d9057463 100644
--- a/client/dbus_helpers.c
+++ b/client/dbus_helpers.c
@@ -27,7 +27,7 @@
#include "input.h"
#include "dbus_helpers.h"
-#define TIMEOUT 60000
+#define TIMEOUT 120000
void __connmanctl_dbus_print(DBusMessageIter *iter, const char *pre,
const char *dict, const char *sep)
@@ -213,6 +213,9 @@ static int append_variant(DBusMessageIter *iter, const char *property,
case DBUS_TYPE_STRING:
type_str = DBUS_TYPE_STRING_AS_STRING;
break;
+ case DBUS_TYPE_INT32:
+ type_str = DBUS_TYPE_INT32_AS_STRING;
+ break;
default:
return -EOPNOTSUPP;
}
diff --git a/configure b/configure
index ca5ddf6a..ec36fdc8 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for connman 1.24.
+# Generated by GNU Autoconf 2.69 for connman 1.26.
#
#
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@@ -587,8 +587,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='connman'
PACKAGE_TARNAME='connman'
-PACKAGE_VERSION='1.24'
-PACKAGE_STRING='connman 1.24'
+PACKAGE_VERSION='1.26'
+PACKAGE_STRING='connman 1.26'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''
@@ -1455,7 +1455,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures connman 1.24 to adapt to many kinds of systems.
+\`configure' configures connman 1.26 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1525,7 +1525,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of connman 1.24:";;
+ short | recursive ) echo "Configuration of connman 1.26:";;
esac
cat <<\_ACEOF
@@ -1687,7 +1687,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-connman configure 1.24
+connman configure 1.26
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2052,7 +2052,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by connman $as_me 1.24, which was
+It was created by connman $as_me 1.26, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -2868,7 +2868,7 @@ fi
# Define the identity of the package.
PACKAGE='connman'
- VERSION='1.24'
+ VERSION='1.26'
cat >>confdefs.h <<_ACEOF
@@ -14101,7 +14101,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by connman $as_me 1.24, which was
+This file was extended by connman $as_me 1.26, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -14167,7 +14167,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-connman config.status 1.24
+connman config.status 1.26
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/configure.ac b/configure.ac
index 805f02ba..6f35c789 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
AC_PREREQ(2.60)
-AC_INIT(connman, 1.24)
+AC_INIT(connman, 1.26)
AM_INIT_AUTOMAKE([foreign subdir-objects color-tests])
AC_CONFIG_HEADERS([config.h])
diff --git a/doc/agent-api.txt b/doc/agent-api.txt
index a98343fe..2ddd19a6 100644
--- a/doc/agent-api.txt
+++ b/doc/agent-api.txt
@@ -23,6 +23,16 @@ Methods void Release()
Possible Errors: net.connman.Agent.Error.Retry
+ void ReportPeerError(object peer, string error)
+
+ This method gets called when an error has to be
+ reported to the user about a peer connection.
+
+ A special return value can be used to trigger a
+ retry of the failed transaction.
+
+ Possible Errors: net.connman.Agent.Error.Retry
+
void RequestBrowser(object service, string url)
This method gets called when it is required
@@ -58,6 +68,25 @@ Methods void Release()
Possible Errors: net.connman.Agent.Error.Canceled
net.connman.Agent.Error.LaunchBrowser
+ dict RequestPeerAuthorization(object peer, dict fields) [experimental]
+
+ This method gets called when trying to connect to a
+ peer or when an incoming peer connection is requested,
+ for which some extra input is required. In this case,
+ it will only deal with WPS input as well as accepting
+ or rejecting an incoming connection.
+
+ The return value should be a dictionary where the
+ keys are the field names and the values are the
+ actual fields. Alternatively an error indicating that
+ the request got canceled or rejected can be returned.
+
+ The dictionary arguments contains field names with
+ their input parameters.
+
+ Possible Errors: net.connman.Agent.Error.Canceled
+ net.connman.Agent.Error.Rejected
+
void Cancel()
This method gets called to indicate that the agent
@@ -107,6 +136,9 @@ Fields string Name
method, or a pin code if user wants to use the pin
method.
+ In case of a RequestPeerAuthorization, this field will
+ be set as mandatory.
+
string Username
Username for WISPr authentication. This field will be
@@ -254,3 +286,20 @@ Examples Requesting a passphrase for WPA2 network
}
==> { "Username" : "foo", "Password": "secret" }
+
+ Requesting a answer about an inconming peer connection:
+
+ RequestPeerAuthorization("/peer3", {})
+
+ ==> { }
+
+ Requesting the WPS details when connecting to a peer:
+
+ RequestPeerAuthorization("/peer4",
+ { "WPS":
+ { "Type" : "wpspin",
+ "Requirement" : "mandatory"
+ }
+ }
+
+ ==> { "WPS" : "" }
diff --git a/doc/config-format.txt b/doc/config-format.txt
index d825070d..b16f4e43 100644
--- a/doc/config-format.txt
+++ b/doc/config-format.txt
@@ -87,6 +87,10 @@ The following options are valid if Type is "wifi"
Prefix the value with "EAP-" to indicate the usage of an EAP-based inner
authentication method (should only be used with EAP = TTLS).
- Passphrase: RSN/WPA/WPA2 Passphrase
+- Security: The security type of the network. Possible values are 'psk'
+ (WPA/WPA2 PSK), 'ieee8021x' (WPA EAP), 'none' and 'wep'. When not set, the
+ default value is 'ieee8021x' if an EAP type is configured, 'psk' if a
+ passphrase is present and 'none' otherwise.
- Hidden: If set to true, then this AP is hidden. If missing or set to false,
then AP is not hidden.
diff --git a/doc/manager-api.txt b/doc/manager-api.txt
index 05d2701d..31e137ca 100644
--- a/doc/manager-api.txt
+++ b/doc/manager-api.txt
@@ -156,6 +156,44 @@ Methods dict GetProperties()
Possible Errors: [service].Error.InvalidArguments
+ void RegisterPeerService(dict specification, boolean master)
+ [experimental]
+
+ Registers a local P2P Peer service
+
+ Even if p2p techonology is not available, it will be
+ possible to register peer services, since a p2p
+ enabled WiFi device might appear at anytime. The
+ registered peer services will automatically be enabled
+ for the p2p WiFi device; the application does not need
+ to do any re-registration.
+
+ A Peer service belongs to the process that registers
+ it, thus if that process dies, its Peer services will
+ be destroyed as well.
+
+ The specification dict follows the format described
+ in the Peer API document.
+
+ ConnMan will be able to determine in most cases
+ whether to be the P2P Group Owner or not. If the
+ service for some reason must belong to a group that
+ this device manages, the "master" property can be
+ set. Do not enable the "master" property unless it
+ is absolutely sure that this is needed for the
+ provided peer service.
+
+ Possible Errors: [service].Error.InvalidArguments
+ [service].Error.AlreadyExists
+ [service].Error.NotSupported
+
+ void UnregisterPeerService(dict specification) [experimental]
+
+ Unregisters an existing local P2P Peer service
+
+ Possible Errors: [service].Error.InvalidArguments
+ [service].Error.NotRegistered
+
Signals TechnologyAdded(object path, dict properties)
Signal that is sent when a new technology is added.
diff --git a/doc/session-overview.txt b/doc/session-overview.txt
index 9af6a675..23931678 100644
--- a/doc/session-overview.txt
+++ b/doc/session-overview.txt
@@ -7,7 +7,7 @@ Connection management algorithm basics
The Session core uses the normal auto-connect algorithm for selecting
which services will be connected or disconnected. That means only
-Services with AutoConnect to set to true will be used. The Session
+Services with AutoConnect set to true will be used. The Session
core will assign a connected Service to a Session if the Service
is matching the AllowedBearer filter.
diff --git a/doc/session-policy-format.txt b/doc/session-policy-format.txt
index ecfbce02..babc2c10 100644
--- a/doc/session-policy-format.txt
+++ b/doc/session-policy-format.txt
@@ -1,7 +1,7 @@
ConnMan policy file format
**************************
-The session policy pluging allows to configure/provision a session.
+The session policy plugin allows to configure/provision a session.
ConnMan will be looking for policy files in STORAGEDIR/session_policy_local
which by default points to /var/lib/connman. Policy file names must
not include other characters than letters or numbers and must have
@@ -41,13 +41,13 @@ Allowed fields:
The policy ConnectionType overrules the settings done via
D-Bus.
-- Priority: A boolean which tells ConnMan to preferred the session
- over other Sessions. This priority value is more for application
+- Priority: A boolean which tells ConnMan to prefer the session
+ over other Sessions. This priority value is more for applications
that want to push themselves up in the asychronization notification
queue once a bearer becomes online.
This actual priority order also depends on the allowed bearers and
- other factors. This is setting is just a little indicator of one
+ other factors. This setting is just a little indicator for one
application being notified before another one.
- RoamingPolicy: The allowed roaming behavior.
diff --git a/gdbus/client.c b/gdbus/client.c
index 3bf883ac..eb68a0f8 100644
--- a/gdbus/client.c
+++ b/gdbus/client.c
@@ -51,6 +51,7 @@ struct GDBusClient {
GDBusWatchFunction connect_func;
void *connect_data;
GDBusWatchFunction disconn_func;
+ gboolean connected;
void *disconn_data;
GDBusMessageFunction signal_func;
void *signal_data;
@@ -1146,6 +1147,8 @@ static void service_connect(DBusConnection *conn, void *user_data)
get_managed_objects(client);
+ client->connected = TRUE;
+
g_dbus_client_unref(client);
}
@@ -1156,8 +1159,10 @@ static void service_disconnect(DBusConnection *conn, void *user_data)
g_list_free_full(client->proxy_list, proxy_free);
client->proxy_list = NULL;
- if (client->disconn_func)
+ if (client->disconn_func) {
client->disconn_func(conn, client->disconn_data);
+ client->connected = FALSE;
+ }
}
static DBusHandlerResult message_filter(DBusConnection *connection,
@@ -1210,6 +1215,7 @@ GDBusClient *g_dbus_client_new(DBusConnection *connection,
client->dbus_conn = dbus_connection_ref(connection);
client->service_name = g_strdup(service);
client->base_path = g_strdup(path);
+ client->connected = FALSE;
client->match_rules = g_ptr_array_sized_new(1);
g_ptr_array_set_free_func(client->match_rules, g_free);
@@ -1284,7 +1290,11 @@ void g_dbus_client_unref(GDBusClient *client)
g_list_free_full(client->proxy_list, proxy_free);
- if (client->disconn_func)
+ /*
+ * Don't call disconn_func twice if disconnection
+ * was previously reported.
+ */
+ if (client->disconn_func && client->connected)
client->disconn_func(client->dbus_conn, client->disconn_data);
g_dbus_remove_watch(client->dbus_conn, client->watch);
diff --git a/gdbus/mainloop.c b/gdbus/mainloop.c
index 435fb93b..3e88eac8 100644
--- a/gdbus/mainloop.c
+++ b/gdbus/mainloop.c
@@ -88,16 +88,22 @@ static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data)
struct watch_info *info = data;
unsigned int flags = 0;
DBusDispatchStatus status;
+ DBusConnection *conn;
if (cond & G_IO_IN) flags |= DBUS_WATCH_READABLE;
if (cond & G_IO_OUT) flags |= DBUS_WATCH_WRITABLE;
if (cond & G_IO_HUP) flags |= DBUS_WATCH_HANGUP;
if (cond & G_IO_ERR) flags |= DBUS_WATCH_ERROR;
+ /* Protect connection from being destroyed by dbus_watch_handle */
+ conn = dbus_connection_ref(info->conn);
+
dbus_watch_handle(info->watch, flags);
- status = dbus_connection_get_dispatch_status(info->conn);
- queue_dispatch(info->conn, status);
+ status = dbus_connection_get_dispatch_status(conn);
+ queue_dispatch(conn, status);
+
+ dbus_connection_unref(conn);
return TRUE;
}
diff --git a/gdbus/object.c b/gdbus/object.c
index 13cf9a95..4d5a64cb 100644
--- a/gdbus/object.c
+++ b/gdbus/object.c
@@ -1088,7 +1088,6 @@ static const GDBusMethodTable introspect_methods[] = {
static void append_interfaces(struct generic_data *data, DBusMessageIter *iter)
{
DBusMessageIter array;
- GSList *l;
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
@@ -1100,12 +1099,7 @@ static void append_interfaces(struct generic_data *data, DBusMessageIter *iter)
DBUS_DICT_ENTRY_END_CHAR_AS_STRING
DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array);
- for (l = data->interfaces; l != NULL; l = l->next) {
- if (g_slist_find(data->added, l->data))
- continue;
-
- append_interface(l->data, &array);
- }
+ g_slist_foreach(data->interfaces, append_interface, &array);
dbus_message_iter_close_container(iter, &array);
}
diff --git a/gdbus/watch.c b/gdbus/watch.c
index 0f99f4f1..0d0054c1 100644
--- a/gdbus/watch.c
+++ b/gdbus/watch.c
@@ -362,6 +362,7 @@ static void service_data_free(struct service_data *data)
callback->data = NULL;
}
+/* Returns TRUE if data is freed */
static gboolean filter_data_remove_callback(struct filter_data *data,
struct filter_callback *cb)
{
@@ -383,7 +384,7 @@ static gboolean filter_data_remove_callback(struct filter_data *data,
/* Don't remove the filter if other callbacks exist or data is lock
* processing callbacks */
if (data->callbacks || data->lock)
- return TRUE;
+ return FALSE;
if (data->registered && !remove_match(data))
return FALSE;
@@ -405,7 +406,9 @@ static DBusHandlerResult signal_filter(DBusConnection *connection,
if (cb->signal_func && !cb->signal_func(connection, message,
cb->user_data)) {
- filter_data_remove_callback(data, cb);
+ if (filter_data_remove_callback(data, cb))
+ break;
+
continue;
}
@@ -489,7 +492,9 @@ static DBusHandlerResult service_filter(DBusConnection *connection,
/* Only auto remove if it is a bus name watch */
if (data->argument[0] == ':' &&
(cb->conn_func == NULL || cb->disc_func == NULL)) {
- filter_data_remove_callback(data, cb);
+ if (filter_data_remove_callback(data, cb))
+ break;
+
continue;
}
@@ -590,7 +595,6 @@ static gboolean update_service(void *user_data)
struct filter_callback *cb = data->callback;
DBusConnection *conn;
- update_name_cache(data->name, data->owner);
conn = dbus_connection_ref(data->conn);
service_data_free(data);
@@ -699,7 +703,8 @@ guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
if (name == NULL)
return 0;
- data = filter_data_get(connection, service_filter, NULL, NULL,
+ data = filter_data_get(connection, service_filter,
+ DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS, "NameOwnerChanged",
name);
if (data == NULL)
diff --git a/gdhcp/client.c b/gdhcp/client.c
index 2b7202a4..66c3a90d 100644
--- a/gdhcp/client.c
+++ b/gdhcp/client.c
@@ -47,11 +47,11 @@
#include "common.h"
#include "ipv4ll.h"
-#define DISCOVER_TIMEOUT 3
-#define DISCOVER_RETRIES 10
+#define DISCOVER_TIMEOUT 5
+#define DISCOVER_RETRIES 6
-#define REQUEST_TIMEOUT 3
-#define REQUEST_RETRIES 5
+#define REQUEST_TIMEOUT 5
+#define REQUEST_RETRIES 3
typedef enum _listen_mode {
L_NONE,
@@ -155,6 +155,7 @@ struct _GDHCPClient {
uint32_t expire;
bool retransmit;
struct timeval start_time;
+ bool request_bcast;
};
static inline void debug(GDHCPClient *client, const char *format, ...)
@@ -455,15 +456,26 @@ static int send_discover(GDHCPClient *dhcp_client, uint32_t requested)
add_send_options(dhcp_client, &packet);
+ /*
+ * If we do not get a reply to DISCOVER packet, then we try with
+ * broadcast flag set. So first packet is sent without broadcast flag,
+ * first retry is with broadcast flag, second retry is without it etc.
+ * Reason is various buggy routers/AP that either eat the other or vice
+ * versa. In the receiving side we then find out what kind of packet
+ * the server can send.
+ */
return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
- INADDR_BROADCAST, SERVER_PORT,
- MAC_BCAST_ADDR, dhcp_client->ifindex);
+ INADDR_BROADCAST, SERVER_PORT,
+ MAC_BCAST_ADDR, dhcp_client->ifindex,
+ dhcp_client->retry_times % 2);
}
static int send_request(GDHCPClient *dhcp_client)
{
struct dhcp_packet packet;
- debug(dhcp_client, "sending DHCP request");
+
+ debug(dhcp_client, "sending DHCP request (state %d)",
+ dhcp_client->state);
init_packet(dhcp_client, &packet, DHCPREQUEST);
@@ -484,17 +496,18 @@ static int send_request(GDHCPClient *dhcp_client)
add_send_options(dhcp_client, &packet);
- if (dhcp_client->state == RENEWING) {
+ if (dhcp_client->state == RENEWING || dhcp_client->state == REBINDING)
packet.ciaddr = htonl(dhcp_client->requested_ip);
+ if (dhcp_client->state == RENEWING)
return dhcp_send_kernel_packet(&packet,
dhcp_client->requested_ip, CLIENT_PORT,
dhcp_client->server_ip, SERVER_PORT);
- }
return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
- INADDR_BROADCAST, SERVER_PORT,
- MAC_BCAST_ADDR, dhcp_client->ifindex);
+ INADDR_BROADCAST, SERVER_PORT,
+ MAC_BCAST_ADDR, dhcp_client->ifindex,
+ dhcp_client->request_bcast);
}
static int send_release(GDHCPClient *dhcp_client,
@@ -1130,6 +1143,7 @@ static void remove_option_value(gpointer data)
GList *option_value = data;
g_list_foreach(option_value, remove_value, NULL);
+ g_list_free(option_value);
}
GDHCPClient *g_dhcp_client_new(GDHCPType type,
@@ -1185,6 +1199,7 @@ GDHCPClient *g_dhcp_client_new(GDHCPType type,
dhcp_client->duid_len = 0;
dhcp_client->last_request = time(NULL);
dhcp_client->expire = 0;
+ dhcp_client->request_bcast = false;
*error = G_DHCP_CLIENT_ERROR_NONE;
@@ -1292,7 +1307,8 @@ static bool sanity_check(struct ip_udp_dhcp_packet *packet, int bytes)
return true;
}
-static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd)
+static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd,
+ struct sockaddr_in *dst_addr)
{
int bytes;
struct ip_udp_dhcp_packet packet;
@@ -1337,6 +1353,8 @@ static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd)
if (dhcp_pkt->cookie != htonl(DHCP_MAGIC))
return -1;
+ dst_addr->sin_addr.s_addr = packet.ip.daddr;
+
return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
}
@@ -2047,7 +2065,7 @@ static GList *get_addresses(GDHCPClient *dhcp_client,
memcpy(&dhcp_client->ia_ta, &addr,
sizeof(struct in6_addr));
- if (valid > dhcp_client->expire)
+ if (valid != dhcp_client->expire)
dhcp_client->expire = valid;
}
@@ -2230,6 +2248,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
gpointer user_data)
{
GDHCPClient *dhcp_client = user_data;
+ struct sockaddr_in dst_addr = { 0 };
struct dhcp_packet packet;
struct dhcpv6_packet *packet6 = NULL;
uint8_t *message_type = NULL, *client_id = NULL, *option,
@@ -2256,7 +2275,9 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
if (dhcp_client->listen_mode == L2) {
re = dhcp_recv_l2_packet(&packet,
- dhcp_client->listener_sockfd);
+ dhcp_client->listener_sockfd,
+ &dst_addr);
+ xid = packet.xid;
} else if (dhcp_client->listen_mode == L3) {
if (dhcp_client->type == G_DHCP_IPV6) {
re = dhcpv6_recv_l3_packet(&packet6, buf, sizeof(buf),
@@ -2328,7 +2349,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
return TRUE;
debug(dhcp_client, "received DHCP packet xid 0x%04x "
- "(current state %d)", xid, dhcp_client->state);
+ "(current state %d)", ntohl(xid), dhcp_client->state);
switch (dhcp_client->state) {
case INIT_SELECTING:
@@ -2345,10 +2366,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;
+
+ debug(dhcp_client, "init ip %s -> %sadding broadcast flag",
+ inet_ntoa(dst_addr.sin_addr),
+ dhcp_client->request_bcast ? "" : "not ");
+
start_request(dhcp_client);
return TRUE;
case REBOOTING:
+ if (dst_addr.sin_addr.s_addr == INADDR_BROADCAST)
+ dhcp_client->request_bcast = true;
+ else
+ dhcp_client->request_bcast = false;
+
+ debug(dhcp_client, "ip %s -> %sadding broadcast flag",
+ inet_ntoa(dst_addr.sin_addr),
+ dhcp_client->request_bcast ? "" : "not ");
+ /* fall through */
case REQUESTING:
case RENEWING:
case REBINDING:
@@ -2366,6 +2405,12 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
g_free(dhcp_client->assigned_ip);
dhcp_client->assigned_ip = get_ip(packet.yiaddr);
+ if (dhcp_client->state == REBOOTING) {
+ option = dhcp_get_option(&packet,
+ DHCP_SERVER_ID);
+ dhcp_client->server_ip = get_be32(option);
+ }
+
/* Address should be set up here */
if (dhcp_client->lease_available_cb)
dhcp_client->lease_available_cb(dhcp_client,
@@ -2833,6 +2878,7 @@ void g_dhcp_client_stop(GDHCPClient *dhcp_client)
dhcp_client->requested_ip = 0;
dhcp_client->state = RELEASED;
dhcp_client->lease_seconds = 0;
+ dhcp_client->request_bcast = false;
}
GList *g_dhcp_client_get_option(GDHCPClient *dhcp_client,
diff --git a/gdhcp/common.c b/gdhcp/common.c
index e1111505..45278a88 100644
--- a/gdhcp/common.c
+++ b/gdhcp/common.c
@@ -511,8 +511,9 @@ int dhcpv6_send_packet(int index, struct dhcpv6_packet *dhcp_pkt, int len)
}
int dhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
- uint32_t source_ip, int source_port, uint32_t dest_ip,
- int dest_port, const uint8_t *dest_arp, int ifindex)
+ uint32_t source_ip, int source_port,
+ uint32_t dest_ip, int dest_port,
+ const uint8_t *dest_arp, int ifindex, bool bcast)
{
struct sockaddr_ll dest;
struct ip_udp_dhcp_packet packet;
@@ -529,7 +530,8 @@ int dhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
if (fd < 0)
return -errno;
- dhcp_pkt->flags |= htons(BROADCAST_FLAG);
+ if (bcast)
+ dhcp_pkt->flags |= htons(BROADCAST_FLAG);
memset(&dest, 0, sizeof(dest));
memset(&packet, 0, sizeof(packet));
diff --git a/gdhcp/common.h b/gdhcp/common.h
index e4a42519..c6927992 100644
--- a/gdhcp/common.h
+++ b/gdhcp/common.h
@@ -196,7 +196,8 @@ void dhcpv6_init_header(struct dhcpv6_packet *packet, uint8_t type);
int dhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
uint32_t source_ip, int source_port,
uint32_t dest_ip, int dest_port,
- const uint8_t *dest_arp, int ifindex);
+ const uint8_t *dest_arp, int ifindex,
+ bool bcast);
int dhcpv6_send_packet(int index, struct dhcpv6_packet *dhcp_pkt, int len);
int dhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt,
uint32_t source_ip, int source_port,
diff --git a/gdhcp/server.c b/gdhcp/server.c
index 728992da..aa40488c 100644
--- a/gdhcp/server.c
+++ b/gdhcp/server.c
@@ -529,7 +529,7 @@ static void send_packet_to_client(GDHCPServer *dhcp_server,
dhcp_send_raw_packet(dhcp_pkt,
dhcp_server->server_nip, SERVER_PORT,
ciaddr, CLIENT_PORT, chaddr,
- dhcp_server->ifindex);
+ dhcp_server->ifindex, false);
}
static void send_offer(GDHCPServer *dhcp_server,
@@ -626,7 +626,7 @@ static void send_NAK(GDHCPServer *dhcp_server,
dhcp_send_raw_packet(&packet,
dhcp_server->server_nip, SERVER_PORT,
INADDR_BROADCAST, CLIENT_PORT, MAC_BCAST_ADDR,
- dhcp_server->ifindex);
+ dhcp_server->ifindex, false);
}
static void send_inform(GDHCPServer *dhcp_server,
diff --git a/gsupplicant/dbus.c b/gsupplicant/dbus.c
index 5b15bcdc..2957979a 100644
--- a/gsupplicant/dbus.c
+++ b/gsupplicant/dbus.c
@@ -59,10 +59,35 @@ static int find_method_call_by_caller(gconstpointer a, gconstpointer b)
return method_call->caller != caller;
}
+static GSList *property_calls;
+
+struct property_call_data {
+ gpointer caller;
+ DBusPendingCall *pending_call;
+ supplicant_dbus_property_function function;
+ void *user_data;
+};
+
+static void property_call_free(void *pointer)
+{
+ struct property_call_data *property_call = pointer;
+ property_calls = g_slist_remove(property_calls, property_call);
+ g_free(property_call);
+}
+
+static int find_property_call_by_caller(gconstpointer a, gconstpointer b)
+{
+ const struct property_call_data *property_call = a;
+ gconstpointer caller = b;
+
+ return property_call->caller != caller;
+}
+
void supplicant_dbus_setup(DBusConnection *conn)
{
connection = conn;
method_calls = NULL;
+ property_calls = NULL;
}
void supplicant_dbus_array_foreach(DBusMessageIter *iter,
@@ -124,14 +149,27 @@ void supplicant_dbus_property_foreach(DBusMessageIter *iter,
}
}
-struct property_get_data {
- supplicant_dbus_property_function function;
- void *user_data;
-};
+void supplicant_dbus_property_call_cancel_all(gpointer caller)
+{
+ while (property_calls) {
+ struct property_call_data *property_call;
+ GSList *elem = g_slist_find_custom(property_calls, caller,
+ find_property_call_by_caller);
+ if (!elem)
+ break;
+
+ property_call = elem->data;
+ property_calls = g_slist_delete_link(property_calls, elem);
+
+ dbus_pending_call_cancel(property_call->pending_call);
+
+ dbus_pending_call_unref(property_call->pending_call);
+ }
+}
static void property_get_all_reply(DBusPendingCall *call, void *user_data)
{
- struct property_get_data *data = user_data;
+ struct property_call_data *property_call = user_data;
DBusMessage *reply;
DBusMessageIter iter;
@@ -143,11 +181,11 @@ static void property_get_all_reply(DBusPendingCall *call, void *user_data)
if (!dbus_message_iter_init(reply, &iter))
goto done;
- supplicant_dbus_property_foreach(&iter, data->function,
- data->user_data);
+ supplicant_dbus_property_foreach(&iter, property_call->function,
+ property_call->user_data);
- if (data->function)
- data->function(NULL, NULL, data->user_data);
+ if (property_call->function)
+ property_call->function(NULL, NULL, property_call->user_data);
done:
dbus_message_unref(reply);
@@ -157,9 +195,9 @@ done:
int supplicant_dbus_property_get_all(const char *path, const char *interface,
supplicant_dbus_property_function function,
- void *user_data)
+ void *user_data, gpointer caller)
{
- struct property_get_data *data;
+ struct property_call_data *property_call = NULL;
DBusMessage *message;
DBusPendingCall *call;
@@ -169,14 +207,14 @@ int supplicant_dbus_property_get_all(const char *path, const char *interface,
if (!path || !interface)
return -EINVAL;
- data = dbus_malloc0(sizeof(*data));
- if (!data)
+ property_call = g_try_new0(struct property_call_data, 1);
+ if (!property_call)
return -ENOMEM;
message = dbus_message_new_method_call(SUPPLICANT_SERVICE, path,
DBUS_INTERFACE_PROPERTIES, "GetAll");
if (!message) {
- dbus_free(data);
+ g_free(property_call);
return -ENOMEM;
}
@@ -187,21 +225,25 @@ int supplicant_dbus_property_get_all(const char *path, const char *interface,
if (!dbus_connection_send_with_reply(connection, message,
&call, TIMEOUT)) {
dbus_message_unref(message);
- dbus_free(data);
+ g_free(property_call);
return -EIO;
}
if (!call) {
dbus_message_unref(message);
- dbus_free(data);
+ g_free(property_call);
return -EIO;
}
- data->function = function;
- data->user_data = user_data;
+ property_call->caller = caller;
+ property_call->pending_call = call;
+ property_call->function = function;
+ property_call->user_data = user_data;
+
+ property_calls = g_slist_prepend(property_calls, property_call);
dbus_pending_call_set_notify(call, property_get_all_reply,
- data, dbus_free);
+ property_call, property_call_free);
dbus_message_unref(message);
@@ -210,7 +252,7 @@ int supplicant_dbus_property_get_all(const char *path, const char *interface,
static void property_get_reply(DBusPendingCall *call, void *user_data)
{
- struct property_get_data *data = user_data;
+ struct property_call_data *property_call = user_data;
DBusMessage *reply;
DBusMessageIter iter;
@@ -227,8 +269,9 @@ static void property_get_reply(DBusPendingCall *call, void *user_data)
dbus_message_iter_recurse(&iter, &variant);
- if (data->function)
- data->function(NULL, &variant, data->user_data);
+ if (property_call->function)
+ property_call->function(NULL, &variant,
+ property_call->user_data);
}
done:
dbus_message_unref(reply);
@@ -239,9 +282,9 @@ done:
int supplicant_dbus_property_get(const char *path, const char *interface,
const char *method,
supplicant_dbus_property_function function,
- void *user_data)
+ void *user_data, gpointer caller)
{
- struct property_get_data *data;
+ struct property_call_data *property_call = NULL;
DBusMessage *message;
DBusPendingCall *call;
@@ -251,15 +294,15 @@ int supplicant_dbus_property_get(const char *path, const char *interface,
if (!path || !interface || !method)
return -EINVAL;
- data = dbus_malloc0(sizeof(*data));
- if (!data)
+ property_call = g_try_new0(struct property_call_data, 1);
+ if (!property_call)
return -ENOMEM;
message = dbus_message_new_method_call(SUPPLICANT_SERVICE, path,
DBUS_INTERFACE_PROPERTIES, "Get");
if (!message) {
- dbus_free(data);
+ g_free(property_call);
return -ENOMEM;
}
@@ -271,35 +314,34 @@ int supplicant_dbus_property_get(const char *path, const char *interface,
if (!dbus_connection_send_with_reply(connection, message,
&call, TIMEOUT)) {
dbus_message_unref(message);
- dbus_free(data);
+ g_free(property_call);
return -EIO;
}
if (!call) {
dbus_message_unref(message);
- dbus_free(data);
+ g_free(property_call);
return -EIO;
}
- data->function = function;
- data->user_data = user_data;
+ property_call->caller = caller;
+ property_call->pending_call = call;
+ property_call->function = function;
+ property_call->user_data = user_data;
+
+ property_calls = g_slist_prepend(property_calls, property_call);
dbus_pending_call_set_notify(call, property_get_reply,
- data, dbus_free);
+ property_call, property_call_free);
dbus_message_unref(message);
return 0;
}
-struct property_set_data {
- supplicant_dbus_result_function function;
- void *user_data;
-};
-
static void property_set_reply(DBusPendingCall *call, void *user_data)
{
- struct property_set_data *data = user_data;
+ struct property_call_data *property_call = user_data;
DBusMessage *reply;
DBusMessageIter iter;
const char *error;
@@ -313,8 +355,8 @@ static void property_set_reply(DBusPendingCall *call, void *user_data)
dbus_message_iter_init(reply, &iter);
- if (data->function)
- data->function(error, &iter, data->user_data);
+ if (property_call->function)
+ property_call->function(error, &iter, property_call->user_data);
dbus_message_unref(reply);
@@ -325,9 +367,9 @@ int supplicant_dbus_property_set(const char *path, const char *interface,
const char *key, const char *signature,
supplicant_dbus_setup_function setup,
supplicant_dbus_result_function function,
- void *user_data)
+ void *user_data, gpointer caller)
{
- struct property_set_data *data;
+ struct property_call_data *property_call = NULL;
DBusMessage *message;
DBusMessageIter iter, value;
DBusPendingCall *call;
@@ -341,14 +383,14 @@ int supplicant_dbus_property_set(const char *path, const char *interface,
if (!key || !signature || !setup)
return -EINVAL;
- data = dbus_malloc0(sizeof(*data));
- if (!data)
+ property_call = g_try_new0(struct property_call_data, 1);
+ if (!property_call)
return -ENOMEM;
message = dbus_message_new_method_call(SUPPLICANT_SERVICE, path,
DBUS_INTERFACE_PROPERTIES, "Set");
if (!message) {
- dbus_free(data);
+ g_free(property_call);
return -ENOMEM;
}
@@ -366,21 +408,25 @@ int supplicant_dbus_property_set(const char *path, const char *interface,
if (!dbus_connection_send_with_reply(connection, message,
&call, TIMEOUT)) {
dbus_message_unref(message);
- dbus_free(data);
+ g_free(property_call);
return -EIO;
}
if (!call) {
dbus_message_unref(message);
- dbus_free(data);
+ g_free(property_call);
return -EIO;
}
- data->function = function;
- data->user_data = user_data;
+ property_call->caller = caller;
+ property_call->pending_call = call;
+ property_call->function = function;
+ property_call->user_data = user_data;
+
+ property_calls = g_slist_prepend(property_calls, property_call);
dbus_pending_call_set_notify(call, property_set_reply,
- data, dbus_free);
+ property_call, property_call_free);
dbus_message_unref(message);
@@ -578,6 +624,7 @@ void supplicant_dbus_property_append_array(DBusMessageIter *iter,
switch (type) {
case DBUS_TYPE_STRING:
+ case DBUS_TYPE_BYTE:
variant_sig = DBUS_TYPE_ARRAY_AS_STRING
DBUS_TYPE_ARRAY_AS_STRING
DBUS_TYPE_BYTE_AS_STRING;
diff --git a/gsupplicant/dbus.h b/gsupplicant/dbus.h
index 0117a1c8..3a904069 100644
--- a/gsupplicant/dbus.h
+++ b/gsupplicant/dbus.h
@@ -54,27 +54,29 @@ void supplicant_dbus_property_foreach(DBusMessageIter *iter,
int supplicant_dbus_property_get_all(const char *path, const char *interface,
supplicant_dbus_property_function function,
- void *user_data);
+ void *user_data, gpointer caller);
int supplicant_dbus_property_get(const char *path, const char *interface,
const char *method,
supplicant_dbus_property_function function,
- void *user_data);
+ void *user_data, gpointer caller);
int supplicant_dbus_property_set(const char *path, const char *interface,
const char *key, const char *signature,
supplicant_dbus_setup_function setup,
supplicant_dbus_result_function function,
- void *user_data);
+ void *user_data, gpointer caller);
+
+void supplicant_dbus_property_call_cancel_all(gpointer caller);
int supplicant_dbus_method_call(const char *path,
const char *interface, const char *method,
supplicant_dbus_setup_function setup,
supplicant_dbus_result_function function,
void *user_data,
- void *caller);
+ gpointer caller);
-void supplicant_dbus_method_call_cancel_all(void *caller);
+void supplicant_dbus_method_call_cancel_all(gpointer caller);
void supplicant_dbus_property_append_basic(DBusMessageIter *iter,
const char *key, int type, void *val);
diff --git a/gsupplicant/gsupplicant.h b/gsupplicant/gsupplicant.h
index a5ec4059..e49aaa6a 100644
--- a/gsupplicant/gsupplicant.h
+++ b/gsupplicant/gsupplicant.h
@@ -49,6 +49,7 @@ extern "C" {
#define G_SUPPLICANT_CAPABILITY_MODE_INFRA (1 << 0)
#define G_SUPPLICANT_CAPABILITY_MODE_IBSS (1 << 1)
#define G_SUPPLICANT_CAPABILITY_MODE_AP (1 << 2)
+#define G_SUPPLICANT_CAPABILITY_MODE_P2P (1 << 3)
#define G_SUPPLICANT_KEYMGMT_NONE (1 << 0)
#define G_SUPPLICANT_KEYMGMT_IEEE8021X (1 << 1)
@@ -78,6 +79,11 @@ extern "C" {
#define G_SUPPLICANT_WPS_PIN (1 << 2)
#define G_SUPPLICANT_WPS_REGISTRAR (1 << 3)
+#define G_SUPPLICANT_WPS_CONFIG_PBC 0x0080
+
+#define G_SUPPLICANT_GROUP_ROLE_CLIENT (1 << 0)
+#define G_SUPPLICANT_GROUP_ROLE_GO (1 << 1)
+
typedef enum {
G_SUPPLICANT_MODE_UNKNOWN,
G_SUPPLICANT_MODE_INFRA,
@@ -113,6 +119,16 @@ typedef enum {
G_SUPPLICANT_WPS_STATE_FAIL,
} GSupplicantWpsState;
+typedef enum {
+ G_SUPPLICANT_PEER_SERVICES_CHANGED,
+ G_SUPPLICANT_PEER_GROUP_CHANGED,
+ G_SUPPLICANT_PEER_GROUP_STARTED,
+ G_SUPPLICANT_PEER_GROUP_FINISHED,
+ G_SUPPLICANT_PEER_GROUP_JOINED,
+ G_SUPPLICANT_PEER_GROUP_DISCONNECTED,
+ G_SUPPLICANT_PEER_GROUP_FAILED,
+} GSupplicantPeerState;
+
struct _GSupplicantSSID {
const void *ssid;
unsigned int ssid_len;
@@ -154,6 +170,27 @@ struct _GSupplicantScanParams {
typedef struct _GSupplicantScanParams GSupplicantScanParams;
+struct _GSupplicantPeerParams {
+ bool master;
+ char *wps_pin;
+ char *path;
+};
+
+typedef struct _GSupplicantPeerParams GSupplicantPeerParams;
+
+struct _GSupplicantP2PServiceParams {
+ int version;
+ char *service;
+ unsigned char *query;
+ int query_length;
+ unsigned char *response;
+ int response_length;
+ unsigned char *wfd_ies;
+ int wfd_ies_length;
+};
+
+typedef struct _GSupplicantP2PServiceParams GSupplicantP2PServiceParams;
+
/* global API */
typedef void (*GSupplicantCountryCallback) (int result,
const char *alpha2,
@@ -165,13 +202,17 @@ int g_supplicant_set_country(const char *alpha2,
/* Interface API */
struct _GSupplicantInterface;
+struct _GSupplicantPeer;
typedef struct _GSupplicantInterface GSupplicantInterface;
+typedef struct _GSupplicantPeer GSupplicantPeer;
typedef void (*GSupplicantInterfaceCallback) (int result,
GSupplicantInterface *interface,
void *user_data);
+void g_supplicant_interface_cancel(GSupplicantInterface *interface);
+
int g_supplicant_interface_create(const char *ifname, const char *driver,
const char *bridge,
GSupplicantInterfaceCallback callback,
@@ -195,6 +236,29 @@ int g_supplicant_interface_p2p_find(GSupplicantInterface *interface,
int g_supplicant_interface_p2p_stop_find(GSupplicantInterface *interface);
+int g_supplicant_interface_p2p_connect(GSupplicantInterface *interface,
+ GSupplicantPeerParams *peer_params,
+ GSupplicantInterfaceCallback callback,
+ void *user_data);
+
+int g_supplicant_interface_p2p_disconnect(GSupplicantInterface *interface,
+ GSupplicantPeerParams *peer_params);
+
+int g_supplicant_interface_p2p_listen(GSupplicantInterface *interface,
+ int period, int interval);
+
+int g_supplicant_interface_p2p_add_service(GSupplicantInterface *interface,
+ GSupplicantInterfaceCallback callback,
+ GSupplicantP2PServiceParams *p2p_service_params,
+ void *user_data);
+
+int g_supplicant_interface_p2p_del_service(GSupplicantInterface *interface,
+ GSupplicantP2PServiceParams *p2p_service_params);
+
+int g_supplicant_set_widi_ies(GSupplicantP2PServiceParams *p2p_service_params,
+ GSupplicantInterfaceCallback callback,
+ void *user_data);
+
int g_supplicant_interface_connect(GSupplicantInterface *interface,
GSupplicantSSID *ssid,
GSupplicantInterfaceCallback callback,
@@ -229,13 +293,19 @@ int g_supplicant_interface_set_country(GSupplicantInterface *interface,
const char *alpha2,
void *user_data);
bool g_supplicant_interface_has_p2p(GSupplicantInterface *interface);
+int g_supplicant_interface_set_p2p_device_config(GSupplicantInterface *interface,
+ const char *device_name,
+ const char *primary_dev_type);
+GSupplicantPeer *g_supplicant_interface_peer_lookup(GSupplicantInterface *interface,
+ const char *identifier);
+bool g_supplicant_interface_is_p2p_finding(GSupplicantInterface *interface);
/* Network and Peer API */
struct _GSupplicantNetwork;
-struct _GSupplicantPeer;
+struct _GSupplicantGroup;
typedef struct _GSupplicantNetwork GSupplicantNetwork;
-typedef struct _GSupplicantPeer GSupplicantPeer;
+typedef struct _GSupplicantGroup GSupplicantGroup;
GSupplicantInterface *g_supplicant_network_get_interface(GSupplicantNetwork *network);
const char *g_supplicant_network_get_name(GSupplicantNetwork *network);
@@ -253,9 +323,18 @@ dbus_bool_t g_supplicant_network_is_wps_pbc(GSupplicantNetwork *network);
dbus_bool_t g_supplicant_network_is_wps_advertizing(GSupplicantNetwork *network);
GSupplicantInterface *g_supplicant_peer_get_interface(GSupplicantPeer *peer);
+const char *g_supplicant_peer_get_path(GSupplicantPeer *peer);
const char *g_supplicant_peer_get_identifier(GSupplicantPeer *peer);
const void *g_supplicant_peer_get_device_address(GSupplicantPeer *peer);
const char *g_supplicant_peer_get_name(GSupplicantPeer *peer);
+const unsigned char *g_supplicant_peer_get_widi_ies(GSupplicantPeer *peer,
+ int *length);
+bool g_supplicant_peer_is_wps_pbc(GSupplicantPeer *peer);
+bool g_supplicant_peer_is_wps_pin(GSupplicantPeer *peer);
+bool g_supplicant_peer_is_in_a_group(GSupplicantPeer *peer);
+GSupplicantInterface *g_supplicant_peer_get_group_interface(GSupplicantPeer *peer);
+bool g_supplicant_peer_is_client(GSupplicantPeer *peer);
+bool g_supplicant_peer_has_requested_connection(GSupplicantPeer *peer);
struct _GSupplicantCallbacks {
void (*system_ready) (void);
@@ -272,6 +351,9 @@ struct _GSupplicantCallbacks {
const char *property);
void (*peer_found) (GSupplicantPeer *peer);
void (*peer_lost) (GSupplicantPeer *peer);
+ void (*peer_changed) (GSupplicantPeer *peer,
+ GSupplicantPeerState state);
+ void (*peer_request) (GSupplicantPeer *peer);
void (*debug) (const char *str);
};
diff --git a/gsupplicant/supplicant.c b/gsupplicant/supplicant.c
index 2674298a..909a6178 100644
--- a/gsupplicant/supplicant.c
+++ b/gsupplicant/supplicant.c
@@ -31,6 +31,8 @@
#include <syslog.h>
#include <ctype.h>
#include <stdbool.h>
+#include <netinet/if_ether.h>
+#include <netinet/in.h>
#include <glib.h>
#include <gdbus.h>
@@ -130,11 +132,15 @@ static struct strvalmap mode_capa_map[] = {
{ "infrastructure", G_SUPPLICANT_CAPABILITY_MODE_INFRA },
{ "ad-hoc", G_SUPPLICANT_CAPABILITY_MODE_IBSS },
{ "ap", G_SUPPLICANT_CAPABILITY_MODE_AP },
+ { "p2p", G_SUPPLICANT_CAPABILITY_MODE_P2P },
{ }
};
static GHashTable *interface_table;
static GHashTable *bss_mapping;
+static GHashTable *peer_mapping;
+static GHashTable *group_mapping;
+static GHashTable *pending_peer_connection;
struct _GSupplicantWpsCredentials {
unsigned char ssid[32];
@@ -153,7 +159,6 @@ struct _GSupplicantInterface {
unsigned int scan_capa;
unsigned int mode_capa;
unsigned int max_scan_ssids;
- bool p2p_checked;
bool p2p_support;
bool p2p_finding;
dbus_bool_t ready;
@@ -169,9 +174,11 @@ struct _GSupplicantInterface {
GSupplicantWpsState wps_state;
GHashTable *network_table;
GHashTable *peer_table;
+ GHashTable *group_table;
GHashTable *net_mapping;
GHashTable *bss_mapping;
void *data;
+ const char *pending_peer_path;
};
struct g_supplicant_bss {
@@ -220,9 +227,24 @@ struct _GSupplicantNetwork {
struct _GSupplicantPeer {
GSupplicantInterface *interface;
char *path;
- unsigned char device_address[6];
+ unsigned char device_address[ETH_ALEN];
+ unsigned char iface_address[ETH_ALEN];
char *name;
+ unsigned char *widi_ies;
+ int widi_ies_length;
char *identifier;
+ unsigned int wps_capabilities;
+ GSList *groups;
+ const GSupplicantInterface *current_group_iface;
+ bool connection_requested;
+};
+
+struct _GSupplicantGroup {
+ GSupplicantInterface *interface;
+ GSupplicantInterface *orig_interface;
+ char *path;
+ int role;
+ GSList *members;
};
static inline void debug(const char *format, ...)
@@ -390,11 +412,9 @@ static void callback_p2p_support(GSupplicantInterface *interface)
{
SUPPLICANT_DBG("");
- if (interface->p2p_checked)
+ if (!interface->p2p_support)
return;
- interface->p2p_checked = true;
-
if (callbacks_pointer && callbacks_pointer->p2p_support)
callbacks_pointer->p2p_support(interface);
}
@@ -477,6 +497,42 @@ static void callback_peer_lost(GSupplicantPeer *peer)
callbacks_pointer->peer_lost(peer);
}
+static void callback_peer_changed(GSupplicantPeer *peer,
+ GSupplicantPeerState state)
+{
+ if (!callbacks_pointer)
+ return;
+
+ if (!callbacks_pointer->peer_changed)
+ return;
+
+ callbacks_pointer->peer_changed(peer, state);
+}
+
+static void callback_peer_request(GSupplicantPeer *peer)
+{
+ if (!callbacks_pointer)
+ return;
+
+ if (!callbacks_pointer->peer_request)
+ return;
+
+ peer->connection_requested = true;
+
+ callbacks_pointer->peer_request(peer);
+}
+
+static void remove_group(gpointer data)
+{
+ GSupplicantGroup *group = data;
+
+ if (group->members)
+ g_slist_free_full(group->members, g_free);
+
+ g_free(group->path);
+ g_free(group);
+}
+
static void remove_interface(gpointer data)
{
GSupplicantInterface *interface = data;
@@ -485,6 +541,7 @@ static void remove_interface(gpointer data)
g_hash_table_destroy(interface->net_mapping);
g_hash_table_destroy(interface->network_table);
g_hash_table_destroy(interface->peer_table);
+ g_hash_table_destroy(interface->group_table);
if (interface->scan_callback) {
SUPPLICANT_DBG("call interface %p callback %p scanning %d",
@@ -540,6 +597,17 @@ static void remove_peer(gpointer data)
{
GSupplicantPeer *peer = data;
+ callback_peer_lost(peer);
+
+ if (peer->groups)
+ g_slist_free_full(peer->groups, g_free);
+
+ if (peer_mapping)
+ g_hash_table_remove(peer_mapping, peer->path);
+
+ if (pending_peer_connection)
+ g_hash_table_remove(pending_peer_connection, peer->path);
+
g_free(peer->path);
g_free(peer->name);
g_free(peer->identifier);
@@ -733,7 +801,7 @@ int g_supplicant_interface_set_apscan(GSupplicantInterface *interface,
return supplicant_dbus_property_set(interface->path,
SUPPLICANT_INTERFACE ".Interface",
"ApScan", DBUS_TYPE_UINT32_AS_STRING,
- set_apscan, NULL, &ap_scan);
+ set_apscan, NULL, &ap_scan, NULL);
}
void g_supplicant_interface_set_data(GSupplicantInterface *interface,
@@ -850,7 +918,7 @@ int g_supplicant_interface_enable_selected_network(GSupplicantInterface *interfa
return supplicant_dbus_property_set(interface->network_path,
SUPPLICANT_INTERFACE ".Network",
"Enabled", DBUS_TYPE_BOOLEAN_AS_STRING,
- set_network_enabled, NULL, &enable);
+ set_network_enabled, NULL, &enable, NULL);
}
dbus_bool_t g_supplicant_interface_get_ready(GSupplicantInterface *interface)
@@ -987,6 +1055,14 @@ GSupplicantInterface *g_supplicant_peer_get_interface(GSupplicantPeer *peer)
return peer->interface;
}
+const char *g_supplicant_peer_get_path(GSupplicantPeer *peer)
+{
+ if (!peer)
+ return NULL;
+
+ return peer->path;
+}
+
const char *g_supplicant_peer_get_identifier(GSupplicantPeer *peer)
{
if (!peer)
@@ -1011,6 +1087,88 @@ const char *g_supplicant_peer_get_name(GSupplicantPeer *peer)
return peer->name;
}
+const unsigned char *g_supplicant_peer_get_widi_ies(GSupplicantPeer *peer,
+ int *length)
+{
+ if (!peer || !length)
+ return NULL;
+
+ *length = peer->widi_ies_length;
+ return peer->widi_ies;
+}
+
+bool g_supplicant_peer_is_wps_pbc(GSupplicantPeer *peer)
+{
+ if (!peer)
+ return false;
+
+ if (peer->wps_capabilities & G_SUPPLICANT_WPS_PBC)
+ return true;
+
+ return false;
+}
+
+bool g_supplicant_peer_is_wps_pin(GSupplicantPeer *peer)
+{
+ if (!peer)
+ return false;
+
+ if (peer->wps_capabilities & G_SUPPLICANT_WPS_PIN)
+ return true;
+
+ return false;
+}
+
+bool g_supplicant_peer_is_in_a_group(GSupplicantPeer *peer)
+{
+ if (!peer || !peer->groups)
+ return false;
+
+ return true;
+}
+
+GSupplicantInterface *g_supplicant_peer_get_group_interface(GSupplicantPeer *peer)
+{
+ if (!peer)
+ return NULL;
+
+ return (GSupplicantInterface *) peer->current_group_iface;
+}
+
+bool g_supplicant_peer_is_client(GSupplicantPeer *peer)
+{
+ GSupplicantGroup *group;
+ GSList *list;
+
+ if (!peer)
+ return false;
+
+ for (list = peer->groups; list; list = list->next) {
+ const char *path = list->data;
+
+ group = g_hash_table_lookup(group_mapping, path);
+ if (!group)
+ continue;
+
+ if (group->role != G_SUPPLICANT_GROUP_ROLE_CLIENT ||
+ group->orig_interface != peer->interface)
+ continue;
+
+ if (group->interface == peer->current_group_iface)
+ return true;
+ }
+
+ return false;
+}
+
+bool g_supplicant_peer_has_requested_connection(GSupplicantPeer *peer)
+{
+ if (!peer)
+ return false;
+
+ return peer->connection_requested;
+}
+
static void merge_network(GSupplicantNetwork *network)
{
GString *str;
@@ -1125,7 +1283,7 @@ static void interface_network_added(DBusMessageIter *iter, void *user_data)
supplicant_dbus_property_get_all(path,
SUPPLICANT_INTERFACE ".Network",
- network_property, network);
+ network_property, network, NULL);
}
static void interface_network_removed(DBusMessageIter *iter, void *user_data)
@@ -1695,7 +1853,7 @@ static void interface_bss_added_without_keys(DBusMessageIter *iter,
supplicant_dbus_property_get_all(bss->path,
SUPPLICANT_INTERFACE ".BSS",
- bss_property, bss);
+ bss_property, bss, NULL);
bss_compute_security(bss);
add_or_replace_bss_to_network(bss);
@@ -1715,7 +1873,7 @@ static void update_signal(gpointer key, gpointer value,
static void update_network_signal(GSupplicantNetwork *network)
{
- if (g_hash_table_size(network->bss_table) <= 1)
+ if (g_hash_table_size(network->bss_table) <= 1 && network->best_bss)
return;
g_hash_table_foreach(network->bss_table,
@@ -1728,6 +1886,7 @@ static void interface_bss_removed(DBusMessageIter *iter, void *user_data)
{
GSupplicantInterface *interface = user_data;
GSupplicantNetwork *network;
+ struct g_supplicant_bss *bss = NULL;
const char *path = NULL;
dbus_message_iter_get_basic(iter, &path);
@@ -1738,6 +1897,12 @@ static void interface_bss_removed(DBusMessageIter *iter, void *user_data)
if (!network)
return;
+ bss = g_hash_table_lookup(network->bss_table, path);
+ if (network->best_bss == bss) {
+ network->best_bss = NULL;
+ network->signal = 0;
+ }
+
g_hash_table_remove(bss_mapping, path);
g_hash_table_remove(interface->bss_mapping, path);
@@ -1749,6 +1914,14 @@ static void interface_bss_removed(DBusMessageIter *iter, void *user_data)
g_hash_table_remove(interface->network_table, network->group);
}
+static void set_config_methods(DBusMessageIter *iter, void *user_data)
+{
+ const char *config_methods = "push_button";
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+ &config_methods);
+}
+
static void interface_property(const char *key, DBusMessageIter *iter,
void *user_data)
{
@@ -1775,6 +1948,12 @@ static void interface_property(const char *key, DBusMessageIter *iter,
debug_strvalmap("Mode capability", mode_capa_map,
interface->mode_capa);
+
+ supplicant_dbus_property_set(interface->path,
+ SUPPLICANT_INTERFACE ".Interface.WPS",
+ "ConfigMethods", DBUS_TYPE_STRING_AS_STRING,
+ set_config_methods, NULL, NULL, NULL);
+
if (interface->ready)
callback_interface_added(interface);
@@ -1784,6 +1963,8 @@ static void interface_property(const char *key, DBusMessageIter *iter,
if (g_strcmp0(key, "Capabilities") == 0) {
supplicant_dbus_property_foreach(iter, interface_capability,
interface);
+ if (interface->mode_capa & G_SUPPLICANT_CAPABILITY_MODE_P2P)
+ interface->p2p_support = true;
} else if (g_strcmp0(key, "State") == 0) {
const char *str = NULL;
@@ -1911,6 +2092,8 @@ static GSupplicantInterface *interface_alloc(const char *path)
g_str_equal, NULL, remove_network);
interface->peer_table = g_hash_table_new_full(g_str_hash,
g_str_equal, NULL, remove_peer);
+ interface->group_table = g_hash_table_new_full(g_str_hash,
+ g_str_equal, NULL, remove_group);
interface->net_mapping = g_hash_table_new_full(g_str_hash, g_str_equal,
NULL, NULL);
interface->bss_mapping = g_hash_table_new_full(g_str_hash, g_str_equal,
@@ -1921,25 +2104,6 @@ static GSupplicantInterface *interface_alloc(const char *path)
return interface;
}
-static void interface_p2p_flush(const char *error,
- DBusMessageIter *iter, void *user_data)
-{
- GSupplicantInterface *interface = user_data;
-
- if (error) {
- if (!g_strcmp0(error,
- "org.freedesktop.DBus.Error.UnknownMethod")) {
- SUPPLICANT_DBG("wpa_supplicant does not support P2P");
- } else {
- SUPPLICANT_DBG("interface %s does not support P2P",
- interface->ifname);
- }
- } else
- interface->p2p_support = true;
-
- callback_p2p_support(interface);
-}
-
static void interface_added(DBusMessageIter *iter, void *user_data)
{
GSupplicantInterface *interface;
@@ -1962,10 +2126,6 @@ static void interface_added(DBusMessageIter *iter, void *user_data)
if (!interface)
return;
- supplicant_dbus_method_call(path,
- SUPPLICANT_INTERFACE ".Interface.P2PDevice", "Flush",
- NULL, interface_p2p_flush, interface, NULL);
-
dbus_message_iter_next(iter);
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INVALID) {
supplicant_dbus_property_foreach(iter, interface_property,
@@ -1976,7 +2136,8 @@ static void interface_added(DBusMessageIter *iter, void *user_data)
supplicant_dbus_property_get_all(path,
SUPPLICANT_INTERFACE ".Interface",
- interface_property, interface);
+ interface_property, interface,
+ interface);
}
static void interface_removed(DBusMessageIter *iter, void *user_data)
@@ -1989,8 +2150,7 @@ static void interface_removed(DBusMessageIter *iter, void *user_data)
return;
interface = g_hash_table_lookup(interface_table, path);
- SUPPLICANT_DBG("Cancelling any pending DBus calls");
- supplicant_dbus_method_call_cancel_all(interface);
+ g_supplicant_interface_cancel(interface);
g_hash_table_remove(interface_table, path);
}
@@ -2078,6 +2238,8 @@ static void signal_name_owner_changed(const char *path, DBusMessageIter *iter)
if (strlen(old) > 0 && strlen(new) == 0) {
system_available = FALSE;
g_hash_table_remove_all(bss_mapping);
+ g_hash_table_remove_all(peer_mapping);
+ g_hash_table_remove_all(group_mapping);
g_hash_table_remove_all(interface_table);
callback_system_killed();
}
@@ -2085,8 +2247,8 @@ static void signal_name_owner_changed(const char *path, DBusMessageIter *iter)
if (strlen(new) > 0 && strlen(old) == 0) {
system_available = TRUE;
supplicant_dbus_property_get_all(SUPPLICANT_PATH,
- SUPPLICANT_INTERFACE,
- service_property, NULL);
+ SUPPLICANT_INTERFACE,
+ service_property, NULL, NULL);
}
}
@@ -2163,7 +2325,7 @@ static void signal_scan_done(const char *path, DBusMessageIter *iter)
}
supplicant_dbus_property_get(path, SUPPLICANT_INTERFACE ".Interface",
- "BSSs", scan_bss_data, interface);
+ "BSSs", scan_bss_data, interface, interface);
}
static void signal_bss_added(const char *path, DBusMessageIter *iter)
@@ -2400,12 +2562,12 @@ static void signal_wps_event(const char *path, DBusMessageIter *iter)
static void create_peer_identifier(GSupplicantPeer *peer)
{
- const unsigned char test[6] = {};
+ const unsigned char test[ETH_ALEN] = {};
if (!peer)
return;
- if (!memcmp(peer->device_address, test, 6)) {
+ if (!memcmp(peer->device_address, test, ETH_ALEN)) {
peer->identifier = g_strdup(peer->name);
return;
}
@@ -2420,10 +2582,45 @@ static void create_peer_identifier(GSupplicantPeer *peer)
peer->device_address[5]);
}
+struct peer_property_data {
+ GSupplicantPeer *peer;
+ GSList *old_groups;
+ bool groups_changed;
+ bool services_changed;
+};
+
+static void peer_groups_relation(DBusMessageIter *iter, void *user_data)
+{
+ struct peer_property_data *data = user_data;
+ GSupplicantPeer *peer = data->peer;
+ GSupplicantGroup *group;
+ const char *str = NULL;
+ GSList *elem;
+
+ dbus_message_iter_get_basic(iter, &str);
+ if (!str)
+ return;
+
+ group = g_hash_table_lookup(group_mapping, str);
+ if (!group)
+ return;
+
+ elem = g_slist_find_custom(data->old_groups, str, g_str_equal);
+ if (elem) {
+ data->old_groups = g_slist_remove_link(data->old_groups, elem);
+ peer->groups = g_slist_concat(elem, peer->groups);
+ } else {
+ peer->groups = g_slist_prepend(peer->groups, g_strdup(str));
+ data->groups_changed = true;
+ }
+}
+
static void peer_property(const char *key, DBusMessageIter *iter,
void *user_data)
{
- GSupplicantPeer *peer = user_data;
+ GSupplicantPeer *pending_peer;
+ struct peer_property_data *data = user_data;
+ GSupplicantPeer *peer = data->peer;
SUPPLICANT_DBG("key: %s", key);
@@ -2434,6 +2631,16 @@ static void peer_property(const char *key, DBusMessageIter *iter,
if (peer->name) {
create_peer_identifier(peer);
callback_peer_found(peer);
+ pending_peer = g_hash_table_lookup(
+ pending_peer_connection, peer->path);
+
+ if (pending_peer && pending_peer == peer) {
+ callback_peer_request(peer);
+ g_hash_table_remove(pending_peer_connection,
+ peer->path);
+ }
+
+ dbus_free(data);
}
return;
@@ -2447,7 +2654,7 @@ static void peer_property(const char *key, DBusMessageIter *iter,
dbus_message_iter_recurse(iter, &array);
dbus_message_iter_get_fixed_array(&array, &dev_addr, &len);
- if (len == 6)
+ if (len == ETH_ALEN)
memcpy(peer->device_address, dev_addr, len);
} else if (g_strcmp0(key, "DeviceName") == 0) {
const char *str = NULL;
@@ -2455,11 +2662,55 @@ static void peer_property(const char *key, DBusMessageIter *iter,
dbus_message_iter_get_basic(iter, &str);
if (str)
peer->name = g_strdup(str);
+ } else if (g_strcmp0(key, "config_method") == 0) {
+ uint16_t wps_config;
+
+ dbus_message_iter_get_basic(iter, &wps_config);
+
+ if (wps_config & G_SUPPLICANT_WPS_CONFIG_PBC)
+ peer->wps_capabilities |= G_SUPPLICANT_WPS_PBC;
+ if (wps_config & ~G_SUPPLICANT_WPS_CONFIG_PBC)
+ peer->wps_capabilities |= G_SUPPLICANT_WPS_PIN;
+ } else if (g_strcmp0(key, "Groups") == 0) {
+ data->old_groups = peer->groups;
+ peer->groups = NULL;
+
+ supplicant_dbus_array_foreach(iter,
+ peer_groups_relation, data);
+ if (g_slist_length(data->old_groups) > 0) {
+ g_slist_free_full(data->old_groups, g_free);
+ data->groups_changed = true;
+ }
+ } else if (g_strcmp0(key, "IEs") == 0) {
+ DBusMessageIter array;
+ unsigned char *ie;
+ int ie_len;
+
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
+
+ if (!ie || ie_len < 2)
+ return;
+
+ if (peer->widi_ies) {
+ if (memcmp(peer->widi_ies, ie, ie_len) == 0)
+ return;
+
+ g_free(peer->widi_ies);
+ peer->widi_ies_length = 0;
+ }
+
+ peer->widi_ies = g_malloc0(ie_len * sizeof(unsigned char));
+
+ memcpy(peer->widi_ies, ie, ie_len);
+ peer->widi_ies_length = ie_len;
+ data->services_changed = true;
}
}
static void signal_peer_found(const char *path, DBusMessageIter *iter)
{
+ struct peer_property_data *property_data;
GSupplicantInterface *interface;
const char *obj_path = NULL;
GSupplicantPeer *peer;
@@ -2485,16 +2736,22 @@ static void signal_peer_found(const char *path, DBusMessageIter *iter)
peer->interface = interface;
peer->path = g_strdup(obj_path);
g_hash_table_insert(interface->peer_table, peer->path, peer);
+ g_hash_table_replace(peer_mapping, peer->path, interface);
+
+ property_data = dbus_malloc0(sizeof(struct peer_property_data));
+ property_data->peer = peer;
dbus_message_iter_next(iter);
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INVALID) {
- supplicant_dbus_property_foreach(iter, peer_property, peer);
- peer_property(NULL, NULL, peer);
+ supplicant_dbus_property_foreach(iter, peer_property,
+ property_data);
+ peer_property(NULL, NULL, property_data);
return;
}
supplicant_dbus_property_get_all(obj_path,
- SUPPLICANT_INTERFACE ".Peer", peer_property, peer);
+ SUPPLICANT_INTERFACE ".Peer",
+ peer_property, property_data, NULL);
}
static void signal_peer_lost(const char *path, DBusMessageIter *iter)
@@ -2517,10 +2774,330 @@ static void signal_peer_lost(const char *path, DBusMessageIter *iter)
if (!peer)
return;
- callback_peer_lost(peer);
g_hash_table_remove(interface->peer_table, obj_path);
}
+static void signal_peer_changed(const char *path, DBusMessageIter *iter)
+{
+ struct peer_property_data *property_data;
+ GSupplicantInterface *interface;
+ GSupplicantPeer *peer;
+
+ SUPPLICANT_DBG("");
+
+ interface = g_hash_table_lookup(peer_mapping, path);
+ if (!interface)
+ return;
+
+ peer = g_hash_table_lookup(interface->peer_table, path);
+ if (!peer) {
+ g_hash_table_remove(peer_mapping, path);
+ return;
+ }
+
+ property_data = dbus_malloc0(sizeof(struct peer_property_data));
+ property_data->peer = peer;
+
+ supplicant_dbus_property_foreach(iter, peer_property, property_data);
+ if (property_data->services_changed)
+ callback_peer_changed(peer,
+ G_SUPPLICANT_PEER_SERVICES_CHANGED);
+
+ if (property_data->groups_changed)
+ callback_peer_changed(peer, G_SUPPLICANT_PEER_GROUP_CHANGED);
+
+ dbus_free(property_data);
+
+ if (!g_supplicant_peer_is_in_a_group(peer))
+ peer->connection_requested = false;
+}
+
+struct group_sig_data {
+ const char *peer_obj_path;
+ unsigned char iface_address[ETH_ALEN];
+ const char *interface_obj_path;
+ const char *group_obj_path;
+ int role;
+};
+
+static void group_sig_property(const char *key, DBusMessageIter *iter,
+ void *user_data)
+{
+ struct group_sig_data *data = user_data;
+
+ if (!key)
+ return;
+
+ if (g_strcmp0(key, "peer_interface_addr") == 0) {
+ unsigned char *dev_addr;
+ DBusMessageIter array;
+ int len;
+
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &dev_addr, &len);
+
+ if (len == ETH_ALEN)
+ memcpy(data->iface_address, dev_addr, len);
+
+ } else if (g_strcmp0(key, "role") == 0) {
+ const char *str = NULL;
+
+ dbus_message_iter_get_basic(iter, &str);
+ if (g_strcmp0(str, "GO") == 0)
+ data->role = G_SUPPLICANT_GROUP_ROLE_GO;
+ else
+ data->role = G_SUPPLICANT_GROUP_ROLE_CLIENT;
+ } else if (g_strcmp0(key, "peer_object") == 0)
+ dbus_message_iter_get_basic(iter, &data->peer_obj_path);
+ else if (g_strcmp0(key, "interface_object") == 0)
+ dbus_message_iter_get_basic(iter, &data->interface_obj_path);
+ else if (g_strcmp0(key, "group_object") == 0)
+ dbus_message_iter_get_basic(iter, &data->group_obj_path);
+
+}
+
+static void signal_group_success(const char *path, DBusMessageIter *iter)
+{
+ GSupplicantInterface *interface;
+ struct group_sig_data data = {};
+ GSupplicantPeer *peer;
+
+ SUPPLICANT_DBG("");
+
+ interface = g_hash_table_lookup(interface_table, path);
+ if (!interface)
+ return;
+
+ supplicant_dbus_property_foreach(iter, group_sig_property, &data);
+ if (!data.peer_obj_path)
+ return;
+
+ peer = g_hash_table_lookup(interface->peer_table, data.peer_obj_path);
+ if (!peer)
+ return;
+
+ memcpy(peer->iface_address, data.iface_address, ETH_ALEN);
+ interface->pending_peer_path = peer->path;
+}
+
+static void signal_group_failure(const char *path, DBusMessageIter *iter)
+{
+ GSupplicantInterface *interface;
+ struct group_sig_data data = {};
+ GSupplicantPeer *peer;
+
+ SUPPLICANT_DBG("");
+
+ interface = g_hash_table_lookup(interface_table, path);
+ if (!interface)
+ return;
+
+ supplicant_dbus_property_foreach(iter, group_sig_property, &data);
+ if (!data.peer_obj_path)
+ return;
+
+ peer = g_hash_table_lookup(interface->peer_table, data.peer_obj_path);
+ if (!peer)
+ return;
+
+ callback_peer_changed(peer, G_SUPPLICANT_PEER_GROUP_FAILED);
+ peer->connection_requested = false;
+}
+
+static void signal_group_started(const char *path, DBusMessageIter *iter)
+{
+ GSupplicantInterface *interface, *g_interface;
+ struct group_sig_data data = {};
+ GSupplicantGroup *group;
+ GSupplicantPeer *peer;
+
+ SUPPLICANT_DBG("");
+
+ interface = g_hash_table_lookup(interface_table, path);
+ if (!interface)
+ return;
+
+ supplicant_dbus_property_foreach(iter, group_sig_property, &data);
+ if (!data.interface_obj_path || !data.group_obj_path)
+ return;
+
+ peer = g_hash_table_lookup(interface->peer_table,
+ interface->pending_peer_path);
+ interface->pending_peer_path = NULL;
+ if (!peer)
+ return;
+
+ g_interface = g_hash_table_lookup(interface_table,
+ data.interface_obj_path);
+ if (!g_interface)
+ return;
+
+ group = g_hash_table_lookup(interface->group_table,
+ data.group_obj_path);
+ if (group)
+ return;
+
+ group = g_try_new0(GSupplicantGroup, 1);
+ if (!group)
+ return;
+
+ group->interface = g_interface;
+ group->orig_interface = interface;
+ group->path = g_strdup(data.group_obj_path);
+ group->role = data.role;
+
+ g_hash_table_insert(interface->group_table, group->path, group);
+ g_hash_table_replace(group_mapping, group->path, group);
+
+ peer->current_group_iface = g_interface;
+ callback_peer_changed(peer, G_SUPPLICANT_PEER_GROUP_STARTED);
+}
+
+static void remove_peer_group_interface(GHashTable *group_table,
+ const char* path)
+{
+ GSupplicantGroup *group;
+ GHashTableIter iter;
+ gpointer value, key;
+
+ if (!group_table)
+ return;
+
+ group = g_hash_table_lookup(group_table, path);
+
+ if (!group || !group->orig_interface)
+ return;
+
+ g_hash_table_iter_init(&iter, group->orig_interface->peer_table);
+
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ GSupplicantPeer *peer = value;
+
+ if (peer->current_group_iface == group->interface)
+ peer->current_group_iface = NULL;
+ }
+}
+
+static void signal_group_finished(const char *path, DBusMessageIter *iter)
+{
+ GSupplicantInterface *interface;
+ struct group_sig_data data = {};
+
+ SUPPLICANT_DBG("");
+
+ interface = g_hash_table_lookup(interface_table, path);
+ if (!interface)
+ return;
+
+ supplicant_dbus_property_foreach(iter, group_sig_property, &data);
+ if (!data.interface_obj_path || !data.group_obj_path)
+ return;
+
+ remove_peer_group_interface(interface->group_table, data.group_obj_path);
+
+ g_hash_table_remove(group_mapping, data.group_obj_path);
+
+ g_hash_table_remove(interface->group_table, data.group_obj_path);
+}
+
+static void signal_group_request(const char *path, DBusMessageIter *iter)
+{
+ GSupplicantInterface *interface;
+ GSupplicantPeer *peer;
+ const char *obj_path;
+
+ SUPPLICANT_DBG("");
+
+ interface = g_hash_table_lookup(interface_table, path);
+ if (!interface)
+ return;
+
+ dbus_message_iter_get_basic(iter, &obj_path);
+ if (!obj_path || !g_strcmp0(obj_path, "/"))
+ return;
+
+ peer = g_hash_table_lookup(interface->peer_table, obj_path);
+ if (!peer)
+ return;
+
+ /*
+ * Peer has been previously found and property set,
+ * otherwise, defer connection to when peer property
+ * is set.
+ */
+ if (peer->identifier)
+ callback_peer_request(peer);
+ else
+ g_hash_table_replace(pending_peer_connection, peer->path, peer);
+}
+
+static void signal_group_peer_joined(const char *path, DBusMessageIter *iter)
+{
+ const char *peer_path = NULL;
+ GSupplicantInterface *interface;
+ GSupplicantGroup *group;
+ GSupplicantPeer *peer;
+
+ SUPPLICANT_DBG("");
+
+ group = g_hash_table_lookup(group_mapping, path);
+ if (!group)
+ return;
+
+ dbus_message_iter_get_basic(iter, &peer_path);
+ if (!peer_path)
+ return;
+
+ interface = g_hash_table_lookup(peer_mapping, peer_path);
+ if (!interface)
+ return;
+
+ peer = g_hash_table_lookup(interface->peer_table, peer_path);
+ if (!peer)
+ return;
+
+ group->members = g_slist_prepend(group->members, g_strdup(peer_path));
+
+ callback_peer_changed(peer, G_SUPPLICANT_PEER_GROUP_JOINED);
+}
+
+static void signal_group_peer_disconnected(const char *path, DBusMessageIter *iter)
+{
+ const char *peer_path = NULL;
+ GSupplicantInterface *interface;
+ GSupplicantGroup *group;
+ GSupplicantPeer *peer;
+ GSList *elem;
+
+ SUPPLICANT_DBG("");
+
+ group = g_hash_table_lookup(group_mapping, path);
+ if (!group)
+ return;
+
+ dbus_message_iter_get_basic(iter, &peer_path);
+ if (!peer_path)
+ return;
+
+ elem = g_slist_find_custom(group->members, peer_path, g_str_equal);
+ if (!elem)
+ return;
+
+ g_free(elem->data);
+ group->members = g_slist_delete_link(group->members, elem);
+
+ interface = g_hash_table_lookup(peer_mapping, peer_path);
+ if (!interface)
+ return;
+
+ peer = g_hash_table_lookup(interface->peer_table, peer_path);
+ if (!peer)
+ return;
+
+ callback_peer_changed(peer, G_SUPPLICANT_PEER_GROUP_DISCONNECTED);
+ peer->connection_requested = false;
+}
+
static struct {
const char *interface;
const char *member;
@@ -2548,6 +3125,17 @@ static struct {
{ SUPPLICANT_INTERFACE ".Interface.P2PDevice", "DeviceFound", signal_peer_found },
{ SUPPLICANT_INTERFACE ".Interface.P2PDevice", "DeviceLost", signal_peer_lost },
+ { SUPPLICANT_INTERFACE ".Peer", "PropertiesChanged", signal_peer_changed },
+
+ { SUPPLICANT_INTERFACE ".Interface.P2PDevice", "GONegotiationSuccess", signal_group_success },
+ { SUPPLICANT_INTERFACE ".Interface.P2PDevice", "GONegotiationFailure", signal_group_failure },
+ { SUPPLICANT_INTERFACE ".Interface.P2PDevice", "GroupStarted", signal_group_started },
+ { SUPPLICANT_INTERFACE ".Interface.P2PDevice", "GroupFinished", signal_group_finished },
+ { SUPPLICANT_INTERFACE ".Interface.P2PDevice", "GONegotiationRequest", signal_group_request },
+
+ { SUPPLICANT_INTERFACE ".Group", "PeerJoined", signal_group_peer_joined },
+ { SUPPLICANT_INTERFACE ".Group", "PeerDisconnected", signal_group_peer_disconnected },
+
{ }
};
@@ -2579,6 +3167,13 @@ static DBusHandlerResult g_supplicant_filter(DBusConnection *conn,
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
+void g_supplicant_interface_cancel(GSupplicantInterface *interface)
+{
+ SUPPLICANT_DBG("Cancelling any pending DBus calls");
+ supplicant_dbus_method_call_cancel_all(interface);
+ supplicant_dbus_property_call_cancel_all(interface);
+}
+
struct supplicant_regdom {
GSupplicantCountryCallback callback;
const char *alpha2;
@@ -2638,7 +3233,7 @@ int g_supplicant_set_country(const char *alpha2,
return supplicant_dbus_property_set(SUPPLICANT_PATH, SUPPLICANT_INTERFACE,
"Country", DBUS_TYPE_STRING_AS_STRING,
country_params, country_result,
- regdom);
+ regdom, NULL);
}
int g_supplicant_interface_set_country(GSupplicantInterface *interface,
@@ -2660,14 +3255,146 @@ int g_supplicant_interface_set_country(GSupplicantInterface *interface,
SUPPLICANT_INTERFACE ".Interface",
"Country", DBUS_TYPE_STRING_AS_STRING,
country_params, country_result,
- regdom);
+ regdom, NULL);
}
bool g_supplicant_interface_has_p2p(GSupplicantInterface *interface)
{
+ if (!interface)
+ return false;
+
return interface->p2p_support;
}
+struct supplicant_p2p_dev_config {
+ char *device_name;
+ char *dev_type;
+};
+
+static void p2p_device_config_result(const char *error,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct supplicant_p2p_dev_config *config = user_data;
+
+ if (error)
+ SUPPLICANT_DBG("Unable to set P2P Device configuration: %s",
+ error);
+
+ g_free(config->device_name);
+ g_free(config->dev_type);
+ dbus_free(config);
+}
+
+static int dev_type_str2bin(const char *type, unsigned char dev_type[8])
+{
+ int length, pos, end;
+ char b[3] = {};
+ char *e = NULL;
+
+ end = strlen(type);
+ for (length = pos = 0; type[pos] != '\0' && length < 8; length++) {
+ if (pos+2 > end)
+ return 0;
+
+ b[0] = type[pos];
+ b[1] = type[pos+1];
+
+ dev_type[length] = strtol(b, &e, 16);
+ if (e && *e != '\0')
+ return 0;
+
+ pos += 2;
+ }
+
+ return 8;
+}
+
+static void p2p_device_config_params(DBusMessageIter *iter, void *user_data)
+{
+ struct supplicant_p2p_dev_config *config = user_data;
+ DBusMessageIter dict;
+
+ supplicant_dbus_dict_open(iter, &dict);
+
+ supplicant_dbus_dict_append_basic(&dict, "DeviceName",
+ DBUS_TYPE_STRING, &config->device_name);
+
+ if (config->dev_type) {
+ unsigned char dev_type[8] = {}, *type;
+ int len;
+
+ len = dev_type_str2bin(config->dev_type, dev_type);
+ if (len) {
+ type = dev_type;
+ supplicant_dbus_dict_append_fixed_array(&dict,
+ "PrimaryDeviceType",
+ DBUS_TYPE_BYTE, &type, len);
+ }
+ }
+
+ supplicant_dbus_dict_close(iter, &dict);
+}
+
+int g_supplicant_interface_set_p2p_device_config(GSupplicantInterface *interface,
+ const char *device_name,
+ const char *primary_dev_type)
+{
+ struct supplicant_p2p_dev_config *config;
+ int ret;
+
+ SUPPLICANT_DBG("P2P Device settings %s/%s",
+ device_name, primary_dev_type);
+
+ config = dbus_malloc0(sizeof(*config));
+ if (!config)
+ return -ENOMEM;
+
+ config->device_name = g_strdup(device_name);
+ config->dev_type = g_strdup(primary_dev_type);
+
+ ret = supplicant_dbus_property_set(interface->path,
+ SUPPLICANT_INTERFACE ".Interface.P2PDevice",
+ "P2PDeviceConfig",
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ p2p_device_config_params,
+ p2p_device_config_result, config, NULL);
+ if (ret < 0) {
+ g_free(config->device_name);
+ g_free(config->dev_type);
+ dbus_free(config);
+ SUPPLICANT_DBG("Unable to set P2P Device configuration");
+ }
+
+ return ret;
+}
+
+static gboolean peer_lookup_by_identifier(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ const GSupplicantPeer *peer = value;
+ const char *identifier = user_data;
+
+ if (!g_strcmp0(identifier, peer->identifier))
+ return TRUE;
+
+ return FALSE;
+}
+
+GSupplicantPeer *g_supplicant_interface_peer_lookup(GSupplicantInterface *interface,
+ const char *identifier)
+{
+ GSupplicantPeer *peer;
+
+ peer = g_hash_table_find(interface->peer_table,
+ peer_lookup_by_identifier,
+ (void *) identifier);
+ return peer;
+}
+
struct interface_data {
GSupplicantInterface *interface;
char *path; /* Interface path cannot be taken from interface (above) as
@@ -2690,7 +3417,10 @@ struct interface_connect_data {
GSupplicantInterface *interface;
char *path;
GSupplicantInterfaceCallback callback;
- GSupplicantSSID *ssid;
+ union {
+ GSupplicantSSID *ssid;
+ GSupplicantPeerParams *peer;
+ };
void *user_data;
};
@@ -2737,8 +3467,10 @@ static void interface_create_property(const char *key, DBusMessageIter *iter,
GSupplicantInterface *interface = data->interface;
if (!key) {
- if (data->callback)
+ if (data->callback) {
data->callback(0, data->interface, data->user_data);
+ callback_p2p_support(interface);
+ }
interface_create_data_free(data);
}
@@ -2783,7 +3515,8 @@ static void interface_create_result(const char *error,
err = supplicant_dbus_property_get_all(path,
SUPPLICANT_INTERFACE ".Interface",
- interface_create_property, data);
+ interface_create_property, data,
+ NULL);
if (err == 0)
return;
@@ -2844,8 +3577,10 @@ static void interface_get_result(const char *error,
goto done;
}
- if (data->callback)
+ if (data->callback) {
data->callback(0, interface, data->user_data);
+ callback_p2p_support(interface);
+ }
interface_create_data_free(data);
@@ -2976,8 +3711,7 @@ int g_supplicant_interface_remove(GSupplicantInterface *interface,
if (!system_available)
return -EFAULT;
- SUPPLICANT_DBG("Cancelling any pending DBus calls");
- supplicant_dbus_method_call_cancel_all(interface);
+ g_supplicant_interface_cancel(interface);
data = dbus_malloc0(sizeof(*data));
if (!data)
@@ -3285,6 +4019,9 @@ static int parse_supplicant_error(DBusMessageIter *iter)
int err = -ECANCELED;
char *key;
+ if (!iter)
+ return err;
+
/* If the given passphrase is malformed wpa_s returns
* "invalid message format" but this error should be interpreted as
* invalid-key.
@@ -3908,7 +4645,7 @@ int g_supplicant_interface_connect(GSupplicantInterface *interface,
ret = supplicant_dbus_property_set(interface->path,
SUPPLICANT_INTERFACE ".Interface.WPS",
"ProcessCredentials", DBUS_TYPE_BOOLEAN_AS_STRING,
- wps_process_credentials, wps_start, data);
+ wps_process_credentials, wps_start, data, interface);
} else
ret = supplicant_dbus_method_call(interface->path,
SUPPLICANT_INTERFACE ".Interface", "AddNetwork",
@@ -4091,7 +4828,7 @@ int g_supplicant_interface_p2p_find(GSupplicantInterface *interface,
return -ENOTSUP;
ret = interface_ready_to_scan(interface);
- if (ret)
+ if (ret && ret != -EALREADY)
return ret;
data = dbus_malloc0(sizeof(*data));
@@ -4115,6 +4852,14 @@ int g_supplicant_interface_p2p_find(GSupplicantInterface *interface,
return ret;
}
+bool g_supplicant_interface_is_p2p_finding(GSupplicantInterface *interface)
+{
+ if (!interface)
+ return false;
+
+ return interface->p2p_finding;
+}
+
int g_supplicant_interface_p2p_stop_find(GSupplicantInterface *interface)
{
if (!interface->p2p_finding)
@@ -4129,6 +4874,355 @@ int g_supplicant_interface_p2p_stop_find(GSupplicantInterface *interface)
NULL, NULL, NULL, NULL);
}
+static void interface_p2p_connect_result(const char *error,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct interface_connect_data *data = user_data;
+ int err = 0;
+
+ SUPPLICANT_DBG("");
+
+ if (error)
+ err = parse_supplicant_error(iter);
+
+ if (data->callback)
+ data->callback(err, data->interface, data->user_data);
+
+ g_free(data->path);
+ g_free(data->peer->wps_pin);
+ g_free(data->peer->path);
+ g_free(data->peer);
+ g_free(data);
+}
+
+static void interface_p2p_connect_params(DBusMessageIter *iter, void *user_data)
+{
+ struct interface_connect_data *data = user_data;
+ const char *wps = "pbc";
+ DBusMessageIter dict;
+ int go_intent = 7;
+
+ SUPPLICANT_DBG("");
+
+ supplicant_dbus_dict_open(iter, &dict);
+
+ if (data->peer->master)
+ go_intent = 15;
+
+ if (data->peer->wps_pin)
+ wps = "pin";
+
+ supplicant_dbus_dict_append_basic(&dict, "peer",
+ DBUS_TYPE_OBJECT_PATH, &data->peer->path);
+ supplicant_dbus_dict_append_basic(&dict, "wps_method",
+ DBUS_TYPE_STRING, &wps);
+ if (data->peer->wps_pin) {
+ supplicant_dbus_dict_append_basic(&dict, "pin",
+ DBUS_TYPE_STRING, &data->peer->wps_pin);
+ }
+
+ supplicant_dbus_dict_append_basic(&dict, "go_intent",
+ DBUS_TYPE_INT32, &go_intent);
+
+ supplicant_dbus_dict_close(iter, &dict);
+}
+
+int g_supplicant_interface_p2p_connect(GSupplicantInterface *interface,
+ GSupplicantPeerParams *peer_params,
+ GSupplicantInterfaceCallback callback,
+ void *user_data)
+{
+ struct interface_connect_data *data;
+ int ret;
+
+ SUPPLICANT_DBG("");
+
+ if (!interface->p2p_support)
+ return -ENOTSUP;
+
+ data = dbus_malloc0(sizeof(*data));
+ data->interface = interface;
+ data->path = g_strdup(interface->path);
+ data->peer = peer_params;
+ data->callback = callback;
+ data->user_data = user_data;
+
+ ret = supplicant_dbus_method_call(interface->path,
+ SUPPLICANT_INTERFACE ".Interface.P2PDevice", "Connect",
+ interface_p2p_connect_params, interface_p2p_connect_result,
+ data, interface);
+ if (ret < 0) {
+ g_free(data->path);
+ dbus_free(data);
+ return ret;
+ }
+
+ return -EINPROGRESS;
+}
+
+int g_supplicant_interface_p2p_disconnect(GSupplicantInterface *interface,
+ GSupplicantPeerParams *peer_params)
+{
+ GSupplicantPeer *peer;
+ int count = 0;
+ GSList *list;
+
+ SUPPLICANT_DBG("");
+
+ if (!interface->p2p_support)
+ return -ENOTSUP;
+
+ peer = g_hash_table_lookup(interface->peer_table, peer_params->path);
+ if (!peer)
+ return -ENODEV;
+
+ for (list = peer->groups; list; list = list->next, count++) {
+ const char *group_obj_path = list->data;
+ GSupplicantInterface *g_interface;
+ GSupplicantGroup *group;
+
+ group = g_hash_table_lookup(group_mapping, group_obj_path);
+ if (!group || !group->interface)
+ continue;
+
+ g_interface = group->interface;
+ supplicant_dbus_method_call(g_interface->path,
+ SUPPLICANT_INTERFACE ".Interface.P2PDevice",
+ "Disconnect", NULL, NULL, NULL, g_interface);
+ }
+
+ if (count == 0 && peer->current_group_iface) {
+ supplicant_dbus_method_call(peer->current_group_iface->path,
+ SUPPLICANT_INTERFACE ".Interface.P2PDevice",
+ "Disconnect", NULL, NULL, NULL,
+ peer->current_group_iface->path);
+ }
+
+ peer->current_group_iface = NULL;
+
+ return -EINPROGRESS;
+}
+
+struct p2p_service_data {
+ bool registration;
+ GSupplicantInterface *interface;
+ GSupplicantP2PServiceParams *service;
+ GSupplicantInterfaceCallback callback;
+ void *user_data;
+};
+
+static void interface_p2p_service_result(const char *error,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct p2p_service_data *data = user_data;
+ int result = 0;
+
+ SUPPLICANT_DBG("%s result - %s", data->registration ?
+ "Registration" : "Deletion",
+ error ? error : "Success");
+ if (error)
+ result = -EINVAL;
+
+ if (data->callback)
+ data->callback(result, data->interface, data->user_data);
+
+ g_free(data->service->query);
+ g_free(data->service->response);
+ g_free(data->service->service);
+ g_free(data->service->wfd_ies);
+ g_free(data->service);
+ dbus_free(data);
+}
+
+static void interface_p2p_service_params(DBusMessageIter *iter,
+ void *user_data)
+{
+ struct p2p_service_data *data = user_data;
+ GSupplicantP2PServiceParams *service;
+ DBusMessageIter dict;
+ const char *type;
+
+ SUPPLICANT_DBG("");
+
+ service = data->service;
+
+ supplicant_dbus_dict_open(iter, &dict);
+
+ if (service->query && service->response) {
+ type = "bonjour";
+ supplicant_dbus_dict_append_basic(&dict, "service_type",
+ DBUS_TYPE_STRING, &type);
+ supplicant_dbus_dict_append_fixed_array(&dict, "query",
+ DBUS_TYPE_BYTE, &service->query,
+ service->query_length);
+ supplicant_dbus_dict_append_fixed_array(&dict, "response",
+ DBUS_TYPE_BYTE, &service->response,
+ service->response_length);
+ } else if (service->version && service->service) {
+ type = "upnp";
+ supplicant_dbus_dict_append_basic(&dict, "service_type",
+ DBUS_TYPE_STRING, &type);
+ supplicant_dbus_dict_append_basic(&dict, "version",
+ DBUS_TYPE_INT32, &service->version);
+ supplicant_dbus_dict_append_basic(&dict, "service",
+ DBUS_TYPE_STRING, &service->service);
+ }
+
+ supplicant_dbus_dict_close(iter, &dict);
+}
+
+int g_supplicant_interface_p2p_add_service(GSupplicantInterface *interface,
+ GSupplicantInterfaceCallback callback,
+ GSupplicantP2PServiceParams *p2p_service_params,
+ void *user_data)
+{
+ struct p2p_service_data *data;
+ int ret;
+
+ SUPPLICANT_DBG("");
+
+ if (!interface->p2p_support)
+ return -ENOTSUP;
+
+ data = dbus_malloc0(sizeof(*data));
+ data->registration = true;
+ data->interface = interface;
+ data->service = p2p_service_params;
+ data->callback = callback;
+ data->user_data = user_data;
+
+ ret = supplicant_dbus_method_call(interface->path,
+ SUPPLICANT_INTERFACE ".Interface.P2PDevice", "AddService",
+ interface_p2p_service_params, interface_p2p_service_result,
+ data, interface);
+ if (ret < 0) {
+ dbus_free(data);
+ return ret;
+ }
+
+ return -EINPROGRESS;
+}
+
+int g_supplicant_interface_p2p_del_service(GSupplicantInterface *interface,
+ GSupplicantP2PServiceParams *p2p_service_params)
+{
+ struct p2p_service_data *data;
+ int ret;
+
+ SUPPLICANT_DBG("");
+
+ if (!interface->p2p_support)
+ return -ENOTSUP;
+
+ data = dbus_malloc0(sizeof(*data));
+ data->interface = interface;
+ data->service = p2p_service_params;
+
+ ret = supplicant_dbus_method_call(interface->path,
+ SUPPLICANT_INTERFACE ".Interface.P2PDevice", "DeleteService",
+ interface_p2p_service_params, interface_p2p_service_result,
+ data, interface);
+ if (ret < 0) {
+ dbus_free(data);
+ return ret;
+ }
+
+ return -EINPROGRESS;
+}
+
+struct p2p_listen_data {
+ int period;
+ int interval;
+};
+
+static void interface_p2p_listen_params(DBusMessageIter *iter, void *user_data)
+{
+ struct p2p_listen_data *params = user_data;
+ DBusMessageIter dict;
+
+ supplicant_dbus_dict_open(iter, &dict);
+
+ supplicant_dbus_dict_append_basic(&dict, "period",
+ DBUS_TYPE_INT32, &params->period);
+ supplicant_dbus_dict_append_basic(&dict, "interval",
+ DBUS_TYPE_INT32, &params->interval);
+ supplicant_dbus_dict_close(iter, &dict);
+}
+
+int g_supplicant_interface_p2p_listen(GSupplicantInterface *interface,
+ int period, int interval)
+{
+ struct p2p_listen_data params;
+
+ SUPPLICANT_DBG("");
+
+ if (!interface->p2p_support)
+ return -ENOTSUP;
+
+ params.period = period;
+ params.interval = interval;
+
+ return supplicant_dbus_method_call(interface->path,
+ SUPPLICANT_INTERFACE ".Interface.P2PDevice",
+ "ExtendedListen", interface_p2p_listen_params,
+ NULL, &params, NULL);
+}
+
+static void widi_ies_params(DBusMessageIter *iter, void *user_data)
+{
+ struct p2p_service_data *data = user_data;
+ GSupplicantP2PServiceParams *service = data->service;
+ DBusMessageIter array;
+
+ SUPPLICANT_DBG("%p - %d", service->wfd_ies, service->wfd_ies_length);
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING, &array);
+
+ if (service->wfd_ies && service->wfd_ies_length > 0) {
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ &service->wfd_ies, service->wfd_ies_length);
+ }
+
+ dbus_message_iter_close_container(iter, &array);
+}
+
+int g_supplicant_set_widi_ies(GSupplicantP2PServiceParams *p2p_service_params,
+ GSupplicantInterfaceCallback callback,
+ void *user_data)
+{
+ struct p2p_service_data *data;
+ int ret;
+
+ SUPPLICANT_DBG("");
+
+ if (!system_available)
+ return -EFAULT;
+
+ data = dbus_malloc0(sizeof(*data));
+ data->service = p2p_service_params;
+ data->callback = callback;
+ data->user_data = user_data;
+
+ if (p2p_service_params->wfd_ies)
+ data->registration = true;
+
+ ret = supplicant_dbus_property_set(SUPPLICANT_PATH,
+ SUPPLICANT_INTERFACE, "WFDIEs",
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_BYTE_AS_STRING,
+ widi_ies_params,
+ interface_p2p_service_result,
+ data, NULL);
+ if (ret < 0 && ret != -EINPROGRESS) {
+ dbus_free(data);
+ return ret;
+ }
+
+ return -EINPROGRESS;
+}
+
+
static const char *g_supplicant_rule0 = "type=signal,"
"path=" DBUS_PATH_DBUS ","
"sender=" DBUS_SERVICE_DBUS ","
@@ -4147,6 +5241,10 @@ static const char *g_supplicant_rule5 = "type=signal,"
"interface=" SUPPLICANT_INTERFACE ".Network";
static const char *g_supplicant_rule6 = "type=signal,"
"interface=" SUPPLICANT_INTERFACE ".Interface.P2PDevice";
+static const char *g_supplicant_rule7 = "type=signal,"
+ "interface=" SUPPLICANT_INTERFACE ".Peer";
+static const char *g_supplicant_rule8 = "type=signal,"
+ "interface=" SUPPLICANT_INTERFACE ".Group";
static void invoke_introspect_method(void)
{
@@ -4186,6 +5284,12 @@ int g_supplicant_register(const GSupplicantCallbacks *callbacks)
bss_mapping = g_hash_table_new_full(g_str_hash, g_str_equal,
NULL, NULL);
+ peer_mapping = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, NULL);
+ group_mapping = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, NULL);
+ pending_peer_connection = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, NULL);
supplicant_dbus_setup(connection);
@@ -4196,6 +5300,8 @@ int g_supplicant_register(const GSupplicantCallbacks *callbacks)
dbus_bus_add_match(connection, g_supplicant_rule4, NULL);
dbus_bus_add_match(connection, g_supplicant_rule5, NULL);
dbus_bus_add_match(connection, g_supplicant_rule6, NULL);
+ dbus_bus_add_match(connection, g_supplicant_rule7, NULL);
+ dbus_bus_add_match(connection, g_supplicant_rule8, NULL);
dbus_connection_flush(connection);
if (dbus_bus_name_has_owner(connection,
@@ -4203,7 +5309,7 @@ int g_supplicant_register(const GSupplicantCallbacks *callbacks)
system_available = TRUE;
supplicant_dbus_property_get_all(SUPPLICANT_PATH,
SUPPLICANT_INTERFACE,
- service_property, NULL);
+ service_property, NULL, NULL);
} else
invoke_introspect_method();
@@ -4237,6 +5343,9 @@ void g_supplicant_unregister(const GSupplicantCallbacks *callbacks)
SUPPLICANT_DBG("");
if (connection) {
+ dbus_bus_remove_match(connection, g_supplicant_rule8, NULL);
+ dbus_bus_remove_match(connection, g_supplicant_rule7, NULL);
+ dbus_bus_remove_match(connection, g_supplicant_rule6, NULL);
dbus_bus_remove_match(connection, g_supplicant_rule5, NULL);
dbus_bus_remove_match(connection, g_supplicant_rule4, NULL);
dbus_bus_remove_match(connection, g_supplicant_rule3, NULL);
@@ -4254,8 +5363,15 @@ void g_supplicant_unregister(const GSupplicantCallbacks *callbacks)
bss_mapping = NULL;
}
- if (system_available)
- callback_system_killed();
+ if (peer_mapping) {
+ g_hash_table_destroy(peer_mapping);
+ peer_mapping = NULL;
+ }
+
+ if (group_mapping) {
+ g_hash_table_destroy(group_mapping);
+ group_mapping = NULL;
+ }
if (interface_table) {
g_hash_table_foreach(interface_table,
@@ -4264,6 +5380,9 @@ void g_supplicant_unregister(const GSupplicantCallbacks *callbacks)
interface_table = NULL;
}
+ if (system_available)
+ callback_system_killed();
+
if (connection) {
dbus_connection_unref(connection);
connection = NULL;
diff --git a/include/agent.h b/include/agent.h
index 05462df8..6961f7a1 100644
--- a/include/agent.h
+++ b/include/agent.h
@@ -52,6 +52,10 @@ void connman_agent_driver_unregister(struct connman_agent_driver *driver);
typedef void (* report_error_cb_t) (void *user_context,
bool retry, void *user_data);
+int connman_agent_report_error_full(void *user_context, const char *path,
+ const char *method, const char *error,
+ report_error_cb_t callback,
+ const char *dbus_sender, void *user_data);
int connman_agent_report_error(void *user_context, const char *path,
const char *error,
report_error_cb_t callback,
diff --git a/include/dbus.h b/include/dbus.h
index 253a23b2..26f94d63 100644
--- a/include/dbus.h
+++ b/include/dbus.h
@@ -188,6 +188,9 @@ int connman_dbus_get_selinux_context(DBusConnection *connection,
connman_dbus_get_context_cb_t func,
void *user_data);
+void connman_dbus_reply_pending(DBusMessage *pending,
+ int error, const char *path);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/inet.h b/include/inet.h
index a836a5bf..6482934a 100644
--- a/include/inet.h
+++ b/include/inet.h
@@ -36,13 +36,9 @@ extern "C" {
int connman_inet_ifindex(const char *name);
char *connman_inet_ifname(int index);
-short int connman_inet_ifflags(int index);
-
int connman_inet_ifup(int index);
int connman_inet_ifdown(int index);
-bool connman_inet_is_cfg80211(int index);
-
int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress);
int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress);
int connman_inet_add_host_route(int index, const char *host, const char *gateway);
diff --git a/include/inotify.h b/include/inotify.h
index 1f642abf..4bc63303 100644
--- a/include/inotify.h
+++ b/include/inotify.h
@@ -2,7 +2,7 @@
*
* Connection Manager
*
- * Copyright (C) 2012 BMW Car IT GmbH. All rights reserved.
+ * Copyright (C) 2012,2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/include/ipaddress.h b/include/ipaddress.h
index 4ced3184..3655ca86 100644
--- a/include/ipaddress.h
+++ b/include/ipaddress.h
@@ -34,6 +34,7 @@ extern "C" {
struct connman_ipaddress;
+unsigned char connman_ipaddress_calc_netmask_len(const char *netmask);
struct connman_ipaddress *connman_ipaddress_alloc(int family);
void connman_ipaddress_free(struct connman_ipaddress *ipaddress);
int connman_ipaddress_set_ipv4(struct connman_ipaddress *ipaddress,
@@ -43,11 +44,14 @@ int connman_ipaddress_set_ipv6(struct connman_ipaddress *ipaddress,
const char *address,
unsigned char prefix_length,
const char *gateway);
+int connman_ipaddress_get_ip(struct connman_ipaddress *ipaddress,
+ const char **address, unsigned char *prefix_length);
void connman_ipaddress_set_peer(struct connman_ipaddress *ipaddress,
const char *peer);
void connman_ipaddress_clear(struct connman_ipaddress *ipaddress);
void connman_ipaddress_copy_address(struct connman_ipaddress *ipaddress,
struct connman_ipaddress *source);
+struct connman_ipaddress *connman_ipaddress_copy(struct connman_ipaddress *ipaddress);
#ifdef __cplusplus
}
diff --git a/include/machine.h b/include/machine.h
new file mode 100644
index 00000000..c8d8735e
--- /dev/null
+++ b/include/machine.h
@@ -0,0 +1,35 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2014 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
+ *
+ */
+
+#ifndef __CONNMAN_MACHINE_H
+#define __CONNMAN_MACHINE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const char *connman_machine_get_type(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_MACHINE_H */
diff --git a/include/network.h b/include/network.h
index fcb6905e..d772699b 100644
--- a/include/network.h
+++ b/include/network.h
@@ -96,7 +96,6 @@ int connman_network_set_associating(struct connman_network *network,
bool associating);
void connman_network_set_error(struct connman_network *network,
enum connman_network_error error);
-void connman_network_clear_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);
diff --git a/include/peer.h b/include/peer.h
index 807c9456..8a690f5f 100644
--- a/include/peer.h
+++ b/include/peer.h
@@ -26,17 +26,88 @@
extern "C" {
#endif
+enum connman_peer_state {
+ CONNMAN_PEER_STATE_UNKNOWN = 0,
+ CONNMAN_PEER_STATE_IDLE = 1,
+ CONNMAN_PEER_STATE_ASSOCIATION = 2,
+ CONNMAN_PEER_STATE_CONFIGURATION = 3,
+ CONNMAN_PEER_STATE_READY = 4,
+ CONNMAN_PEER_STATE_DISCONNECT = 5,
+ CONNMAN_PEER_STATE_FAILURE = 6,
+};
+
+enum connman_peer_wps_method {
+ CONNMAN_PEER_WPS_UNKNOWN = 0,
+ CONNMAN_PEER_WPS_PBC = 1,
+ CONNMAN_PEER_WPS_PIN = 2,
+};
+
+enum connman_peer_service_type {
+ CONNMAN_PEER_SERVICE_UNKNOWN = 0,
+ CONNMAN_PEER_SERVICE_WIFI_DISPLAY = 1,
+};
+
struct connman_peer;
struct connman_peer *connman_peer_create(const char *identifier);
-void connman_peer_destroy(struct connman_peer *peer);
+#define connman_peer_ref(peer) \
+ connman_peer_ref_debug(peer, __FILE__, __LINE__, __func__)
+
+#define connman_peer_unref(peer) \
+ connman_peer_unref_debug(peer, __FILE__, __LINE__, __func__)
+
+struct connman_peer *connman_peer_ref_debug(struct connman_peer *peer,
+ const char *file, int line, const char *caller);
+void connman_peer_unref_debug(struct connman_peer *peer,
+ const char *file, int line, const char *caller);
+
+const char *connman_peer_get_identifier(struct connman_peer *peer);
void connman_peer_set_name(struct connman_peer *peer, const char *name);
+void connman_peer_set_device(struct connman_peer *peer,
+ struct connman_device *device);
+struct connman_device *connman_peer_get_device(struct connman_peer *peer);
+void connman_peer_set_sub_device(struct connman_peer *peer,
+ struct connman_device *device);
+void connman_peer_set_as_master(struct connman_peer *peer, bool master);
+int connman_peer_set_state(struct connman_peer *peer,
+ enum connman_peer_state new_state);
+int connman_peer_request_connection(struct connman_peer *peer);
+void connman_peer_reset_services(struct connman_peer *peer);
+void connman_peer_add_service(struct connman_peer *peer,
+ enum connman_peer_service_type type,
+ const unsigned char *data, int data_length);
+void connman_peer_services_changed(struct connman_peer *peer);
int connman_peer_register(struct connman_peer *peer);
void connman_peer_unregister(struct connman_peer *peer);
-struct connman_peer *connman_peer_get(const char *identifier);
+struct connman_peer *connman_peer_get(struct connman_device *device,
+ const char *identifier);
+
+typedef void (* peer_service_registration_cb_t) (int result, void *user_data);
+
+struct connman_peer_driver {
+ int (*connect) (struct connman_peer *peer,
+ enum connman_peer_wps_method wps_method,
+ const char *wps_pin);
+ int (*disconnect) (struct connman_peer *peer);
+ int (*register_service) (const unsigned char *specification,
+ int specification_length,
+ const unsigned char *query,
+ int query_length, int version,
+ peer_service_registration_cb_t callback,
+ void *user_data);
+ int (*unregister_service) (const unsigned char *specification,
+ int specification_length,
+ const unsigned char *query,
+ int query_length, int version);
+};
+
+int connman_peer_driver_register(struct connman_peer_driver *driver);
+void connman_peer_driver_unregister(struct connman_peer_driver *driver);
+
+bool connman_peer_service_is_master(void);
#ifdef __cplusplus
}
diff --git a/include/session.h b/include/session.h
index 3f666649..37dfc4e4 100644
--- a/include/session.h
+++ b/include/session.h
@@ -2,7 +2,7 @@
*
* Connection Manager
*
- * Copyright (C) 2012 BMW Car IT GbmH. All rights reserved.
+ * Copyright (C) 2012-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/plugins/dundee.c b/plugins/dundee.c
index b72c69e5..b5420acf 100644
--- a/plugins/dundee.c
+++ b/plugins/dundee.c
@@ -2,7 +2,7 @@
*
* Connection Manager
*
- * Copyright (C) 2012 BMW Car IT GmbH. All rights reserved.
+ * Copyright (C) 2012-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/plugins/ofono.c b/plugins/ofono.c
index fecef9cf..7af551ba 100644
--- a/plugins/ofono.c
+++ b/plugins/ofono.c
@@ -4,7 +4,7 @@
*
* Copyright (C) 2007-2013 Intel Corporation. All rights reserved.
* Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
- * Copyright (C) 2011 BWM Car IT GmbH. All rights reserved.
+ * Copyright (C) 2011-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/plugins/session_policy_local.c b/plugins/session_policy_local.c
index 4e8b5f21..b2369bdc 100644
--- a/plugins/session_policy_local.c
+++ b/plugins/session_policy_local.c
@@ -2,7 +2,7 @@
*
* Connection Manager
*
- * Copyright (C) 2012 BMW Car IT GbmH. All rights reserved.
+ * Copyright (C) 2012-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/plugins/vpn.c b/plugins/vpn.c
index 25711d70..e6e4c8e0 100644
--- a/plugins/vpn.c
+++ b/plugins/vpn.c
@@ -1078,7 +1078,7 @@ static struct vpn_route *parse_user_route(const char *user_route)
char *ptr;
long int value = strtol(netmask, &ptr, 10);
if (ptr != netmask && *ptr == '\0' &&
- value <= 32)
+ value && value <= 32)
prefix_len = value;
}
diff --git a/plugins/wifi.c b/plugins/wifi.c
index ef4dd951..5f2ebf1c 100644
--- a/plugins/wifi.c
+++ b/plugins/wifi.c
@@ -54,6 +54,8 @@
#include <connman/storage.h>
#include <include/setting.h>
#include <connman/provision.h>
+#include <connman/utsname.h>
+#include <connman/machine.h>
#include <gsupplicant/gsupplicant.h>
@@ -65,6 +67,9 @@
#define AUTOSCAN_DEFAULT "exponential:3:300"
#define P2P_FIND_TIMEOUT 30
+#define P2P_CONNECTION_TIMEOUT 100
+#define P2P_LISTEN_PERIOD 500
+#define P2P_LISTEN_INTERVAL 2000
static struct connman_technology *wifi_technology = NULL;
static struct connman_technology *p2p_technology = NULL;
@@ -117,10 +122,20 @@ struct wifi_data {
GSupplicantScanParams *scan_params;
unsigned int p2p_find_timeout;
+ unsigned int p2p_connection_timeout;
+ struct connman_peer *pending_peer;
+ GSupplicantPeer *peer;
+ bool p2p_connecting;
+ bool p2p_device;
+ int servicing;
};
static GList *iface_list = NULL;
+static GList *pending_wifi_device = NULL;
+static GList *p2p_iface_list = NULL;
+bool wfd_service_registered = false;
+
static void start_autoscan(struct connman_device *device);
static int p2p_tech_probe(struct connman_technology *technology)
@@ -142,6 +157,527 @@ static struct connman_technology_driver p2p_tech_driver = {
.remove = p2p_tech_remove,
};
+static bool is_p2p_connecting(void)
+{
+ GList *list;
+
+ for (list = iface_list; list; list = list->next) {
+ struct wifi_data *wifi = list->data;
+
+ if (wifi->p2p_connecting)
+ return true;
+ }
+
+ return false;
+}
+
+static void add_pending_wifi_device(struct wifi_data *wifi)
+{
+ if (g_list_find(pending_wifi_device, wifi))
+ return;
+
+ pending_wifi_device = g_list_append(pending_wifi_device, wifi);
+}
+
+static struct wifi_data *get_pending_wifi_data(const char *ifname)
+{
+ GList *list;
+
+ for (list = pending_wifi_device; list; list = list->next) {
+ struct wifi_data *wifi;
+ const char *dev_name;
+
+ wifi = list->data;
+ if (!wifi || !wifi->device)
+ continue;
+
+ dev_name = connman_device_get_string(wifi->device, "Interface");
+ if (!g_strcmp0(ifname, dev_name)) {
+ pending_wifi_device = g_list_delete_link(
+ pending_wifi_device, list);
+ return wifi;
+ }
+ }
+
+ return NULL;
+}
+
+static void remove_pending_wifi_device(struct wifi_data *wifi)
+{
+ GList *link;
+
+ link = g_list_find(pending_wifi_device, wifi);
+
+ if (!link)
+ return;
+
+ pending_wifi_device = g_list_delete_link(pending_wifi_device, link);
+}
+
+static void peer_cancel_timeout(struct wifi_data *wifi)
+{
+ if (wifi->p2p_connection_timeout > 0)
+ g_source_remove(wifi->p2p_connection_timeout);
+
+ wifi->p2p_connection_timeout = 0;
+ wifi->p2p_connecting = false;
+
+ if (wifi->pending_peer) {
+ connman_peer_unref(wifi->pending_peer);
+ wifi->pending_peer = NULL;
+ }
+
+ wifi->peer = NULL;
+}
+
+static gboolean peer_connect_timeout(gpointer data)
+{
+ struct wifi_data *wifi = data;
+
+ DBG("");
+
+ if (wifi->p2p_connecting) {
+ enum connman_peer_state state = CONNMAN_PEER_STATE_FAILURE;
+
+ if (g_supplicant_peer_has_requested_connection(wifi->peer))
+ state = CONNMAN_PEER_STATE_IDLE;
+
+ connman_peer_set_state(wifi->pending_peer, state);
+ }
+
+ peer_cancel_timeout(wifi);
+
+ return FALSE;
+}
+
+static void peer_connect_callback(int result, GSupplicantInterface *interface,
+ void *user_data)
+{
+ struct wifi_data *wifi = user_data;
+ struct connman_peer *peer = wifi->pending_peer;
+
+ DBG("peer %p - %d", peer, result);
+
+ if (!peer)
+ return;
+
+ if (result < 0) {
+ peer_connect_timeout(wifi);
+ return;
+ }
+
+ connman_peer_set_state(peer, CONNMAN_PEER_STATE_ASSOCIATION);
+
+ wifi->p2p_connection_timeout = g_timeout_add_seconds(
+ P2P_CONNECTION_TIMEOUT,
+ peer_connect_timeout, wifi);
+}
+
+static int peer_connect(struct connman_peer *peer,
+ enum connman_peer_wps_method wps_method,
+ const char *wps_pin)
+{
+ struct connman_device *device = connman_peer_get_device(peer);
+ GSupplicantPeerParams *peer_params;
+ GSupplicantPeer *gs_peer;
+ struct wifi_data *wifi;
+ bool pbc, pin;
+ int ret;
+
+ DBG("peer %p", peer);
+
+ if (!device)
+ return -ENODEV;
+
+ wifi = connman_device_get_data(device);
+ if (!wifi)
+ return -ENODEV;
+
+ if (wifi->p2p_connecting)
+ return -EBUSY;
+
+ wifi->peer = NULL;
+
+ gs_peer = g_supplicant_interface_peer_lookup(wifi->interface,
+ connman_peer_get_identifier(peer));
+ if (!gs_peer)
+ return -EINVAL;
+
+ pbc = g_supplicant_peer_is_wps_pbc(gs_peer);
+ pin = g_supplicant_peer_is_wps_pin(gs_peer);
+
+ switch (wps_method) {
+ case CONNMAN_PEER_WPS_UNKNOWN:
+ if ((pbc && pin) || pin)
+ return -ENOKEY;
+ break;
+ case CONNMAN_PEER_WPS_PBC:
+ if (!pbc)
+ return -EINVAL;
+ wps_pin = NULL;
+ break;
+ case CONNMAN_PEER_WPS_PIN:
+ if (!pin || !wps_pin)
+ return -EINVAL;
+ break;
+ }
+
+ peer_params = g_try_malloc0(sizeof(GSupplicantPeerParams));
+ if (!peer_params)
+ return -ENOMEM;
+
+ peer_params->path = g_strdup(g_supplicant_peer_get_path(gs_peer));
+ if (wps_pin)
+ peer_params->wps_pin = g_strdup(wps_pin);
+
+ peer_params->master = connman_peer_service_is_master();
+
+ ret = g_supplicant_interface_p2p_connect(wifi->interface, peer_params,
+ peer_connect_callback, wifi);
+ if (ret == -EINPROGRESS) {
+ wifi->pending_peer = connman_peer_ref(peer);
+ wifi->peer = gs_peer;
+ wifi->p2p_connecting = true;
+ } else if (ret < 0)
+ g_free(peer_params);
+
+ return ret;
+}
+
+static int peer_disconnect(struct connman_peer *peer)
+{
+ struct connman_device *device = connman_peer_get_device(peer);
+ GSupplicantPeerParams peer_params = {};
+ GSupplicantPeer *gs_peer;
+ struct wifi_data *wifi;
+ int ret;
+
+ DBG("peer %p", peer);
+
+ if (!device)
+ return -ENODEV;
+
+ wifi = connman_device_get_data(device);
+ if (!wifi)
+ return -ENODEV;
+
+ gs_peer = g_supplicant_interface_peer_lookup(wifi->interface,
+ connman_peer_get_identifier(peer));
+ if (!gs_peer)
+ return -EINVAL;
+
+ peer_params.path = g_strdup(g_supplicant_peer_get_path(gs_peer));
+
+ ret = g_supplicant_interface_p2p_disconnect(wifi->interface,
+ &peer_params);
+ g_free(peer_params.path);
+
+ if (ret == -EINPROGRESS)
+ peer_cancel_timeout(wifi);
+
+ return ret;
+}
+
+struct peer_service_registration {
+ peer_service_registration_cb_t callback;
+ void *user_data;
+};
+
+static bool is_service_wfd(const unsigned char *specs, int length)
+{
+ if (length < 9 || specs[0] != 0 || specs[1] != 0 || specs[2] != 6)
+ return false;
+
+ return true;
+}
+
+static void apply_p2p_listen_on_iface(gpointer data, gpointer user_data)
+{
+ struct wifi_data *wifi = data;
+
+ if (!wifi->interface ||
+ !g_supplicant_interface_has_p2p(wifi->interface))
+ return;
+
+ if (!wifi->servicing) {
+ g_supplicant_interface_p2p_listen(wifi->interface,
+ P2P_LISTEN_PERIOD, P2P_LISTEN_INTERVAL);
+ }
+
+ wifi->servicing++;
+}
+
+static void register_wfd_service_cb(int result,
+ GSupplicantInterface *iface, void *user_data)
+{
+ struct peer_service_registration *reg_data = user_data;
+
+ DBG("");
+
+ if (result == 0)
+ g_list_foreach(iface_list, apply_p2p_listen_on_iface, NULL);
+
+ if (reg_data && reg_data->callback) {
+ reg_data->callback(result, reg_data->user_data);
+ g_free(reg_data);
+ }
+}
+
+static GSupplicantP2PServiceParams *fill_in_peer_service_params(
+ const unsigned char *spec,
+ int spec_length, const unsigned char *query,
+ int query_length, int version)
+{
+ GSupplicantP2PServiceParams *params;
+
+ params = g_try_malloc0(sizeof(GSupplicantP2PServiceParams));
+ if (!params)
+ return NULL;
+
+ if (version > 0) {
+ params->version = version;
+ params->service = g_memdup(spec, spec_length);
+ } else if (query_length > 0 && spec_length > 0) {
+ params->query = g_memdup(query, query_length);
+ params->query_length = query_length;
+
+ params->response = g_memdup(spec, spec_length);
+ params->response_length = spec_length;
+ } else {
+ params->wfd_ies = g_memdup(spec, spec_length);
+ params->wfd_ies_length = spec_length;
+ }
+
+ return params;
+}
+
+static void free_peer_service_params(GSupplicantP2PServiceParams *params)
+{
+ if (!params)
+ return;
+
+ g_free(params->service);
+ g_free(params->query);
+ g_free(params->response);
+ g_free(params->wfd_ies);
+
+ g_free(params);
+}
+
+static int peer_register_wfd_service(const unsigned char *specification,
+ int specification_length,
+ peer_service_registration_cb_t callback,
+ void *user_data)
+{
+ struct peer_service_registration *reg_data = NULL;
+ static GSupplicantP2PServiceParams *params;
+ int ret;
+
+ DBG("");
+
+ if (wfd_service_registered)
+ return -EBUSY;
+
+ params = fill_in_peer_service_params(specification,
+ specification_length, NULL, 0, 0);
+ if (!params)
+ return -ENOMEM;
+
+ reg_data = g_try_malloc0(sizeof(*reg_data));
+ if (!reg_data) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ reg_data->callback = callback;
+ reg_data->user_data = user_data;
+
+ ret = g_supplicant_set_widi_ies(params,
+ register_wfd_service_cb, reg_data);
+ if (ret < 0 && ret != -EINPROGRESS)
+ goto error;
+
+ wfd_service_registered = true;
+
+ return ret;
+error:
+ free_peer_service_params(params);
+ g_free(reg_data);
+
+ return ret;
+}
+
+static void register_peer_service_cb(int result,
+ GSupplicantInterface *iface, void *user_data)
+{
+ struct wifi_data *wifi = g_supplicant_interface_get_data(iface);
+ struct peer_service_registration *reg_data = user_data;
+
+ DBG("");
+
+ if (result == 0)
+ apply_p2p_listen_on_iface(wifi, NULL);
+
+ if (reg_data->callback)
+ reg_data->callback(result, reg_data->user_data);
+
+ g_free(reg_data);
+}
+
+static int peer_register_service(const unsigned char *specification,
+ int specification_length,
+ const unsigned char *query,
+ int query_length, int version,
+ peer_service_registration_cb_t callback,
+ void *user_data)
+{
+ struct peer_service_registration *reg_data;
+ GSupplicantP2PServiceParams *params;
+ bool found = false;
+ int ret, ret_f;
+ GList *list;
+
+ DBG("");
+
+ if (specification && !version && !query &&
+ is_service_wfd(specification, specification_length)) {
+ return peer_register_wfd_service(specification,
+ specification_length, callback, user_data);
+ }
+
+ reg_data = g_try_malloc0(sizeof(*reg_data));
+ if (!reg_data)
+ return -ENOMEM;
+
+ reg_data->callback = callback;
+ reg_data->user_data = user_data;
+
+ ret_f = -EOPNOTSUPP;
+
+ for (list = iface_list; list; list = list->next) {
+ struct wifi_data *wifi = list->data;
+ GSupplicantInterface *iface = wifi->interface;
+
+ if (!g_supplicant_interface_has_p2p(iface))
+ continue;
+
+ params = fill_in_peer_service_params(specification,
+ specification_length, query,
+ query_length, version);
+ if (!params) {
+ ret = -ENOMEM;
+ continue;
+ }
+
+ if (!found) {
+ ret_f = g_supplicant_interface_p2p_add_service(iface,
+ register_peer_service_cb, params, reg_data);
+ if (ret_f == 0 || ret_f == -EINPROGRESS)
+ found = true;
+ ret = ret_f;
+ } else
+ ret = g_supplicant_interface_p2p_add_service(iface,
+ register_peer_service_cb, params, NULL);
+ if (ret != 0 && ret != -EINPROGRESS)
+ free_peer_service_params(params);
+ }
+
+ if (ret_f != 0 && ret_f != -EINPROGRESS)
+ g_free(reg_data);
+
+ return ret_f;
+}
+
+static int peer_unregister_wfd_service(void)
+{
+ GSupplicantP2PServiceParams *params;
+ GList *list;
+
+ if (!wfd_service_registered)
+ return -EALREADY;
+
+ params = fill_in_peer_service_params(NULL, 0, NULL, 0, 0);
+ if (!params)
+ return -ENOMEM;
+
+ wfd_service_registered = false;
+
+ g_supplicant_set_widi_ies(params, NULL, NULL);
+
+ for (list = iface_list; list; list = list->next) {
+ struct wifi_data *wifi = list->data;
+
+ if (!g_supplicant_interface_has_p2p(wifi->interface))
+ continue;
+
+ wifi->servicing--;
+ if (!wifi->servicing || wifi->servicing < 0) {
+ g_supplicant_interface_p2p_listen(wifi->interface,
+ 0, 0);
+ wifi->servicing = 0;
+ }
+ }
+
+ return 0;
+}
+
+static int peer_unregister_service(const unsigned char *specification,
+ int specification_length,
+ const unsigned char *query,
+ int query_length, int version)
+{
+ GSupplicantP2PServiceParams *params;
+ bool wfd = false;
+ GList *list;
+ int ret;
+
+ if (specification && !version && !query &&
+ is_service_wfd(specification, specification_length)) {
+ ret = peer_unregister_wfd_service();
+ if (ret != 0 && ret != -EINPROGRESS)
+ return ret;
+ wfd = true;
+ }
+
+ for (list = iface_list; list; list = list->next) {
+ struct wifi_data *wifi = list->data;
+ GSupplicantInterface *iface = wifi->interface;
+
+ if (wfd)
+ goto stop_listening;
+
+ if (!g_supplicant_interface_has_p2p(iface))
+ continue;
+
+ params = fill_in_peer_service_params(specification,
+ specification_length, query,
+ query_length, version);
+ if (!params) {
+ ret = -ENOMEM;
+ continue;
+ }
+
+ ret = g_supplicant_interface_p2p_del_service(iface, params);
+ if (ret != 0 && ret != -EINPROGRESS)
+ free_peer_service_params(params);
+stop_listening:
+ wifi->servicing--;
+ if (!wifi->servicing || wifi->servicing < 0) {
+ g_supplicant_interface_p2p_listen(iface, 0, 0);
+ wifi->servicing = 0;
+ }
+ }
+
+ return 0;
+}
+
+static struct connman_peer_driver peer_driver = {
+ .connect = peer_connect,
+ .disconnect = peer_disconnect,
+ .register_service = peer_register_service,
+ .unregister_service = peer_unregister_service,
+};
+
static void handle_tethering(struct wifi_data *wifi)
{
if (!wifi->tethering)
@@ -210,8 +746,10 @@ static int wifi_probe(struct connman_device *device)
wifi->watch = connman_rtnl_add_newlink_watch(wifi->index,
wifi_newlink, device);
-
- iface_list = g_list_append(iface_list, wifi);
+ if (is_p2p_connecting())
+ add_pending_wifi_device(wifi);
+ else
+ iface_list = g_list_append(iface_list, wifi);
return 0;
}
@@ -280,8 +818,10 @@ static void check_p2p_technology(void)
p2p_exists = true;
}
- if (!p2p_exists)
+ if (!p2p_exists) {
connman_technology_driver_unregister(&p2p_tech_driver);
+ connman_peer_driver_unregister(&peer_driver);
+ }
}
static void wifi_remove(struct connman_device *device)
@@ -295,15 +835,23 @@ static void wifi_remove(struct connman_device *device)
stop_autoscan(device);
- iface_list = g_list_remove(iface_list, wifi);
+ if (wifi->p2p_device)
+ p2p_iface_list = g_list_remove(p2p_iface_list, wifi);
+ else
+ iface_list = g_list_remove(iface_list, wifi);
check_p2p_technology();
+ remove_pending_wifi_device(wifi);
+
if (wifi->p2p_find_timeout) {
g_source_remove(wifi->p2p_find_timeout);
connman_device_unref(wifi->device);
}
+ if (wifi->p2p_connection_timeout)
+ g_source_remove(wifi->p2p_connection_timeout);
+
remove_networks(device, wifi);
connman_device_set_powered(device, false);
@@ -313,6 +861,8 @@ static void wifi_remove(struct connman_device *device)
g_supplicant_interface_set_data(wifi->interface, NULL);
+ g_supplicant_interface_cancel(wifi->interface);
+
if (wifi->scan_params)
g_supplicant_free_scan_params(wifi->scan_params);
@@ -787,6 +1337,9 @@ static void start_autoscan(struct connman_device *device)
if (!wifi)
return;
+ if (wifi->p2p_device)
+ return;
+
autoscan = wifi->autoscan;
if (!autoscan)
return;
@@ -871,6 +1424,9 @@ static void finalize_interface_creation(struct wifi_data *wifi)
if (!connman_setting_get_bool("BackgroundScanning"))
return;
+ if (wifi->p2p_device)
+ return;
+
/* Setting up automatic scanning */
if (g_supplicant_interface_autoscan(interface, AUTOSCAN_DEFAULT,
interface_autoscan_callback, wifi) < 0) {
@@ -915,6 +1471,9 @@ static int wifi_enable(struct connman_device *device)
if (!wifi || index < 0)
return -ENODEV;
+ if (is_p2p_connecting())
+ return -EINPROGRESS;
+
interface = connman_inet_ifname(index);
ret = g_supplicant_interface_create(interface, driver, NULL,
interface_create_callback,
@@ -948,6 +1507,7 @@ static int wifi_disable(struct connman_device *device)
if (wifi->p2p_find_timeout) {
g_source_remove(wifi->p2p_find_timeout);
wifi->p2p_find_timeout = 0;
+ connman_device_set_scanning(device, CONNMAN_SERVICE_TYPE_P2P, false);
connman_device_unref(wifi->device);
}
@@ -1153,7 +1713,7 @@ error:
static int p2p_find(struct connman_device *device)
{
- struct wifi_data *wifi = connman_device_get_data(device);
+ struct wifi_data *wifi;
int ret;
DBG("");
@@ -1161,6 +1721,11 @@ static int p2p_find(struct connman_device *device)
if (!p2p_technology)
return -ENOTSUP;
+ wifi = connman_device_get_data(device);
+
+ if (g_supplicant_interface_is_p2p_finding(wifi->interface))
+ return -EALREADY;
+
reset_autoscan(device);
connman_device_ref(device);
@@ -1199,6 +1764,9 @@ static int wifi_scan(enum connman_service_type type,
if (!wifi)
return -ENODEV;
+ if (wifi->p2p_device)
+ return 0;
+
if (type == CONNMAN_SERVICE_TYPE_P2P)
return p2p_find(device);
@@ -1588,14 +2156,15 @@ static void interface_added(GSupplicantInterface *interface)
struct wifi_data *wifi;
wifi = g_supplicant_interface_get_data(interface);
+ if (!wifi) {
+ wifi = get_pending_wifi_data(ifname);
+ if (!wifi)
+ return;
- /*
- * We can get here with a NULL wifi pointer when
- * the interface added signal is sent before the
- * interface creation callback is called.
- */
- if (!wifi)
- return;
+ g_supplicant_interface_set_data(interface, wifi);
+ p2p_iface_list = g_list_append(p2p_iface_list, wifi);
+ wifi->p2p_device = true;
+ }
DBG("ifname %s driver %s wifi %p tethering %d",
ifname, driver, wifi, wifi->tethering);
@@ -1606,9 +2175,6 @@ static void interface_added(GSupplicantInterface *interface)
}
connman_device_set_powered(wifi->device, true);
-
- if (wifi->tethering)
- return;
}
static bool is_idle(struct wifi_data *wifi)
@@ -1874,6 +2440,9 @@ static void interface_removed(GSupplicantInterface *interface)
wifi = g_supplicant_interface_get_data(interface);
+ if (wifi)
+ wifi->interface = NULL;
+
if (wifi && wifi->tethering)
return;
@@ -1882,21 +2451,59 @@ static void interface_removed(GSupplicantInterface *interface)
return;
}
- wifi->interface = NULL;
connman_device_set_powered(wifi->device, false);
check_p2p_technology();
}
+static void set_device_type(const char *type, char dev_type[17])
+{
+ const char *oui = "0050F204";
+ const char *category = "0100";
+ const char *sub_category = "0000";
+
+ if (!g_strcmp0(type, "handset")) {
+ category = "0A00";
+ sub_category = "0500";
+ } else if (!g_strcmp0(type, "vm") || !g_strcmp0(type, "container"))
+ sub_category = "0100";
+ else if (!g_strcmp0(type, "server"))
+ sub_category = "0200";
+ else if (!g_strcmp0(type, "laptop"))
+ sub_category = "0500";
+ else if (!g_strcmp0(type, "desktop"))
+ sub_category = "0600";
+ else if (!g_strcmp0(type, "tablet"))
+ sub_category = "0900";
+ else if (!g_strcmp0(type, "watch"))
+ category = "FF00";
+
+ snprintf(dev_type, 17, "%s%s%s", category, oui, sub_category);
+}
+
static void p2p_support(GSupplicantInterface *interface)
{
+ char dev_type[17] = {};
+ const char *hostname;
+
DBG("");
if (!g_supplicant_interface_has_p2p(interface))
return;
- if (connman_technology_driver_register(&p2p_tech_driver) == 0)
+ if (connman_technology_driver_register(&p2p_tech_driver) < 0) {
DBG("Could not register P2P technology driver");
+ return;
+ }
+
+ hostname = connman_utsname_get_hostname();
+ if (!hostname)
+ hostname = "ConnMan";
+
+ set_device_type(connman_machine_get_type(), dev_type);
+ g_supplicant_interface_set_p2p_device_config(interface,
+ hostname, dev_type);
+ connman_peer_driver_register(&peer_driver);
}
static void scan_started(GSupplicantInterface *interface)
@@ -2072,38 +2679,171 @@ static void network_changed(GSupplicantNetwork *network, const char *property)
}
}
+static void apply_peer_services(GSupplicantPeer *peer,
+ struct connman_peer *connman_peer)
+{
+ const unsigned char *data;
+ int length;
+
+ DBG("");
+
+ connman_peer_reset_services(connman_peer);
+
+ data = g_supplicant_peer_get_widi_ies(peer, &length);
+ if (data) {
+ connman_peer_add_service(connman_peer,
+ CONNMAN_PEER_SERVICE_WIFI_DISPLAY, data, length);
+ }
+}
+
static void peer_found(GSupplicantPeer *peer)
{
+ GSupplicantInterface *iface = g_supplicant_peer_get_interface(peer);
+ struct wifi_data *wifi = g_supplicant_interface_get_data(iface);
struct connman_peer *connman_peer;
const char *identifier, *name;
+ int ret;
identifier = g_supplicant_peer_get_identifier(peer);
name = g_supplicant_peer_get_name(peer);
DBG("ident: %s", identifier);
- connman_peer = connman_peer_get(identifier);
+ connman_peer = connman_peer_get(wifi->device, identifier);
if (connman_peer)
return;
connman_peer = connman_peer_create(identifier);
connman_peer_set_name(connman_peer, name);
+ connman_peer_set_device(connman_peer, wifi->device);
+ apply_peer_services(peer, connman_peer);
- connman_peer_register(connman_peer);
+ ret = connman_peer_register(connman_peer);
+ if (ret < 0 && ret != -EALREADY)
+ connman_peer_unref(connman_peer);
}
static void peer_lost(GSupplicantPeer *peer)
{
+ GSupplicantInterface *iface = g_supplicant_peer_get_interface(peer);
+ struct wifi_data *wifi = g_supplicant_interface_get_data(iface);
struct connman_peer *connman_peer;
const char *identifier;
+ if (!wifi)
+ return;
+
identifier = g_supplicant_peer_get_identifier(peer);
DBG("ident: %s", identifier);
- connman_peer = connman_peer_get(identifier);
- if (connman_peer)
+ connman_peer = connman_peer_get(wifi->device, identifier);
+ if (connman_peer) {
+ if (wifi->p2p_connecting &&
+ wifi->pending_peer == connman_peer) {
+ peer_connect_timeout(wifi);
+ }
connman_peer_unregister(connman_peer);
+ connman_peer_unref(connman_peer);
+ }
+}
+
+static void peer_changed(GSupplicantPeer *peer, GSupplicantPeerState state)
+{
+ GSupplicantInterface *iface = g_supplicant_peer_get_interface(peer);
+ struct wifi_data *wifi = g_supplicant_interface_get_data(iface);
+ enum connman_peer_state p_state = CONNMAN_PEER_STATE_UNKNOWN;
+ struct connman_peer *connman_peer;
+ const char *identifier;
+
+ identifier = g_supplicant_peer_get_identifier(peer);
+
+ DBG("ident: %s", identifier);
+
+ connman_peer = connman_peer_get(wifi->device, identifier);
+ if (!connman_peer)
+ return;
+
+ switch (state) {
+ case G_SUPPLICANT_PEER_SERVICES_CHANGED:
+ apply_peer_services(peer, connman_peer);
+ connman_peer_services_changed(connman_peer);
+ return;
+ case G_SUPPLICANT_PEER_GROUP_CHANGED:
+ if (!g_supplicant_peer_is_in_a_group(peer))
+ p_state = CONNMAN_PEER_STATE_IDLE;
+ else
+ p_state = CONNMAN_PEER_STATE_CONFIGURATION;
+ break;
+ case G_SUPPLICANT_PEER_GROUP_STARTED:
+ break;
+ case G_SUPPLICANT_PEER_GROUP_FINISHED:
+ p_state = CONNMAN_PEER_STATE_IDLE;
+ break;
+ case G_SUPPLICANT_PEER_GROUP_JOINED:
+ if (!g_supplicant_peer_is_in_a_group(peer))
+ break;
+ p_state = CONNMAN_PEER_STATE_READY;
+ break;
+ case G_SUPPLICANT_PEER_GROUP_DISCONNECTED:
+ p_state = CONNMAN_PEER_STATE_IDLE;
+ break;
+ case G_SUPPLICANT_PEER_GROUP_FAILED:
+ if (g_supplicant_peer_has_requested_connection(peer))
+ p_state = CONNMAN_PEER_STATE_IDLE;
+ else
+ p_state = CONNMAN_PEER_STATE_FAILURE;
+ break;
+ }
+
+ if (p_state == CONNMAN_PEER_STATE_CONFIGURATION ||
+ p_state == CONNMAN_PEER_STATE_FAILURE) {
+ if (wifi->p2p_connecting
+ && connman_peer == wifi->pending_peer)
+ peer_cancel_timeout(wifi);
+ else
+ p_state = CONNMAN_PEER_STATE_UNKNOWN;
+ }
+
+ if (p_state == CONNMAN_PEER_STATE_UNKNOWN)
+ return;
+
+ if (p_state == CONNMAN_PEER_STATE_CONFIGURATION) {
+ GSupplicantInterface *g_iface;
+ struct wifi_data *g_wifi;
+
+ g_iface = g_supplicant_peer_get_group_interface(peer);
+ if (!g_iface)
+ return;
+
+ g_wifi = g_supplicant_interface_get_data(g_iface);
+ if (!g_wifi)
+ return;
+
+ connman_peer_set_as_master(connman_peer,
+ !g_supplicant_peer_is_client(peer));
+ connman_peer_set_sub_device(connman_peer, g_wifi->device);
+ }
+
+ connman_peer_set_state(connman_peer, p_state);
+}
+
+static void peer_request(GSupplicantPeer *peer)
+{
+ GSupplicantInterface *iface = g_supplicant_peer_get_interface(peer);
+ struct wifi_data *wifi = g_supplicant_interface_get_data(iface);
+ struct connman_peer *connman_peer;
+ const char *identifier;
+
+ identifier = g_supplicant_peer_get_identifier(peer);
+
+ DBG("ident: %s", identifier);
+
+ connman_peer = connman_peer_get(wifi->device, identifier);
+ if (!connman_peer)
+ return;
+
+ connman_peer_request_connection(connman_peer);
}
static void debug(const char *str)
@@ -2126,6 +2866,8 @@ static const GSupplicantCallbacks callbacks = {
.network_changed = network_changed,
.peer_found = peer_found,
.peer_lost = peer_lost,
+ .peer_changed = peer_changed,
+ .peer_request = peer_request,
.debug = debug,
};
diff --git a/scripts/openvpn-script.c b/scripts/openvpn-script.c
index e703c867..6ba0d292 100644
--- a/scripts/openvpn-script.c
+++ b/scripts/openvpn-script.c
@@ -3,7 +3,7 @@
* Connection Manager
*
* Copyright (C) 2007-2012 Intel Corporation. All rights reserved.
- * Copyright (C) 2010 BMW Car IT GmbH. All rights reserved.
+ * Copyright (C) 2010,2012-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -83,7 +83,9 @@ int main(int argc, char *argv[])
reason = getenv("script_type");
if (!busname || !interface || !path || !reason) {
- print("Required environment variables not set");
+ print("Required environment variables not set; "
+ "bus=%s iface=%s path=%s reason=%s",
+ busname, interface, path, reason);
ret = 1;
goto out;
}
diff --git a/src/agent-connman.c b/src/agent-connman.c
index ab538f38..b2049a3d 100644
--- a/src/agent-connman.c
+++ b/src/agent-connman.c
@@ -56,7 +56,12 @@ static bool check_reply_has_dict(DBusMessage *reply)
struct request_input_reply {
struct connman_service *service;
- authentication_cb_t callback;
+ struct connman_peer *peer;
+ union {
+ authentication_cb_t service_callback;
+ peer_wps_cb_t peer_callback;
+ };
+ bool wps_requested;
void *user_data;
};
@@ -151,12 +156,10 @@ static void request_input_passphrase_reply(DBusMessage *reply, void *user_data)
}
done:
- passphrase_reply->callback(passphrase_reply->service, values_received,
- name, name_len,
- identity, passphrase,
- wps, wpspin, error,
- passphrase_reply->user_data);
-
+ passphrase_reply->service_callback(passphrase_reply->service,
+ values_received, name, name_len,
+ identity, passphrase, wps, wpspin,
+ error, passphrase_reply->user_data);
out:
g_free(passphrase_reply);
}
@@ -236,13 +239,21 @@ static void request_input_append_passphrase(DBusMessageIter *iter,
}
}
+struct request_wps_data {
+ bool peer;
+};
+
static void request_input_append_wps(DBusMessageIter *iter, void *user_data)
{
+ struct request_wps_data *wps = user_data;
const char *str = "wpspin";
connman_dbus_dict_append_basic(iter, "Type",
DBUS_TYPE_STRING, &str);
- str = "alternate";
+ if (wps && wps->peer)
+ str = "mandatory";
+ else
+ str = "alternate";
connman_dbus_dict_append_basic(iter, "Requirement",
DBUS_TYPE_STRING, &str);
}
@@ -399,12 +410,10 @@ static void request_input_login_reply(DBusMessage *reply, void *user_data)
}
done:
- username_password_reply->callback(username_password_reply->service,
- values_received, NULL, 0,
- username, password,
- FALSE, NULL, error,
- username_password_reply->user_data);
-
+ username_password_reply->service_callback(
+ username_password_reply->service, values_received,
+ NULL, 0, username, password, FALSE, NULL, error,
+ username_password_reply->user_data);
out:
g_free(username_password_reply);
}
@@ -477,7 +486,7 @@ int __connman_agent_request_passphrase_input(struct connman_service *service,
}
passphrase_reply->service = service;
- passphrase_reply->callback = callback;
+ passphrase_reply->service_callback = callback;
passphrase_reply->user_data = user_data;
err = connman_agent_queue_message(service, message,
@@ -542,7 +551,7 @@ int __connman_agent_request_login_input(struct connman_service *service,
}
username_password_reply->service = service;
- username_password_reply->callback = callback;
+ username_password_reply->service_callback = callback;
username_password_reply->user_data = user_data;
err = connman_agent_queue_message(service, message,
@@ -644,3 +653,135 @@ int __connman_agent_request_browser(struct connman_service *service,
return -EINPROGRESS;
}
+
+int __connman_agent_report_peer_error(struct connman_peer *peer,
+ const char *path, const char *error,
+ report_error_cb_t callback,
+ const char *dbus_sender,
+ void *user_data)
+{
+ return connman_agent_report_error_full(peer, path, "ReportPeerError",
+ error, callback, dbus_sender, user_data);
+}
+
+static void request_peer_authorization_reply(DBusMessage *reply,
+ void *user_data)
+{
+ struct request_input_reply *auth_reply = user_data;
+ DBusMessageIter iter, dict;
+ const char *error = NULL;
+ bool choice_done = false;
+ char *wpspin = NULL;
+ char *key;
+
+ if (!reply)
+ goto out;
+
+ if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+ error = dbus_message_get_error_name(reply);
+ goto done;
+ }
+
+ if (!check_reply_has_dict(reply))
+ goto done;
+
+ dbus_message_iter_init(reply, &iter);
+ dbus_message_iter_recurse(&iter, &dict);
+ while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter entry, value;
+
+ dbus_message_iter_recurse(&dict, &entry);
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+ break;
+
+ dbus_message_iter_get_basic(&entry, &key);
+
+ if (g_str_equal(key, "WPS")) {
+ choice_done = true;
+
+ dbus_message_iter_next(&entry);
+ if (dbus_message_iter_get_arg_type(&entry)
+ != DBUS_TYPE_VARIANT)
+ break;
+ dbus_message_iter_recurse(&entry, &value);
+ dbus_message_iter_get_basic(&value, &wpspin);
+ break;
+ }
+ dbus_message_iter_next(&dict);
+ }
+
+ if (!auth_reply->wps_requested)
+ choice_done = true;
+
+done:
+ auth_reply->peer_callback(auth_reply->peer, choice_done, wpspin,
+ error, auth_reply->user_data);
+out:
+ g_free(auth_reply);
+}
+
+int __connman_agent_request_peer_authorization(struct connman_peer *peer,
+ peer_wps_cb_t callback,
+ bool wps_requested,
+ const char *dbus_sender,
+ void *user_data)
+{
+ struct request_wps_data wps = { .peer = true };
+ const char *path, *agent_sender, *agent_path;
+ struct request_input_reply *auth_reply;
+ DBusMessageIter dict, iter;
+ DBusMessage *message;
+ void *agent;
+ int err;
+
+ agent = connman_agent_get_info(dbus_sender, &agent_sender,
+ &agent_path);
+ DBG("agent %p peer %p path %s", agent, peer, agent_path);
+
+ if (!peer || !agent || !agent_path || !callback)
+ return -ESRCH;
+
+ message = dbus_message_new_method_call(agent_sender, agent_path,
+ CONNMAN_AGENT_INTERFACE, "RequestPeerAuthorization");
+ if (!message)
+ return -ENOMEM;
+
+ dbus_message_iter_init_append(message, &iter);
+
+ path = __connman_peer_get_path(peer);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+ connman_dbus_dict_open(&iter, &dict);
+
+ if (wps_requested)
+ connman_dbus_dict_append_dict(&dict, "WPS",
+ request_input_append_wps, &wps);
+
+ connman_dbus_dict_close(&iter, &dict);
+
+ auth_reply = g_try_new0(struct request_input_reply, 1);
+ if (!auth_reply) {
+ dbus_message_unref(message);
+ return -ENOMEM;
+ }
+
+ auth_reply->peer = peer;
+ auth_reply->peer_callback = callback;
+ auth_reply->wps_requested = wps_requested;
+ auth_reply->user_data = user_data;
+
+ err = connman_agent_queue_message(peer, message,
+ connman_timeout_input_request(),
+ request_peer_authorization_reply,
+ auth_reply, agent);
+ if (err < 0 && err != -EBUSY) {
+ DBG("error %d sending agent message", err);
+ dbus_message_unref(message);
+ g_free(auth_reply);
+ return err;
+ }
+
+ dbus_message_unref(message);
+
+ return -EINPROGRESS;
+}
diff --git a/src/agent.c b/src/agent.c
index 37cf5247..a3400262 100644
--- a/src/agent.c
+++ b/src/agent.c
@@ -350,6 +350,9 @@ static void report_error_reply(DBusMessage *reply, void *user_data)
bool retry = false;
const char *dbus_err;
+ if (!reply)
+ goto out;
+
if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
dbus_err = dbus_message_get_error_name(reply);
if (dbus_err &&
@@ -360,11 +363,12 @@ static void report_error_reply(DBusMessage *reply, void *user_data)
report_error->callback(report_error->user_context, retry,
report_error->user_data);
+out:
g_free(report_error);
}
-int connman_agent_report_error(void *user_context, const char *path,
- const char *error,
+int connman_agent_report_error_full(void *user_context, const char *path,
+ const char *method, const char *error,
report_error_cb_t callback,
const char *dbus_sender, void *user_data)
{
@@ -383,8 +387,7 @@ int connman_agent_report_error(void *user_context, const char *path,
return -ESRCH;
message = dbus_message_new_method_call(agent->owner, agent->path,
- CONNMAN_AGENT_INTERFACE,
- "ReportError");
+ CONNMAN_AGENT_INTERFACE, method);
if (!message)
return -ENOMEM;
@@ -421,6 +424,16 @@ int connman_agent_report_error(void *user_context, const char *path,
return -EINPROGRESS;
}
+int connman_agent_report_error(void *user_context, const char *path,
+ const char *error,
+ report_error_cb_t callback,
+ const char *dbus_sender, void *user_data)
+{
+ return connman_agent_report_error_full(user_context, path,
+ "ReportError", error, callback, dbus_sender,
+ user_data);
+}
+
static gint compare_priority(gconstpointer a, gconstpointer b)
{
const struct connman_agent_driver *driver1 = a;
diff --git a/src/bridge.c b/src/bridge.c
index 034fa139..ba200969 100644
--- a/src/bridge.c
+++ b/src/bridge.c
@@ -3,7 +3,7 @@
* Connection Manager
*
* Copyright (C) 2007-2013 Intel Corporation. All rights reserved.
- * Copyright (C) 2012-2013 BMW Car IT GmbH. All rights reserved.
+ * Copyright (C) 2012-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/src/config.c b/src/config.c
index 330ae817..93a788a1 100644
--- a/src/config.c
+++ b/src/config.c
@@ -52,6 +52,7 @@ struct connman_config_service {
char *private_key_passphrase_type;
char *phase2;
char *passphrase;
+ enum connman_service_security security;
GSList *service_identifiers;
char *config_ident; /* file prefix */
char *config_entry; /* entry name */
@@ -99,6 +100,7 @@ static bool cleanup = false;
#define SERVICE_KEY_IDENTITY "Identity"
#define SERVICE_KEY_PHASE2 "Phase2"
#define SERVICE_KEY_PASSPHRASE "Passphrase"
+#define SERVICE_KEY_SECURITY "Security"
#define SERVICE_KEY_HIDDEN "Hidden"
#define SERVICE_KEY_IPv4 "IPv4"
@@ -129,6 +131,7 @@ static const char *service_possible_keys[] = {
SERVICE_KEY_IDENTITY,
SERVICE_KEY_PHASE2,
SERVICE_KEY_PASSPHRASE,
+ SERVICE_KEY_SECURITY,
SERVICE_KEY_HIDDEN,
SERVICE_KEY_IPv4,
SERVICE_KEY_IPv6,
@@ -399,7 +402,7 @@ static bool load_service_generic(GKeyFile *keyfile,
char *ptr;
long int value = strtol(mask, &ptr, 10);
- if (ptr != mask && *ptr == '\0' && value <= 32)
+ if (ptr != mask && *ptr == '\0' && value && value <= 32)
prefix_len = value;
addr = 0xffffffff << (32 - prefix_len);
@@ -513,6 +516,7 @@ static bool load_service(GKeyFile *keyfile, const char *group,
struct connman_config_service *service;
const char *ident;
char *str, *hex_ssid;
+ enum connman_service_security security;
bool service_created = false;
/* Strip off "service_" prefix */
@@ -664,6 +668,38 @@ static bool load_service(GKeyFile *keyfile, const char *group,
service->passphrase = str;
}
+ str = __connman_config_get_string(keyfile, group, SERVICE_KEY_SECURITY,
+ NULL);
+ security = __connman_service_string2security(str);
+
+ if (service->eap) {
+
+ if (str && security != CONNMAN_SERVICE_SECURITY_8021X)
+ connman_info("Mismatch between EAP configuration and "
+ "setting %s = %s",
+ SERVICE_KEY_SECURITY, str);
+
+ service->security = CONNMAN_SERVICE_SECURITY_8021X;
+
+ } else if (service->passphrase) {
+
+ if (str) {
+ if (security == CONNMAN_SERVICE_SECURITY_PSK ||
+ security == CONNMAN_SERVICE_SECURITY_WEP) {
+ service->security = security;
+ } else {
+ connman_info("Mismatch with passphrase and "
+ "setting %s = %s",
+ SERVICE_KEY_SECURITY, str);
+
+ service->security =
+ CONNMAN_SERVICE_SECURITY_PSK;
+ }
+
+ } else
+ service->security = CONNMAN_SERVICE_SECURITY_PSK;
+ }
+
service->config_ident = g_strdup(config->ident);
service->config_entry = g_strdup_printf("service_%s", service->ident);
@@ -1062,22 +1098,7 @@ static int try_provision_service(struct connman_config_service *config,
enum connman_service_type type;
const void *ssid;
unsigned int ssid_len;
-
- type = connman_service_get_type(service);
- if (type == CONNMAN_SERVICE_TYPE_WIFI &&
- g_strcmp0(config->type, "wifi") != 0)
- return -ENOENT;
-
- if (type == CONNMAN_SERVICE_TYPE_ETHERNET &&
- g_strcmp0(config->type, "ethernet") != 0)
- return -ENOENT;
-
- if (type == CONNMAN_SERVICE_TYPE_GADGET &&
- g_strcmp0(config->type, "gadget") != 0)
- return -ENOENT;
-
- DBG("service %p ident %s", service,
- __connman_service_get_ident(service));
+ const char *str;
network = __connman_service_get_network(service);
if (!network) {
@@ -1088,6 +1109,54 @@ static int try_provision_service(struct connman_config_service *config,
DBG("network %p ident %s", network,
connman_network_get_identifier(network));
+ type = connman_service_get_type(service);
+
+ switch(type) {
+ case CONNMAN_SERVICE_TYPE_WIFI:
+ if (__connman_service_string2type(config->type) != type)
+ return -ENOENT;
+
+ ssid = connman_network_get_blob(network, "WiFi.SSID",
+ &ssid_len);
+ if (!ssid) {
+ connman_error("Network SSID not set");
+ return -EINVAL;
+ }
+
+ if (!config->ssid || ssid_len != config->ssid_len)
+ return -ENOENT;
+
+ 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:
+ case CONNMAN_SERVICE_TYPE_GADGET:
+
+ if (__connman_service_string2type(config->type) != type)
+ return -ENOENT;
+
+ break;
+
+ case CONNMAN_SERVICE_TYPE_UNKNOWN:
+ case CONNMAN_SERVICE_TYPE_SYSTEM:
+ case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+ case CONNMAN_SERVICE_TYPE_CELLULAR:
+ case CONNMAN_SERVICE_TYPE_GPS:
+ case CONNMAN_SERVICE_TYPE_VPN:
+ case CONNMAN_SERVICE_TYPE_P2P:
+
+ return -ENOENT;
+ }
+
+ DBG("service %p ident %s", service,
+ __connman_service_get_ident(service));
+
if (config->mac) {
struct connman_device *device;
const char *device_addr;
@@ -1106,22 +1175,6 @@ static int try_provision_service(struct connman_config_service *config,
return -ENOENT;
}
- if (g_strcmp0(config->type, "wifi") == 0 &&
- type == CONNMAN_SERVICE_TYPE_WIFI) {
- ssid = connman_network_get_blob(network, "WiFi.SSID",
- &ssid_len);
- if (!ssid) {
- connman_error("Network SSID not set");
- return -EINVAL;
- }
-
- if (!config->ssid || ssid_len != config->ssid_len)
- return -ENOENT;
-
- if (memcmp(config->ssid, ssid, ssid_len) != 0)
- return -ENOENT;
- }
-
if (!config->ipv6_address) {
connman_network_set_ipv6_method(network,
CONNMAN_IPCONFIG_METHOD_AUTO);
@@ -1210,9 +1263,6 @@ static int try_provision_service(struct connman_config_service *config,
g_slist_prepend(config->service_identifiers,
g_strdup(service_id));
- if (!config->virtual)
- __connman_service_set_immutable(service, true);
-
__connman_service_set_favorite_delayed(service, true, true);
__connman_service_set_config(service, config->config_ident,
@@ -1240,13 +1290,10 @@ static int try_provision_service(struct connman_config_service *config,
__connman_service_set_timeservers(service,
config->timeservers);
- if (g_strcmp0(config->type, "wifi") == 0 &&
- type == CONNMAN_SERVICE_TYPE_WIFI) {
+ if (type == CONNMAN_SERVICE_TYPE_WIFI) {
provision_service_wifi(config, service, network,
ssid, ssid_len);
- } else
- __connman_service_connect(service,
- CONNMAN_SERVICE_CONNECT_REASON_AUTO);
+ }
__connman_service_mark_dirty();
@@ -1260,8 +1307,21 @@ static int try_provision_service(struct connman_config_service *config,
virtual->vfile = config->virtual_file;
g_timeout_add(0, remove_virtual_config, virtual);
- } else
- __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO);
+
+ return 0;
+ }
+
+ __connman_service_set_immutable(service, true);
+
+ if (type == CONNMAN_SERVICE_TYPE_ETHERNET ||
+ type == CONNMAN_SERVICE_TYPE_GADGET) {
+ __connman_service_connect(service,
+ CONNMAN_SERVICE_CONNECT_REASON_AUTO);
+
+ return 0;
+ }
+
+ __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO);
return 0;
}
diff --git a/src/connection.c b/src/connection.c
index e98ccb5c..8fe97258 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -3,7 +3,7 @@
* Connection Manager
*
* Copyright (C) 2007-2013 Intel Corporation. All rights reserved.
- * Copyright (C) 2011-2013 BMW Car IT GmbH. All rights reserved.
+ * Copyright (C) 2011-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/src/connman.h b/src/connman.h
index 24db5f8d..da012152 100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -88,12 +88,13 @@ int __connman_counter_unregister(const char *owner, const char *path);
int __connman_counter_init(void);
void __connman_counter_cleanup(void);
+#include <connman/agent.h>
+
struct connman_service;
+struct connman_peer;
void __connman_agent_cancel(struct connman_service *service);
-int __connman_service_add_passphrase(struct connman_service *service,
- const gchar *passphrase);
typedef void (* authentication_cb_t) (struct connman_service *service,
bool values_received,
const char *name, int name_len,
@@ -103,6 +104,9 @@ typedef void (* authentication_cb_t) (struct connman_service *service,
typedef void (* browser_authentication_cb_t) (struct connman_service *service,
bool authentication_done,
const char *error, void *user_data);
+typedef void (* peer_wps_cb_t) (struct connman_peer *peer, bool choice_done,
+ const char *wpspin, const char *error,
+ void *user_data);
int __connman_agent_request_passphrase_input(struct connman_service *service,
authentication_cb_t callback,
const char *dbus_sender, void *user_data);
@@ -111,6 +115,16 @@ int __connman_agent_request_login_input(struct connman_service *service,
int __connman_agent_request_browser(struct connman_service *service,
browser_authentication_cb_t callback,
const char *url, void *user_data);
+int __connman_agent_report_peer_error(struct connman_peer *peer,
+ const char *path, const char *error,
+ report_error_cb_t callback,
+ const char *dbus_sender,
+ void *user_data);
+int __connman_agent_request_peer_authorization(struct connman_peer *peer,
+ peer_wps_cb_t callback,
+ bool wps_requested,
+ const char *dbus_sender,
+ void *user_data);
#include <connman/log.h>
@@ -383,7 +397,6 @@ int __connman_ipconfig_address_remove(struct connman_ipconfig *ipconfig);
int __connman_ipconfig_address_unset(struct connman_ipconfig *ipconfig);
int __connman_ipconfig_gateway_add(struct connman_ipconfig *ipconfig);
void __connman_ipconfig_gateway_remove(struct connman_ipconfig *ipconfig);
-unsigned char __connman_ipaddress_netmask_prefix_len(const char *netmask);
int __connman_ipconfig_set_proxy_autoconfig(struct connman_ipconfig *ipconfig,
const char *url);
@@ -400,6 +413,7 @@ int __connman_ipconfig_load(struct connman_ipconfig *ipconfig,
int __connman_ipconfig_save(struct connman_ipconfig *ipconfig,
GKeyFile *keyfile, const char *identifier, const char *prefix);
bool __connman_ipconfig_ipv6_privacy_enabled(struct connman_ipconfig *ipconfig);
+int __connman_ipconfig_ipv6_reset_privacy(struct connman_ipconfig *ipconfig);
int __connman_ipconfig_ipv6_set_privacy(struct connman_ipconfig *ipconfig,
const char *value);
bool __connman_ipconfig_ipv6_is_enabled(struct connman_ipconfig *ipconfig);
@@ -434,10 +448,13 @@ enum __connman_dhcpv6_status {
typedef void (* dhcpv6_cb) (struct connman_network *network,
enum __connman_dhcpv6_status status, gpointer data);
-typedef void (* dhcp_cb) (struct connman_network *network,
+typedef void (* dhcp_cb) (struct connman_ipconfig *ipconfig,
+ struct connman_network *opt_network,
bool success, gpointer data);
-int __connman_dhcp_start(struct connman_network *network, dhcp_cb callback);
-void __connman_dhcp_stop(struct connman_network *network);
+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);
int __connman_dhcp_init(void);
void __connman_dhcp_cleanup(void);
int __connman_dhcpv6_init(void);
@@ -711,8 +728,6 @@ void __connman_service_set_hidden_data(struct connman_service *service,
gpointer user_data);
void __connman_service_return_error(struct connman_service *service,
int error, gpointer user_data);
-void __connman_service_reply_dbus_pending(DBusMessage *pending, int error,
- const char *path);
int __connman_service_provision_changed(const char *ident);
void __connman_service_set_config(struct connman_service *service,
@@ -720,6 +735,7 @@ void __connman_service_set_config(struct connman_service *service,
const char *__connman_service_type2string(enum connman_service_type type);
enum connman_service_type __connman_service_string2type(const char *str);
+enum connman_service_security __connman_service_string2security(const char *str);
int __connman_service_nameserver_append(struct connman_service *service,
const char *nameserver, bool is_auto);
@@ -780,6 +796,23 @@ int __connman_peer_init(void);
void __connman_peer_cleanup(void);
void __connman_peer_list_struct(DBusMessageIter *array);
+const char *__connman_peer_get_path(struct connman_peer *peer);
+
+int __connman_peer_service_init(void);
+void __connman_peer_service_cleanup(void);
+
+void __connman_peer_service_set_driver(struct connman_peer_driver *driver);
+int __connman_peer_service_register(const char *owner, DBusMessage *msg,
+ const unsigned char *specification,
+ int specification_length,
+ const unsigned char *query,
+ int query_length, int version,
+ bool master);
+int __connman_peer_service_unregister(const char *owner,
+ const unsigned char *specification,
+ int specification_length,
+ const unsigned char *query,
+ int query_length, int version);
#include <connman/session.h>
@@ -996,3 +1029,8 @@ int __connman_nfacct_disable(struct nfacct_context *ctx,
void *user_data);
void __connman_nfacct_cleanup(void);
+
+#include <connman/machine.h>
+
+int __connman_machine_init(void);
+void __connman_machine_cleanup(void);
diff --git a/src/dbus.c b/src/dbus.c
index 4fa0b362..d80a46ce 100644
--- a/src/dbus.c
+++ b/src/dbus.c
@@ -653,6 +653,38 @@ err:
return err;
}
+void connman_dbus_reply_pending(DBusMessage *pending,
+ int error, const char *path)
+{
+ if (pending) {
+ if (error > 0) {
+ DBusMessage *reply;
+
+ reply = __connman_error_failed(pending, error);
+ if (reply)
+ g_dbus_send_message(connection, reply);
+ } else {
+ const char *sender;
+
+ sender = dbus_message_get_interface(pending);
+ if (!path)
+ path = dbus_message_get_path(pending);
+
+ DBG("sender %s path %s", sender, path);
+
+ if (g_strcmp0(sender, CONNMAN_MANAGER_INTERFACE) == 0)
+ g_dbus_send_reply(connection, pending,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID);
+ else
+ g_dbus_send_reply(connection, pending,
+ DBUS_TYPE_INVALID);
+ }
+
+ dbus_message_unref(pending);
+ }
+}
+
DBusConnection *connman_dbus_get_connection(void)
{
if (!connection)
diff --git a/src/device.c b/src/device.c
index a97d790e..c0683abd 100644
--- a/src/device.c
+++ b/src/device.c
@@ -185,6 +185,12 @@ int __connman_device_enable(struct connman_device *device)
if (device->powered_pending == PENDING_NONE && device->powered)
return -EALREADY;
+ if (device->index > 0) {
+ err = connman_inet_ifup(device->index);
+ if (err < 0 && err != -EALREADY)
+ return err;
+ }
+
device->powered_pending = PENDING_ENABLE;
err = device->driver->enable(device);
diff --git a/src/dhcp.c b/src/dhcp.c
index 83d7dfb3..505d9f06 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -39,8 +39,10 @@
#define RATE_LIMIT_INTERVAL 60 /* delay between successive attempts */
struct connman_dhcp {
+ struct connman_ipconfig *ipconfig;
struct connman_network *network;
dhcp_cb callback;
+ gpointer user_data;
char **nameservers;
char **timeservers;
@@ -54,7 +56,7 @@ struct connman_dhcp {
char *dhcp_debug_prefix;
};
-static GHashTable *network_table;
+static GHashTable *ipconfig_table;
static bool ipv4ll_running;
static void dhcp_free(struct connman_dhcp *dhcp)
@@ -70,41 +72,36 @@ static void dhcp_free(struct connman_dhcp *dhcp)
g_free(dhcp);
}
-/**
- * dhcp_invalidate: Invalidate an existing DHCP lease
- * @dhcp: pointer to the DHCP lease to invalidate.
- * @callback: flag indicating whether or not to invoke the client callback
- * if present.
- *
- * Invalidates an existing DHCP lease, optionally invoking the client
- * callback. The caller may wish to avoid the client callback invocation
- * when the invocation of that callback might otherwise unnecessarily upset
- * service state due to the IP configuration change implied by this
- * invalidation.
- */
-static void dhcp_invalidate(struct connman_dhcp *dhcp, bool callback)
+static void ipv4ll_stop_client(struct connman_dhcp *dhcp)
{
- struct connman_service *service;
- struct connman_ipconfig *ipconfig;
- int i;
+ if (!dhcp->ipv4ll_client)
+ return;
- DBG("dhcp %p callback %u", dhcp, callback);
+ g_dhcp_client_stop(dhcp->ipv4ll_client);
+ g_dhcp_client_unref(dhcp->ipv4ll_client);
+ dhcp->ipv4ll_client = NULL;
+ ipv4ll_running = false;
- if (!dhcp)
- return;
+ g_free(dhcp->ipv4ll_debug_prefix);
+ dhcp->ipv4ll_debug_prefix = NULL;
+}
- service = connman_service_lookup_from_network(dhcp->network);
- if (!service)
- return;
+static bool apply_dhcp_invalidate_on_network(struct connman_dhcp *dhcp)
+{
+ struct connman_service *service;
+ int i;
- ipconfig = __connman_service_get_ip4config(service);
- if (!ipconfig)
- return;
+ if (!dhcp->network)
+ return true;
- __connman_6to4_remove(ipconfig);
+ service = connman_service_lookup_from_network(dhcp->network);
+ if (!service) {
+ connman_error("Can not lookup service");
+ return false;
+ }
__connman_service_set_domainname(service, NULL);
- __connman_service_set_pac(service, NULL);
+ __connman_ipconfig_set_proxy_autoconfig(dhcp->ipconfig, NULL);
if (dhcp->timeservers) {
for (i = 0; dhcp->timeservers[i]; i++) {
@@ -112,7 +109,6 @@ static void dhcp_invalidate(struct connman_dhcp *dhcp, bool callback)
dhcp->timeservers[i]);
}
}
-
if (dhcp->nameservers) {
for (i = 0; dhcp->nameservers[i]; i++) {
__connman_service_nameserver_remove(service,
@@ -120,25 +116,55 @@ static void dhcp_invalidate(struct connman_dhcp *dhcp, bool callback)
}
}
- __connman_ipconfig_set_dhcp_address(ipconfig,
- __connman_ipconfig_get_local(ipconfig));
- DBG("last address %s", __connman_ipconfig_get_dhcp_address(ipconfig));
+ return true;
+}
- __connman_ipconfig_address_remove(ipconfig);
+/**
+ * dhcp_invalidate: Invalidate an existing DHCP lease
+ * @dhcp: pointer to the DHCP lease to invalidate.
+ * @callback: flag indicating whether or not to invoke the client callback
+ * if present.
+ *
+ * Invalidates an existing DHCP lease, optionally invoking the client
+ * callback. The caller may wish to avoid the client callback invocation
+ * when the invocation of that callback might otherwise unnecessarily upset
+ * service state due to the IP configuration change implied by this
+ * invalidation.
+ */
+static void dhcp_invalidate(struct connman_dhcp *dhcp, bool callback)
+{
+ DBG("dhcp %p callback %u", dhcp, callback);
+
+ if (!dhcp)
+ return;
+
+ __connman_6to4_remove(dhcp->ipconfig);
- __connman_ipconfig_set_local(ipconfig, NULL);
- __connman_ipconfig_set_broadcast(ipconfig, NULL);
- __connman_ipconfig_set_gateway(ipconfig, NULL);
- __connman_ipconfig_set_prefixlen(ipconfig, 0);
+ if (!apply_dhcp_invalidate_on_network(dhcp))
+ return;
+
+ __connman_ipconfig_set_dhcp_address(dhcp->ipconfig,
+ __connman_ipconfig_get_local(dhcp->ipconfig));
+ DBG("last address %s",
+ __connman_ipconfig_get_dhcp_address(dhcp->ipconfig));
+
+ __connman_ipconfig_address_remove(dhcp->ipconfig);
+
+ __connman_ipconfig_set_local(dhcp->ipconfig, NULL);
+ __connman_ipconfig_set_broadcast(dhcp->ipconfig, NULL);
+ __connman_ipconfig_set_gateway(dhcp->ipconfig, NULL);
+ __connman_ipconfig_set_prefixlen(dhcp->ipconfig, 0);
if (dhcp->callback && callback)
- dhcp->callback(dhcp->network, false, NULL);
+ dhcp->callback(dhcp->ipconfig, dhcp->network,
+ false, dhcp->user_data);
}
static void dhcp_valid(struct connman_dhcp *dhcp)
{
if (dhcp->callback)
- dhcp->callback(dhcp->network, true, NULL);
+ dhcp->callback(dhcp->ipconfig, dhcp->network,
+ true, dhcp->user_data);
}
static void dhcp_debug(const char *str, void *data)
@@ -146,20 +172,6 @@ static void dhcp_debug(const char *str, void *data)
connman_info("%s: %s", (const char *) data, str);
}
-static void ipv4ll_stop_client(struct connman_dhcp *dhcp)
-{
- if (!dhcp->ipv4ll_client)
- return;
-
- g_dhcp_client_stop(dhcp->ipv4ll_client);
- g_dhcp_client_unref(dhcp->ipv4ll_client);
- dhcp->ipv4ll_client = NULL;
- ipv4ll_running = false;
-
- g_free(dhcp->ipv4ll_debug_prefix);
- dhcp->ipv4ll_debug_prefix = NULL;
-}
-
static void ipv4ll_lost_cb(GDHCPClient *dhcp_client, gpointer user_data);
static void ipv4ll_available_cb(GDHCPClient *ipv4ll_client, gpointer user_data);
@@ -174,7 +186,7 @@ static int ipv4ll_start_client(struct connman_dhcp *dhcp)
if (dhcp->ipv4ll_client)
return -EALREADY;
- index = connman_network_get_index(dhcp->network);
+ index = __connman_ipconfig_get_index(dhcp->ipconfig);
ipv4ll_client = g_dhcp_client_new(G_DHCP_IPV4LL, index, &error);
if (error != G_DHCP_CLIENT_ERROR_NONE)
@@ -189,10 +201,12 @@ static int ipv4ll_start_client(struct connman_dhcp *dhcp)
g_dhcp_client_set_id(ipv4ll_client);
- hostname = connman_utsname_get_hostname();
- if (hostname)
- g_dhcp_client_set_send(ipv4ll_client, G_DHCP_HOST_NAME,
- hostname);
+ if (dhcp->network) {
+ hostname = connman_utsname_get_hostname();
+ if (hostname)
+ g_dhcp_client_set_send(ipv4ll_client,
+ G_DHCP_HOST_NAME, hostname);
+ }
g_dhcp_client_register_event(ipv4ll_client,
G_DHCP_CLIENT_EVENT_IPV4LL_LOST, ipv4ll_lost_cb, dhcp);
@@ -216,16 +230,11 @@ static int ipv4ll_start_client(struct connman_dhcp *dhcp)
static gboolean dhcp_retry_cb(gpointer user_data)
{
struct connman_dhcp *dhcp = user_data;
- struct connman_service *service;
- struct connman_ipconfig *ipconfig;
dhcp->timeout = 0;
- service = connman_service_lookup_from_network(dhcp->network);
- ipconfig = __connman_service_get_ip4config(service);
-
g_dhcp_client_start(dhcp->dhcp_client,
- __connman_ipconfig_get_dhcp_address(ipconfig));
+ __connman_ipconfig_get_dhcp_address(dhcp->ipconfig));
return FALSE;
}
@@ -296,78 +305,33 @@ static bool compare_string_arrays(char **array_a, char **array_b)
return true;
}
-static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
+static bool apply_lease_available_on_network(GDHCPClient *dhcp_client,
+ struct connman_dhcp *dhcp)
{
- struct connman_dhcp *dhcp = user_data;
- GList *list, *option = NULL;
- char *address, *netmask = NULL, *gateway = NULL;
- const char *c_address, *c_gateway;
char **nameservers, **timeservers, *pac = NULL;
- int ns_entries;
- struct connman_ipconfig *ipconfig;
struct connman_service *service;
- unsigned char prefixlen, c_prefixlen;
- bool ip_change;
+ GList *list, *option = NULL;
+ int ns_entries;
int i;
- DBG("Lease available");
-
- if (dhcp->ipv4ll_client) {
- ipv4ll_stop_client(dhcp);
- dhcp_invalidate(dhcp, false);
- }
+ if (!dhcp->network)
+ return true;
service = connman_service_lookup_from_network(dhcp->network);
if (!service) {
connman_error("Can not lookup service");
- return;
- }
-
- ipconfig = __connman_service_get_ip4config(service);
- if (!ipconfig) {
- connman_error("Could not lookup ipconfig");
- return;
+ return false;
}
- c_address = __connman_ipconfig_get_local(ipconfig);
- c_gateway = __connman_ipconfig_get_gateway(ipconfig);
- c_prefixlen = __connman_ipconfig_get_prefixlen(ipconfig);
-
- address = g_dhcp_client_get_address(dhcp_client);
-
- __connman_ipconfig_set_dhcp_address(ipconfig, address);
- DBG("last address %s", address);
-
- option = g_dhcp_client_get_option(dhcp_client, G_DHCP_SUBNET);
- if (option)
- netmask = g_strdup(option->data);
-
- option = g_dhcp_client_get_option(dhcp_client, G_DHCP_ROUTER);
+ option = g_dhcp_client_get_option(dhcp_client, 252);
if (option)
- gateway = g_strdup(option->data);
-
- prefixlen = __connman_ipaddress_netmask_prefix_len(netmask);
- if (prefixlen == 255)
- connman_warn("netmask: %s is invalid", netmask);
-
- DBG("c_address %s", c_address);
-
- if (address && c_address && g_strcmp0(address, c_address) != 0)
- ip_change = true;
- else if (gateway && c_gateway && g_strcmp0(gateway, c_gateway) != 0)
- ip_change = true;
- else if (prefixlen != c_prefixlen)
- ip_change = true;
- else if (!c_address || !c_gateway)
- ip_change = true;
- else
- ip_change = false;
+ pac = g_strdup(option->data);
option = g_dhcp_client_get_option(dhcp_client, G_DHCP_DNS_SERVER);
ns_entries = g_list_length(option);
nameservers = g_try_new0(char *, ns_entries + 1);
if (nameservers) {
- for (i = 0, list = option; list; list = list->next, i++)
+ for (i = 0, list = option;list; list = list->next, i++)
nameservers[i] = g_strdup(list->data);
nameservers[ns_entries] = NULL;
}
@@ -389,18 +353,6 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
timeservers[ns_entries] = NULL;
}
- option = g_dhcp_client_get_option(dhcp_client, 252);
- if (option)
- pac = g_strdup(option->data);
-
- __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_DHCP);
-
- if (ip_change) {
- __connman_ipconfig_set_local(ipconfig, address);
- __connman_ipconfig_set_prefixlen(ipconfig, prefixlen);
- __connman_ipconfig_set_gateway(ipconfig, gateway);
- }
-
if (!compare_string_arrays(nameservers, dhcp->nameservers)) {
if (dhcp->nameservers) {
for (i = 0; dhcp->nameservers[i]; i++) {
@@ -412,8 +364,7 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
dhcp->nameservers = nameservers;
- for (i = 0; dhcp->nameservers &&
- dhcp->nameservers[i]; i++) {
+ for (i = 0; dhcp->nameservers && dhcp->nameservers[i]; i++) {
__connman_service_nameserver_append(service,
dhcp->nameservers[i], false);
}
@@ -432,8 +383,7 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
dhcp->timeservers = timeservers;
- for (i = 0; dhcp->timeservers &&
- dhcp->timeservers[i]; i++) {
+ for (i = 0; dhcp->timeservers && dhcp->timeservers[i]; i++) {
__connman_service_timeserver_append(service,
dhcp->timeservers[i]);
}
@@ -445,14 +395,77 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
g_free(dhcp->pac);
dhcp->pac = pac;
- __connman_service_set_pac(service, dhcp->pac);
+ __connman_ipconfig_set_proxy_autoconfig(dhcp->ipconfig,
+ dhcp->pac);
+ }
+
+ __connman_6to4_probe(service);
+
+ return true;
+}
+
+static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+ struct connman_dhcp *dhcp = user_data;
+ GList *option = NULL;
+ char *address, *netmask = NULL, *gateway = NULL;
+ const char *c_address, *c_gateway;
+ unsigned char prefixlen, c_prefixlen;
+ bool ip_change;
+
+ DBG("Lease available");
+
+ if (dhcp->ipv4ll_client) {
+ ipv4ll_stop_client(dhcp);
+ dhcp_invalidate(dhcp, false);
+ }
+
+ c_address = __connman_ipconfig_get_local(dhcp->ipconfig);
+ c_gateway = __connman_ipconfig_get_gateway(dhcp->ipconfig);
+ c_prefixlen = __connman_ipconfig_get_prefixlen(dhcp->ipconfig);
+
+ address = g_dhcp_client_get_address(dhcp_client);
+
+ __connman_ipconfig_set_dhcp_address(dhcp->ipconfig, address);
+ DBG("last address %s", address);
+
+ option = g_dhcp_client_get_option(dhcp_client, G_DHCP_SUBNET);
+ if (option)
+ netmask = g_strdup(option->data);
+
+ option = g_dhcp_client_get_option(dhcp_client, G_DHCP_ROUTER);
+ if (option)
+ gateway = g_strdup(option->data);
+
+ prefixlen = connman_ipaddress_calc_netmask_len(netmask);
+ if (prefixlen == 255)
+ connman_warn("netmask: %s is invalid", netmask);
+
+ DBG("c_address %s", c_address);
+
+ if (g_strcmp0(address, c_address))
+ ip_change = true;
+ else if (g_strcmp0(gateway, c_gateway))
+ ip_change = true;
+ else if (prefixlen != c_prefixlen)
+ ip_change = true;
+ else
+ ip_change = false;
+
+ __connman_ipconfig_set_method(dhcp->ipconfig,
+ CONNMAN_IPCONFIG_METHOD_DHCP);
+ if (ip_change) {
+ __connman_ipconfig_set_local(dhcp->ipconfig, address);
+ __connman_ipconfig_set_prefixlen(dhcp->ipconfig, prefixlen);
+ __connman_ipconfig_set_gateway(dhcp->ipconfig, gateway);
}
+ if (!apply_lease_available_on_network(dhcp_client, dhcp))
+ return;
+
if (ip_change)
dhcp_valid(dhcp);
- __connman_6to4_probe(service);
-
g_free(address);
g_free(netmask);
g_free(gateway);
@@ -462,29 +475,20 @@ static void ipv4ll_available_cb(GDHCPClient *ipv4ll_client, gpointer user_data)
{
struct connman_dhcp *dhcp = user_data;
char *address, *netmask;
- struct connman_service *service;
- struct connman_ipconfig *ipconfig;
unsigned char prefixlen;
DBG("IPV4LL available");
- service = connman_service_lookup_from_network(dhcp->network);
- if (!service)
- return;
-
- ipconfig = __connman_service_get_ip4config(service);
- if (!ipconfig)
- return;
-
address = g_dhcp_client_get_address(ipv4ll_client);
netmask = g_dhcp_client_get_netmask(ipv4ll_client);
- prefixlen = __connman_ipaddress_netmask_prefix_len(netmask);
+ prefixlen = connman_ipaddress_calc_netmask_len(netmask);
- __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_DHCP);
- __connman_ipconfig_set_local(ipconfig, address);
- __connman_ipconfig_set_prefixlen(ipconfig, prefixlen);
- __connman_ipconfig_set_gateway(ipconfig, NULL);
+ __connman_ipconfig_set_method(dhcp->ipconfig,
+ CONNMAN_IPCONFIG_METHOD_DHCP);
+ __connman_ipconfig_set_local(dhcp->ipconfig, address);
+ __connman_ipconfig_set_prefixlen(dhcp->ipconfig, prefixlen);
+ __connman_ipconfig_set_gateway(dhcp->ipconfig, NULL);
dhcp_valid(dhcp);
@@ -494,15 +498,13 @@ static void ipv4ll_available_cb(GDHCPClient *ipv4ll_client, gpointer user_data)
static int dhcp_initialize(struct connman_dhcp *dhcp)
{
- struct connman_service *service;
GDHCPClient *dhcp_client;
GDHCPClientError error;
- const char *hostname;
int index;
DBG("dhcp %p", dhcp);
- index = connman_network_get_index(dhcp->network);
+ index = __connman_ipconfig_get_index(dhcp->ipconfig);
dhcp_client = g_dhcp_client_new(G_DHCP_IPV4, index, &error);
if (error != G_DHCP_CLIENT_ERROR_NONE)
@@ -517,22 +519,29 @@ static int dhcp_initialize(struct connman_dhcp *dhcp)
g_dhcp_client_set_id(dhcp_client);
- service = connman_service_lookup_from_network(dhcp->network);
+ if (dhcp->network) {
+ struct connman_service *service;
+ const char *hostname;
- hostname = __connman_service_get_hostname(service);
- if (!hostname)
- hostname = connman_utsname_get_hostname();
+ service = connman_service_lookup_from_network(dhcp->network);
+
+ hostname = __connman_service_get_hostname(service);
+ if (!hostname)
+ hostname = connman_utsname_get_hostname();
- if (hostname)
- g_dhcp_client_set_send(dhcp_client, G_DHCP_HOST_NAME, hostname);
+ if (hostname)
+ g_dhcp_client_set_send(dhcp_client,
+ G_DHCP_HOST_NAME, hostname);
+
+ g_dhcp_client_set_request(dhcp_client, G_DHCP_HOST_NAME);
+ g_dhcp_client_set_request(dhcp_client, G_DHCP_DNS_SERVER);
+ g_dhcp_client_set_request(dhcp_client, G_DHCP_DOMAIN_NAME);
+ g_dhcp_client_set_request(dhcp_client, G_DHCP_NTP_SERVER);
+ g_dhcp_client_set_request(dhcp_client, 252);
+ }
- g_dhcp_client_set_request(dhcp_client, G_DHCP_HOST_NAME);
g_dhcp_client_set_request(dhcp_client, G_DHCP_SUBNET);
- g_dhcp_client_set_request(dhcp_client, G_DHCP_DNS_SERVER);
- g_dhcp_client_set_request(dhcp_client, G_DHCP_DOMAIN_NAME);
- g_dhcp_client_set_request(dhcp_client, G_DHCP_NTP_SERVER);
g_dhcp_client_set_request(dhcp_client, G_DHCP_ROUTER);
- g_dhcp_client_set_request(dhcp_client, 252);
g_dhcp_client_register_event(dhcp_client,
G_DHCP_CLIENT_EVENT_LEASE_AVAILABLE,
@@ -553,8 +562,10 @@ static int dhcp_release(struct connman_dhcp *dhcp)
{
DBG("dhcp %p", dhcp);
- if (dhcp->timeout > 0)
+ if (dhcp->timeout > 0) {
g_source_remove(dhcp->timeout);
+ dhcp->timeout = 0;
+ }
if (dhcp->dhcp_client) {
g_dhcp_client_stop(dhcp->dhcp_client);
@@ -571,56 +582,74 @@ static int dhcp_release(struct connman_dhcp *dhcp)
return 0;
}
-int __connman_dhcp_start(struct connman_network *network, dhcp_cb callback)
+int __connman_dhcp_start(struct connman_ipconfig *ipconfig,
+ struct connman_network *network, dhcp_cb callback,
+ gpointer user_data)
{
- struct connman_service *service;
- struct connman_ipconfig *ipconfig;
const char *last_addr = NULL;
struct connman_dhcp *dhcp;
+ int err;
DBG("");
- service = connman_service_lookup_from_network(network);
- if (!service)
- return -EINVAL;
+ if (network) {
+ struct connman_service *service;
- ipconfig = __connman_service_get_ip4config(service);
- if (ipconfig)
- last_addr = __connman_ipconfig_get_dhcp_address(ipconfig);
+ service = connman_service_lookup_from_network(network);
+ if (!service)
+ return -EINVAL;
+ }
+
+ last_addr = __connman_ipconfig_get_dhcp_address(ipconfig);
- dhcp = g_hash_table_lookup(network_table, network);
+ dhcp = g_hash_table_lookup(ipconfig_table, ipconfig);
if (!dhcp) {
dhcp = g_try_new0(struct connman_dhcp, 1);
if (!dhcp)
return -ENOMEM;
- dhcp->network = network;
- connman_network_ref(network);
+ dhcp->ipconfig = ipconfig;
+ __connman_ipconfig_ref(ipconfig);
+
+ if (network) {
+ dhcp->network = network;
+ connman_network_ref(network);
+ }
+
+ err = dhcp_initialize(dhcp);
- g_hash_table_insert(network_table, network, dhcp);
+ if (err < 0) {
+ if (network)
+ connman_network_unref(network);
+ g_free(dhcp);
+ return err;
+ }
- dhcp_initialize(dhcp);
+ g_hash_table_insert(ipconfig_table, ipconfig, dhcp);
}
dhcp->callback = callback;
+ dhcp->user_data = user_data;
return g_dhcp_client_start(dhcp->dhcp_client, last_addr);
}
-void __connman_dhcp_stop(struct connman_network *network)
+void __connman_dhcp_stop(struct connman_ipconfig *ipconfig)
{
struct connman_dhcp *dhcp;
- DBG("network_table %p network %p", network_table, network);
+ DBG("ipconfig_table %p ipconfig %p", ipconfig_table, ipconfig);
- if (!network_table)
+ if (!ipconfig_table)
return;
- dhcp = g_hash_table_lookup(network_table, network);
+ dhcp = g_hash_table_lookup(ipconfig_table, ipconfig);
if (dhcp) {
- g_hash_table_remove(network_table, network);
- connman_network_unref(network);
+ g_hash_table_remove(ipconfig_table, ipconfig);
+ __connman_ipconfig_unref(ipconfig);
+ if (dhcp->network)
+ connman_network_unref(dhcp->network);
dhcp_release(dhcp);
dhcp_invalidate(dhcp, false);
dhcp_free(dhcp);
@@ -631,8 +660,8 @@ int __connman_dhcp_init(void)
{
DBG("");
- network_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
- NULL, NULL);
+ ipconfig_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+ NULL, NULL);
return 0;
}
@@ -641,6 +670,6 @@ void __connman_dhcp_cleanup(void)
{
DBG("");
- g_hash_table_destroy(network_table);
- network_table = NULL;
+ g_hash_table_destroy(ipconfig_table);
+ ipconfig_table = NULL;
}
diff --git a/src/dhcpv6.c b/src/dhcpv6.c
index 2ede854e..5f8029f1 100644
--- a/src/dhcpv6.c
+++ b/src/dhcpv6.c
@@ -631,6 +631,7 @@ static void set_address(int ifindex, struct connman_ipconfig *ipconfig,
/* Is this prefix part of the subnet we are suppose to use? */
prefix_len = check_ipv6_addr_prefix(prefixes, address);
+ __connman_ipconfig_address_remove(ipconfig);
__connman_ipconfig_set_local(ipconfig, address);
__connman_ipconfig_set_prefixlen(ipconfig, prefix_len);
@@ -804,7 +805,8 @@ static void dad_reply(struct nd_neighbor_advert *reply,
service = __connman_service_lookup_from_index(
data->ifindex);
network = __connman_service_get_network(service);
- data->callback(network, status, NULL);
+ if (network)
+ data->callback(network, status, NULL);
}
}
@@ -1118,6 +1120,7 @@ static void rebind_cb(GDHCPClient *dhcp_client, gpointer user_data)
{
DBG("");
+ g_dhcpv6_client_reset_request(dhcp_client);
g_dhcpv6_client_clear_retransmit(dhcp_client);
re_cb(REQ_REBIND, dhcp_client, user_data);
@@ -1305,6 +1308,7 @@ static void renew_cb(GDHCPClient *dhcp_client, gpointer user_data)
{
DBG("");
+ g_dhcpv6_client_reset_request(dhcp_client);
g_dhcpv6_client_clear_retransmit(dhcp_client);
re_cb(REQ_RENEW, dhcp_client, user_data);
@@ -1398,7 +1402,7 @@ int __connman_dhcpv6_start_renew(struct connman_network *network,
dhcpv6_cb callback)
{
struct connman_dhcpv6 *dhcp;
- uint32_t T1, T2;
+ uint32_t T1, T2, delta;
time_t started, current, expired;
dhcp = g_hash_table_lookup(network_table, network);
@@ -1421,11 +1425,13 @@ int __connman_dhcpv6_start_renew(struct connman_network *network,
/* RFC 3315, 22.4 */
return 0;
- if (T1 == 0)
+ if (T1 == 0) {
/* RFC 3315, 22.4
* Client can choose the timeout.
*/
- T1 = 1800;
+ T1 = (expired - started) / 2;
+ T2 = (expired - started) / 10 * 8;
+ }
dhcp->callback = callback;
@@ -1436,22 +1442,23 @@ int __connman_dhcpv6_start_renew(struct connman_network *network,
if (T2 != 0xffffffff && T2 > 0) {
if ((unsigned)current >= (unsigned)started + T2) {
/* RFC 3315, chapter 18.1.3, start rebind */
- DBG("rebind after %d secs", T2);
+ DBG("start rebind immediately");
- dhcp->timeout = g_timeout_add_seconds(T2, start_rebind,
+ dhcp->timeout = g_timeout_add_seconds(0, start_rebind,
dhcp);
} else if ((unsigned)current < (unsigned)started + T1) {
- DBG("renew after %d secs", T1);
+ delta = started + T1 - current;
+ DBG("renew after %d secs", delta);
- dhcp->timeout = g_timeout_add_seconds(T1, start_renew,
- dhcp);
+ dhcp->timeout = g_timeout_add_seconds(delta,
+ start_renew, dhcp);
} else {
- DBG("rebind after %d secs", T2 - T1);
+ delta = started + T2 - current;
+ DBG("rebind after %d secs", delta);
- dhcp->timeout = g_timeout_add_seconds(T2 - T1,
- start_rebind,
- dhcp);
+ dhcp->timeout = g_timeout_add_seconds(delta,
+ start_rebind, dhcp);
}
}
@@ -1765,145 +1772,11 @@ static gboolean start_solicitation(gpointer user_data)
return FALSE;
}
-static void confirm_cb(GDHCPClient *dhcp_client, gpointer user_data)
-{
- struct connman_dhcpv6 *dhcp = user_data;
- int status = g_dhcpv6_client_get_status(dhcp_client);
-
- DBG("dhcpv6 confirm msg %p status %d", dhcp, status);
-
- clear_timer(dhcp);
-
- g_dhcpv6_client_clear_retransmit(dhcp_client);
-
- /*
- * If confirm fails, start from scratch.
- */
- if (status != 0) {
- g_dhcp_client_unref(dhcp->dhcp_client);
- start_solicitation(dhcp);
- } else {
- do_dad(dhcp_client, dhcp);
- }
-}
-
-static int dhcpv6_confirm(struct connman_dhcpv6 *dhcp)
-{
- GDHCPClient *dhcp_client;
- GDHCPClientError error;
- struct connman_service *service;
- struct connman_ipconfig *ipconfig_ipv6;
- int index, ret;
-
- DBG("dhcp %p", dhcp);
-
- index = connman_network_get_index(dhcp->network);
-
- dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
- if (error != G_DHCP_CLIENT_ERROR_NONE) {
- clear_timer(dhcp);
- return -EINVAL;
- }
-
- if (getenv("CONNMAN_DHCPV6_DEBUG"))
- g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
-
- service = connman_service_lookup_from_network(dhcp->network);
- if (!service) {
- clear_timer(dhcp);
- g_dhcp_client_unref(dhcp_client);
- return -EINVAL;
- }
-
- ret = set_duid(service, dhcp->network, dhcp_client, index);
- if (ret < 0) {
- clear_timer(dhcp);
- g_dhcp_client_unref(dhcp_client);
- return ret;
- }
-
- g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
- g_dhcp_client_set_request(dhcp_client, G_DHCPV6_RAPID_COMMIT);
-
- ipconfig_ipv6 = __connman_service_get_ip6config(service);
- dhcp->use_ta = __connman_ipconfig_ipv6_privacy_enabled(ipconfig_ipv6);
-
- g_dhcpv6_client_set_ia(dhcp_client, index,
- dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
- NULL, NULL, TRUE,
- __connman_ipconfig_get_dhcp_address(ipconfig_ipv6));
-
- clear_callbacks(dhcp_client);
-
- g_dhcp_client_register_event(dhcp_client,
- G_DHCP_CLIENT_EVENT_CONFIRM,
- confirm_cb, dhcp);
-
- dhcp->dhcp_client = dhcp_client;
-
- return g_dhcp_client_start(dhcp_client, NULL);
-}
-
-static gboolean timeout_confirm(gpointer user_data)
-{
- struct connman_dhcpv6 *dhcp = user_data;
-
- dhcp->RT = calc_delay(dhcp->RT, CNF_MAX_RT);
-
- DBG("confirm RT timeout %d msec", dhcp->RT);
-
- dhcp->timeout = g_timeout_add(dhcp->RT, timeout_confirm, dhcp);
-
- g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
-
- g_dhcp_client_start(dhcp->dhcp_client, NULL);
-
- return FALSE;
-}
-
-static gboolean timeout_max_confirm(gpointer user_data)
-{
- struct connman_dhcpv6 *dhcp = user_data;
-
- dhcp->MRD = 0;
-
- clear_timer(dhcp);
-
- DBG("confirm max retransmit duration timeout");
-
- g_dhcpv6_client_clear_retransmit(dhcp->dhcp_client);
-
- if (dhcp->callback)
- dhcp->callback(dhcp->network, CONNMAN_DHCPV6_STATUS_FAIL,
- NULL);
-
- return FALSE;
-}
-
-static gboolean start_confirm(gpointer user_data)
-{
- struct connman_dhcpv6 *dhcp = user_data;
-
- /* Set the confirm timeout, RFC 3315 chapter 14 */
- dhcp->RT = CNF_TIMEOUT * (1 + get_random());
-
- DBG("confirm initial RT timeout %d msec", dhcp->RT);
-
- dhcp->timeout = g_timeout_add(dhcp->RT, timeout_confirm, dhcp);
- dhcp->MRD = g_timeout_add(CNF_MAX_RD, timeout_max_confirm, dhcp);
-
- dhcpv6_confirm(dhcp);
-
- return FALSE;
-}
-
int __connman_dhcpv6_start(struct connman_network *network,
GSList *prefixes, dhcpv6_cb callback)
{
struct connman_service *service;
- struct connman_ipconfig *ipconfig_ipv6;
struct connman_dhcpv6 *dhcp;
- char *last_address;
int delay;
DBG("");
@@ -1936,24 +1809,18 @@ int __connman_dhcpv6_start(struct connman_network *network,
/* Initial timeout, RFC 3315, 17.1.2 */
delay = rand() % 1000;
- ipconfig_ipv6 = __connman_service_get_ip6config(service);
- last_address = __connman_ipconfig_get_dhcp_address(ipconfig_ipv6);
-
- if (prefixes && last_address &&
- check_ipv6_addr_prefix(prefixes,
- last_address) != 128) {
- /*
- * So we are in the same subnet
- * RFC 3315, chapter 18.1.2 Confirm message
- */
- dhcp->timeout = g_timeout_add(delay, start_confirm, dhcp);
- } else {
- /*
- * Start from scratch.
- * RFC 3315, chapter 17.1.2 Solicitation message
- */
- dhcp->timeout = g_timeout_add(delay, start_solicitation, dhcp);
- }
+ /*
+ * Start from scratch.
+ * RFC 3315, chapter 17.1.2 Solicitation message
+ *
+ * Note that we do not send CONFIRM message here as it does
+ * not make much sense because we do not save expiration time
+ * so we cannot really know how long the saved address is valid
+ * anyway. The reply to CONFIRM message does not send
+ * expiration times back to us. Because of this we need to
+ * start using SOLICITATION anyway.
+ */
+ dhcp->timeout = g_timeout_add(delay, start_solicitation, dhcp);
return 0;
}
diff --git a/src/dnsproxy.c b/src/dnsproxy.c
index 7232b987..bdd7fd5c 100644
--- a/src/dnsproxy.c
+++ b/src/dnsproxy.c
@@ -356,8 +356,7 @@ static int dns_name_length(unsigned char *buf)
static void update_cached_ttl(unsigned char *buf, int len, int new_ttl)
{
unsigned char *c;
- uint32_t *i;
- uint16_t *w;
+ uint16_t w;
int l;
/* skip the header */
@@ -387,17 +386,19 @@ static void update_cached_ttl(unsigned char *buf, int len, int new_ttl)
break;
/* now the 4 byte TTL field */
- i = (uint32_t *)c;
- *i = htonl(new_ttl);
+ c[0] = new_ttl >> 24 & 0xff;
+ c[1] = new_ttl >> 16 & 0xff;
+ c[2] = new_ttl >> 8 & 0xff;
+ c[3] = new_ttl & 0xff;
c += 4;
len -= 4;
if (len < 0)
break;
/* now the 2 byte rdlen field */
- w = (uint16_t *)c;
- c += ntohs(*w) + 2;
- len -= ntohs(*w) + 2;
+ w = c[0] << 8 | c[1];
+ c += w + 2;
+ len -= w + 2;
}
}
@@ -435,7 +436,7 @@ static void send_cached_response(int sk, unsigned char *buf, int len,
hdr->id = id;
hdr->qr = 1;
- hdr->rcode = 0;
+ hdr->rcode = ns_r_noerror;
hdr->ancount = htons(answers);
hdr->nscount = 0;
hdr->arcount = 0;
@@ -482,7 +483,7 @@ static void send_response(int sk, unsigned char *buf, int len,
DBG("id 0x%04x qr %d opcode %d", hdr->id, hdr->qr, hdr->opcode);
hdr->qr = 1;
- hdr->rcode = 2;
+ hdr->rcode = ns_r_servfail;
hdr->ancount = 0;
hdr->nscount = 0;
@@ -1344,7 +1345,6 @@ static void cache_refresh(void)
static int reply_query_type(unsigned char *msg, int len)
{
unsigned char *c;
- uint16_t *w;
int l;
int type;
@@ -1358,8 +1358,7 @@ static int reply_query_type(unsigned char *msg, int len)
/* now the query, which is a name and 2 16 bit words */
l = dns_name_length(c) + 1;
c += l;
- w = (uint16_t *) c;
- type = ntohs(*w);
+ type = c[0] << 8 | c[1];
return type;
}
@@ -1401,7 +1400,7 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
DBG("offset %d hdr %p msg %p rcode %d", offset, hdr, msg, hdr->rcode);
/* Continue only if response code is 0 (=ok) */
- if (hdr->rcode != 0)
+ if (hdr->rcode != ns_r_noerror)
return 0;
if (!cache)
@@ -1760,14 +1759,11 @@ static char *uncompress(int16_t field_count, char *start, char *end,
int pos; /* position in compressed string */
char name[NS_MAXLABEL]; /* tmp label */
uint16_t dns_type, dns_class;
+ int comp_pos;
- pos = dn_expand((const u_char *)start, (u_char *)end,
- (u_char *)ptr, name, NS_MAXLABEL);
- if (pos < 0) {
- DBG("uncompress error [%d/%s]", errno,
- strerror(errno));
+ if (!convert_label(start, end, ptr, name, NS_MAXLABEL,
+ &pos, &comp_pos))
goto out;
- }
/*
* Copy the uncompressed resource record, type, class and \0 to
@@ -1775,7 +1771,6 @@ static char *uncompress(int16_t field_count, char *start, char *end,
*/
ulen = strlen(name);
- *uptr++ = ulen;
strncpy(uptr, name, uncomp_len - (uptr - uncompressed));
DBG("pos %d ulen %d left %d name %s", pos, ulen,
@@ -1807,8 +1802,6 @@ static char *uncompress(int16_t field_count, char *start, char *end,
* so we need to uncompress it also when necessary.
*/
if (dns_type == ns_t_cname) {
- int comp_pos;
-
if (!convert_label(start, end, ptr, uptr,
uncomp_len - (uptr - uncompressed),
&pos, &comp_pos))
@@ -1833,7 +1826,6 @@ static char *uncompress(int16_t field_count, char *start, char *end,
ptr += dlen;
} else if (dns_type == ns_t_soa) {
- int comp_pos;
int total_len = 0;
char *len_ptr;
@@ -1884,6 +1876,45 @@ out:
return NULL;
}
+static int strip_domains(char *name, char *answers, int maxlen)
+{
+ uint16_t data_len;
+ int name_len = strlen(name);
+ char *ptr, *start = answers, *end = answers + maxlen;
+
+ while (maxlen > 0) {
+ ptr = strstr(answers, name);
+ if (ptr) {
+ char *domain = ptr + name_len;
+
+ if (*domain) {
+ int domain_len = strlen(domain);
+
+ memmove(answers + name_len,
+ domain + domain_len,
+ end - (domain + domain_len));
+
+ end -= domain_len;
+ maxlen -= domain_len;
+ }
+ }
+
+ answers += strlen(answers) + 1;
+ answers += 2 + 2 + 4; /* skip type, class and ttl fields */
+
+ data_len = answers[0] << 8 | answers[1];
+ answers += 2; /* skip the length field */
+
+ if (answers + data_len > end)
+ return -EINVAL;
+
+ answers += data_len;
+ maxlen -= answers - ptr;
+ }
+
+ return end - start;
+}
+
static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
struct server_data *data)
{
@@ -1911,7 +1942,7 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
req->numresp++;
- if (hdr->rcode == 0 || !req->resp) {
+ if (hdr->rcode == ns_r_noerror || !req->resp) {
unsigned char *new_reply = NULL;
/*
@@ -1979,6 +2010,8 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
*/
if (domain_len > 0) {
int len = host_len + 1;
+ int new_len, fixed_len;
+ char *answers;
/*
* First copy host (without domain name) into
@@ -2001,6 +2034,8 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
*/
ptr += NS_QFIXEDSZ;
uptr += NS_QFIXEDSZ;
+ answers = uptr;
+ fixed_len = answers - uncompressed;
/*
* We then uncompress the result to buffer
@@ -2032,22 +2067,39 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
goto out;
/*
+ * The uncompressed buffer now contains almost
+ * valid response. Final step is to get rid of
+ * the domain name because at least glibc
+ * gethostbyname() implementation does extra
+ * checks and expects to find an answer without
+ * domain name if we asked a query without
+ * domain part. Note that glibc getaddrinfo()
+ * works differently and accepts FQDN in answer
+ */
+ new_len = strip_domains(uncompressed, answers,
+ uptr - answers);
+ if (new_len < 0) {
+ DBG("Corrupted packet");
+ return -EINVAL;
+ }
+
+ /*
* Because we have now uncompressed the answers
- * we must create a bigger buffer to hold all
- * that data.
+ * we might have to create a bigger buffer to
+ * hold all that data.
*/
- new_reply = g_try_malloc(header_len +
- uptr - uncompressed);
+ reply_len = header_len + new_len + fixed_len;
+
+ new_reply = g_try_malloc(reply_len);
if (!new_reply)
return -ENOMEM;
memcpy(new_reply, reply, header_len);
memcpy(new_reply + header_len, uncompressed,
- uptr - uncompressed);
+ new_len + fixed_len);
reply = new_reply;
- reply_len = header_len + uptr - uncompressed;
}
}
@@ -2068,8 +2120,13 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
}
out:
- if (hdr->rcode > 0 && req->numresp < req->numserv)
- return -EINVAL;
+ if (req->numresp < req->numserv) {
+ if (hdr->rcode > ns_r_noerror) {
+ return -EINVAL;
+ } else if (hdr->ancount == 0 && req->append_domain) {
+ return -EINVAL;
+ }
+ }
request_list = g_slist_remove(request_list, req);
@@ -2147,7 +2204,8 @@ static void destroy_server(struct server_data *server)
* without any good reason. The small delay allows the new RDNSS to
* create a new DNS server instance and the refcount does not go to 0.
*/
- g_timeout_add_seconds(3, try_remove_cache, NULL);
+ if (cache)
+ g_timeout_add_seconds(3, try_remove_cache, NULL);
g_free(server);
}
diff --git a/src/inet.c b/src/inet.c
index 61116297..cd220ffc 100644
--- a/src/inet.c
+++ b/src/inet.c
@@ -240,36 +240,6 @@ char *connman_inet_ifname(int index)
return g_strdup(ifr.ifr_name);
}
-short int connman_inet_ifflags(int index)
-{
- struct ifreq ifr;
- int sk, err;
-
- sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (sk < 0)
- return -errno;
-
- memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_ifindex = index;
-
- if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
- err = -errno;
- goto done;
- }
-
- if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
- err = -errno;
- goto done;
- }
-
- err = ifr.ifr_flags;
-
-done:
- close(sk);
-
- return err;
-}
-
int connman_inet_ifup(int index)
{
struct ifreq ifr;
@@ -360,36 +330,6 @@ done:
return err;
}
-bool connman_inet_is_cfg80211(int index)
-{
- bool result = false;
- char phy80211_path[PATH_MAX];
- struct stat st;
- struct ifreq ifr;
- int sk;
-
- sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (sk < 0)
- return false;
-
- memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_ifindex = index;
-
- if (ioctl(sk, SIOCGIFNAME, &ifr) < 0)
- goto done;
-
- snprintf(phy80211_path, PATH_MAX,
- "/sys/class/net/%s/phy80211", ifr.ifr_name);
-
- if (stat(phy80211_path, &st) == 0 && (st.st_mode & S_IFDIR))
- result = true;
-
-done:
- close(sk);
-
- return result;
-}
-
struct in6_ifreq {
struct in6_addr ifr6_addr;
__u32 ifr6_prefixlen;
@@ -480,7 +420,8 @@ int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress)
broadcast = ipaddress->broadcast;
peer = ipaddress->peer;
- DBG("index %d address %s prefix_len %d", index, address, prefix_len);
+ DBG("index %d address %s prefix_len %d peer %s broadcast %s", index,
+ address, prefix_len, peer, broadcast);
if (!address)
return -EINVAL;
diff --git a/src/inotify.c b/src/inotify.c
index 72ba6f68..1ab3807c 100644
--- a/src/inotify.c
+++ b/src/inotify.c
@@ -3,7 +3,7 @@
* Connection Manager
*
* Copyright (C) 2007-2012 Intel Corporation. All rights reserved.
- * Copyright (C) 2012-2013 BMW Car IT GmbH. All rights reserved.
+ * Copyright (C) 2012-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/src/ipaddress.c b/src/ipaddress.c
index 57f9435d..d63d95c3 100644
--- a/src/ipaddress.c
+++ b/src/ipaddress.c
@@ -33,6 +33,29 @@
#include "connman.h"
+unsigned char connman_ipaddress_calc_netmask_len(const char *netmask)
+{
+ unsigned char bits;
+ in_addr_t mask;
+ in_addr_t host;
+
+ if (!netmask)
+ return 32;
+
+ mask = inet_network(netmask);
+ host = ~mask;
+
+ /* a valid netmask must be 2^n - 1 */
+ if ((host & (host + 1)) != 0)
+ return -1;
+
+ bits = 0;
+ for (; mask; mask <<= 1)
+ ++bits;
+
+ return bits;
+}
+
struct connman_ipaddress *connman_ipaddress_alloc(int family)
{
struct connman_ipaddress *ipaddress;
@@ -63,29 +86,6 @@ void connman_ipaddress_free(struct connman_ipaddress *ipaddress)
g_free(ipaddress);
}
-unsigned char __connman_ipaddress_netmask_prefix_len(const char *netmask)
-{
- unsigned char bits;
- in_addr_t mask;
- in_addr_t host;
-
- if (!netmask)
- return 32;
-
- mask = inet_network(netmask);
- host = ~mask;
-
- /* a valid netmask must be 2^n - 1 */
- if ((host & (host + 1)) != 0)
- return -1;
-
- bits = 0;
- for (; mask; mask <<= 1)
- ++bits;
-
- return bits;
-}
-
static bool check_ipv6_address(const char *address)
{
unsigned char buf[sizeof(struct in6_addr)];
@@ -128,6 +128,19 @@ int connman_ipaddress_set_ipv6(struct connman_ipaddress *ipaddress,
return 0;
}
+int connman_ipaddress_get_ip(struct connman_ipaddress *ipaddress,
+ const char **address,
+ unsigned char *netmask_prefix_length)
+{
+ if (!ipaddress)
+ return -EINVAL;
+
+ *netmask_prefix_length = ipaddress->prefixlen;
+ *address = ipaddress->local;
+
+ return 0;
+}
+
int connman_ipaddress_set_ipv4(struct connman_ipaddress *ipaddress,
const char *address, const char *netmask, const char *gateway)
{
@@ -136,7 +149,7 @@ int connman_ipaddress_set_ipv4(struct connman_ipaddress *ipaddress,
ipaddress->family = AF_INET;
- ipaddress->prefixlen = __connman_ipaddress_netmask_prefix_len(netmask);
+ ipaddress->prefixlen = connman_ipaddress_calc_netmask_len(netmask);
g_free(ipaddress->local);
ipaddress->local = g_strdup(address);
@@ -179,8 +192,7 @@ void connman_ipaddress_clear(struct connman_ipaddress *ipaddress)
/*
* Note that this copy function only copies the actual address and
- * prefixlen. If you need full copy of ipaddress struct, then you need
- * to create a new function that does that.
+ * prefixlen. Use the other copy function to copy the whole struct.
*/
void connman_ipaddress_copy_address(struct connman_ipaddress *ipaddress,
struct connman_ipaddress *source)
@@ -194,3 +206,23 @@ void connman_ipaddress_copy_address(struct connman_ipaddress *ipaddress,
g_free(ipaddress->local);
ipaddress->local = g_strdup(source->local);
}
+
+struct connman_ipaddress *
+connman_ipaddress_copy(struct connman_ipaddress *ipaddress)
+{
+ struct connman_ipaddress *copy;
+
+ if (!ipaddress)
+ return NULL;
+
+ copy = g_new0(struct connman_ipaddress, 1);
+
+ copy->family = ipaddress->family;
+ copy->prefixlen = ipaddress->prefixlen;
+ copy->local = g_strdup(ipaddress->local);
+ copy->peer = g_strdup(ipaddress->peer);
+ copy->broadcast = g_strdup(ipaddress->broadcast);
+ copy->gateway = g_strdup(ipaddress->gateway);
+
+ return copy;
+}
diff --git a/src/ipconfig.c b/src/ipconfig.c
index b23df160..ae70745f 100644
--- a/src/ipconfig.c
+++ b/src/ipconfig.c
@@ -1090,8 +1090,6 @@ int __connman_ipconfig_gateway_add(struct connman_ipconfig *ipconfig)
if (!service)
return -EINVAL;
- __connman_connection_gateway_remove(service, ipconfig->type);
-
DBG("type %d gw %s peer %s", ipconfig->type,
ipconfig->address->gateway, ipconfig->address->peer);
@@ -1703,10 +1701,6 @@ int __connman_ipconfig_disable(struct connman_ipconfig *ipconfig)
if (ipdevice->config_ipv6 == ipconfig) {
ipconfig_list = g_list_remove(ipconfig_list, ipconfig);
- if (ipdevice->config_ipv6->method ==
- CONNMAN_IPCONFIG_METHOD_AUTO)
- disable_ipv6(ipdevice->config_ipv6);
-
connman_ipaddress_clear(ipdevice->config_ipv6->system);
__connman_ipconfig_unref(ipdevice->config_ipv6);
ipdevice->config_ipv6 = NULL;
@@ -1776,6 +1770,25 @@ static int string2privacy(const char *privacy)
return 0;
}
+int __connman_ipconfig_ipv6_reset_privacy(struct connman_ipconfig *ipconfig)
+{
+ struct connman_ipdevice *ipdevice;
+ int err;
+
+ if (!ipconfig)
+ return -EINVAL;
+
+ ipdevice = g_hash_table_lookup(ipdevice_hash,
+ GINT_TO_POINTER(ipconfig->index));
+ if (!ipdevice)
+ return -ENODEV;
+
+ err = __connman_ipconfig_ipv6_set_privacy(ipconfig, privacy2string(
+ ipdevice->ipv6_privacy));
+
+ return err;
+}
+
int __connman_ipconfig_ipv6_set_privacy(struct connman_ipconfig *ipconfig,
const char *value)
{
@@ -2093,8 +2106,7 @@ int __connman_ipconfig_set_config(struct connman_ipconfig *ipconfig,
case CONNMAN_IPCONFIG_METHOD_OFF:
ipconfig->method = method;
- if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6)
- disable_ipv6(ipconfig);
+
break;
case CONNMAN_IPCONFIG_METHOD_AUTO:
@@ -2104,7 +2116,7 @@ int __connman_ipconfig_set_config(struct connman_ipconfig *ipconfig,
ipconfig->method = method;
if (privacy_string)
ipconfig->ipv6_privacy_config = privacy;
- enable_ipv6(ipconfig);
+
break;
case CONNMAN_IPCONFIG_METHOD_MANUAL:
@@ -2139,6 +2151,7 @@ int __connman_ipconfig_set_config(struct connman_ipconfig *ipconfig,
return connman_ipaddress_set_ipv6(
ipconfig->address, address,
prefix_length, gateway);
+
break;
case CONNMAN_IPCONFIG_METHOD_DHCP:
@@ -2168,9 +2181,11 @@ void __connman_ipconfig_append_ethernet(struct connman_ipconfig *ipconfig,
if (ipconfig->index >= 0) {
char *ifname = connman_inet_ifname(ipconfig->index);
- connman_dbus_dict_append_basic(iter, "Interface",
- DBUS_TYPE_STRING, &ifname);
- g_free(ifname);
+ if (ifname) {
+ connman_dbus_dict_append_basic(iter, "Interface",
+ DBUS_TYPE_STRING, &ifname);
+ g_free(ifname);
+ }
}
if (ipdevice->address)
diff --git a/src/ippool.c b/src/ippool.c
index 558e9662..bb8568d9 100644
--- a/src/ippool.c
+++ b/src/ippool.c
@@ -3,7 +3,7 @@
* Connection Manager
*
* Copyright (C) 2007-2013 Intel Corporation. All rights reserved.
- * Copyright (C) 2012-2013 BMW Car IT GmbH. All rights reserved.
+ * Copyright (C) 2012-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/src/machine.c b/src/machine.c
new file mode 100644
index 00000000..14ea3667
--- /dev/null
+++ b/src/machine.c
@@ -0,0 +1,125 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2014 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 <gdbus.h>
+
+#include "connman.h"
+
+#define DEFAULT_MACHINE_TYPE "laptop"
+
+#define HOSTNAMED_SERVICE "org.freedesktop.hostname1"
+#define HOSTNAMED_INTERFACE HOSTNAMED_SERVICE
+#define HOSTNAMED_PATH "/org/freedesktop/hostname1"
+
+static GDBusClient *hostnamed_client = NULL;
+static GDBusProxy *hostnamed_proxy = NULL;
+static char *machine_type = NULL;
+
+const char *connman_machine_get_type(void)
+{
+ if (machine_type)
+ return machine_type;
+
+ return DEFAULT_MACHINE_TYPE;
+}
+
+static void machine_property_changed(GDBusProxy *proxy, const char *name,
+ DBusMessageIter *iter, void *user_data)
+{
+ DBG("Property %s", name);
+
+ if (g_str_equal(name, "Chassis")) {
+ const char *str;
+
+ if (!iter) {
+ g_dbus_proxy_refresh_property(proxy, name);
+ return;
+ }
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+ return;
+
+ dbus_message_iter_get_basic(iter, &str);
+ g_free(machine_type);
+ machine_type = g_strdup(str);
+
+ DBG("Machine type set to %s", machine_type);
+ }
+}
+
+int __connman_machine_init(void)
+{
+ DBusConnection *connection;
+ int err = -EIO;
+
+ DBG("");
+
+ connection = connman_dbus_get_connection();
+
+ hostnamed_client = g_dbus_client_new(connection, HOSTNAMED_SERVICE,
+ HOSTNAMED_PATH);
+ if (!hostnamed_client)
+ goto error;
+
+ hostnamed_proxy = g_dbus_proxy_new(hostnamed_client, HOSTNAMED_PATH,
+ HOSTNAMED_INTERFACE);
+ if (!hostnamed_proxy)
+ goto error;
+
+ g_dbus_proxy_set_property_watch(hostnamed_proxy,
+ machine_property_changed, NULL);
+
+ dbus_connection_unref(connection);
+
+ return 0;
+error:
+ if (hostnamed_client) {
+ g_dbus_client_unref(hostnamed_client);
+ hostnamed_client = NULL;
+ }
+
+ dbus_connection_unref(connection);
+
+ return err;
+}
+
+void __connman_machine_cleanup(void)
+{
+ DBG("");
+
+ if (hostnamed_proxy) {
+ g_dbus_proxy_unref(hostnamed_proxy);
+ hostnamed_proxy = NULL;
+ }
+
+ if (hostnamed_client) {
+ g_dbus_client_unref(hostnamed_client);
+ hostnamed_client = NULL;
+ }
+
+ g_free(machine_type);
+ machine_type = NULL;
+}
diff --git a/src/main.c b/src/main.c
index 4f635de5..21d1e06f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -644,6 +644,7 @@ int main(int argc, char *argv[])
__connman_notifier_init();
__connman_agent_init();
__connman_service_init();
+ __connman_peer_service_init();
__connman_peer_init();
__connman_provider_init();
__connman_network_init();
@@ -678,6 +679,7 @@ int main(int argc, char *argv[])
__connman_wpad_init();
__connman_wispr_init();
__connman_rfkill_init();
+ __connman_machine_init();
g_free(option_config);
g_free(option_device);
@@ -689,6 +691,7 @@ int main(int argc, char *argv[])
g_source_remove(signal);
+ __connman_machine_cleanup();
__connman_rfkill_cleanup();
__connman_wispr_cleanup();
__connman_wpad_cleanup();
@@ -713,12 +716,13 @@ int main(int argc, char *argv[])
__connman_nat_cleanup();
__connman_firewall_cleanup();
__connman_iptables_cleanup();
+ __connman_peer_service_cleanup();
+ __connman_peer_cleanup();
__connman_ippool_cleanup();
__connman_device_cleanup();
__connman_network_cleanup();
__connman_dhcp_cleanup();
__connman_service_cleanup();
- __connman_peer_cleanup();
__connman_agent_cleanup();
__connman_ipconfig_cleanup();
__connman_notifier_cleanup();
diff --git a/src/manager.c b/src/manager.c
index b31ab4c7..d15ce203 100644
--- a/src/manager.c
+++ b/src/manager.c
@@ -380,6 +380,126 @@ static DBusMessage *release_private_network(DBusConnection *conn,
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
+static int parse_peers_service_specs(DBusMessageIter *array,
+ const unsigned char **spec, int *spec_len,
+ const unsigned char **query, int *query_len,
+ int *version)
+{
+ *spec = *query = NULL;
+ *spec_len = *query_len = *version = 0;
+
+ while (dbus_message_iter_get_arg_type(array) ==
+ DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter entry, inter, value;
+ const char *key;
+
+ dbus_message_iter_recurse(array, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+
+ dbus_message_iter_recurse(&entry, &inter);
+
+ if (!g_strcmp0(key, "BonjourResponse")) {
+ dbus_message_iter_recurse(&inter, &value);
+ dbus_message_iter_get_fixed_array(&value,
+ spec, spec_len);
+ } else if (!g_strcmp0(key, "BonjourQuery")) {
+ dbus_message_iter_recurse(&inter, &value);
+ dbus_message_iter_get_fixed_array(&value,
+ query, query_len);
+ } else if (!g_strcmp0(key, "UpnpService")) {
+ dbus_message_iter_get_basic(&inter, spec);
+ *spec_len = strlen((const char *)*spec)+1;
+ } else if (!g_strcmp0(key, "UpnpVersion")) {
+ dbus_message_iter_get_basic(&inter, version);
+ } else if (!g_strcmp0(key, "WiFiDisplayIEs")) {
+ if (*spec || *query)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(&inter, &value);
+ dbus_message_iter_get_fixed_array(&value,
+ spec, spec_len);
+ } else
+ return -EINVAL;
+
+ dbus_message_iter_next(array);
+ }
+
+ if ((*query && !*spec && !*version) ||
+ (!*spec && !*query) || (!*spec && *version))
+ return -EINVAL;
+
+ return 0;
+}
+
+static DBusMessage *register_peer_service(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const unsigned char *spec, *query;
+ DBusMessageIter iter, array;
+ int spec_len, query_len;
+ dbus_bool_t master;
+ const char *owner;
+ int version;
+ int ret;
+
+ DBG("");
+
+ owner = dbus_message_get_sender(msg);
+
+ dbus_message_iter_init(msg, &iter);
+ dbus_message_iter_recurse(&iter, &array);
+
+ ret = parse_peers_service_specs(&array, &spec, &spec_len,
+ &query, &query_len, &version);
+ if (ret)
+ goto error;
+
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_get_basic(&iter, &master);
+
+ ret = __connman_peer_service_register(owner, msg, spec, spec_len,
+ query, query_len, version,master);
+ if (!ret)
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+ if (ret == -EINPROGRESS)
+ return NULL;
+error:
+ return __connman_error_failed(msg, -ret);
+}
+
+static DBusMessage *unregister_peer_service(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const unsigned char *spec, *query;
+ DBusMessageIter iter, array;
+ int spec_len, query_len;
+ const char *owner;
+ int version;
+ int ret;
+
+ DBG("");
+
+ owner = dbus_message_get_sender(msg);
+
+ dbus_message_iter_init(msg, &iter);
+ dbus_message_iter_recurse(&iter, &array);
+
+ ret = parse_peers_service_specs(&array, &spec, &spec_len,
+ &query, &query_len, &version);
+ if (ret)
+ goto error;
+
+ ret = __connman_peer_service_unregister(owner, spec, spec_len,
+ query, query_len, version);
+ if (!ret)
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+error:
+ return __connman_error_failed(msg, -ret);
+
+}
+
static const GDBusMethodTable manager_methods[] = {
{ GDBUS_METHOD("GetProperties",
NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
@@ -432,6 +552,13 @@ static const GDBusMethodTable manager_methods[] = {
{ GDBUS_METHOD("ReleasePrivateNetwork",
GDBUS_ARGS({ "path", "o" }), NULL,
release_private_network) },
+ { GDBUS_ASYNC_METHOD("RegisterPeerService",
+ GDBUS_ARGS({ "specification", "a{sv}" },
+ { "master", "b" }), NULL,
+ register_peer_service) },
+ { GDBUS_METHOD("UnregisterPeerService",
+ GDBUS_ARGS({ "specification", "a{sv}" }), NULL,
+ unregister_peer_service) },
{ },
};
diff --git a/src/nat.c b/src/nat.c
index 4d235504..063f0851 100644
--- a/src/nat.c
+++ b/src/nat.c
@@ -3,7 +3,7 @@
* Connection Manager
*
* Copyright (C) 2007-2012 Intel Corporation. All rights reserved.
- * Copyright (C) 2012 BMW Car IT GmbH. All rights reserved.
+ * Copyright (C) 2012-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/src/net.connman.service.in b/src/net.connman.service.in
index 0bb1e8b9..e76969bc 100644
--- a/src/net.connman.service.in
+++ b/src/net.connman.service.in
@@ -1,5 +1,5 @@
[D-BUS Service]
Name=net.connman
-Exec=@prefix@/sbin/connman -n
+Exec=@prefix@/sbin/connmand -n
User=root
SystemdService=connman.service
diff --git a/src/network.c b/src/network.c
index 160bd061..b388995f 100644
--- a/src/network.c
+++ b/src/network.c
@@ -202,7 +202,8 @@ static void dhcp_failure(struct connman_network *network)
__connman_ipconfig_gateway_remove(ipconfig_ipv4);
}
-static void dhcp_callback(struct connman_network *network,
+static void dhcp_callback(struct connman_ipconfig *ipconfig,
+ struct connman_network *network,
bool success, gpointer data)
{
if (success)
@@ -285,13 +286,19 @@ err:
static int set_connected_dhcp(struct connman_network *network)
{
+ struct connman_service *service;
+ struct connman_ipconfig *ipconfig_ipv4;
int err;
DBG("network %p", network);
set_configuration(network, CONNMAN_IPCONFIG_TYPE_IPV4);
- err = __connman_dhcp_start(network, dhcp_callback);
+ service = connman_service_lookup_from_network(network);
+ ipconfig_ipv4 = __connman_service_get_ip4config(service);
+
+ err = __connman_dhcp_start(ipconfig_ipv4, network,
+ dhcp_callback, NULL);
if (err < 0) {
connman_error("Can not request DHCP lease");
return err;
@@ -457,6 +464,7 @@ static void check_dhcpv6(struct nd_router_advert *reply,
unsigned int length, void *user_data)
{
struct connman_network *network = user_data;
+ struct connman_service *service;
GSList *prefixes;
DBG("reply %p", reply);
@@ -492,6 +500,23 @@ static void check_dhcpv6(struct nd_router_advert *reply,
prefixes = __connman_inet_ipv6_get_prefixes(reply, length);
/*
+ * If IPv6 config is missing from service, then create it.
+ * The ipconfig might be missing if we got a rtnl message
+ * that disabled IPv6 config and thus removed it. This
+ * can happen if we are switching from one service to
+ * another in the same interface. The only way to get IPv6
+ * config back is to re-create it here.
+ */
+ service = connman_service_lookup_from_network(network);
+ if (service) {
+ connman_service_create_ip6config(service, network->index);
+
+ __connman_service_ipconfig_indicate_state(service,
+ CONNMAN_SERVICE_STATE_CONFIGURATION,
+ CONNMAN_IPCONFIG_TYPE_IPV6);
+ }
+
+ /*
* We do stateful/stateless DHCPv6 if router advertisement says so.
*/
if (reply->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)
@@ -587,6 +612,8 @@ static void autoconf_ipv6_set(struct connman_network *network)
if (!ipconfig)
return;
+ __connman_ipconfig_address_remove(ipconfig);
+
index = __connman_ipconfig_get_index(ipconfig);
connman_network_ref(network);
@@ -717,7 +744,7 @@ static void set_disconnected(struct connman_network *network)
case CONNMAN_IPCONFIG_METHOD_MANUAL:
break;
case CONNMAN_IPCONFIG_METHOD_DHCP:
- __connman_dhcp_stop(network);
+ __connman_dhcp_stop(ipconfig_ipv4);
break;
}
}
@@ -1385,22 +1412,6 @@ void connman_network_set_error(struct connman_network *network,
network_change(network);
}
-void connman_network_clear_error(struct connman_network *network)
-{
- struct connman_service *service;
-
- DBG("network %p", network);
-
- if (!network)
- return;
-
- if (network->connecting || network->associating)
- return;
-
- service = connman_service_lookup_from_network(network);
- __connman_service_clear_error(service);
-}
-
/**
* connman_network_set_connected:
* @network: network structure
@@ -1469,7 +1480,7 @@ void connman_network_clear_hidden(void *user_data)
* error to the caller telling that we could not find
* any network that we could connect to.
*/
- __connman_service_reply_dbus_pending(user_data, EIO, NULL);
+ connman_dbus_reply_pending(user_data, EIO, NULL);
}
int connman_network_connect_hidden(struct connman_network *network,
@@ -1489,7 +1500,7 @@ int connman_network_connect_hidden(struct connman_network *network,
__connman_service_set_agent_identity(service, identity);
if (passphrase)
- err = __connman_service_add_passphrase(service, passphrase);
+ err = __connman_service_set_passphrase(service, passphrase);
if (err == -ENOKEY) {
__connman_service_indicate_error(service,
@@ -1607,6 +1618,7 @@ int __connman_network_clear_ipconfig(struct connman_network *network,
struct connman_ipconfig *ipconfig)
{
struct connman_service *service;
+ struct connman_ipconfig *ipconfig_ipv4;
enum connman_ipconfig_method method;
enum connman_ipconfig_type type;
@@ -1614,6 +1626,7 @@ int __connman_network_clear_ipconfig(struct connman_network *network,
if (!service)
return -EINVAL;
+ ipconfig_ipv4 = __connman_service_get_ip4config(service);
method = __connman_ipconfig_get_method(ipconfig);
type = __connman_ipconfig_get_config_type(ipconfig);
@@ -1629,7 +1642,7 @@ int __connman_network_clear_ipconfig(struct connman_network *network,
__connman_ipconfig_address_remove(ipconfig);
break;
case CONNMAN_IPCONFIG_METHOD_DHCP:
- __connman_dhcp_stop(network);
+ __connman_dhcp_stop(ipconfig_ipv4);
break;
}
@@ -1691,7 +1704,8 @@ int __connman_network_set_ipconfig(struct connman_network *network,
case CONNMAN_IPCONFIG_METHOD_MANUAL:
return manual_ipv4_set(network, ipconfig_ipv4);
case CONNMAN_IPCONFIG_METHOD_DHCP:
- return __connman_dhcp_start(network, dhcp_callback);
+ return __connman_dhcp_start(ipconfig_ipv4,
+ network, dhcp_callback, NULL);
}
}
diff --git a/src/peer.c b/src/peer.c
index ce3b582e..caff70c8 100644
--- a/src/peer.c
+++ b/src/peer.c
@@ -24,7 +24,11 @@
#endif
#include <errno.h>
+#include <ctype.h>
#include <gdbus.h>
+#include <gdhcp/gdhcp.h>
+
+#include <connman/agent.h>
#include "connman.h"
@@ -32,21 +36,304 @@ static DBusConnection *connection = NULL;
static GHashTable *peers_table = NULL;
+static struct connman_peer_driver *peer_driver;
+
+struct _peers_notify {
+ int id;
+ GHashTable *add;
+ GHashTable *remove;
+} *peers_notify;
+
+struct _peer_service {
+ enum connman_peer_service_type type;
+ unsigned char *data;
+ int length;
+};
+
struct connman_peer {
+ int refcount;
+ struct connman_device *device;
+ struct connman_device *sub_device;
char *identifier;
char *name;
char *path;
+ enum connman_peer_state state;
+ struct connman_ipconfig *ipconfig;
+ DBusMessage *pending;
+ bool registered;
+ bool connection_master;
+ struct connman_ippool *ip_pool;
+ GDHCPServer *dhcp_server;
+ GSList *services;
};
+static void stop_dhcp_server(struct connman_peer *peer)
+{
+ DBG("");
+
+ if (peer->dhcp_server)
+ g_dhcp_server_unref(peer->dhcp_server);
+
+ peer->dhcp_server = NULL;
+
+ if (peer->ip_pool)
+ __connman_ippool_unref(peer->ip_pool);
+ peer->ip_pool = NULL;
+}
+
+static void dhcp_server_debug(const char *str, void *data)
+{
+ connman_info("%s: %s\n", (const char *) data, str);
+}
+
+static gboolean dhcp_server_started(gpointer data)
+{
+ struct connman_peer *peer = data;
+
+ connman_peer_set_state(peer, CONNMAN_PEER_STATE_READY);
+ connman_peer_unref(peer);
+
+ return FALSE;
+}
+
+static int start_dhcp_server(struct connman_peer *peer)
+{
+ const char *start_ip, *end_ip;
+ GDHCPServerError dhcp_error;
+ const char *broadcast;
+ const char *gateway;
+ const char *subnet;
+ int prefixlen;
+ int index;
+ int err;
+
+ DBG("");
+
+ err = -ENOMEM;
+
+ if (peer->sub_device)
+ index = connman_device_get_index(peer->sub_device);
+ else
+ index = connman_device_get_index(peer->device);
+
+ peer->ip_pool = __connman_ippool_create(index, 2, 1, NULL, NULL);
+ if (!peer->ip_pool)
+ goto error;
+
+ gateway = __connman_ippool_get_gateway(peer->ip_pool);
+ subnet = __connman_ippool_get_subnet_mask(peer->ip_pool);
+ broadcast = __connman_ippool_get_broadcast(peer->ip_pool);
+ start_ip = __connman_ippool_get_start_ip(peer->ip_pool);
+ end_ip = __connman_ippool_get_end_ip(peer->ip_pool);
+
+ prefixlen = connman_ipaddress_calc_netmask_len(subnet);
+
+ err = __connman_inet_modify_address(RTM_NEWADDR,
+ NLM_F_REPLACE | NLM_F_ACK, index, AF_INET,
+ gateway, NULL, prefixlen, broadcast);
+ if (err < 0)
+ goto error;
+
+ peer->dhcp_server = g_dhcp_server_new(G_DHCP_IPV4, index, &dhcp_error);
+ if (!peer->dhcp_server)
+ goto error;
+
+ g_dhcp_server_set_debug(peer->dhcp_server,
+ dhcp_server_debug, "Peer DHCP server");
+ g_dhcp_server_set_lease_time(peer->dhcp_server, 3600);
+ g_dhcp_server_set_option(peer->dhcp_server, G_DHCP_SUBNET, subnet);
+ g_dhcp_server_set_option(peer->dhcp_server, G_DHCP_ROUTER, gateway);
+ g_dhcp_server_set_option(peer->dhcp_server, G_DHCP_DNS_SERVER, NULL);
+ g_dhcp_server_set_ip_range(peer->dhcp_server, start_ip, end_ip);
+
+ err = g_dhcp_server_start(peer->dhcp_server);
+ if (err < 0)
+ goto error;
+
+ g_timeout_add_seconds(0, dhcp_server_started, connman_peer_ref(peer));
+
+ return 0;
+
+error:
+ stop_dhcp_server(peer);
+ return err;
+}
+
+static void reply_pending(struct connman_peer *peer, int error)
+{
+ if (!peer->pending)
+ return;
+
+ connman_dbus_reply_pending(peer->pending, error, NULL);
+ peer->pending = NULL;
+}
+
static void peer_free(gpointer data)
{
struct connman_peer *peer = data;
- connman_peer_destroy(peer);
+
+ reply_pending(peer, ENOENT);
+
+ connman_peer_unregister(peer);
+
+ if (peer->path) {
+ g_free(peer->path);
+ peer->path = NULL;
+ }
+
+ if (peer->ipconfig) {
+ __connman_ipconfig_set_ops(peer->ipconfig, NULL);
+ __connman_ipconfig_set_data(peer->ipconfig, NULL);
+ __connman_ipconfig_unref(peer->ipconfig);
+ peer->ipconfig = NULL;
+ }
+
+ stop_dhcp_server(peer);
+
+ if (peer->device) {
+ connman_device_unref(peer->device);
+ peer->device = NULL;
+ }
+
+ if (peer->services)
+ connman_peer_reset_services(peer);
+
+ g_free(peer->identifier);
+ g_free(peer->name);
+
+ g_free(peer);
+}
+
+static const char *state2string(enum connman_peer_state state)
+{
+ switch (state) {
+ case CONNMAN_PEER_STATE_UNKNOWN:
+ break;
+ case CONNMAN_PEER_STATE_IDLE:
+ return "idle";
+ case CONNMAN_PEER_STATE_ASSOCIATION:
+ return "association";
+ case CONNMAN_PEER_STATE_CONFIGURATION:
+ return "configuration";
+ case CONNMAN_PEER_STATE_READY:
+ return "ready";
+ case CONNMAN_PEER_STATE_DISCONNECT:
+ return "disconnect";
+ case CONNMAN_PEER_STATE_FAILURE:
+ return "failure";
+ }
+
+ return NULL;
+}
+
+static bool is_connecting(struct connman_peer *peer)
+{
+ if (peer->state == CONNMAN_PEER_STATE_ASSOCIATION ||
+ peer->state == CONNMAN_PEER_STATE_CONFIGURATION ||
+ peer->pending)
+ return true;
+
+ return false;
+}
+
+static bool is_connected(struct connman_peer *peer)
+{
+ if (peer->state == CONNMAN_PEER_STATE_READY)
+ return true;
+
+ return false;
+}
+
+static bool allow_property_changed(struct connman_peer *peer)
+{
+ if (g_hash_table_lookup_extended(peers_notify->add, peer->path,
+ NULL, NULL))
+ return false;
+
+ return true;
+}
+
+static void append_dhcp_server_ipv4(DBusMessageIter *iter, void *user_data)
+{
+ struct connman_peer *peer = user_data;
+ const char *str = "dhcp";
+ const char *gateway;
+ const char *subnet;
+
+ if (!peer->ip_pool)
+ return;
+
+ gateway = __connman_ippool_get_gateway(peer->ip_pool);
+ subnet = __connman_ippool_get_subnet_mask(peer->ip_pool);
+
+ connman_dbus_dict_append_basic(iter, "Method", DBUS_TYPE_STRING, &str);
+ connman_dbus_dict_append_basic(iter, "Address",
+ DBUS_TYPE_STRING, &gateway);
+ connman_dbus_dict_append_basic(iter, "Netmask",
+ DBUS_TYPE_STRING, &subnet);
+ connman_dbus_dict_append_basic(iter, "Gateway",
+ DBUS_TYPE_STRING, &gateway);
+}
+
+static void append_ipv4(DBusMessageIter *iter, void *user_data)
+{
+ struct connman_peer *peer = user_data;
+
+ if (!is_connected(peer))
+ return;
+
+ if (peer->connection_master)
+ append_dhcp_server_ipv4(iter, peer);
+ else if (peer->ipconfig)
+ __connman_ipconfig_append_ipv4(peer->ipconfig, iter);
+}
+
+static void append_peer_service(DBusMessageIter *iter,
+ struct _peer_service *service)
+{
+ DBusMessageIter dict;
+
+ connman_dbus_dict_open(iter, &dict);
+
+ switch (service->type) {
+ case CONNMAN_PEER_SERVICE_UNKNOWN:
+ /* Should never happen */
+ break;
+ case CONNMAN_PEER_SERVICE_WIFI_DISPLAY:
+ connman_dbus_dict_append_fixed_array(&dict,
+ "WiFiDisplayIEs", DBUS_TYPE_BYTE,
+ &service->data, service->length);
+ break;
+ }
+
+ connman_dbus_dict_close(iter, &dict);
+}
+
+static void append_peer_services(DBusMessageIter *iter, void *user_data)
+{
+ struct connman_peer *peer = user_data;
+ DBusMessageIter container;
+ GSList *list;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
+ NULL, &container);
+
+ if (!peer->services) {
+ DBusMessageIter dict;
+
+ connman_dbus_dict_open(&container, &dict);
+ connman_dbus_dict_close(&container, &dict);
+ } else {
+ for (list = peer->services; list; list = list->next)
+ append_peer_service(&container, list->data);
+ }
+
+ dbus_message_iter_close_container(iter, &container);
}
static void append_properties(DBusMessageIter *iter, struct connman_peer *peer)
{
- const char *state = "disconnected";
+ const char *state = state2string(peer->state);
DBusMessageIter dict;
connman_dbus_dict_open(iter, &dict);
@@ -55,11 +342,23 @@ static void append_properties(DBusMessageIter *iter, struct connman_peer *peer)
DBUS_TYPE_STRING, &state);
connman_dbus_dict_append_basic(&dict, "Name",
DBUS_TYPE_STRING, &peer->name);
- connman_dbus_dict_append_dict(&dict, "IPv4", NULL, NULL);
-
+ connman_dbus_dict_append_dict(&dict, "IPv4", append_ipv4, peer);
+ connman_dbus_dict_append_array(&dict, "Services",
+ DBUS_TYPE_DICT_ENTRY,
+ append_peer_services, peer);
connman_dbus_dict_close(iter, &dict);
}
+static void settings_changed(struct connman_peer *peer)
+{
+ if (!allow_property_changed(peer))
+ return;
+
+ connman_dbus_property_changed_dict(peer->path,
+ CONNMAN_PEER_INTERFACE, "IPv4",
+ append_ipv4, peer);
+}
+
static DBusMessage *get_peer_properties(DBusConnection *conn,
DBusMessage *msg, void *data)
{
@@ -92,31 +391,44 @@ static void append_peer_struct(gpointer key, gpointer value,
dbus_message_iter_close_container(array, &entry);
}
-struct _peers_notify {
- int id;
- GHashTable *add;
- GHashTable *remove;
-} *peers_notify;
+static void state_changed(struct connman_peer *peer)
+{
+ const char *state;
+
+ state = state2string(peer->state);
+ if (!state || !allow_property_changed(peer))
+ return;
+
+ connman_dbus_property_changed_basic(peer->path,
+ CONNMAN_PEER_INTERFACE, "State",
+ DBUS_TYPE_STRING, &state);
+}
static void append_existing_and_new_peers(gpointer key,
gpointer value, gpointer user_data)
{
struct connman_peer *peer = value;
DBusMessageIter *iter = user_data;
- DBusMessageIter entry;
+ DBusMessageIter entry, dict;
+
+ if (!peer || !peer->registered)
+ return;
if (g_hash_table_lookup(peers_notify->add, peer->path)) {
DBG("new %s", peer->path);
- append_peer_struct(key, value, user_data);
+ append_peer_struct(key, peer, iter);
g_hash_table_remove(peers_notify->add, peer->path);
- } else {
+ } else if (!g_hash_table_lookup(peers_notify->remove, peer->path)) {
DBG("existing %s", peer->path);
dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
NULL, &entry);
dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
&peer->path);
+ connman_dbus_dict_open(&entry, &dict);
+ connman_dbus_dict_close(&entry, &dict);
+
dbus_message_iter_close_container(iter, &entry);
}
}
@@ -195,32 +507,216 @@ static void peer_removed(struct connman_peer *peer)
peer_schedule_changed();
}
+static const char *get_dbus_sender(struct connman_peer *peer)
+{
+ if (!peer->pending)
+ return NULL;
+
+ return dbus_message_get_sender(peer->pending);
+}
+
+static enum connman_peer_wps_method check_wpspin(struct connman_peer *peer,
+ const char *wpspin)
+{
+ int len, i;
+
+ if (!wpspin)
+ return CONNMAN_PEER_WPS_PBC;
+
+ len = strlen(wpspin);
+ if (len == 0)
+ return CONNMAN_PEER_WPS_PBC;
+
+ if (len != 8)
+ return CONNMAN_PEER_WPS_UNKNOWN;
+ for (i = 0; i < 8; i++) {
+ if (!isdigit((unsigned char) wpspin[i]))
+ return CONNMAN_PEER_WPS_UNKNOWN;
+ }
+
+ return CONNMAN_PEER_WPS_PIN;
+}
+
+static void request_authorization_cb(struct connman_peer *peer,
+ bool choice_done, const char *wpspin,
+ const char *error, void *user_data)
+{
+ enum connman_peer_wps_method wps_method;
+ int err;
+
+ DBG("RequestInput return, %p", peer);
+
+ if (error) {
+ if (g_strcmp0(error,
+ "net.connman.Agent.Error.Canceled") == 0 ||
+ g_strcmp0(error,
+ "net.connman.Agent.Error.Rejected") == 0) {
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (!choice_done || !peer_driver->connect) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ wps_method = check_wpspin(peer, wpspin);
+
+ err = peer_driver->connect(peer, wps_method, wpspin);
+ if (err == -EINPROGRESS)
+ return;
+
+out:
+ reply_pending(peer, EIO);
+ connman_peer_set_state(peer, CONNMAN_PEER_STATE_IDLE);
+}
+
+static int peer_connect(struct connman_peer *peer)
+{
+ int err = -ENOTSUP;
+
+ if (peer_driver->connect)
+ err = peer_driver->connect(peer,
+ CONNMAN_PEER_WPS_UNKNOWN, NULL);
+
+ if (err == -ENOKEY) {
+ err = __connman_agent_request_peer_authorization(peer,
+ request_authorization_cb, true,
+ get_dbus_sender(peer), NULL);
+ }
+
+ return err;
+}
+
+static int peer_disconnect(struct connman_peer *peer)
+{
+ int err = -ENOTSUP;
+
+ connman_agent_cancel(peer);
+ reply_pending(peer, ECONNABORTED);
+
+ connman_peer_set_state(peer, CONNMAN_PEER_STATE_DISCONNECT);
+
+ if (peer->connection_master)
+ stop_dhcp_server(peer);
+ else
+ __connman_dhcp_stop(peer->ipconfig);
+
+ if (peer_driver->disconnect)
+ err = peer_driver->disconnect(peer);
+
+ connman_peer_set_state(peer, CONNMAN_PEER_STATE_IDLE);
+
+ return err;
+}
+
+static DBusMessage *connect_peer(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct connman_peer *peer = user_data;
+ GList *list, *start;
+ int err;
+
+ DBG("peer %p", peer);
+
+ if (peer->pending)
+ return __connman_error_in_progress(msg);
+
+ list = g_hash_table_get_values(peers_table);
+ start = list;
+ for (; list; list = list->next) {
+ struct connman_peer *temp = list->data;
+
+ if (temp == peer || temp->device != peer->device)
+ continue;
+
+ if (is_connecting(temp) || is_connected(temp)) {
+ if (peer_disconnect(temp) == -EINPROGRESS) {
+ g_list_free(start);
+ return __connman_error_in_progress(msg);
+ }
+ }
+ }
+
+ g_list_free(start);
+
+ peer->pending = dbus_message_ref(msg);
+
+ err = peer_connect(peer);
+ if (err == -EINPROGRESS)
+ return NULL;
+
+ if (err < 0) {
+ dbus_message_unref(peer->pending);
+ peer->pending = NULL;
+
+ return __connman_error_failed(msg, -err);
+ }
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *disconnect_peer(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct connman_peer *peer = user_data;
+ int err;
+
+ DBG("peer %p", peer);
+
+ err = peer_disconnect(peer);
+ if (err < 0 && err != -EINPROGRESS)
+ return __connman_error_failed(msg, -err);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
struct connman_peer *connman_peer_create(const char *identifier)
{
struct connman_peer *peer;
peer = g_malloc0(sizeof(struct connman_peer));
- peer->identifier = g_strdup_printf("peer_%s", identifier);
+ peer->identifier = g_strdup(identifier);
+ peer->state = CONNMAN_PEER_STATE_IDLE;
+
+ peer->refcount = 1;
return peer;
}
-void connman_peer_destroy(struct connman_peer *peer)
+struct connman_peer *connman_peer_ref_debug(struct connman_peer *peer,
+ const char *file, int line, const char *caller)
{
- if (!peer)
+ DBG("%p ref %d by %s:%d:%s()", peer, peer->refcount + 1,
+ file, line, caller);
+
+ __sync_fetch_and_add(&peer->refcount, 1);
+
+ return peer;
+}
+
+void connman_peer_unref_debug(struct connman_peer *peer,
+ const char *file, int line, const char *caller)
+{
+ DBG("%p ref %d by %s:%d:%s()", peer, peer->refcount - 1,
+ file, line, caller);
+
+ if (__sync_fetch_and_sub(&peer->refcount, 1) != 1)
return;
- if (peer->path) {
- peer_removed(peer);
- g_dbus_unregister_interface(connection, peer->path,
- CONNMAN_PEER_INTERFACE);
- g_free(peer->path);
- }
+ if (!peer->registered && !peer->path)
+ return peer_free(peer);
- g_free(peer->identifier);
- g_free(peer->name);
+ g_hash_table_remove(peers_table, peer->path);
+}
- g_free(peer);
+const char *connman_peer_get_identifier(struct connman_peer *peer)
+{
+ if (!peer)
+ return NULL;
+
+ return peer->identifier;
}
void connman_peer_set_name(struct connman_peer *peer, const char *name)
@@ -229,12 +725,304 @@ void connman_peer_set_name(struct connman_peer *peer, const char *name)
peer->name = g_strdup(name);
}
+void connman_peer_set_device(struct connman_peer *peer,
+ struct connman_device *device)
+{
+ if (!peer || !device)
+ return;
+
+ peer->device = device;
+ connman_device_ref(device);
+}
+
+struct connman_device *connman_peer_get_device(struct connman_peer *peer)
+{
+ if (!peer)
+ return NULL;
+
+ return peer->device;
+}
+
+void connman_peer_set_sub_device(struct connman_peer *peer,
+ struct connman_device *device)
+{
+ if (!peer || !device || peer->sub_device)
+ return;
+
+ peer->sub_device = device;
+}
+
+void connman_peer_set_as_master(struct connman_peer *peer, bool master)
+{
+ if (!peer || !is_connecting(peer))
+ return;
+
+ peer->connection_master = master;
+}
+
+static void dhcp_callback(struct connman_ipconfig *ipconfig,
+ struct connman_network *network,
+ bool success, gpointer data)
+{
+ struct connman_peer *peer = data;
+ int err;
+
+ if (!success)
+ goto error;
+
+ DBG("lease acquired for ipconfig %p", ipconfig);
+
+ err = __connman_ipconfig_address_add(ipconfig);
+ if (err < 0)
+ goto error;
+
+ return;
+
+error:
+ __connman_ipconfig_address_remove(ipconfig);
+ connman_peer_set_state(peer, CONNMAN_PEER_STATE_FAILURE);
+}
+
+static int start_dhcp_client(struct connman_peer *peer)
+{
+ if (peer->sub_device)
+ __connman_ipconfig_set_index(peer->ipconfig,
+ connman_device_get_index(peer->sub_device));
+
+ __connman_ipconfig_enable(peer->ipconfig);
+
+ return __connman_dhcp_start(peer->ipconfig, NULL, dhcp_callback, peer);
+}
+
+static void report_error_cb(void *user_context, bool retry, void *user_data)
+{
+ struct connman_peer *peer = user_context;
+
+ if (retry) {
+ int err;
+ err = peer_connect(peer);
+
+ if (err == 0 || err == -EINPROGRESS)
+ return;
+ }
+
+ reply_pending(peer, ENOTCONN);
+
+ peer_disconnect(peer);
+
+ if (!peer->connection_master) {
+ __connman_dhcp_stop(peer->ipconfig);
+ __connman_ipconfig_disable(peer->ipconfig);
+ } else
+ stop_dhcp_server(peer);
+
+ peer->connection_master = false;
+ peer->sub_device = NULL;
+}
+
+static int manage_peer_error(struct connman_peer *peer)
+{
+ int err;
+
+ err = __connman_agent_report_peer_error(peer, peer->path,
+ "connect-failed", report_error_cb,
+ get_dbus_sender(peer), NULL);
+ if (err != -EINPROGRESS) {
+ report_error_cb(peer, false, NULL);
+ return err;
+ }
+
+ return 0;
+}
+
+int connman_peer_set_state(struct connman_peer *peer,
+ enum connman_peer_state new_state)
+{
+ enum connman_peer_state old_state = peer->state;
+ int err;
+
+ DBG("peer (%s) old state %d new state %d", peer->name,
+ old_state, new_state);
+
+ if (old_state == new_state)
+ return -EALREADY;
+
+ switch (new_state) {
+ case CONNMAN_PEER_STATE_UNKNOWN:
+ return -EINVAL;
+ case CONNMAN_PEER_STATE_IDLE:
+ if (is_connecting(peer) || is_connected(peer))
+ return peer_disconnect(peer);
+ peer->sub_device = NULL;
+ break;
+ case CONNMAN_PEER_STATE_ASSOCIATION:
+ break;
+ case CONNMAN_PEER_STATE_CONFIGURATION:
+ if (peer->connection_master)
+ err = start_dhcp_server(peer);
+ else
+ err = start_dhcp_client(peer);
+ if (err < 0)
+ return connman_peer_set_state(peer,
+ CONNMAN_PEER_STATE_FAILURE);
+ break;
+ case CONNMAN_PEER_STATE_READY:
+ reply_pending(peer, 0);
+ break;
+ case CONNMAN_PEER_STATE_DISCONNECT:
+ if (peer->connection_master)
+ stop_dhcp_server(peer);
+ peer->connection_master = false;
+ peer->sub_device = NULL;
+
+ break;
+ case CONNMAN_PEER_STATE_FAILURE:
+ if (manage_peer_error(peer) == 0)
+ return 0;
+ break;
+ };
+
+ peer->state = new_state;
+ state_changed(peer);
+
+ return 0;
+}
+
+int connman_peer_request_connection(struct connman_peer *peer)
+{
+ return __connman_agent_request_peer_authorization(peer,
+ request_authorization_cb, false,
+ NULL, NULL);
+}
+
+static void peer_service_free(gpointer data)
+{
+ struct _peer_service *service = data;
+
+ if (!service)
+ return;
+
+ g_free(service->data);
+ g_free(service);
+}
+
+void connman_peer_reset_services(struct connman_peer *peer)
+{
+ if (!peer)
+ return;
+
+ g_slist_free_full(peer->services, peer_service_free);
+ peer->services = NULL;
+}
+
+void connman_peer_services_changed(struct connman_peer *peer)
+{
+ if (!peer || !peer->registered || !allow_property_changed(peer))
+ return;
+
+ connman_dbus_property_changed_array(peer->path,
+ CONNMAN_PEER_INTERFACE, "Services",
+ DBUS_TYPE_DICT_ENTRY, append_peer_services, peer);
+}
+
+void connman_peer_add_service(struct connman_peer *peer,
+ enum connman_peer_service_type type,
+ const unsigned char *data, int data_length)
+{
+ struct _peer_service *service;
+
+ if (!peer || !data || type == CONNMAN_PEER_SERVICE_UNKNOWN)
+ return;
+
+ service = g_malloc0(sizeof(struct _peer_service));
+ service->type = type;
+ service->data = g_memdup(data, data_length * sizeof(unsigned char));
+ service->length = data_length;
+
+ peer->services = g_slist_prepend(peer->services, service);
+}
+
+static void peer_up(struct connman_ipconfig *ipconfig, const char *ifname)
+{
+ DBG("%s up", ifname);
+}
+
+static void peer_down(struct connman_ipconfig *ipconfig, const char *ifname)
+{
+ DBG("%s down", ifname);
+}
+
+static void peer_lower_up(struct connman_ipconfig *ipconfig,
+ const char *ifname)
+{
+ DBG("%s lower up", ifname);
+}
+
+static void peer_lower_down(struct connman_ipconfig *ipconfig,
+ const char *ifname)
+{
+ struct connman_peer *peer = __connman_ipconfig_get_data(ipconfig);
+
+ DBG("%s lower down", ifname);
+
+ __connman_ipconfig_disable(ipconfig);
+ connman_peer_set_state(peer, CONNMAN_PEER_STATE_DISCONNECT);
+}
+
+static void peer_ip_bound(struct connman_ipconfig *ipconfig,
+ const char *ifname)
+{
+ struct connman_peer *peer = __connman_ipconfig_get_data(ipconfig);
+
+ DBG("%s ip bound", ifname);
+
+ settings_changed(peer);
+ connman_peer_set_state(peer, CONNMAN_PEER_STATE_READY);
+}
+
+static void peer_ip_release(struct connman_ipconfig *ipconfig,
+ const char *ifname)
+{
+ struct connman_peer *peer = __connman_ipconfig_get_data(ipconfig);
+
+ DBG("%s ip release", ifname);
+
+ settings_changed(peer);
+}
+
+static const struct connman_ipconfig_ops peer_ip_ops = {
+ .up = peer_up,
+ .down = peer_down,
+ .lower_up = peer_lower_up,
+ .lower_down = peer_lower_down,
+ .ip_bound = peer_ip_bound,
+ .ip_release = peer_ip_release,
+ .route_set = NULL,
+ .route_unset = NULL,
+};
+
+static struct connman_ipconfig *create_ipconfig(int index, void *user_data)
+{
+ struct connman_ipconfig *ipconfig;
+
+ ipconfig = __connman_ipconfig_create(index,
+ CONNMAN_IPCONFIG_TYPE_IPV4);
+ if (!ipconfig)
+ return NULL;
+
+ __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_DHCP);
+ __connman_ipconfig_set_data(ipconfig, user_data);
+ __connman_ipconfig_set_ops(ipconfig, &peer_ip_ops);
+
+ return ipconfig;
+}
+
static const GDBusMethodTable peer_methods[] = {
{ GDBUS_METHOD("GetProperties",
NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
get_peer_properties) },
- { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, NULL) },
- { GDBUS_METHOD("Disconnect", NULL, NULL, NULL) },
+ { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, connect_peer) },
+ { GDBUS_METHOD("Disconnect", NULL, NULL, disconnect_peer) },
{ },
};
@@ -244,23 +1032,37 @@ static const GDBusSignalTable peer_signals[] = {
{ },
};
+static char *get_peer_path(struct connman_device *device,
+ const char *identifier)
+{
+ return g_strdup_printf("%s/peer/peer_%s_%s", CONNMAN_PATH,
+ connman_device_get_ident(device), identifier);
+}
+
int connman_peer_register(struct connman_peer *peer)
{
+ int index;
+
DBG("peer %p", peer);
- if (peer->path)
+ if (peer->path && peer->registered)
return -EALREADY;
- peer->path = g_strdup_printf("%s/peer/%s", CONNMAN_PATH,
- peer->identifier);
+ index = connman_device_get_index(peer->device);
+ peer->ipconfig = create_ipconfig(index, peer);
+ if (!peer->ipconfig)
+ return -ENOMEM;
+
+ peer->path = get_peer_path(peer->device, peer->identifier);
DBG("path %s", peer->path);
- g_hash_table_insert(peers_table, peer->identifier, peer);
+ g_hash_table_insert(peers_table, peer->path, peer);
g_dbus_register_interface(connection, peer->path,
CONNMAN_PEER_INTERFACE,
peer_methods, peer_signals,
NULL, peer, NULL);
+ peer->registered = true;
peer_added(peer);
return 0;
@@ -270,15 +1072,22 @@ void connman_peer_unregister(struct connman_peer *peer)
{
DBG("peer %p", peer);
- if (peer->path)
- g_hash_table_remove(peers_table, peer->identifier);
- else
- connman_peer_destroy(peer);
+ if (!peer->path || !peer->registered)
+ return;
+
+ connman_agent_cancel(peer);
+ reply_pending(peer, EIO);
+
+ g_dbus_unregister_interface(connection, peer->path,
+ CONNMAN_PEER_INTERFACE);
+ peer->registered = false;
+ peer_removed(peer);
}
-struct connman_peer *connman_peer_get(const char *identifier)
+struct connman_peer *connman_peer_get(struct connman_device *device,
+ const char *identifier)
{
- char *ident = g_strdup_printf("peer_%s", identifier);
+ char *ident = get_peer_path(device, identifier);
struct connman_peer *peer;
peer = g_hash_table_lookup(peers_table, ident);
@@ -287,11 +1096,41 @@ struct connman_peer *connman_peer_get(const char *identifier)
return peer;
}
+int connman_peer_driver_register(struct connman_peer_driver *driver)
+{
+ if (peer_driver && peer_driver != driver)
+ return -EINVAL;
+
+ peer_driver = driver;
+
+ __connman_peer_service_set_driver(driver);
+
+ return 0;
+}
+
+void connman_peer_driver_unregister(struct connman_peer_driver *driver)
+{
+ if (peer_driver != driver)
+ return;
+
+ peer_driver = NULL;
+
+ __connman_peer_service_set_driver(NULL);
+}
+
void __connman_peer_list_struct(DBusMessageIter *array)
{
g_hash_table_foreach(peers_table, append_peer_struct, array);
}
+const char *__connman_peer_get_path(struct connman_peer *peer)
+{
+ if (!peer || !peer->registered)
+ return NULL;
+
+ return peer->path;
+}
+
int __connman_peer_init(void)
{
DBG("");
@@ -313,5 +1152,7 @@ void __connman_peer_cleanup(void)
DBG("");
g_hash_table_destroy(peers_table);
+ peers_table = NULL;
dbus_connection_unref(connection);
+ connection = NULL;
}
diff --git a/src/peer_service.c b/src/peer_service.c
new file mode 100644
index 00000000..053672af
--- /dev/null
+++ b/src/peer_service.c
@@ -0,0 +1,429 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2014 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 <gdbus.h>
+
+#include "connman.h"
+
+static DBusConnection *connection;
+
+struct _peer_service {
+ bool registered;
+ const char *owner;
+ DBusMessage *pending;
+
+ GBytes *specification;
+ GBytes *query;
+ int version;
+
+ bool master;
+};
+
+struct _peer_service_owner {
+ char *owner;
+ guint watch;
+ GList *services;
+};
+
+static struct connman_peer_driver *peer_driver;
+
+static GHashTable *owners_map;
+static GHashTable *services_map;
+static int peer_master;
+
+static void reply_pending(struct _peer_service *service, int error)
+{
+ if (!service->pending)
+ return;
+
+ connman_dbus_reply_pending(service->pending, error, NULL);
+ service->pending = NULL;
+}
+
+static struct _peer_service *find_peer_service(GBytes *specification,
+ GBytes *query, int version,
+ const char *owner, bool remove)
+{
+ struct _peer_service *service = NULL;
+ struct _peer_service_owner *ps_owner;
+ GList *list;
+
+ ps_owner = g_hash_table_lookup(services_map, specification);
+ if (!ps_owner)
+ return NULL;
+
+ if (owner && g_strcmp0(owner, ps_owner->owner) != 0)
+ return NULL;
+
+ for (list = ps_owner->services; list; list = list->next) {
+ service = list->data;
+
+ if (service->specification == specification)
+ break;
+
+ if (version) {
+ if (!service->version)
+ continue;
+ if (version != service->version)
+ continue;
+ }
+
+ if (query) {
+ if (!service->query)
+ continue;
+ if (g_bytes_equal(service->query, query))
+ continue;
+ }
+
+ if (g_bytes_equal(service->specification, specification))
+ break;
+ }
+
+ if (!service)
+ return NULL;
+
+ if (owner && remove)
+ ps_owner->services = g_list_delete_link(ps_owner->services,
+ list);
+
+ return service;
+}
+
+static void unregister_peer_service(struct _peer_service *service)
+{
+ gsize spec_length, query_length = 0;
+ const void *spec, *query = NULL;
+
+ if (!peer_driver || !service->specification)
+ return;
+
+ spec = g_bytes_get_data(service->specification, &spec_length);
+ if (service->query)
+ query = g_bytes_get_data(service->query, &query_length);
+
+ peer_driver->unregister_service(spec, spec_length, query,
+ query_length, service->version);
+}
+
+static void remove_peer_service(gpointer user_data)
+{
+ struct _peer_service *service = user_data;
+
+ reply_pending(service, ECONNABORTED);
+
+ if (service->registered)
+ unregister_peer_service(service);
+
+ if (service->specification) {
+ if (service->owner) {
+ find_peer_service(service->specification,
+ service->query, service->version,
+ service->owner, true);
+ }
+
+ g_hash_table_remove(services_map, service->specification);
+ g_bytes_unref(service->specification);
+ }
+
+ if (service->query)
+ g_bytes_unref(service->query);
+
+ if (service->master)
+ peer_master--;
+
+ g_free(service);
+}
+
+static void apply_peer_service_removal(gpointer user_data)
+{
+ struct _peer_service *service = user_data;
+
+ service->owner = NULL;
+ remove_peer_service(user_data);
+}
+
+static void remove_peer_service_owner(gpointer user_data)
+{
+ struct _peer_service_owner *ps_owner = user_data;
+
+ DBG("owner %s", ps_owner->owner);
+
+ if (ps_owner->watch > 0)
+ g_dbus_remove_watch(connection, ps_owner->watch);
+
+ if (ps_owner->services) {
+ g_list_free_full(ps_owner->services,
+ apply_peer_service_removal);
+ }
+
+ g_free(ps_owner->owner);
+ g_free(ps_owner);
+}
+
+static void owner_disconnect(DBusConnection *conn, void *user_data)
+{
+ struct _peer_service_owner *ps_owner = user_data;
+
+ ps_owner->watch = 0;
+ g_hash_table_remove(owners_map, ps_owner->owner);
+}
+
+static void service_registration_result(int result, void *user_data)
+{
+ struct _peer_service *service = user_data;
+
+ reply_pending(service, -result);
+
+ if (service->registered)
+ return;
+
+ if (result == 0) {
+ service->registered = true;
+ if (service->master)
+ peer_master++;
+ return;
+ }
+
+ remove_peer_service(service);
+}
+
+static int register_peer_service(struct _peer_service *service)
+{
+ gsize spec_length, query_length = 0;
+ const void *spec, *query = NULL;
+
+ if (!peer_driver)
+ return 0;
+
+ spec = g_bytes_get_data(service->specification, &spec_length);
+ if (service->query)
+ query = g_bytes_get_data(service->query, &query_length);
+
+ return peer_driver->register_service(spec, spec_length, query,
+ query_length, service->version,
+ service_registration_result, service);
+}
+
+static void register_all_services(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ struct _peer_service_owner *ps_owner = value;
+ GList *list;
+
+ for (list = ps_owner->services; list; list = list->next) {
+ struct _peer_service *service = list->data;
+
+ if (service->registered)
+ register_peer_service(service);
+ }
+}
+
+void __connman_peer_service_set_driver(struct connman_peer_driver *driver)
+{
+ peer_driver = driver;
+ if (!peer_driver)
+ return;
+
+ g_hash_table_foreach(owners_map, register_all_services, NULL);
+}
+
+int __connman_peer_service_register(const char *owner, DBusMessage *msg,
+ const unsigned char *specification,
+ int specification_length,
+ const unsigned char *query,
+ int query_length, int version,
+ bool master)
+{
+ struct _peer_service_owner *ps_owner;
+ GBytes *spec, *query_spec = NULL;
+ struct _peer_service *service;
+ bool new = false;
+ int ret = 0;
+
+ DBG("owner %s - spec %p/length %d - query %p/length %d - version %d",
+ owner,specification, specification_length,
+ query, query_length, version);
+
+ if (!specification || specification_length == 0)
+ return -EINVAL;
+
+ ps_owner = g_hash_table_lookup(owners_map, owner);
+ if (!ps_owner) {
+ ps_owner = g_try_new0(struct _peer_service_owner, 1);
+ if (!ps_owner)
+ return -ENOMEM;
+
+ ps_owner->owner = g_strdup(owner);
+ ps_owner->watch = g_dbus_add_disconnect_watch(connection,
+ owner, owner_disconnect,
+ ps_owner, NULL);
+ g_hash_table_insert(owners_map, ps_owner->owner, ps_owner);
+ new = true;
+ }
+
+ spec = g_bytes_new(specification, specification_length);
+ if (query)
+ query_spec = g_bytes_new(query, query_length);
+
+ service = find_peer_service(spec, query_spec, version, NULL, false);
+ if (service) {
+ DBG("Found one existing service %p", service);
+
+ if (g_strcmp0(service->owner, owner))
+ ret = -EBUSY;
+
+ if (service->pending)
+ ret = -EINPROGRESS;
+ else
+ ret = -EEXIST;
+
+ service = NULL;
+ goto error;
+ }
+
+ service = g_try_new0(struct _peer_service, 1);
+ if (!service) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ service->owner = ps_owner->owner;
+ service->specification = spec;
+ service->query = query_spec;
+ service->version = version;
+ service->master = master;
+
+ g_hash_table_insert(services_map, spec, ps_owner);
+ spec = query_spec = NULL;
+
+ ret = register_peer_service(service);
+ if (ret != 0 && ret != -EINPROGRESS)
+ goto error;
+ else if (ret == -EINPROGRESS)
+ service->pending = dbus_message_ref(msg);
+ else {
+ service->registered = true;
+ if (master)
+ peer_master++;
+ }
+
+ ps_owner->services = g_list_prepend(ps_owner->services, service);
+
+ return ret;
+error:
+ if (spec)
+ g_bytes_unref(spec);
+ if (query_spec)
+ g_bytes_unref(query_spec);
+
+ if (service)
+ remove_peer_service(service);
+
+ if (new)
+ g_hash_table_remove(owners_map, ps_owner->owner);
+
+ return ret;
+}
+
+int __connman_peer_service_unregister(const char *owner,
+ const unsigned char *specification,
+ int specification_length,
+ const unsigned char *query,
+ int query_length, int version)
+{
+ struct _peer_service_owner *ps_owner;
+ GBytes *spec, *query_spec = NULL;
+ struct _peer_service *service;
+
+ DBG("owner %s - spec %p/length %d - query %p/length %d - version %d",
+ owner,specification, specification_length,
+ query, query_length, version);
+
+ ps_owner = g_hash_table_lookup(owners_map, owner);
+ if (!ps_owner)
+ return -ESRCH;
+
+ spec = g_bytes_new(specification, specification_length);
+ if (query)
+ query_spec = g_bytes_new(query, query_length);
+
+ service = find_peer_service(spec, query_spec, version, owner, true);
+
+ g_bytes_unref(spec);
+ g_bytes_unref(query_spec);
+
+ if (!service)
+ return -ESRCH;
+
+ remove_peer_service(service);
+
+ if (!ps_owner->services)
+ g_hash_table_remove(owners_map, ps_owner->owner);
+
+ return 0;
+}
+
+bool connman_peer_service_is_master(void)
+{
+ if (!peer_master || !peer_driver)
+ return false;
+
+ return true;
+}
+
+int __connman_peer_service_init(void)
+{
+ DBG("");
+ connection = connman_dbus_get_connection();
+ if (!connection)
+ return -1;
+
+ peer_driver = NULL;
+
+ owners_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
+ remove_peer_service_owner);
+ services_map = g_hash_table_new_full(g_bytes_hash, g_bytes_equal,
+ NULL, NULL);
+ peer_master = 0;
+
+ return 0;
+}
+
+void __connman_peer_service_cleanup(void)
+{
+ DBG("");
+
+ if (!connection)
+ return;
+
+ g_hash_table_destroy(owners_map);
+ g_hash_table_destroy(services_map);
+ peer_master = 0;
+
+ dbus_connection_unref(connection);
+ connection = NULL;
+}
diff --git a/src/service.c b/src/service.c
index cbca669e..87a2f2cd 100644
--- a/src/service.c
+++ b/src/service.c
@@ -234,6 +234,23 @@ enum connman_service_type __connman_service_string2type(const char *str)
return CONNMAN_SERVICE_TYPE_UNKNOWN;
}
+enum connman_service_security __connman_service_string2security(const char *str)
+{
+ if (!str)
+ return CONNMAN_SERVICE_SECURITY_UNKNOWN;
+
+ if (!strcmp(str, "psk"))
+ return CONNMAN_SERVICE_SECURITY_PSK;
+ if (!strcmp(str, "ieee8021x"))
+ return CONNMAN_SERVICE_SECURITY_8021X;
+ if (!strcmp(str, "none"))
+ return CONNMAN_SERVICE_SECURITY_NONE;
+ if (!strcmp(str, "wep"))
+ return CONNMAN_SERVICE_SECURITY_WEP;
+
+ return CONNMAN_SERVICE_SECURITY_UNKNOWN;
+}
+
static const char *security2string(enum connman_service_security security)
{
switch (security) {
@@ -302,18 +319,6 @@ static const char *error2string(enum connman_service_error error)
return NULL;
}
-static enum connman_service_error string2error(const char *error)
-{
- if (g_strcmp0(error, "dhcp-failed") == 0)
- return CONNMAN_SERVICE_ERROR_DHCP_FAILED;
- else if (g_strcmp0(error, "pin-missing") == 0)
- return CONNMAN_SERVICE_ERROR_PIN_MISSING;
- else if (g_strcmp0(error, "invalid-key") == 0)
- return CONNMAN_SERVICE_ERROR_INVALID_KEY;
-
- return CONNMAN_SERVICE_ERROR_UNKNOWN;
-}
-
static const char *proxymethod2string(enum connman_service_proxy_method method)
{
switch (method) {
@@ -480,15 +485,6 @@ static int service_load(struct connman_service *service)
service->favorite = g_key_file_get_boolean(keyfile,
service->identifier, "Favorite", NULL);
- str = g_key_file_get_string(keyfile,
- service->identifier, "Failure", NULL);
- if (str) {
- if (!service->favorite)
- service->state_ipv4 = service->state_ipv6 =
- CONNMAN_SERVICE_STATE_FAILURE;
- service->error = string2error(str);
- g_free(str);
- }
/* fall through */
case CONNMAN_SERVICE_TYPE_ETHERNET:
@@ -655,17 +651,9 @@ static int service_save(struct connman_service *service)
g_key_file_set_boolean(keyfile, service->identifier,
"Favorite", service->favorite);
- if (service->state_ipv4 == CONNMAN_SERVICE_STATE_FAILURE ||
- service->state_ipv6 == CONNMAN_SERVICE_STATE_FAILURE) {
- const char *failure = error2string(service->error);
- if (failure)
- g_key_file_set_string(keyfile,
- service->identifier,
- "Failure", failure);
- } else {
- g_key_file_remove_key(keyfile, service->identifier,
- "Failure", NULL);
- }
+ g_key_file_remove_key(keyfile, service->identifier,
+ "Failure", NULL);
+
/* fall through */
case CONNMAN_SERVICE_TYPE_ETHERNET:
@@ -2804,30 +2792,29 @@ void __connman_service_set_agent_identity(struct connman_service *service,
service->agent_identity);
}
-static int check_passphrase(struct connman_service *service,
- enum connman_service_security security,
- const char *passphrase)
+static int check_passphrase(enum connman_service_security security,
+ const char *passphrase)
{
guint i;
gsize length;
- if (!passphrase) {
- /*
- * This will prevent __connman_service_set_passphrase() to
- * wipe the passphrase out in case of -ENOKEY error for a
- * favorite service. */
- if (service->favorite)
- return 1;
- else
- return 0;
- }
+ if (!passphrase)
+ return 0;
length = strlen(passphrase);
switch (security) {
- case CONNMAN_SERVICE_SECURITY_PSK:
+ case CONNMAN_SERVICE_SECURITY_UNKNOWN:
+ case CONNMAN_SERVICE_SECURITY_NONE:
case CONNMAN_SERVICE_SECURITY_WPA:
case CONNMAN_SERVICE_SECURITY_RSN:
+
+ DBG("service security '%s' (%d) not handled",
+ security2string(security), security);
+
+ return -EOPNOTSUPP;
+
+ case CONNMAN_SERVICE_SECURITY_PSK:
/* A raw key is always 64 bytes length,
* its content is in hex representation.
* A PSK key must be between [8..63].
@@ -2852,8 +2839,7 @@ static int check_passphrase(struct connman_service *service,
} else if (length != 5 && length != 13)
return -ENOKEY;
break;
- case CONNMAN_SERVICE_SECURITY_UNKNOWN:
- case CONNMAN_SERVICE_SECURITY_NONE:
+
case CONNMAN_SERVICE_SECURITY_8021X:
break;
}
@@ -2864,25 +2850,29 @@ static int check_passphrase(struct connman_service *service,
int __connman_service_set_passphrase(struct connman_service *service,
const char *passphrase)
{
- int err = 0;
+ int err;
- if (service->immutable || service->hidden)
+ if (service->hidden)
+ return -EINVAL;
+
+ if (service->immutable &&
+ service->security != CONNMAN_SERVICE_SECURITY_8021X)
return -EINVAL;
- err = check_passphrase(service, service->security, passphrase);
+ err = check_passphrase(service->security, passphrase);
- if (err == 0) {
- g_free(service->passphrase);
- service->passphrase = g_strdup(passphrase);
+ if (err < 0)
+ return err;
- if (service->network)
- connman_network_set_string(service->network,
- "WiFi.Passphrase",
- service->passphrase);
- service_save(service);
- }
+ g_free(service->passphrase);
+ service->passphrase = g_strdup(passphrase);
- return err;
+ if (service->network)
+ connman_network_set_string(service->network, "WiFi.Passphrase",
+ service->passphrase);
+ service_save(service);
+
+ return 0;
}
const char *__connman_service_get_passphrase(struct connman_service *service)
@@ -2893,6 +2883,16 @@ const char *__connman_service_get_passphrase(struct connman_service *service)
return service->passphrase;
}
+static void clear_passphrase(struct connman_service *service)
+{
+ g_free(service->passphrase);
+ service->passphrase = NULL;
+
+ if (service->network)
+ connman_network_set_string(service->network, "WiFi.Passphrase",
+ service->passphrase);
+}
+
static DBusMessage *get_properties(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
@@ -3133,6 +3133,7 @@ int __connman_service_reset_ipconfig(struct connman_service *service,
if (is_connecting_state(service, state) ||
is_connected_state(service, state))
__connman_network_clear_ipconfig(service->network, ipconfig);
+
__connman_ipconfig_unref(ipconfig);
if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
@@ -3140,13 +3141,16 @@ int __connman_service_reset_ipconfig(struct connman_service *service,
else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
service->ipconfig_ipv6 = new_ipconfig;
- __connman_ipconfig_enable(new_ipconfig);
+ if (is_connecting_state(service, state) ||
+ is_connected_state(service, state))
+ __connman_ipconfig_enable(new_ipconfig);
if (new_state && new_method != old_method) {
if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
*new_state = service->state_ipv4;
else
*new_state = service->state_ipv6;
+
__connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO);
}
@@ -3800,50 +3804,17 @@ static void remove_timeout(struct connman_service *service)
}
}
-void __connman_service_reply_dbus_pending(DBusMessage *pending, int error,
- const char *path)
-{
- if (pending) {
- if (error > 0) {
- DBusMessage *reply;
-
- reply = __connman_error_failed(pending, error);
- if (reply)
- g_dbus_send_message(connection, reply);
- } else {
- const char *sender;
-
- sender = dbus_message_get_interface(pending);
- if (!path)
- path = dbus_message_get_path(pending);
-
- DBG("sender %s path %s", sender, path);
-
- if (g_strcmp0(sender, CONNMAN_MANAGER_INTERFACE) == 0)
- g_dbus_send_reply(connection, pending,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID);
- else
- g_dbus_send_reply(connection, pending,
- DBUS_TYPE_INVALID);
- }
-
- dbus_message_unref(pending);
- }
-}
-
static void reply_pending(struct connman_service *service, int error)
{
remove_timeout(service);
if (service->pending) {
- __connman_service_reply_dbus_pending(service->pending, error,
- NULL);
+ connman_dbus_reply_pending(service->pending, error, NULL);
service->pending = NULL;
}
if (service->provider_pending) {
- __connman_service_reply_dbus_pending(service->provider_pending,
+ connman_dbus_reply_pending(service->provider_pending,
error, service->path);
service->provider_pending = NULL;
}
@@ -3955,34 +3926,11 @@ static gboolean connect_timeout(gpointer user_data)
return FALSE;
}
-static bool is_interface_available(struct connman_service *service,
- struct connman_service *other_service)
-{
- unsigned int index = 0, other_index = 0;
-
- if (service->ipconfig_ipv4)
- index = __connman_ipconfig_get_index(service->ipconfig_ipv4);
- else if (service->ipconfig_ipv6)
- index = __connman_ipconfig_get_index(service->ipconfig_ipv6);
-
- if (other_service->ipconfig_ipv4)
- other_index = __connman_ipconfig_get_index(
- other_service->ipconfig_ipv4);
- else if (other_service->ipconfig_ipv6)
- other_index = __connman_ipconfig_get_index(
- other_service->ipconfig_ipv6);
-
- if (index > 0 && other_index != index)
- return true;
-
- return false;
-}
-
static DBusMessage *connect_service(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
struct connman_service *service = user_data;
- int err = 0;
+ int index, err = 0;
GList *list;
DBG("service %p", service);
@@ -3990,27 +3938,27 @@ static DBusMessage *connect_service(DBusConnection *conn,
if (service->pending)
return __connman_error_in_progress(msg);
+ index = __connman_service_get_index(service);
+
for (list = service_list; list; list = list->next) {
struct connman_service *temp = list->data;
- /*
- * We should allow connection if there are available
- * interfaces for a given technology type (like having
- * more than one wifi card).
- */
if (!is_connecting(temp) && !is_connected(temp))
break;
+ if (service == temp)
+ continue;
+
if (service->type != temp->type)
continue;
- if(!is_interface_available(service, temp)) {
- if (__connman_service_disconnect(temp) == -EINPROGRESS)
- err = -EINPROGRESS;
- }
+ if (__connman_service_get_index(temp) == index &&
+ __connman_service_disconnect(temp) == -EINPROGRESS)
+ err = -EINPROGRESS;
+
}
if (err == -EINPROGRESS)
- return __connman_error_in_progress(msg);
+ return __connman_error_operation_timeout(msg);
service->ignore = false;
@@ -4022,8 +3970,10 @@ static DBusMessage *connect_service(DBusConnection *conn,
if (err == -EINPROGRESS)
return NULL;
- dbus_message_unref(service->pending);
- service->pending = NULL;
+ if (service->pending) {
+ dbus_message_unref(service->pending);
+ service->pending = NULL;
+ }
if (err < 0)
return __connman_error_failed(msg, -err);
@@ -4042,10 +3992,8 @@ static DBusMessage *disconnect_service(DBusConnection *conn,
service->ignore = true;
err = __connman_service_disconnect(service);
- if (err < 0) {
- if (err != -EINPROGRESS)
- return __connman_error_failed(msg, -err);
- }
+ if (err < 0 && err != -EINPROGRESS)
+ return __connman_error_failed(msg, -err);
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
@@ -4082,6 +4030,8 @@ bool __connman_service_remove(struct connman_service *service)
__connman_service_set_favorite(service, false);
+ __connman_ipconfig_ipv6_reset_privacy(service->ipconfig_ipv6);
+
service_save(service);
return true;
@@ -4397,13 +4347,13 @@ static void service_schedule_added(struct connman_service *service)
static void service_schedule_removed(struct connman_service *service)
{
- DBG("service %p %s", service, service->path);
-
if (!service || !service->path) {
DBG("service %p or path is NULL", service);
return;
}
+ DBG("service %p %s", service, service->path);
+
g_hash_table_remove(services_notify->add, service->path);
g_hash_table_replace(services_notify->remove, g_strdup(service->path),
NULL);
@@ -5003,10 +4953,8 @@ void __connman_service_set_string(struct connman_service *service,
} else if (g_str_equal(key, "Phase2")) {
g_free(service->phase2);
service->phase2 = g_strdup(value);
- } else if (g_str_equal(key, "Passphrase")) {
- g_free(service->passphrase);
- service->passphrase = g_strdup(value);
- }
+ } else if (g_str_equal(key, "Passphrase"))
+ __connman_service_set_passphrase(service, value);
}
void __connman_service_set_search_domains(struct connman_service *service,
@@ -5062,38 +5010,13 @@ static void report_error_cb(void *user_context, bool retry,
else {
/* It is not relevant to stay on Failure state
* when failing is due to wrong user input */
- service->state = CONNMAN_SERVICE_STATE_IDLE;
+ __connman_service_clear_error(service);
service_complete(service);
__connman_connection_update_gateway();
}
}
-int __connman_service_add_passphrase(struct connman_service *service,
- const gchar *passphrase)
-{
- int err = 0;
-
- switch (service->security) {
- case CONNMAN_SERVICE_SECURITY_WEP:
- case CONNMAN_SERVICE_SECURITY_PSK:
- case CONNMAN_SERVICE_SECURITY_8021X:
- err = __connman_service_set_passphrase(service, passphrase);
- break;
-
- case CONNMAN_SERVICE_SECURITY_UNKNOWN:
- case CONNMAN_SERVICE_SECURITY_NONE:
- case CONNMAN_SERVICE_SECURITY_WPA:
- case CONNMAN_SERVICE_SECURITY_RSN:
- DBG("service security '%s' (%d) not handled",
- security2string(service->security),
- service->security);
- break;
- }
-
- return err;
-}
-
static int check_wpspin(struct connman_service *service, const char *wpspin)
{
int length;
@@ -5187,7 +5110,7 @@ static void request_input_cb(struct connman_service *service,
__connman_service_set_agent_identity(service, identity);
if (passphrase)
- err = __connman_service_add_passphrase(service, passphrase);
+ err = __connman_service_set_passphrase(service, passphrase);
done:
if (err >= 0) {
@@ -5311,6 +5234,7 @@ static int service_indicate_state(struct connman_service *service)
{
enum connman_service_state old_state, new_state;
struct connman_service *def_service;
+ enum connman_ipconfig_method method;
int result;
if (!service)
@@ -5344,13 +5268,22 @@ static int service_indicate_state(struct connman_service *service)
service->state = new_state;
state_changed(service);
- if (new_state == CONNMAN_SERVICE_STATE_IDLE &&
- old_state != CONNMAN_SERVICE_STATE_DISCONNECT) {
+ switch(new_state) {
+ case CONNMAN_SERVICE_STATE_UNKNOWN:
- __connman_service_disconnect(service);
- }
+ break;
+
+ case CONNMAN_SERVICE_STATE_IDLE:
+ if (old_state != CONNMAN_SERVICE_STATE_DISCONNECT)
+ __connman_service_disconnect(service);
+
+ break;
+
+ case CONNMAN_SERVICE_STATE_ASSOCIATION:
- if (new_state == CONNMAN_SERVICE_STATE_CONFIGURATION) {
+ break;
+
+ case CONNMAN_SERVICE_STATE_CONFIGURATION:
if (!service->new_service &&
__connman_stats_service_register(service) == 0) {
/*
@@ -5362,11 +5295,10 @@ static int service_indicate_state(struct connman_service *service)
__connman_stats_get(service, true,
&service->stats_roaming.data);
}
- }
- if (new_state == CONNMAN_SERVICE_STATE_READY) {
- enum connman_ipconfig_method method;
+ break;
+ case CONNMAN_SERVICE_STATE_READY:
if (service->new_service &&
__connman_stats_service_register(service) == 0) {
/*
@@ -5426,7 +5358,16 @@ static int service_indicate_state(struct connman_service *service)
else if (service->type != CONNMAN_SERVICE_TYPE_VPN)
vpn_auto_connect();
- } else if (new_state == CONNMAN_SERVICE_STATE_DISCONNECT) {
+ break;
+
+ case CONNMAN_SERVICE_STATE_ONLINE:
+
+ break;
+
+ case CONNMAN_SERVICE_STATE_DISCONNECT:
+
+ reply_pending(service, ECONNABORTED);
+
def_service = __connman_service_get_default();
if (!__connman_notifier_is_connected() &&
@@ -5452,9 +5393,9 @@ static int service_indicate_state(struct connman_service *service)
downgrade_connected_services();
__connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO);
- }
+ break;
- if (new_state == CONNMAN_SERVICE_STATE_FAILURE) {
+ case CONNMAN_SERVICE_STATE_FAILURE:
if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER &&
connman_agent_report_error(service, service->path,
@@ -5464,7 +5405,11 @@ static int service_indicate_state(struct connman_service *service)
NULL) == -EINPROGRESS)
return 0;
service_complete(service);
- } else
+
+ break;
+ }
+
+ if (new_state != CONNMAN_SERVICE_STATE_FAILURE)
set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN);
service_list_sort();
@@ -5502,7 +5447,7 @@ int __connman_service_indicate_error(struct connman_service *service,
*/
if (service->error == CONNMAN_SERVICE_ERROR_INVALID_KEY ||
service->security == CONNMAN_SERVICE_SECURITY_8021X)
- __connman_service_set_passphrase(service, NULL);
+ clear_passphrase(service);
__connman_service_set_agent_identity(service, NULL);
@@ -5517,6 +5462,8 @@ int __connman_service_indicate_error(struct connman_service *service,
int __connman_service_clear_error(struct connman_service *service)
{
+ DBusMessage *pending, *provider_pending;
+
DBG("service %p", service);
if (!service)
@@ -5525,25 +5472,23 @@ int __connman_service_clear_error(struct connman_service *service)
if (service->state != CONNMAN_SERVICE_STATE_FAILURE)
return -EINVAL;
- service->state_ipv4 = service->state_ipv6 =
- CONNMAN_SERVICE_STATE_UNKNOWN;
- set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN);
+ pending = service->pending;
+ service->pending = NULL;
+ provider_pending = service->provider_pending;
+ service->provider_pending = NULL;
__connman_service_ipconfig_indicate_state(service,
- CONNMAN_SERVICE_STATE_IDLE,
- CONNMAN_IPCONFIG_TYPE_IPV6);
-
- /*
- * Toggling the IPv6 state to IDLE could trigger the auto connect
- * machinery and consequently the IPv4 state.
- */
- if (service->state_ipv4 != CONNMAN_SERVICE_STATE_UNKNOWN &&
- service->state_ipv4 != CONNMAN_SERVICE_STATE_FAILURE)
- return 0;
+ CONNMAN_SERVICE_STATE_IDLE,
+ CONNMAN_IPCONFIG_TYPE_IPV6);
- return __connman_service_ipconfig_indicate_state(service,
+ __connman_service_ipconfig_indicate_state(service,
CONNMAN_SERVICE_STATE_IDLE,
CONNMAN_IPCONFIG_TYPE_IPV4);
+
+ service->pending = pending;
+ service->provider_pending = provider_pending;
+
+ return 0;
}
int __connman_service_indicate_default(struct connman_service *service)
@@ -5708,38 +5653,45 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service,
enum connman_ipconfig_type type)
{
struct connman_ipconfig *ipconfig = NULL;
- enum connman_service_state old_state;
+ enum connman_service_state *old_state;
enum connman_ipconfig_method method;
if (!service)
return -EINVAL;
- if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
- old_state = service->state_ipv4;
+ switch (type) {
+ case CONNMAN_IPCONFIG_TYPE_UNKNOWN:
+ return -EINVAL;
+
+ case CONNMAN_IPCONFIG_TYPE_IPV4:
+ old_state = &service->state_ipv4;
ipconfig = service->ipconfig_ipv4;
- } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
- old_state = service->state_ipv6;
+
+ break;
+
+ case CONNMAN_IPCONFIG_TYPE_IPV6:
+ old_state = &service->state_ipv6;
ipconfig = service->ipconfig_ipv6;
+
+ break;
}
if (!ipconfig)
return -EINVAL;
/* Any change? */
- if (old_state == new_state)
+ if (*old_state == new_state)
return -EALREADY;
- DBG("service %p (%s) state %d (%s) type %d (%s)",
+ DBG("service %p (%s) old state %d (%s) new state %d (%s) type %d (%s)",
service, service ? service->identifier : NULL,
+ *old_state, state2string(*old_state),
new_state, state2string(new_state),
type, __connman_ipconfig_type2string(type));
switch (new_state) {
case CONNMAN_SERVICE_STATE_UNKNOWN:
case CONNMAN_SERVICE_STATE_IDLE:
- if (service->state == CONNMAN_SERVICE_STATE_FAILURE)
- return -EINVAL;
- break;
case CONNMAN_SERVICE_STATE_ASSOCIATION:
break;
case CONNMAN_SERVICE_STATE_CONFIGURATION:
@@ -5772,25 +5724,23 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service,
the state to IDLE so that it will not affect the combined state
in the future.
*/
- if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
- method = __connman_ipconfig_get_method(service->ipconfig_ipv4);
-
- if (method == CONNMAN_IPCONFIG_METHOD_OFF ||
- method == CONNMAN_IPCONFIG_METHOD_UNKNOWN)
- new_state = CONNMAN_SERVICE_STATE_IDLE;
-
- service->state_ipv4 = new_state;
-
- } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
- method = __connman_ipconfig_get_method(service->ipconfig_ipv6);
+ method = __connman_ipconfig_get_method(ipconfig);
+ switch (method) {
+ case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+ case CONNMAN_IPCONFIG_METHOD_OFF:
+ new_state = CONNMAN_SERVICE_STATE_IDLE;
+ break;
- if (method == CONNMAN_IPCONFIG_METHOD_OFF ||
- method == CONNMAN_IPCONFIG_METHOD_UNKNOWN)
- new_state = CONNMAN_SERVICE_STATE_IDLE;
+ case CONNMAN_IPCONFIG_METHOD_FIXED:
+ case CONNMAN_IPCONFIG_METHOD_MANUAL:
+ case CONNMAN_IPCONFIG_METHOD_DHCP:
+ case CONNMAN_IPCONFIG_METHOD_AUTO:
+ break;
- service->state_ipv6 = new_state;
}
+ *old_state = new_state;
+
update_nameservers(service);
return service_indicate_state(service);
@@ -5896,10 +5846,9 @@ static int service_connect(struct connman_service *service)
if (!service->wps ||
!connman_network_get_bool(service->network, "WiFi.UseWPS"))
return -ENOKEY;
- } else if (service->error ==
- CONNMAN_SERVICE_ERROR_INVALID_KEY)
- return -ENOKEY;
+ }
break;
+
case CONNMAN_SERVICE_SECURITY_8021X:
if (!service->eap)
return -EINVAL;
@@ -5995,13 +5944,23 @@ int __connman_service_connect(struct connman_service *service,
case CONNMAN_SERVICE_TYPE_GPS:
case CONNMAN_SERVICE_TYPE_P2P:
return -EINVAL;
- default:
- if (!is_ipconfig_usable(service))
- return -ENOLINK;
- err = service_connect(service);
+ case CONNMAN_SERVICE_TYPE_ETHERNET:
+ case CONNMAN_SERVICE_TYPE_GADGET:
+ case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+ case CONNMAN_SERVICE_TYPE_CELLULAR:
+ case CONNMAN_SERVICE_TYPE_VPN:
+ case CONNMAN_SERVICE_TYPE_WIFI:
+ break;
}
+ if (!is_ipconfig_usable(service))
+ return -ENOLINK;
+
+ __connman_service_clear_error(service);
+
+ err = service_connect(service);
+
service->connect_reason = reason;
if (err >= 0) {
set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN);
diff --git a/src/session.c b/src/session.c
index 00ef3696..08facc1d 100644
--- a/src/session.c
+++ b/src/session.c
@@ -3,7 +3,7 @@
* Connection Manager
*
* Copyright (C) 2007-2014 Intel Corporation. All rights reserved.
- * Copyright (C) 2011-2014 BWM CarIT GmbH. All rights reserved.
+ * Copyright (C) 2011-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/src/shared/netlink.c b/src/shared/netlink.c
index d72294cc..b32ab854 100644
--- a/src/shared/netlink.c
+++ b/src/shared/netlink.c
@@ -3,7 +3,7 @@
* Connection Manager
*
* Copyright (C) 2011-2012 Intel Corporation. All rights reserved.
- * Copyright (C) 2013 BWM CarIT GmbH.
+ * Copyright (C) 2013-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
diff --git a/src/stats.c b/src/stats.c
index df5ab4eb..26343b13 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -2,7 +2,7 @@
*
* Connection Manager
*
- * Copyright (C) 2010 BMW Car IT GmbH. All rights reserved.
+ * Copyright (C) 2010-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/src/tethering.c b/src/tethering.c
index c7e17f5d..ceeec746 100644
--- a/src/tethering.c
+++ b/src/tethering.c
@@ -222,7 +222,7 @@ void __connman_tethering_set_enabled(void)
end_ip = __connman_ippool_get_end_ip(dhcp_ippool);
err = __connman_bridge_enable(BRIDGE_NAME, gateway,
- __connman_ipaddress_netmask_prefix_len(subnet_mask),
+ connman_ipaddress_calc_netmask_len(subnet_mask),
broadcast);
if (err < 0 && err != -EALREADY) {
__connman_ippool_unref(dhcp_ippool);
@@ -267,7 +267,7 @@ void __connman_tethering_set_enabled(void)
return;
}
- prefixlen = __connman_ipaddress_netmask_prefix_len(subnet_mask);
+ prefixlen = connman_ipaddress_calc_netmask_len(subnet_mask);
err = __connman_nat_enable(BRIDGE_NAME, start_ip, prefixlen);
if (err < 0) {
connman_error("Cannot enable NAT %d/%s", err, strerror(-err));
@@ -340,8 +340,7 @@ static void setup_tun_interface(unsigned int flags, unsigned change,
subnet_mask = __connman_ippool_get_subnet_mask(pn->pool);
server_ip = __connman_ippool_get_start_ip(pn->pool);
peer_ip = __connman_ippool_get_end_ip(pn->pool);
- prefixlen =
- __connman_ipaddress_netmask_prefix_len(subnet_mask);
+ prefixlen = connman_ipaddress_calc_netmask_len(subnet_mask);
if ((__connman_inet_modify_address(RTM_NEWADDR,
NLM_F_REPLACE | NLM_F_ACK, pn->index, AF_INET,
diff --git a/src/timeserver.c b/src/timeserver.c
index d41fa404..f0d33e5e 100644
--- a/src/timeserver.c
+++ b/src/timeserver.c
@@ -117,7 +117,7 @@ static void resolv_result(GResolvResultStatus status, char **results,
* 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 a IP for the NTP
+ * timeserver. We only resolve the URLs. Once we have an IP for the NTP
* server, we start querying it for time corrections.
*/
void __connman_timeserver_sync_next()
@@ -137,7 +137,7 @@ void __connman_timeserver_sync_next()
ts_list = g_slist_delete_link(ts_list, ts_list);
- /* if its a IP , directly query it. */
+ /* if it's an IP, directly query it. */
if (connman_inet_check_ipaddress(ts_current) > 0) {
DBG("Using timeserver %s", ts_current);
@@ -146,7 +146,7 @@ void __connman_timeserver_sync_next()
return;
}
- DBG("Resolving server %s", ts_current);
+ DBG("Resolving timeserver %s", ts_current);
resolv_id = g_resolv_lookup_hostname(resolv, ts_current,
resolv_result, NULL);
@@ -200,7 +200,7 @@ GSList *__connman_timeserver_get_all(struct connman_service *service)
service_ts = connman_service_get_timeservers(service);
- /* First add Service Timeservers via DHCP to the list */
+ /* Then add Service Timeservers via DHCP to the list */
for (i = 0; service_ts && service_ts[i]; i++)
list = __connman_timeserver_add_list(list, service_ts[i]);
@@ -286,7 +286,7 @@ static void ts_recheck_enable(void)
/*
* This function must be called everytime the default service changes, the
- * service timeserver(s) or gatway changes or the global timeserver(s) changes.
+ * service timeserver(s) or gateway changes or the global timeserver(s) changes.
*/
int __connman_timeserver_sync(struct connman_service *default_service)
{
diff --git a/src/wispr.c b/src/wispr.c
index dcce93cc..c4fcd60b 100644
--- a/src/wispr.c
+++ b/src/wispr.c
@@ -711,6 +711,11 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data)
DBG("status: %03u", status);
switch (status) {
+ case 000:
+ __connman_agent_request_browser(wp_context->service,
+ wispr_portal_browser_reply_cb,
+ wp_context->status_url, wp_context);
+ break;
case 200:
if (wp_context->wispr_msg.message_type >= 0)
break;
@@ -755,6 +760,11 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data)
}
break;
+ case 505:
+ __connman_agent_request_browser(wp_context->service,
+ wispr_portal_browser_reply_cb,
+ wp_context->status_url, wp_context);
+ break;
default:
break;
}
diff --git a/test/p2p-on-supplicant b/test/p2p-on-supplicant
index 826656ee..8cc76e81 100755
--- a/test/p2p-on-supplicant
+++ b/test/p2p-on-supplicant
@@ -163,19 +163,25 @@ class Wpa_s:
if args[0].debug:
print_tuple(args[1:])
- """
- It should be: __DeviceFound(self, object_path, properties)
- wpa_supplicant's DBus API is buggy here:
- - no properties are given
- """
+ def __peer_if_p2p_property_changed(*args, **kwargs):
+ print 'Peer - ',
+ args[0].__p2p_property_changed(*args, **kwargs)
+
def __DeviceFound(self, object_path):
self.peers[object_path] = None
peer = self.bus.get_object(WPA_INTF, object_path)
peer_if = dbus.Interface(peer, DBUS_PROPERTIES_INTF)
+ self.bus.add_signal_receiver(self.__peer_if_p2p_property_changed,
+ dbus_interface=WPA_PEER_INTF,
+ path=object_path, member_keyword='signal')
+
self.peers[object_path] = peer_if.GetAll(WPA_PEER_INTF)
+ if self.debug:
+ print_dict(self.peers[object_path])
+
def __DeviceLost(self, object_path):
if object_path in self.peers:
del self.peers[object_path]
@@ -194,10 +200,13 @@ class Wpa_s:
print 'Group - ',
args[0].__p2p_property_changed(*args, **kwargs)
- def __GroupFinished(self, ifname, role):
+ def __GroupFinished(self, properties):
print 'Group running on %s is being removed' % ifname
self.group_obj = self.group_if = self.group_iface_path = None
+ if self.debug:
+ print_dict(properties)
+
def __InvitationResult(self, response):
print 'Invitation result status: %d ' % response['status']
@@ -233,7 +242,7 @@ class Wpa_s:
self.bus.add_signal_receiver(self.__GroupFinished,
dbus_interface=WPA_P2P_INTF,
path=self.group_iface_path,
- member_keyword='signal')
+ signal_name='GroupFinished')
self.bus.add_signal_receiver(self.__InvitationResult,
dbus_interface=WPA_P2P_INTF,
path=self.iface_path,
@@ -396,9 +405,13 @@ class Wpa_s:
@checkarg(nb_args = 1)
def p2p_peer(self, args):
- peer = self.__find_peer(args[0])
- if peer:
- print_dict(peer)
+ peer_path = self.__find_peer(args[0], True)
+ if peer_path:
+ peer = self.bus.get_object(WPA_INTF, peer_path)
+ peer_if = dbus.Interface(peer, DBUS_PROPERTIES_INTF)
+ self.peers[peer_path] = peer_if.GetAll(WPA_PEER_INTF)
+
+ print_dict(self.peers[peer_path])
@checkarg(nb_args = 1)
def p2p_connect(self, args):
diff --git a/tools/iptables-unit.c b/tools/iptables-unit.c
index b9273cee..7e427e21 100644
--- a/tools/iptables-unit.c
+++ b/tools/iptables-unit.c
@@ -2,7 +2,7 @@
*
* Connection Manager
*
- * Copyright (C) 2013 BWM CarIT GmbH. All rights reserved.
+ * Copyright (C) 2013-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/tools/manager-api.c b/tools/manager-api.c
index 9098fca5..e082962d 100644
--- a/tools/manager-api.c
+++ b/tools/manager-api.c
@@ -2,7 +2,7 @@
*
* Connection Manager
*
- * Copyright (C) 2011 BWM CarIT GmbH. All rights reserved.
+ * Copyright (C) 2011-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/tools/netlink-test.c b/tools/netlink-test.c
index c07806a3..221e3490 100644
--- a/tools/netlink-test.c
+++ b/tools/netlink-test.c
@@ -2,7 +2,7 @@
*
* Connection Manager
*
- * Copyright (C) 2013 BWM CarIT GmbH.
+ * Copyright (C) 2013-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/tools/session-api.c b/tools/session-api.c
index 7162ade6..b97cfc01 100644
--- a/tools/session-api.c
+++ b/tools/session-api.c
@@ -2,7 +2,7 @@
*
* Connection Manager
*
- * Copyright (C) 2011 BWM CarIT GmbH. All rights reserved.
+ * Copyright (C) 2011-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/tools/session-test.c b/tools/session-test.c
index 18fd588c..4319e5a4 100644
--- a/tools/session-test.c
+++ b/tools/session-test.c
@@ -2,7 +2,7 @@
*
* Connection Manager
*
- * Copyright (C) 2011 BWM CarIT GmbH. All rights reserved.
+ * Copyright (C) 2011 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/tools/session-utils.c b/tools/session-utils.c
index 92273e61..51cec5c3 100644
--- a/tools/session-utils.c
+++ b/tools/session-utils.c
@@ -2,7 +2,7 @@
*
* Connection Manager
*
- * Copyright (C) 2011 BWM CarIT GmbH. All rights reserved.
+ * Copyright (C) 2011-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/tools/stats-tool.c b/tools/stats-tool.c
index 7957c471..7d117fdc 100644
--- a/tools/stats-tool.c
+++ b/tools/stats-tool.c
@@ -2,7 +2,7 @@
*
* Connection Manager
*
- * Copyright (C) 2010 BMW Car IT GmbH. All rights reserved.
+ * Copyright (C) 2010-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/unit/test-ippool.c b/unit/test-ippool.c
index a11220d1..e8d077a6 100644
--- a/unit/test-ippool.c
+++ b/unit/test-ippool.c
@@ -2,7 +2,7 @@
*
* Connection Manager
*
- * Copyright (C) 2012 BWM CarIT GmbH. All rights reserved.
+ * Copyright (C) 2012-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/vpn/plugins/l2tp.c b/vpn/plugins/l2tp.c
index 91acc85f..22f9dcf8 100644
--- a/vpn/plugins/l2tp.c
+++ b/vpn/plugins/l2tp.c
@@ -2,7 +2,7 @@
*
* ConnMan VPN daemon
*
- * Copyright (C) 2010,2013 BMW Car IT GmbH. All rights reserved.
+ * Copyright (C) 2010,2013 BMW Car IT GmbH.
* Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
diff --git a/vpn/plugins/openconnect.c b/vpn/plugins/openconnect.c
index c6b9cd65..5feaed9d 100644
--- a/vpn/plugins/openconnect.c
+++ b/vpn/plugins/openconnect.c
@@ -552,7 +552,6 @@ static int oc_error_code(struct vpn_provider *provider, int exit_code)
switch (exit_code) {
case 1:
- return VPN_PROVIDER_ERROR_CONNECT_FAILED;
case 2:
vpn_provider_set_string_hide_value(provider,
"OpenConnect.Cookie", NULL);
diff --git a/vpn/plugins/openvpn.c b/vpn/plugins/openvpn.c
index 35013c40..9ee5795c 100644
--- a/vpn/plugins/openvpn.c
+++ b/vpn/plugins/openvpn.c
@@ -2,7 +2,7 @@
*
* ConnMan VPN daemon
*
- * Copyright (C) 2010 BMW Car IT GmbH. All rights reserved.
+ * Copyright (C) 2010-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/vpn/plugins/pptp.c b/vpn/plugins/pptp.c
index a6e51e79..9f2a214d 100644
--- a/vpn/plugins/pptp.c
+++ b/vpn/plugins/pptp.c
@@ -2,7 +2,7 @@
*
* ConnMan VPN daemon
*
- * Copyright (C) 2010,2013 BMW Car IT GmbH. All rights reserved.
+ * Copyright (C) 2010,2013-2014 BMW Car IT GmbH.
* Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
diff --git a/vpn/plugins/vpn.c b/vpn/plugins/vpn.c
index b407573e..b438d06e 100644
--- a/vpn/plugins/vpn.c
+++ b/vpn/plugins/vpn.c
@@ -138,9 +138,9 @@ void vpn_died(struct connman_task *task, int exit_code, void *user_data)
vpn_provider_set_data(provider, NULL);
if (data->watch != 0) {
- vpn_provider_unref(provider);
vpn_rtnl_remove_watch(data->watch);
data->watch = 0;
+ vpn_provider_unref(provider);
}
vpn_exit:
@@ -160,8 +160,6 @@ vpn_exit:
ret = VPN_PROVIDER_ERROR_UNKNOWN;
vpn_provider_indicate_error(provider, ret);
-
- vpn_provider_set_state(provider, VPN_PROVIDER_STATE_FAILURE);
} else
vpn_provider_set_state(provider, VPN_PROVIDER_STATE_IDLE);
@@ -245,6 +243,31 @@ static DBusMessage *vpn_notify(struct connman_task *task,
switch (state) {
case VPN_STATE_CONNECT:
case VPN_STATE_READY:
+ if (data->state == VPN_STATE_READY) {
+ /*
+ * This is the restart case, in which case we must
+ * just set the IP address.
+ *
+ * We need to remove first the old address, just
+ * replacing the old address will not work as expected
+ * because the old address will linger in the interface
+ * and not disapper so the clearing is needed here.
+ *
+ * Also the state must change, otherwise the routes
+ * will not be set properly.
+ */
+ vpn_provider_set_state(provider,
+ VPN_PROVIDER_STATE_CONNECT);
+
+ vpn_provider_clear_address(provider, AF_INET);
+ vpn_provider_clear_address(provider, AF_INET6);
+
+ vpn_provider_change_address(provider);
+ vpn_provider_set_state(provider,
+ VPN_PROVIDER_STATE_READY);
+ break;
+ }
+
index = vpn_provider_get_index(provider);
vpn_provider_ref(provider);
data->watch = vpn_rtnl_add_newlink_watch(index,
diff --git a/vpn/plugins/vpn.h b/vpn/plugins/vpn.h
index 3d2b66c5..bf56728d 100644
--- a/vpn/plugins/vpn.h
+++ b/vpn/plugins/vpn.h
@@ -2,7 +2,7 @@
*
* ConnMan VPN daemon
*
- * Copyright (C) 2010 BMW Car IT GmbH. All rights reserved.
+ * Copyright (C) 2010,2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/vpn/plugins/vpnc.c b/vpn/plugins/vpnc.c
index 04235c86..09674bd8 100644
--- a/vpn/plugins/vpnc.c
+++ b/vpn/plugins/vpnc.c
@@ -2,7 +2,7 @@
*
* ConnMan VPN daemon
*
- * Copyright (C) 2010,2013 BMW Car IT GmbH. All rights reserved.
+ * Copyright (C) 2010,2013 BMW Car IT GmbH.
* Copyright (C) 2010,2012-2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
diff --git a/vpn/vpn-ipconfig.c b/vpn/vpn-ipconfig.c
index c3e61453..c096fa37 100644
--- a/vpn/vpn-ipconfig.c
+++ b/vpn/vpn-ipconfig.c
@@ -69,27 +69,13 @@ struct vpn_ipdevice {
static GHashTable *ipdevice_hash = NULL;
-unsigned char __vpn_ipconfig_netmask_prefix_len(const char *netmask)
+struct connman_ipaddress *
+__vpn_ipconfig_get_address(struct vpn_ipconfig *ipconfig)
{
- unsigned char bits;
- in_addr_t mask;
- in_addr_t host;
-
- if (!netmask)
- return 32;
-
- mask = inet_network(netmask);
- host = ~mask;
-
- /* a valid netmask must be 2^n - 1 */
- if ((host & (host + 1)) != 0)
- return -1;
-
- bits = 0;
- for (; mask; mask <<= 1)
- ++bits;
+ if (!ipconfig)
+ return NULL;
- return bits;
+ return ipconfig->address;
}
const char *__vpn_ipconfig_get_peer(struct vpn_ipconfig *ipconfig)
diff --git a/vpn/vpn-provider.c b/vpn/vpn-provider.c
index c39ebf9d..16c0c2be 100644
--- a/vpn/vpn-provider.c
+++ b/vpn/vpn-provider.c
@@ -84,6 +84,8 @@ struct vpn_provider {
char *config_file;
char *config_entry;
bool immutable;
+ struct connman_ipaddress *prev_ipv4_addr;
+ struct connman_ipaddress *prev_ipv6_addr;
};
static void append_properties(DBusMessageIter *iter,
@@ -1000,6 +1002,8 @@ static void provider_destruct(struct vpn_provider *provider)
g_strfreev(provider->host_ip);
g_free(provider->config_file);
g_free(provider->config_entry);
+ connman_ipaddress_free(provider->prev_ipv4_addr);
+ connman_ipaddress_free(provider->prev_ipv6_addr);
g_free(provider);
}
@@ -1307,13 +1311,6 @@ static int provider_indicate_state(struct vpn_provider *provider,
VPN_CONNECTION_INTERFACE, "State",
DBUS_TYPE_STRING, &str);
- /*
- * We do not stay in failure state as clients like connmand can
- * get confused about our current state.
- */
- if (provider->state == VPN_PROVIDER_STATE_FAILURE)
- provider->state = VPN_PROVIDER_STATE_IDLE;
-
return 0;
}
@@ -1550,15 +1547,16 @@ int vpn_provider_indicate_error(struct vpn_provider *provider,
DBG("provider %p id %s error %d", provider, provider->identifier,
error);
+ vpn_provider_set_state(provider, VPN_PROVIDER_STATE_FAILURE);
+
switch (error) {
- case VPN_PROVIDER_ERROR_LOGIN_FAILED:
- break;
- case VPN_PROVIDER_ERROR_AUTH_FAILED:
- vpn_provider_set_state(provider, VPN_PROVIDER_STATE_FAILURE);
- break;
+ case VPN_PROVIDER_ERROR_UNKNOWN:
case VPN_PROVIDER_ERROR_CONNECT_FAILED:
break;
- default:
+
+ case VPN_PROVIDER_ERROR_LOGIN_FAILED:
+ case VPN_PROVIDER_ERROR_AUTH_FAILED:
+ vpn_provider_set_state(provider, VPN_PROVIDER_STATE_IDLE);
break;
}
@@ -2304,19 +2302,41 @@ int vpn_provider_set_ipaddress(struct vpn_provider *provider,
break;
}
- DBG("provider %p ipconfig %p family %d", provider, ipconfig,
- ipaddress->family);
+ DBG("provider %p state %d ipconfig %p family %d", provider,
+ provider->state, ipconfig, ipaddress->family);
if (!ipconfig)
return -EINVAL;
provider->family = ipaddress->family;
- __vpn_ipconfig_set_local(ipconfig, ipaddress->local);
- __vpn_ipconfig_set_peer(ipconfig, ipaddress->peer);
- __vpn_ipconfig_set_broadcast(ipconfig, ipaddress->broadcast);
- __vpn_ipconfig_set_gateway(ipconfig, ipaddress->gateway);
- __vpn_ipconfig_set_prefixlen(ipconfig, ipaddress->prefixlen);
+ if (provider->state == VPN_PROVIDER_STATE_CONNECT ||
+ provider->state == VPN_PROVIDER_STATE_READY) {
+ struct connman_ipaddress *addr =
+ __vpn_ipconfig_get_address(ipconfig);
+
+ /*
+ * Remember the old address so that we can remove it in notify
+ * function in plugins/vpn.c if we ever restart
+ */
+ if (ipaddress->family == AF_INET6) {
+ connman_ipaddress_free(provider->prev_ipv6_addr);
+ provider->prev_ipv6_addr =
+ connman_ipaddress_copy(addr);
+ } else {
+ connman_ipaddress_free(provider->prev_ipv4_addr);
+ provider->prev_ipv4_addr =
+ connman_ipaddress_copy(addr);
+ }
+ }
+
+ if (ipaddress->local) {
+ __vpn_ipconfig_set_local(ipconfig, ipaddress->local);
+ __vpn_ipconfig_set_peer(ipconfig, ipaddress->peer);
+ __vpn_ipconfig_set_broadcast(ipconfig, ipaddress->broadcast);
+ __vpn_ipconfig_set_gateway(ipconfig, ipaddress->gateway);
+ __vpn_ipconfig_set_prefixlen(ipconfig, ipaddress->prefixlen);
+ }
return 0;
}
@@ -2544,6 +2564,63 @@ const char *vpn_provider_get_path(struct vpn_provider *provider)
return provider->path;
}
+void vpn_provider_change_address(struct vpn_provider *provider)
+{
+ switch (provider->family) {
+ case AF_INET:
+ connman_inet_set_address(provider->index,
+ __vpn_ipconfig_get_address(provider->ipconfig_ipv4));
+ break;
+ case AF_INET6:
+ connman_inet_set_ipv6_address(provider->index,
+ __vpn_ipconfig_get_address(provider->ipconfig_ipv6));
+ break;
+ default:
+ break;
+ }
+}
+
+void vpn_provider_clear_address(struct vpn_provider *provider, int family)
+{
+ const char *address;
+ unsigned char len;
+
+ DBG("provider %p family %d ipv4 %p ipv6 %p", provider, family,
+ provider->prev_ipv4_addr, provider->prev_ipv6_addr);
+
+ switch (family) {
+ case AF_INET:
+ if (provider->prev_ipv4_addr) {
+ connman_ipaddress_get_ip(provider->prev_ipv4_addr,
+ &address, &len);
+
+ DBG("ipv4 %s/%d", address, len);
+
+ connman_inet_clear_address(provider->index,
+ provider->prev_ipv4_addr);
+ connman_ipaddress_free(provider->prev_ipv4_addr);
+ provider->prev_ipv4_addr = NULL;
+ }
+ break;
+ case AF_INET6:
+ if (provider->prev_ipv6_addr) {
+ connman_ipaddress_get_ip(provider->prev_ipv6_addr,
+ &address, &len);
+
+ DBG("ipv6 %s/%d", address, len);
+
+ connman_inet_clear_ipv6_address(provider->index,
+ address, len);
+
+ connman_ipaddress_free(provider->prev_ipv6_addr);
+ provider->prev_ipv6_addr = NULL;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
static int agent_probe(struct connman_agent *agent)
{
DBG("agent %p", agent);
diff --git a/vpn/vpn-provider.h b/vpn/vpn-provider.h
index 22b2062d..8105d7f1 100644
--- a/vpn/vpn-provider.h
+++ b/vpn/vpn-provider.h
@@ -107,6 +107,8 @@ const char *vpn_provider_get_save_group(struct vpn_provider *provider);
const char *vpn_provider_get_name(struct vpn_provider *provider);
const char *vpn_provider_get_host(struct vpn_provider *provider);
const char *vpn_provider_get_path(struct vpn_provider *provider);
+void vpn_provider_change_address(struct vpn_provider *provider);
+void vpn_provider_clear_address(struct vpn_provider *provider, int family);
typedef void (* vpn_provider_connect_cb_t) (struct vpn_provider *provider,
void *user_data, int error);
diff --git a/vpn/vpn.h b/vpn/vpn.h
index 3fca03cc..8bf86bd1 100644
--- a/vpn/vpn.h
+++ b/vpn/vpn.h
@@ -32,7 +32,7 @@ void __vpn_manager_cleanup(void);
struct vpn_ipconfig;
-unsigned char __vpn_ipconfig_netmask_prefix_len(const char *netmask);
+struct connman_ipaddress *__vpn_ipconfig_get_address(struct vpn_ipconfig *ipconfig);
unsigned short __vpn_ipconfig_get_type_from_index(int index);
unsigned int __vpn_ipconfig_get_flags_from_index(int index);
void __vpn_ipconfig_foreach(void (*function) (int index,