Imported Upstream version 1.26 upstream/1.26
authorZhang zhengguang <zhengguang.zhang@intel.com>
Wed, 29 Oct 2014 03:03:47 +0000 (11:03 +0800)
committerZhang zhengguang <zhengguang.zhang@intel.com>
Wed, 29 Oct 2014 03:03:47 +0000 (11:03 +0800)
94 files changed:
AUTHORS
ChangeLog
Makefile.am
Makefile.in
Makefile.plugins
README
client/agent.c
client/commands.c
client/dbus_helpers.c
configure
configure.ac
doc/agent-api.txt
doc/config-format.txt
doc/manager-api.txt
doc/session-overview.txt
doc/session-policy-format.txt
gdbus/client.c
gdbus/mainloop.c
gdbus/object.c
gdbus/watch.c
gdhcp/client.c
gdhcp/common.c
gdhcp/common.h
gdhcp/server.c
gsupplicant/dbus.c
gsupplicant/dbus.h
gsupplicant/gsupplicant.h
gsupplicant/supplicant.c
include/agent.h
include/dbus.h
include/inet.h
include/inotify.h
include/ipaddress.h
include/machine.h [new file with mode: 0644]
include/network.h
include/peer.h
include/session.h
plugins/dundee.c
plugins/ofono.c
plugins/session_policy_local.c
plugins/vpn.c
plugins/wifi.c
scripts/openvpn-script.c
src/agent-connman.c
src/agent.c
src/bridge.c
src/config.c
src/connection.c
src/connman.h
src/dbus.c
src/device.c
src/dhcp.c
src/dhcpv6.c
src/dnsproxy.c
src/inet.c
src/inotify.c
src/ipaddress.c
src/ipconfig.c
src/ippool.c
src/machine.c [new file with mode: 0644]
src/main.c
src/manager.c
src/nat.c
src/net.connman.service.in
src/network.c
src/peer.c
src/peer_service.c [new file with mode: 0644]
src/service.c
src/session.c
src/shared/netlink.c
src/stats.c
src/tethering.c
src/timeserver.c
src/wispr.c
test/p2p-on-supplicant
tools/iptables-unit.c
tools/manager-api.c
tools/netlink-test.c
tools/session-api.c
tools/session-test.c
tools/session-utils.c
tools/stats-tool.c
unit/test-ippool.c
vpn/plugins/l2tp.c
vpn/plugins/openconnect.c
vpn/plugins/openvpn.c
vpn/plugins/pptp.c
vpn/plugins/vpn.c
vpn/plugins/vpn.h
vpn/plugins/vpnc.c
vpn/vpn-ipconfig.c
vpn/vpn-provider.c
vpn/vpn-provider.h
vpn/vpn.h

diff --git a/AUTHORS b/AUTHORS
index df0b8f1..aad802d 100644 (file)
--- 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>
index d622a84..6e5c689 100644 (file)
--- 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.
index 53bf799..a574170 100644 (file)
@@ -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 \
index f65924d..3fe3485 100644 (file)
@@ -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
index 294cae0..e90ad19 100644 (file)
@@ -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 9361287..b1367dd 100644 (file)
--- 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
 ===========
 
index baa0a87..d020889 100644 (file)
@@ -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) },
        { },
 };
 
index 9c01fd5..9208016 100644 (file)
@@ -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;
index 826111f..d905746 100644 (file)
@@ -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;
        }
index ca5ddf6..ec36fdc 100755 (executable)
--- 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\\"
 
index 805f02b..6f35c78 100644 (file)
@@ -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])
index a98343f..2ddd19a 100644 (file)
@@ -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" : "" }
index d825070..b16f4e4 100644 (file)
@@ -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.
 
index 05d2701..31e137c 100644 (file)
@@ -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.
index 9af6a67..2393167 100644 (file)
@@ -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.
 
index ecfbce0..babc2c1 100644 (file)
@@ -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.
index 3bf883a..eb68a0f 100644 (file)
@@ -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);
index 435fb93..3e88eac 100644 (file)
@@ -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;
 }
