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>
+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.
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
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@ \
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
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 \
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) \
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 \
@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)
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) \
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 \
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@ \
@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
@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@
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
@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
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)
@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) \
-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)
-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)
-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)
@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@
@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@
@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@
@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
@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
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
- 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)
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
===========
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;
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) {
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);
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) },
{ },
};
#include <stdbool.h>
#include <sys/types.h>
#include <unistd.h>
+#include <ctype.h>
#include <glib.h>
#include <gdbus.h>
static int cmd_connect(char *args[], int num, struct connman_option *options)
{
+ const char *iface = "net.connman.Service";
char *path;
if (num > 2)
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,
static int cmd_disconnect(char *args[], int num, struct connman_option *options)
{
+ const char *iface = "net.connman.Service";
char *path;
if (num > 2)
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,
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;
{ "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,
"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,
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;
}
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;
}
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;
#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)
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;
}
#! /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.
# 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=''
# 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]...
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
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.
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 $@
# Define the identity of the package.
PACKAGE='connman'
- VERSION='1.24'
+ VERSION='1.26'
cat >>confdefs.h <<_ACEOF
# 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
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\\"
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])
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
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
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
}
==> { "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" : "" }
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.
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.
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.
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
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.
GDBusWatchFunction connect_func;
void *connect_data;
GDBusWatchFunction disconn_func;
+ gboolean connected;
void *disconn_data;
GDBusMessageFunction signal_func;
void *signal_data;
get_managed_objects(client);
+ client->connected = TRUE;
+
g_dbus_client_unref(client);
}
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,
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);
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);
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;
}
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
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);
}
callback->data = NULL;
}
+/* Returns TRUE if data is freed */
static gboolean filter_data_remove_callback(struct filter_data *data,
struct filter_callback *cb)
{
/* 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;
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;
}
/* 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;
}
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);
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)
#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,
uint32_t expire;
bool retransmit;
struct timeval start_time;
+ bool request_bcast;
};
static inline void debug(GDHCPClient *client, const char *format, ...)
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);
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,
GList *option_value = data;
g_list_foreach(option_value, remove_value, NULL);
+ g_list_free(option_value);
}
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;
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;
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));
}
memcpy(&dhcp_client->ia_ta, &addr,
sizeof(struct in6_addr));
- if (valid > dhcp_client->expire)
+ if (valid != dhcp_client->expire)
dhcp_client->expire = valid;
}
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,
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),
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:
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:
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,
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,
}
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;
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));
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,
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,
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,
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,
}
}
-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;
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);
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;
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;
}
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);
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;
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);
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;
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;
}
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;
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);
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;
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;
}
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);
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;
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);
#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)
#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,
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;
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,
/* 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,
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,
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);
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);
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);
};
#include <syslog.h>
#include <ctype.h>
#include <stdbool.h>
+#include <netinet/if_ether.h>
+#include <netinet/in.h>
#include <glib.h>
#include <gdbus.h>
{ "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];
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;
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 {
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, ...)
{
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);
}
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;
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",
{
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);
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,
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)
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)
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;
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)
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);
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,
{
GSupplicantInterface *interface = user_data;
GSupplicantNetwork *network;
+ struct g_supplicant_bss *bss = NULL;
const char *path = NULL;
dbus_message_iter_get_basic(iter, &path);
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);
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)
{
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);
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;
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,
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;
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,
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)
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);
}
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();
}
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);
}
}
}
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)
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;
}
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);
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;
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;
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;
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)
if (!peer)
return;
- callback_peer_lost(peer);
g_hash_table_remove(interface->peer_table, obj_path);
}
-static struct {
- const char *interface;
- const char *member;
- void (*function) (const char *path, DBusMessageIter *iter);
-} signal_map[] = {
- { DBUS_INTERFACE_DBUS, "NameOwnerChanged", signal_name_owner_changed },
+static void signal_peer_changed(const char *path, DBusMessageIter *iter)
+{
+ struct peer_property_data *property_data;
+ GSupplicantInterface *interface;
+ GSupplicantPeer *peer;
- { SUPPLICANT_INTERFACE, "PropertiesChanged", signal_properties_changed },
- { SUPPLICANT_INTERFACE, "InterfaceAdded", signal_interface_added },
- { SUPPLICANT_INTERFACE, "InterfaceCreated", signal_interface_added },
- { SUPPLICANT_INTERFACE, "InterfaceRemoved", signal_interface_removed },
+ SUPPLICANT_DBG("");
- { SUPPLICANT_INTERFACE ".Interface", "PropertiesChanged", signal_interface_changed },
- { SUPPLICANT_INTERFACE ".Interface", "ScanDone", signal_scan_done },
- { SUPPLICANT_INTERFACE ".Interface", "BSSAdded", signal_bss_added },
- { SUPPLICANT_INTERFACE ".Interface", "BSSRemoved", signal_bss_removed },
- { SUPPLICANT_INTERFACE ".Interface", "NetworkAdded", signal_network_added },
- { SUPPLICANT_INTERFACE ".Interface", "NetworkRemoved", signal_network_removed },
+ interface = g_hash_table_lookup(peer_mapping, path);
+ if (!interface)
+ return;
- { SUPPLICANT_INTERFACE ".BSS", "PropertiesChanged", signal_bss_changed },
+ peer = g_hash_table_lookup(interface->peer_table, path);
+ if (!peer) {
+ g_hash_table_remove(peer_mapping, path);
+ return;
+ }
- { SUPPLICANT_INTERFACE ".Interface.WPS", "Credentials", signal_wps_credentials },
- { SUPPLICANT_INTERFACE ".Interface.WPS", "Event", signal_wps_event },
+ property_data = dbus_malloc0(sizeof(struct peer_property_data));
+ property_data->peer = peer;
- { SUPPLICANT_INTERFACE ".Interface.P2PDevice", "DeviceFound", signal_peer_found },
- { SUPPLICANT_INTERFACE ".Interface.P2PDevice", "DeviceLost", signal_peer_lost },
+ supplicant_dbus_property_foreach(iter, peer_property, property_data);
+ if (property_data->services_changed)
+ callback_peer_changed(peer,
+ G_SUPPLICANT_PEER_SERVICES_CHANGED);
- { }
+ if (property_data->groups_changed)
+ callback_peer_changed(peer, G_SUPPLICANT_PEER_GROUP_CHANGED);
+
+ dbus_free(property_data);
+
+ if (!g_supplicant_peer_is_in_a_group(peer))
+ peer->connection_requested = false;
+}
+
+struct group_sig_data {
+ const char *peer_obj_path;
+ unsigned char iface_address[ETH_ALEN];
+ const char *interface_obj_path;
+ const char *group_obj_path;
+ int role;
};
-static DBusHandlerResult g_supplicant_filter(DBusConnection *conn,
- DBusMessage *message, void *data)
+static void group_sig_property(const char *key, DBusMessageIter *iter,
+ void *user_data)
{
- DBusMessageIter iter;
- const char *path;
- int i;
+ struct group_sig_data *data = user_data;
- path = dbus_message_get_path(message);
- if (!path)
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ if (!key)
+ return;
- if (!dbus_message_iter_init(message, &iter))
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ if (g_strcmp0(key, "peer_interface_addr") == 0) {
+ unsigned char *dev_addr;
+ DBusMessageIter array;
+ int len;
- for (i = 0; signal_map[i].interface; i++) {
- if (!dbus_message_has_interface(message, signal_map[i].interface))
- continue;
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &dev_addr, &len);
- if (!dbus_message_has_member(message, signal_map[i].member))
- continue;
+ if (len == ETH_ALEN)
+ memcpy(data->iface_address, dev_addr, len);
- signal_map[i].function(path, &iter);
- break;
- }
+ } else if (g_strcmp0(key, "role") == 0) {
+ const char *str = NULL;
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
+ dbus_message_iter_get_basic(iter, &str);
+ if (g_strcmp0(str, "GO") == 0)
+ data->role = G_SUPPLICANT_GROUP_ROLE_GO;
+ else
+ data->role = G_SUPPLICANT_GROUP_ROLE_CLIENT;
+ } else if (g_strcmp0(key, "peer_object") == 0)
+ dbus_message_iter_get_basic(iter, &data->peer_obj_path);
+ else if (g_strcmp0(key, "interface_object") == 0)
+ dbus_message_iter_get_basic(iter, &data->interface_obj_path);
+ else if (g_strcmp0(key, "group_object") == 0)
+ dbus_message_iter_get_basic(iter, &data->group_obj_path);
-struct supplicant_regdom {
- GSupplicantCountryCallback callback;
- const char *alpha2;
- const void *user_data;
-};
+}
-static void country_result(const char *error,
- DBusMessageIter *iter, void *user_data)
+static void signal_group_success(const char *path, DBusMessageIter *iter)
{
- struct supplicant_regdom *regdom = user_data;
- int result = 0;
+ GSupplicantInterface *interface;
+ struct group_sig_data data = {};
+ GSupplicantPeer *peer;
- SUPPLICANT_DBG("Country setting result");
+ SUPPLICANT_DBG("");
- if (!user_data)
+ interface = g_hash_table_lookup(interface_table, path);
+ if (!interface)
return;
- if (error) {
- SUPPLICANT_DBG("Country setting failure %s", error);
- result = -EINVAL;
- }
+ supplicant_dbus_property_foreach(iter, group_sig_property, &data);
+ if (!data.peer_obj_path)
+ return;
- if (regdom->callback)
- regdom->callback(result, regdom->alpha2,
- (void *) regdom->user_data);
+ peer = g_hash_table_lookup(interface->peer_table, data.peer_obj_path);
+ if (!peer)
+ return;
- g_free(regdom);
+ memcpy(peer->iface_address, data.iface_address, ETH_ALEN);
+ interface->pending_peer_path = peer->path;
}
-static void country_params(DBusMessageIter *iter, void *user_data)
+static void signal_group_failure(const char *path, DBusMessageIter *iter)
{
- struct supplicant_regdom *regdom = user_data;
+ GSupplicantInterface *interface;
+ struct group_sig_data data = {};
+ GSupplicantPeer *peer;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
- ®dom->alpha2);
-}
+ SUPPLICANT_DBG("");
+
+ interface = g_hash_table_lookup(interface_table, path);
+ if (!interface)
+ return;
+
+ supplicant_dbus_property_foreach(iter, group_sig_property, &data);
+ if (!data.peer_obj_path)
+ return;
+
+ peer = g_hash_table_lookup(interface->peer_table, data.peer_obj_path);
+ if (!peer)
+ return;
+
+ callback_peer_changed(peer, G_SUPPLICANT_PEER_GROUP_FAILED);
+ peer->connection_requested = false;
+}
+
+static void signal_group_started(const char *path, DBusMessageIter *iter)
+{
+ GSupplicantInterface *interface, *g_interface;
+ struct group_sig_data data = {};
+ GSupplicantGroup *group;
+ GSupplicantPeer *peer;
+
+ SUPPLICANT_DBG("");
+
+ interface = g_hash_table_lookup(interface_table, path);
+ if (!interface)
+ return;
+
+ supplicant_dbus_property_foreach(iter, group_sig_property, &data);
+ if (!data.interface_obj_path || !data.group_obj_path)
+ return;
+
+ peer = g_hash_table_lookup(interface->peer_table,
+ interface->pending_peer_path);
+ interface->pending_peer_path = NULL;
+ if (!peer)
+ return;
+
+ g_interface = g_hash_table_lookup(interface_table,
+ data.interface_obj_path);
+ if (!g_interface)
+ return;
+
+ group = g_hash_table_lookup(interface->group_table,
+ data.group_obj_path);
+ if (group)
+ return;
+
+ group = g_try_new0(GSupplicantGroup, 1);
+ if (!group)
+ return;
+
+ group->interface = g_interface;
+ group->orig_interface = interface;
+ group->path = g_strdup(data.group_obj_path);
+ group->role = data.role;
+
+ g_hash_table_insert(interface->group_table, group->path, group);
+ g_hash_table_replace(group_mapping, group->path, group);
+
+ peer->current_group_iface = g_interface;
+ callback_peer_changed(peer, G_SUPPLICANT_PEER_GROUP_STARTED);
+}
+
+static void remove_peer_group_interface(GHashTable *group_table,
+ const char* path)
+{
+ GSupplicantGroup *group;
+ GHashTableIter iter;
+ gpointer value, key;
+
+ if (!group_table)
+ return;
+
+ group = g_hash_table_lookup(group_table, path);
+
+ if (!group || !group->orig_interface)
+ return;
+
+ g_hash_table_iter_init(&iter, group->orig_interface->peer_table);
+
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ GSupplicantPeer *peer = value;
+
+ if (peer->current_group_iface == group->interface)
+ peer->current_group_iface = NULL;
+ }
+}
+
+static void signal_group_finished(const char *path, DBusMessageIter *iter)
+{
+ GSupplicantInterface *interface;
+ struct group_sig_data data = {};
+
+ SUPPLICANT_DBG("");
+
+ interface = g_hash_table_lookup(interface_table, path);
+ if (!interface)
+ return;
+
+ supplicant_dbus_property_foreach(iter, group_sig_property, &data);
+ if (!data.interface_obj_path || !data.group_obj_path)
+ return;
+
+ remove_peer_group_interface(interface->group_table, data.group_obj_path);
+
+ g_hash_table_remove(group_mapping, data.group_obj_path);
+
+ g_hash_table_remove(interface->group_table, data.group_obj_path);
+}
+
+static void signal_group_request(const char *path, DBusMessageIter *iter)
+{
+ GSupplicantInterface *interface;
+ GSupplicantPeer *peer;
+ const char *obj_path;
+
+ SUPPLICANT_DBG("");
+
+ interface = g_hash_table_lookup(interface_table, path);
+ if (!interface)
+ return;
+
+ dbus_message_iter_get_basic(iter, &obj_path);
+ if (!obj_path || !g_strcmp0(obj_path, "/"))
+ return;
+
+ peer = g_hash_table_lookup(interface->peer_table, obj_path);
+ if (!peer)
+ return;
+
+ /*
+ * Peer has been previously found and property set,
+ * otherwise, defer connection to when peer property
+ * is set.
+ */
+ if (peer->identifier)
+ callback_peer_request(peer);
+ else
+ g_hash_table_replace(pending_peer_connection, peer->path, peer);
+}
+
+static void signal_group_peer_joined(const char *path, DBusMessageIter *iter)
+{
+ const char *peer_path = NULL;
+ GSupplicantInterface *interface;
+ GSupplicantGroup *group;
+ GSupplicantPeer *peer;
+
+ SUPPLICANT_DBG("");
+
+ group = g_hash_table_lookup(group_mapping, path);
+ if (!group)
+ return;
+
+ dbus_message_iter_get_basic(iter, &peer_path);
+ if (!peer_path)
+ return;
+
+ interface = g_hash_table_lookup(peer_mapping, peer_path);
+ if (!interface)
+ return;
+
+ peer = g_hash_table_lookup(interface->peer_table, peer_path);
+ if (!peer)
+ return;
+
+ group->members = g_slist_prepend(group->members, g_strdup(peer_path));
+
+ callback_peer_changed(peer, G_SUPPLICANT_PEER_GROUP_JOINED);
+}
+
+static void signal_group_peer_disconnected(const char *path, DBusMessageIter *iter)
+{
+ const char *peer_path = NULL;
+ GSupplicantInterface *interface;
+ GSupplicantGroup *group;
+ GSupplicantPeer *peer;
+ GSList *elem;
+
+ SUPPLICANT_DBG("");
+
+ group = g_hash_table_lookup(group_mapping, path);
+ if (!group)
+ return;
+
+ dbus_message_iter_get_basic(iter, &peer_path);
+ if (!peer_path)
+ return;
+
+ elem = g_slist_find_custom(group->members, peer_path, g_str_equal);
+ if (!elem)
+ return;
+
+ g_free(elem->data);
+ group->members = g_slist_delete_link(group->members, elem);
+
+ interface = g_hash_table_lookup(peer_mapping, peer_path);
+ if (!interface)
+ return;
+
+ peer = g_hash_table_lookup(interface->peer_table, peer_path);
+ if (!peer)
+ return;
+
+ callback_peer_changed(peer, G_SUPPLICANT_PEER_GROUP_DISCONNECTED);
+ peer->connection_requested = false;
+}
+
+static struct {
+ const char *interface;
+ const char *member;
+ void (*function) (const char *path, DBusMessageIter *iter);
+} signal_map[] = {
+ { DBUS_INTERFACE_DBUS, "NameOwnerChanged", signal_name_owner_changed },
+
+ { SUPPLICANT_INTERFACE, "PropertiesChanged", signal_properties_changed },
+ { SUPPLICANT_INTERFACE, "InterfaceAdded", signal_interface_added },
+ { SUPPLICANT_INTERFACE, "InterfaceCreated", signal_interface_added },
+ { SUPPLICANT_INTERFACE, "InterfaceRemoved", signal_interface_removed },
+
+ { SUPPLICANT_INTERFACE ".Interface", "PropertiesChanged", signal_interface_changed },
+ { SUPPLICANT_INTERFACE ".Interface", "ScanDone", signal_scan_done },
+ { SUPPLICANT_INTERFACE ".Interface", "BSSAdded", signal_bss_added },
+ { SUPPLICANT_INTERFACE ".Interface", "BSSRemoved", signal_bss_removed },
+ { SUPPLICANT_INTERFACE ".Interface", "NetworkAdded", signal_network_added },
+ { SUPPLICANT_INTERFACE ".Interface", "NetworkRemoved", signal_network_removed },
+
+ { SUPPLICANT_INTERFACE ".BSS", "PropertiesChanged", signal_bss_changed },
+
+ { SUPPLICANT_INTERFACE ".Interface.WPS", "Credentials", signal_wps_credentials },
+ { SUPPLICANT_INTERFACE ".Interface.WPS", "Event", signal_wps_event },
+
+ { SUPPLICANT_INTERFACE ".Interface.P2PDevice", "DeviceFound", signal_peer_found },
+ { SUPPLICANT_INTERFACE ".Interface.P2PDevice", "DeviceLost", signal_peer_lost },
+
+ { SUPPLICANT_INTERFACE ".Peer", "PropertiesChanged", signal_peer_changed },
+
+ { SUPPLICANT_INTERFACE ".Interface.P2PDevice", "GONegotiationSuccess", signal_group_success },
+ { SUPPLICANT_INTERFACE ".Interface.P2PDevice", "GONegotiationFailure", signal_group_failure },
+ { SUPPLICANT_INTERFACE ".Interface.P2PDevice", "GroupStarted", signal_group_started },
+ { SUPPLICANT_INTERFACE ".Interface.P2PDevice", "GroupFinished", signal_group_finished },
+ { SUPPLICANT_INTERFACE ".Interface.P2PDevice", "GONegotiationRequest", signal_group_request },
+
+ { SUPPLICANT_INTERFACE ".Group", "PeerJoined", signal_group_peer_joined },
+ { SUPPLICANT_INTERFACE ".Group", "PeerDisconnected", signal_group_peer_disconnected },
+
+ { }
+};
+
+static DBusHandlerResult g_supplicant_filter(DBusConnection *conn,
+ DBusMessage *message, void *data)
+{
+ DBusMessageIter iter;
+ const char *path;
+ int i;
+
+ path = dbus_message_get_path(message);
+ if (!path)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (!dbus_message_iter_init(message, &iter))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ for (i = 0; signal_map[i].interface; i++) {
+ if (!dbus_message_has_interface(message, signal_map[i].interface))
+ continue;
+
+ if (!dbus_message_has_member(message, signal_map[i].member))
+ continue;
+
+ signal_map[i].function(path, &iter);
+ break;
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+void g_supplicant_interface_cancel(GSupplicantInterface *interface)
+{
+ SUPPLICANT_DBG("Cancelling any pending DBus calls");
+ supplicant_dbus_method_call_cancel_all(interface);
+ supplicant_dbus_property_call_cancel_all(interface);
+}
+
+struct supplicant_regdom {
+ GSupplicantCountryCallback callback;
+ const char *alpha2;
+ const void *user_data;
+};
+
+static void country_result(const char *error,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct supplicant_regdom *regdom = user_data;
+ int result = 0;
+
+ SUPPLICANT_DBG("Country setting result");
+
+ if (!user_data)
+ return;
+
+ if (error) {
+ SUPPLICANT_DBG("Country setting failure %s", error);
+ result = -EINVAL;
+ }
+
+ if (regdom->callback)
+ regdom->callback(result, regdom->alpha2,
+ (void *) regdom->user_data);
+
+ g_free(regdom);
+}
+
+static void country_params(DBusMessageIter *iter, void *user_data)
+{
+ struct supplicant_regdom *regdom = user_data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+ ®dom->alpha2);
+}
int g_supplicant_set_country(const char *alpha2,
GSupplicantCountryCallback callback,
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,
{
struct supplicant_regdom *regdom;
- regdom = dbus_malloc0(sizeof(*regdom));
- if (!regdom)
- return -ENOMEM;
+ regdom = dbus_malloc0(sizeof(*regdom));
+ if (!regdom)
+ return -ENOMEM;
+
+ regdom->callback = callback;
+ regdom->alpha2 = alpha2;
+ regdom->user_data = user_data;
+
+ return supplicant_dbus_property_set(interface->path,
+ SUPPLICANT_INTERFACE ".Interface",
+ "Country", DBUS_TYPE_STRING_AS_STRING,
+ country_params, country_result,
+ regdom, NULL);
+}
+
+bool g_supplicant_interface_has_p2p(GSupplicantInterface *interface)
+{
+ if (!interface)
+ return false;
+
+ return interface->p2p_support;
+}
+
+struct supplicant_p2p_dev_config {
+ char *device_name;
+ char *dev_type;
+};
+
+static void p2p_device_config_result(const char *error,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct supplicant_p2p_dev_config *config = user_data;
+
+ if (error)
+ SUPPLICANT_DBG("Unable to set P2P Device configuration: %s",
+ error);
+
+ g_free(config->device_name);
+ g_free(config->dev_type);
+ dbus_free(config);
+}
+
+static int dev_type_str2bin(const char *type, unsigned char dev_type[8])
+{
+ int length, pos, end;
+ char b[3] = {};
+ char *e = NULL;
+
+ end = strlen(type);
+ for (length = pos = 0; type[pos] != '\0' && length < 8; length++) {
+ if (pos+2 > end)
+ return 0;
+
+ b[0] = type[pos];
+ b[1] = type[pos+1];
+
+ dev_type[length] = strtol(b, &e, 16);
+ if (e && *e != '\0')
+ return 0;
+
+ pos += 2;
+ }
+
+ return 8;
+}
+
+static void p2p_device_config_params(DBusMessageIter *iter, void *user_data)
+{
+ struct supplicant_p2p_dev_config *config = user_data;
+ DBusMessageIter dict;
+
+ supplicant_dbus_dict_open(iter, &dict);
+
+ supplicant_dbus_dict_append_basic(&dict, "DeviceName",
+ DBUS_TYPE_STRING, &config->device_name);
+
+ if (config->dev_type) {
+ unsigned char dev_type[8] = {}, *type;
+ int len;
+
+ len = dev_type_str2bin(config->dev_type, dev_type);
+ if (len) {
+ type = dev_type;
+ supplicant_dbus_dict_append_fixed_array(&dict,
+ "PrimaryDeviceType",
+ DBUS_TYPE_BYTE, &type, len);
+ }
+ }
+
+ supplicant_dbus_dict_close(iter, &dict);
+}
+
+int g_supplicant_interface_set_p2p_device_config(GSupplicantInterface *interface,
+ const char *device_name,
+ const char *primary_dev_type)
+{
+ struct supplicant_p2p_dev_config *config;
+ int ret;
+
+ SUPPLICANT_DBG("P2P Device settings %s/%s",
+ device_name, primary_dev_type);
+
+ config = dbus_malloc0(sizeof(*config));
+ if (!config)
+ return -ENOMEM;
+
+ config->device_name = g_strdup(device_name);
+ config->dev_type = g_strdup(primary_dev_type);
+
+ ret = supplicant_dbus_property_set(interface->path,
+ SUPPLICANT_INTERFACE ".Interface.P2PDevice",
+ "P2PDeviceConfig",
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ p2p_device_config_params,
+ p2p_device_config_result, config, NULL);
+ if (ret < 0) {
+ g_free(config->device_name);
+ g_free(config->dev_type);
+ dbus_free(config);
+ SUPPLICANT_DBG("Unable to set P2P Device configuration");
+ }
+
+ return ret;
+}
+
+static gboolean peer_lookup_by_identifier(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ const GSupplicantPeer *peer = value;
+ const char *identifier = user_data;
- regdom->callback = callback;
- regdom->alpha2 = alpha2;
- regdom->user_data = user_data;
+ if (!g_strcmp0(identifier, peer->identifier))
+ return TRUE;
- return supplicant_dbus_property_set(interface->path,
- SUPPLICANT_INTERFACE ".Interface",
- "Country", DBUS_TYPE_STRING_AS_STRING,
- country_params, country_result,
- regdom);
+ return FALSE;
}
-bool g_supplicant_interface_has_p2p(GSupplicantInterface *interface)
+GSupplicantPeer *g_supplicant_interface_peer_lookup(GSupplicantInterface *interface,
+ const char *identifier)
{
- return interface->p2p_support;
+ GSupplicantPeer *peer;
+
+ peer = g_hash_table_find(interface->peer_table,
+ peer_lookup_by_identifier,
+ (void *) identifier);
+ return peer;
}
struct interface_data {
GSupplicantInterface *interface;
char *path;
GSupplicantInterfaceCallback callback;
- GSupplicantSSID *ssid;
+ union {
+ GSupplicantSSID *ssid;
+ GSupplicantPeerParams *peer;
+ };
void *user_data;
};
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);
}
err = supplicant_dbus_property_get_all(path,
SUPPLICANT_INTERFACE ".Interface",
- interface_create_property, data);
+ interface_create_property, data,
+ NULL);
if (err == 0)
return;
goto done;
}
- if (data->callback)
+ if (data->callback) {
data->callback(0, interface, data->user_data);
+ callback_p2p_support(interface);
+ }
interface_create_data_free(data);
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)
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.
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",
return -ENOTSUP;
ret = interface_ready_to_scan(interface);
- if (ret)
+ if (ret && ret != -EALREADY)
return ret;
data = dbus_malloc0(sizeof(*data));
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)
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 ","
"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)
{
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);
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,
system_available = TRUE;
supplicant_dbus_property_get_all(SUPPLICANT_PATH,
SUPPLICANT_INTERFACE,
- service_property, NULL);
+ service_property, NULL, NULL);
} else
invoke_introspect_method();
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);
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,
interface_table = NULL;
}
+ if (system_available)
+ callback_system_killed();
+
if (connection) {
dbus_connection_unref(connection);
connection = NULL;
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,
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
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);
*
* 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
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,
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
}
--- /dev/null
+/*
+ *
+ * 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 */
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);
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
}
*
* 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
*
* 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
*
* 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
*
* 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
char *ptr;
long int value = strtol(netmask, &ptr, 10);
if (ptr != netmask && *ptr == '\0' &&
- value <= 32)
+ value && value <= 32)
prefix_len = value;
}
#include <connman/storage.h>
#include <include/setting.h>
#include <connman/provision.h>
+#include <connman/utsname.h>
+#include <connman/machine.h>
#include <gsupplicant/gsupplicant.h>
#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;
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)
.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)
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;
}
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)
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);
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);
if (!wifi)
return;
+ if (wifi->p2p_device)
+ return;
+
autoscan = wifi->autoscan;
if (!autoscan)
return;
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) {
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,
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);
}
static int p2p_find(struct connman_device *device)
{
- struct wifi_data *wifi = connman_device_get_data(device);
+ struct wifi_data *wifi;
int ret;
DBG("");
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);
if (!wifi)
return -ENODEV;
+ if (wifi->p2p_device)
+ return 0;
+
if (type == CONNMAN_SERVICE_TYPE_P2P)
return p2p_find(device);
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);
}
connman_device_set_powered(wifi->device, true);
-
- if (wifi->tethering)
- return;
}
static bool is_idle(struct wifi_data *wifi)
wifi = g_supplicant_interface_get_data(interface);
+ if (wifi)
+ wifi->interface = NULL;
+
if (wifi && wifi->tethering)
return;
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)
}
}
+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)
.network_changed = network_changed,
.peer_found = peer_found,
.peer_lost = peer_lost,
+ .peer_changed = peer_changed,
+ .peer_request = peer_request,
.debug = debug,
};
* 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
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;
}
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;
};
}
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);
}
}
}
+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);
}
}
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);
}
}
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,
}
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,
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;
+}
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 &&
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)
{
return -ESRCH;
message = dbus_message_new_method_call(agent->owner, agent->path,
- CONNMAN_AGENT_INTERFACE,
- "ReportError");
+ CONNMAN_AGENT_INTERFACE, method);
if (!message)
return -ENOMEM;
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;
* 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
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 */
#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"
SERVICE_KEY_IDENTITY,
SERVICE_KEY_PHASE2,
SERVICE_KEY_PASSPHRASE,
+ SERVICE_KEY_SECURITY,
SERVICE_KEY_HIDDEN,
SERVICE_KEY_IPv4,
SERVICE_KEY_IPv6,
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);
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 */
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);
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) {
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;
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);
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,
__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();
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;
}
* 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
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,
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);
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>
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);
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);
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);
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,
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);
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>
void *user_data);
void __connman_nfacct_cleanup(void);
+
+#include <connman/machine.h>
+
+int __connman_machine_init(void);
+void __connman_machine_cleanup(void);
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)
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);
#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;
char *dhcp_debug_prefix;
};
-static GHashTable *network_table;
+static GHashTable *ipconfig_table;
static bool ipv4ll_running;
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++) {
dhcp->timeservers[i]);
}
}
-
if (dhcp->nameservers) {
for (i = 0; dhcp->nameservers[i]; i++) {
__connman_service_nameserver_remove(service,
}
}
- __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)
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);
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)
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);
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;
}
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;
}
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++) {
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);
}
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]);
}
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);
{
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);
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)
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,
{
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);
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);
{
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;
}
{
DBG("");
- g_hash_table_destroy(network_table);
- network_table = NULL;
+ g_hash_table_destroy(ipconfig_table);
+ ipconfig_table = NULL;
}
/* 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);
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);
}
}
{
DBG("");
+ g_dhcpv6_client_reset_request(dhcp_client);
g_dhcpv6_client_clear_retransmit(dhcp_client);
re_cb(REQ_REBIND, dhcp_client, 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);
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);
/* 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;
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);
}
}
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("");
/* 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;
}
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 */
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;
}
}
hdr->id = id;
hdr->qr = 1;
- hdr->rcode = 0;
+ hdr->rcode = ns_r_noerror;
hdr->ancount = htons(answers);
hdr->nscount = 0;
hdr->arcount = 0;
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;
static int reply_query_type(unsigned char *msg, int len)
{
unsigned char *c;
- uint16_t *w;
int l;
int type;
/* 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;
}
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)
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
*/
ulen = strlen(name);
- *uptr++ = ulen;
strncpy(uptr, name, uncomp_len - (uptr - uncompressed));
DBG("pos %d ulen %d left %d name %s", pos, ulen,
* 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))
ptr += dlen;
} else if (dns_type == ns_t_soa) {
- int comp_pos;
int total_len = 0;
char *len_ptr;
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)
{
req->numresp++;
- if (hdr->rcode == 0 || !req->resp) {
+ if (hdr->rcode == ns_r_noerror || !req->resp) {
unsigned char *new_reply = NULL;
/*
*/
if (domain_len > 0) {
int len = host_len + 1;
+ int new_len, fixed_len;
+ char *answers;
/*
* First copy host (without domain name) into
*/
ptr += NS_QFIXEDSZ;
uptr += NS_QFIXEDSZ;
+ answers = uptr;
+ fixed_len = answers - uncompressed;
/*
* We then uncompress the result to buffer
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;
}
}
}
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);
* 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);
}
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;
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;
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;
* 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
#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;
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)];
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)
{
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);
/*
* 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)
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;
+}
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);
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;
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)
{
case CONNMAN_IPCONFIG_METHOD_OFF:
ipconfig->method = method;
- if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6)
- disable_ipv6(ipconfig);
+
break;
case CONNMAN_IPCONFIG_METHOD_AUTO:
ipconfig->method = method;
if (privacy_string)
ipconfig->ipv6_privacy_config = privacy;
- enable_ipv6(ipconfig);
+
break;
case CONNMAN_IPCONFIG_METHOD_MANUAL:
return connman_ipaddress_set_ipv6(
ipconfig->address, address,
prefix_length, gateway);
+
break;
case CONNMAN_IPCONFIG_METHOD_DHCP:
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)
* 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
--- /dev/null
+/*
+ *
+ * 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;
+}
__connman_notifier_init();
__connman_agent_init();
__connman_service_init();
+ __connman_peer_service_init();
__connman_peer_init();
__connman_provider_init();
__connman_network_init();
__connman_wpad_init();
__connman_wispr_init();
__connman_rfkill_init();
+ __connman_machine_init();
g_free(option_config);
g_free(option_device);
g_source_remove(signal);
+ __connman_machine_cleanup();
__connman_rfkill_cleanup();
__connman_wispr_cleanup();
__connman_wpad_cleanup();
__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();
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}" }),
{ 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) },
{ },
};
* 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
[D-BUS Service]
Name=net.connman
-Exec=@prefix@/sbin/connman -n
+Exec=@prefix@/sbin/connmand -n
User=root
SystemdService=connman.service
__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)
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;
unsigned int length, void *user_data)
{
struct connman_network *network = user_data;
+ struct connman_service *service;
GSList *prefixes;
DBG("reply %p", 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)
if (!ipconfig)
return;
+ __connman_ipconfig_address_remove(ipconfig);
+
index = __connman_ipconfig_get_index(ipconfig);
connman_network_ref(network);
case CONNMAN_IPCONFIG_METHOD_MANUAL:
break;
case CONNMAN_IPCONFIG_METHOD_DHCP:
- __connman_dhcp_stop(network);
+ __connman_dhcp_stop(ipconfig_ipv4);
break;
}
}
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
* 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,
__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,
struct connman_ipconfig *ipconfig)
{
struct connman_service *service;
+ struct connman_ipconfig *ipconfig_ipv4;
enum connman_ipconfig_method method;
enum connman_ipconfig_type type;
if (!service)
return -EINVAL;
+ ipconfig_ipv4 = __connman_service_get_ip4config(service);
method = __connman_ipconfig_get_method(ipconfig);
type = __connman_ipconfig_get_config_type(ipconfig);
__connman_ipconfig_address_remove(ipconfig);
break;
case CONNMAN_IPCONFIG_METHOD_DHCP:
- __connman_dhcp_stop(network);
+ __connman_dhcp_stop(ipconfig_ipv4);
break;
}
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);
}
}
#endif
#include <errno.h>
+#include <ctype.h>
#include <gdbus.h>
+#include <gdhcp/gdhcp.h>
+
+#include <connman/agent.h>
#include "connman.h"
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);
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)
{
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);
}
}
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)
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) },
{ },
};
{ },
};
+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;
{
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);
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("");
DBG("");
g_hash_table_destroy(peers_table);
+ peers_table = NULL;
dbus_connection_unref(connection);
+ connection = NULL;
}
--- /dev/null
+/*
+ *
+ * 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;
+}
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) {
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) {
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:
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:
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].
} 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;
}
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)
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)
{
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)
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);
}
}
}
-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;
}
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);
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;
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);
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);
}
__connman_service_set_favorite(service, false);
+ __connman_ipconfig_ipv6_reset_privacy(service->ipconfig_ipv6);
+
service_save(service);
return true;
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);
} 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,
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;
__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) {
{
enum connman_service_state old_state, new_state;
struct connman_service *def_service;
+ enum connman_ipconfig_method method;
int result;
if (!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) {
/*
__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) {
/*
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() &&
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,
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();
*/
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);
int __connman_service_clear_error(struct connman_service *service)
{
+ DBusMessage *pending, *provider_pending;
+
DBG("service %p", service);
if (!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)
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:
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);
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;
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);
* 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
* 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
*
* 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
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);
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));
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,
* 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()
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);
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);
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]);
/*
* 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)
{
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;
}
break;
+ case 505:
+ __connman_agent_request_browser(wp_context->service,
+ wispr_portal_browser_reply_cb,
+ wp_context->status_url, wp_context);
+ break;
default:
break;
}
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]
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']
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,
@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):
*
* 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
*
* 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
*
* 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
*
* 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
*
* 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
*
* 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
*
* 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
*
* 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
*
* 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
switch (exit_code) {
case 1:
- return VPN_PROVIDER_ERROR_CONNECT_FAILED;
case 2:
vpn_provider_set_string_hide_value(provider,
"OpenConnect.Cookie", NULL);
*
* 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
*
* 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
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:
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);
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,
*
* 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
*
* 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
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)
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,
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);
}
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;
}
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;
}
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;
}
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);
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);
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,