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