index 13cf9a9..4d5a64c 100644 (file)
@@ -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);
 }
index 0f99f4f..0d0054c 100644 (file)
@@ -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)
index 2b7202a..66c3a90 100644 (file)
 #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,
index e111150..45278a8 100644 (file)
@@ -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));
index e4a4251..c692799 100644 (file)
@@ -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,
index 728992d..aa40488 100644 (file)
@@ -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,
index 5b15bcd..2957979 100644 (file)
@@ -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;
index 0117a1c..3a90406 100644 (file)
@@ -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);
index a5ec405..e49aaa6 100644 (file)
@@ -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);
 };
 
index 2674298..909a617 100644 (file)
@@ -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,104 +2774,442 @@ 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 struct {
-       const char *interface;
-       const char *member;
-       void (*function) (const char *path, DBusMessageIter *iter);
-} signal_map[] = {
-       { DBUS_INTERFACE_DBUS,  "NameOwnerChanged",  signal_name_owner_changed },
+static void signal_peer_changed(const char *path, DBusMessageIter *iter)
+{
+       struct peer_property_data *property_data;
+       GSupplicantInterface *interface;
+       GSupplicantPeer *peer;
 
-       { SUPPLICANT_INTERFACE, "PropertiesChanged", signal_properties_changed },
-       { SUPPLICANT_INTERFACE, "InterfaceAdded",    signal_interface_added    },
-       { SUPPLICANT_INTERFACE, "InterfaceCreated",  signal_interface_added    },
-       { SUPPLICANT_INTERFACE, "InterfaceRemoved",  signal_interface_removed  },
+       SUPPLICANT_DBG("");
 
-       { SUPPLICANT_INTERFACE ".Interface", "PropertiesChanged", signal_interface_changed },
-       { SUPPLICANT_INTERFACE ".Interface", "ScanDone",          signal_scan_done         },
-       { SUPPLICANT_INTERFACE ".Interface", "BSSAdded",          signal_bss_added         },
-       { SUPPLICANT_INTERFACE ".Interface", "BSSRemoved",        signal_bss_removed       },
-       { SUPPLICANT_INTERFACE ".Interface", "NetworkAdded",      signal_network_added     },
-       { SUPPLICANT_INTERFACE ".Interface", "NetworkRemoved",    signal_network_removed   },
+       interface = g_hash_table_lookup(peer_mapping, path);
+       if (!interface)
+               return;
 
-       { SUPPLICANT_INTERFACE ".BSS", "PropertiesChanged", signal_bss_changed   },
+       peer = g_hash_table_lookup(interface->peer_table, path);
+       if (!peer) {
+               g_hash_table_remove(peer_mapping, path);
+               return;
+       }
 
-       { SUPPLICANT_INTERFACE ".Interface.WPS", "Credentials", signal_wps_credentials },
-       { SUPPLICANT_INTERFACE ".Interface.WPS", "Event",       signal_wps_event       },
+       property_data = dbus_malloc0(sizeof(struct peer_property_data));
+       property_data->peer = peer;
 
-       { SUPPLICANT_INTERFACE ".Interface.P2PDevice", "DeviceFound", signal_peer_found },
-       { SUPPLICANT_INTERFACE ".Interface.P2PDevice", "DeviceLost",  signal_peer_lost  },
+       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 DBusHandlerResult g_supplicant_filter(DBusConnection *conn,
-                                       DBusMessage *message, void *data)
+static void group_sig_property(const char *key, DBusMessageIter *iter,
+                                                       void *user_data)
 {
-       DBusMessageIter iter;
-       const char *path;
-       int i;
+       struct group_sig_data *data = user_data;
 
-       path = dbus_message_get_path(message);
-       if (!path)
-               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       if (!key)
+               return;
 
-       if (!dbus_message_iter_init(message, &iter))
-               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       if (g_strcmp0(key, "peer_interface_addr") == 0) {
+               unsigned char *dev_addr;
+               DBusMessageIter array;
+               int len;
 
-       for (i = 0; signal_map[i].interface; i++) {
-               if (!dbus_message_has_interface(message, signal_map[i].interface))
-                       continue;
+               dbus_message_iter_recurse(iter, &array);
+               dbus_message_iter_get_fixed_array(&array, &dev_addr, &len);
 
-               if (!dbus_message_has_member(message, signal_map[i].member))
-                       continue;
+               if (len == ETH_ALEN)
+                       memcpy(data->iface_address, dev_addr, len);
 
-               signal_map[i].function(path, &iter);
-               break;
-       }
+       } else if (g_strcmp0(key, "role") == 0) {
+               const char *str = NULL;
 
-       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
+               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);
 
-struct supplicant_regdom {
-       GSupplicantCountryCallback callback;
-       const char *alpha2;
-       const void *user_data;
-};
+}
 
-static void country_result(const char *error,
-                               DBusMessageIter *iter, void *user_data)
+static void signal_group_success(const char *path, DBusMessageIter *iter)
 {
-       struct supplicant_regdom *regdom = user_data;
-       int result = 0;
+       GSupplicantInterface *interface;
+       struct group_sig_data data = {};
+       GSupplicantPeer *peer;
 
-       SUPPLICANT_DBG("Country setting result");
+       SUPPLICANT_DBG("");
 
-       if (!user_data)
+       interface = g_hash_table_lookup(interface_table, path);
+       if (!interface)
                return;
 
-       if (error) {
-               SUPPLICANT_DBG("Country setting failure %s", error);
-               result = -EINVAL;
-       }
+       supplicant_dbus_property_foreach(iter, group_sig_property, &data);
+       if (!data.peer_obj_path)
+               return;
 
-       if (regdom->callback)
-               regdom->callback(result, regdom->alpha2,
-                                       (void *) regdom->user_data);
+       peer = g_hash_table_lookup(interface->peer_table, data.peer_obj_path);
+       if (!peer)
+               return;
 
-       g_free(regdom);
+       memcpy(peer->iface_address, data.iface_address, ETH_ALEN);
+       interface->pending_peer_path = peer->path;
 }
 
-static void country_params(DBusMessageIter *iter, void *user_data)
+static void signal_group_failure(const char *path, DBusMessageIter *iter)
 {
-       struct supplicant_regdom *regdom = user_data;
+       GSupplicantInterface *interface;
+       struct group_sig_data data = {};
+       GSupplicantPeer *peer;
 
-       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
-                                                       &regdom->alpha2);
-}
+       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;
+       void (*function) (const char *path, DBusMessageIter *iter);
+} signal_map[] = {
+       { DBUS_INTERFACE_DBUS,  "NameOwnerChanged",  signal_name_owner_changed },
+
+       { SUPPLICANT_INTERFACE, "PropertiesChanged", signal_properties_changed },
+       { SUPPLICANT_INTERFACE, "InterfaceAdded",    signal_interface_added    },
+       { SUPPLICANT_INTERFACE, "InterfaceCreated",  signal_interface_added    },
+       { SUPPLICANT_INTERFACE, "InterfaceRemoved",  signal_interface_removed  },
+
+       { SUPPLICANT_INTERFACE ".Interface", "PropertiesChanged", signal_interface_changed },
+       { SUPPLICANT_INTERFACE ".Interface", "ScanDone",          signal_scan_done         },
+       { SUPPLICANT_INTERFACE ".Interface", "BSSAdded",          signal_bss_added         },
+       { SUPPLICANT_INTERFACE ".Interface", "BSSRemoved",        signal_bss_removed       },
+       { SUPPLICANT_INTERFACE ".Interface", "NetworkAdded",      signal_network_added     },
+       { SUPPLICANT_INTERFACE ".Interface", "NetworkRemoved",    signal_network_removed   },
+
+       { SUPPLICANT_INTERFACE ".BSS", "PropertiesChanged", signal_bss_changed   },
+
+       { SUPPLICANT_INTERFACE ".Interface.WPS", "Credentials", signal_wps_credentials },
+       { SUPPLICANT_INTERFACE ".Interface.WPS", "Event",       signal_wps_event       },
+
+       { 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 },
+
+       { }
+};
+
+static DBusHandlerResult g_supplicant_filter(DBusConnection *conn,
+                                       DBusMessage *message, void *data)
+{
+       DBusMessageIter iter;
+       const char *path;
+       int i;
+
+       path = dbus_message_get_path(message);
+       if (!path)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       if (!dbus_message_iter_init(message, &iter))
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       for (i = 0; signal_map[i].interface; i++) {
+               if (!dbus_message_has_interface(message, signal_map[i].interface))
+                       continue;
+
+               if (!dbus_message_has_member(message, signal_map[i].member))
+                       continue;
+
+               signal_map[i].function(path, &iter);
+               break;
+       }
+
+       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;
+       const void *user_data;
+};
+
+static void country_result(const char *error,
+                               DBusMessageIter *iter, void *user_data)
+{
+       struct supplicant_regdom *regdom = user_data;
+       int result = 0;
+
+       SUPPLICANT_DBG("Country setting result");
+
+       if (!user_data)
+               return;
+
+       if (error) {
+               SUPPLICANT_DBG("Country setting failure %s", error);
+               result = -EINVAL;
+       }
+
+       if (regdom->callback)
+               regdom->callback(result, regdom->alpha2,
+                                       (void *) regdom->user_data);
+
+       g_free(regdom);
+}
+
+static void country_params(DBusMessageIter *iter, void *user_data)
+{
+       struct supplicant_regdom *regdom = user_data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+                                                       &regdom->alpha2);
+}
 
 int g_supplicant_set_country(const char *alpha2,
                                GSupplicantCountryCallback callback,
@@ -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,
@@ -2648,24 +3243,156 @@ int g_supplicant_interface_set_country(GSupplicantInterface *interface,
 {
        struct supplicant_regdom *regdom;
 
-       regdom = dbus_malloc0(sizeof(*regdom));
-       if (!regdom)
-               return -ENOMEM;
+       regdom = dbus_malloc0(sizeof(*regdom));
+       if (!regdom)
+               return -ENOMEM;
+
+       regdom->callback = callback;
+       regdom->alpha2 = alpha2;
+       regdom->user_data = user_data;
+
+       return supplicant_dbus_property_set(interface->path,
+                               SUPPLICANT_INTERFACE ".Interface",
+                               "Country", DBUS_TYPE_STRING_AS_STRING,
+                               country_params, country_result,
+                                       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;
 
-       regdom->callback = callback;
-       regdom->alpha2 = alpha2;
-       regdom->user_data = user_data;
+       if (!g_strcmp0(identifier, peer->identifier))
+               return TRUE;
 
-       return supplicant_dbus_property_set(interface->path,
-                               SUPPLICANT_INTERFACE ".Interface",
-                               "Country", DBUS_TYPE_STRING_AS_STRING,
-                               country_params, country_result,
-                                       regdom);
+       return FALSE;
 }
 
-bool g_supplicant_interface_has_p2p(GSupplicantInterface *interface)
+GSupplicantPeer *g_supplicant_interface_peer_lookup(GSupplicantInterface *interface,
+                                                       const char *identifier)
 {
-       return interface->p2p_support;
+       GSupplicantPeer *peer;
+
+       peer = g_hash_table_find(interface->peer_table,
+                                       peer_lookup_by_identifier,
+                                       (void *) identifier);
+       return peer;
 }
 
 struct interface_data {
@@ -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;
index 05462df..6961f7a 100644 (file)
@@ -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,
index 253a23b..26f94d6 100644 (file)
@@ -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
index a836a5b..6482934 100644 (file)
@@ -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);
index 1f642ab..4bc6330 100644 (file)
@@ -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
index 4ced318..3655ca8 100644 (file)
@@ -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 (file)
index 0000000..c8d8735
--- /dev/null
@@ -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 */
index fcb6905..d772699 100644 (file)
@@ -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);
index 807c945..8a690f5 100644 (file)
 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
 }
index 3f66664..37dfc4e 100644 (file)
@@ -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
index b72c69e..b5420ac 100644 (file)
@@ -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
index fecef9c..7af551b 100644 (file)
@@ -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
index 4e8b5f2..b2369bd 100644 (file)
@@ -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
index 25711d7..e6e4c8e 100644 (file)
@@ -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;
                        }
 
index ef4dd95..5f2ebf1 100644 (file)
@@ -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,
 };
 
index e703c86..6ba0d29 100644 (file)
@@ -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;
        }
index ab538f3..b2049a3 100644 (file)
@@ -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;
+}
index 37cf524..a340026 100644 (file)
@@ -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;
index 034fa13..ba20096 100644 (file)
@@ -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
index 330ae81..93a788a 100644 (file)
@@ -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;
 }
index e98ccb5..8fe9725 100644 (file)
@@ -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
index 24db5f8..da01215 100644 (file)
@@ -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);
index 4fa0b36..d80a46c 100644 (file)
@@ -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)
index a97d790..c0683ab 100644 (file)
@@ -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);
index 83d7dfb..505d9f0 100644 (file)
 #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;
 }
index 2ede854..5f8029f 100644 (file)
@@ -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;
 }
index 7232b98..bdd7fd5 100644 (file)
@@ -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);
 }
index 6111629..cd220ff 100644 (file)
@@ -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;
index 72ba6f6..1ab3807 100644 (file)
@@ -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
index 57f9435..d63d95c 100644 (file)
 
 #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;
+}
index b23df16..ae70745 100644 (file)
@@ -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)
index 558e966..bb8568d 100644 (file)
@@ -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 (file)
index 0000000..14ea366
--- /dev/null
@@ -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;
+}
index 4f635de..21d1e06 100644 (file)
@@ -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();
index b31ab4c..d15ce20 100644 (file)
@@ -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) },
        { },
 };
 
index 4d23550..063f085 100644 (file)
--- 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
index 0bb1e8b..e76969b 100644 (file)
@@ -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
index 160bd06..b388995 100644 (file)
@@ -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);
                }
        }
 
index ce3b582..caff70c 100644 (file)
 #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 (file)
index 0000000..053672a
--- /dev/null
@@ -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;
+}
index cbca669..87a2f2c 100644 (file)
@@ -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);
index 00ef369..08facc1 100644 (file)
@@ -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
index d72294c..b32ab85 100644 (file)
@@ -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
index df5ab4e..26343b1 100644 (file)
@@ -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
index c7e17f5..ceeec74 100644 (file)
@@ -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,
index d41fa40..f0d33e5 100644 (file)
@@ -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)
 {
index dcce93c..c4fcd60 100644 (file)
@@ -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;
        }
index 826656e..8cc76e8 100755 (executable)
@@ -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):
index b9273ce..7e427e2 100644 (file)
@@ -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
index 9098fca..e082962 100644 (file)
@@ -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
index c07806a..221e349 100644 (file)
@@ -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
index 7162ade..b97cfc0 100644 (file)
@@ -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
index 18fd588..4319e5a 100644 (file)
@@ -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
index 92273e6..51cec5c 100644 (file)
@@ -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
index 7957c47..7d117fd 100644 (file)
@@ -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
index a11220d..e8d077a 100644 (file)
@@ -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
index 91acc85..22f9dcf 100644 (file)
@@ -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
index c6b9cd6..5feaed9 100644 (file)
@@ -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);
index 35013c4..9ee5795 100644 (file)
@@ -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
index a6e51e7..9f2a214 100644 (file)
@@ -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
index b407573..b438d06 100644 (file)
@@ -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,
index 3d2b66c..bf56728 100644 (file)
@@ -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
index 04235c8..09674bd 100644 (file)
@@ -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
index c3e6145..c096fa3 100644 (file)
@@ -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)
index c39ebf9..16c0c2b 100644 (file)
@@ -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);
index 22b2062..8105d7f 100644 (file)
@@ -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);
index 3fca03c..8bf86bd 100644 (file)
--- 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,