diff options
| author | Michael Biebl <biebl@debian.org> | 2010-11-29 00:47:11 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2010-11-29 00:47:11 +0100 |
| commit | 9fae01fa351805b2903e535736e06971bc0b925e (patch) | |
| tree | c19bccf84c9f894e50fae678ba6ee2b2044e2d30 /src/vpn-manager | |
Imported Upstream version 0.8.1 upstream/0.8.1
Diffstat (limited to 'src/vpn-manager')
| -rw-r--r-- | src/vpn-manager/Makefile.am | 46 | ||||
| -rw-r--r-- | src/vpn-manager/Makefile.in | 611 | ||||
| -rw-r--r-- | src/vpn-manager/nm-vpn-connection.c | 1192 | ||||
| -rw-r--r-- | src/vpn-manager/nm-vpn-connection.h | 78 | ||||
| -rw-r--r-- | src/vpn-manager/nm-vpn-manager.c | 369 | ||||
| -rw-r--r-- | src/vpn-manager/nm-vpn-manager.h | 86 | ||||
| -rw-r--r-- | src/vpn-manager/nm-vpn-service.c | 478 | ||||
| -rw-r--r-- | src/vpn-manager/nm-vpn-service.h | 60 |
8 files changed, 2920 insertions, 0 deletions
diff --git a/src/vpn-manager/Makefile.am b/src/vpn-manager/Makefile.am new file mode 100644 index 00000000..56e4a4ce --- /dev/null +++ b/src/vpn-manager/Makefile.am @@ -0,0 +1,46 @@ +INCLUDES = \ + -I${top_srcdir} \ + -I${top_srcdir}/include \ + -I${top_srcdir}/libnm-util \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/src \ + -I${top_builddir}/marshallers \ + -I${top_srcdir}/src/named-manager \ + -DVPN_NAME_FILES_DIR=\""$(sysconfdir)/NetworkManager/VPN"\" + + +noinst_LTLIBRARIES = libvpn-manager.la + +libvpn_manager_la_SOURCES = \ + nm-vpn-manager.c \ + nm-vpn-manager.h \ + nm-vpn-service.c \ + nm-vpn-service.h \ + nm-vpn-connection.c \ + nm-vpn-connection.h + +libvpn_manager_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + -DG_DISABLE_DEPRECATED + +libvpn_manager_la_LIBADD = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) + +nm-vpn-connection-glue.h: $(top_srcdir)/introspection/nm-vpn-connection.xml + dbus-binding-tool --prefix=nm_vpn_connection --mode=glib-server --output=$@ $< + +nm-vpn-plugin-bindings.h: $(top_srcdir)/introspection/nm-vpn-plugin.xml + dbus-binding-tool --prefix=nm_vpn_plugin --mode=glib-client --output=$@ $< + + +BUILT_SOURCES = \ + nm-vpn-connection-glue.h \ + nm-vpn-plugin-bindings.h + +CLEANFILES = $(BUILT_SOURCES) + diff --git a/src/vpn-manager/Makefile.in b/src/vpn-manager/Makefile.in new file mode 100644 index 00000000..dc1ba727 --- /dev/null +++ b/src/vpn-manager/Makefile.in @@ -0,0 +1,611 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/vpn-manager +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \ + $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +libvpn_manager_la_DEPENDENCIES = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(top_builddir)/libnm-util/libnm-util.la $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am_libvpn_manager_la_OBJECTS = libvpn_manager_la-nm-vpn-manager.lo \ + libvpn_manager_la-nm-vpn-service.lo \ + libvpn_manager_la-nm-vpn-connection.lo +libvpn_manager_la_OBJECTS = $(am_libvpn_manager_la_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libvpn_manager_la_SOURCES) +DIST_SOURCES = $(libvpn_manager_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_SYS_DIR = @DBUS_SYS_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DHCLIENT_PATH = @DHCLIENT_PATH@ +DHCPCD_PATH = @DHCPCD_PATH@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GMODULE_CFLAGS = @GMODULE_CFLAGS@ +GMODULE_LIBS = @GMODULE_LIBS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +IPTABLES_PATH = @IPTABLES_PATH@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBDL = @LIBDL@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBM = @LIBM@ +LIBNL_CFLAGS = @LIBNL_CFLAGS@ +LIBNL_LIBS = @LIBNL_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NSS_CFLAGS = @NSS_CFLAGS@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +POFILES = @POFILES@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +RANLIB = @RANLIB@ +RESOLVCONF_PATH = @RESOLVCONF_PATH@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ +UDEV_BASE_DIR = @UDEV_BASE_DIR@ +USE_NLS = @USE_NLS@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = \ + -I${top_srcdir} \ + -I${top_srcdir}/include \ + -I${top_srcdir}/libnm-util \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/src \ + -I${top_builddir}/marshallers \ + -I${top_srcdir}/src/named-manager \ + -DVPN_NAME_FILES_DIR=\""$(sysconfdir)/NetworkManager/VPN"\" + +noinst_LTLIBRARIES = libvpn-manager.la +libvpn_manager_la_SOURCES = \ + nm-vpn-manager.c \ + nm-vpn-manager.h \ + nm-vpn-service.c \ + nm-vpn-service.h \ + nm-vpn-connection.c \ + nm-vpn-connection.h + +libvpn_manager_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + -DG_DISABLE_DEPRECATED + +libvpn_manager_la_LIBADD = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) + +BUILT_SOURCES = \ + nm-vpn-connection-glue.h \ + nm-vpn-plugin-bindings.h + +CLEANFILES = $(BUILT_SOURCES) +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/vpn-manager/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/vpn-manager/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libvpn-manager.la: $(libvpn_manager_la_OBJECTS) $(libvpn_manager_la_DEPENDENCIES) + $(LINK) $(libvpn_manager_la_OBJECTS) $(libvpn_manager_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvpn_manager_la-nm-vpn-connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvpn_manager_la-nm-vpn-manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvpn_manager_la-nm-vpn-service.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +libvpn_manager_la-nm-vpn-manager.lo: nm-vpn-manager.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libvpn_manager_la-nm-vpn-manager.lo -MD -MP -MF $(DEPDIR)/libvpn_manager_la-nm-vpn-manager.Tpo -c -o libvpn_manager_la-nm-vpn-manager.lo `test -f 'nm-vpn-manager.c' || echo '$(srcdir)/'`nm-vpn-manager.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libvpn_manager_la-nm-vpn-manager.Tpo $(DEPDIR)/libvpn_manager_la-nm-vpn-manager.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-vpn-manager.c' object='libvpn_manager_la-nm-vpn-manager.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libvpn_manager_la-nm-vpn-manager.lo `test -f 'nm-vpn-manager.c' || echo '$(srcdir)/'`nm-vpn-manager.c + +libvpn_manager_la-nm-vpn-service.lo: nm-vpn-service.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libvpn_manager_la-nm-vpn-service.lo -MD -MP -MF $(DEPDIR)/libvpn_manager_la-nm-vpn-service.Tpo -c -o libvpn_manager_la-nm-vpn-service.lo `test -f 'nm-vpn-service.c' || echo '$(srcdir)/'`nm-vpn-service.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libvpn_manager_la-nm-vpn-service.Tpo $(DEPDIR)/libvpn_manager_la-nm-vpn-service.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-vpn-service.c' object='libvpn_manager_la-nm-vpn-service.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libvpn_manager_la-nm-vpn-service.lo `test -f 'nm-vpn-service.c' || echo '$(srcdir)/'`nm-vpn-service.c + +libvpn_manager_la-nm-vpn-connection.lo: nm-vpn-connection.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libvpn_manager_la-nm-vpn-connection.lo -MD -MP -MF $(DEPDIR)/libvpn_manager_la-nm-vpn-connection.Tpo -c -o libvpn_manager_la-nm-vpn-connection.lo `test -f 'nm-vpn-connection.c' || echo '$(srcdir)/'`nm-vpn-connection.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libvpn_manager_la-nm-vpn-connection.Tpo $(DEPDIR)/libvpn_manager_la-nm-vpn-connection.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-vpn-connection.c' object='libvpn_manager_la-nm-vpn-connection.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libvpn_manager_la-nm-vpn-connection.lo `test -f 'nm-vpn-connection.c' || echo '$(srcdir)/'`nm-vpn-connection.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: all check install install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am + + +nm-vpn-connection-glue.h: $(top_srcdir)/introspection/nm-vpn-connection.xml + dbus-binding-tool --prefix=nm_vpn_connection --mode=glib-server --output=$@ $< + +nm-vpn-plugin-bindings.h: $(top_srcdir)/introspection/nm-vpn-plugin.xml + dbus-binding-tool --prefix=nm_vpn_plugin --mode=glib-client --output=$@ $< + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c new file mode 100644 index 00000000..7eae972e --- /dev/null +++ b/src/vpn-manager/nm-vpn-connection.c @@ -0,0 +1,1192 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2010 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#include <glib.h> +#include <string.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <errno.h> +#include <stdlib.h> + +#include "NetworkManager.h" +#include "NetworkManagerVPN.h" +#include "nm-vpn-connection.h" +#include "nm-setting-connection.h" +#include "nm-setting-vpn.h" +#include "nm-setting-ip4-config.h" +#include "nm-dbus-manager.h" +#include "nm-manager.h" +#include "nm-system.h" +#include "nm-logging.h" +#include "nm-utils.h" +#include "nm-vpn-plugin-bindings.h" +#include "nm-marshal.h" +#include "nm-active-connection.h" +#include "nm-properties-changed-signal.h" +#include "nm-dbus-glib-types.h" +#include "NetworkManagerUtils.h" +#include "nm-named-manager.h" +#include "nm-netlink-monitor.h" +#include "nm-glib-compat.h" + +#include "nm-vpn-connection-glue.h" + +static void secrets_provider_interface_init (NMSecretsProviderInterface *sp_interface_class); + +G_DEFINE_TYPE_EXTENDED (NMVPNConnection, nm_vpn_connection, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (NM_TYPE_SECRETS_PROVIDER_INTERFACE, + secrets_provider_interface_init)) + +typedef struct { + gboolean disposed; + + NMConnection *connection; + + NMActRequest *act_request; + char *ac_path; + + NMDevice *parent_dev; + gulong device_monitor; + gulong device_ip4; + + gboolean is_default; + gboolean is_default6; + NMActiveConnectionState state; + + NMVPNConnectionState vpn_state; + NMVPNConnectionStateReason failure_reason; + DBusGProxy *proxy; + guint ipconfig_timeout; + NMIP4Config *ip4_config; + guint32 ip4_internal_gw; + char *tundev; + char *banner; + + struct rtnl_route *gw_route; +} NMVPNConnectionPrivate; + +#define NM_VPN_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_CONNECTION, NMVPNConnectionPrivate)) + +enum { + PROPERTIES_CHANGED, + VPN_STATE_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +enum { + PROP_0, + PROP_SERVICE_NAME, + PROP_CONNECTION, + PROP_SPECIFIC_OBJECT, + PROP_DEVICES, + PROP_STATE, + PROP_DEFAULT, + PROP_DEFAULT6, + PROP_VPN, + PROP_VPN_STATE, + PROP_BANNER, + + LAST_PROP +}; + +static void +nm_vpn_connection_set_vpn_state (NMVPNConnection *connection, + NMVPNConnectionState vpn_state, + NMVPNConnectionStateReason reason) +{ + NMVPNConnectionPrivate *priv; + NMActiveConnectionState new_ac_state; + NMVPNConnectionState old_vpn_state; + char *ip_iface; + + g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); + + priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + if (vpn_state == priv->vpn_state) + return; + + old_vpn_state = priv->vpn_state; + priv->vpn_state = vpn_state; + + /* Save ip_iface since when the VPN goes down it may get freed + * before we're done with it. + */ + ip_iface = g_strdup (priv->tundev); + + /* Set the NMActiveConnection state based on VPN state */ + switch (vpn_state) { + case NM_VPN_CONNECTION_STATE_PREPARE: + case NM_VPN_CONNECTION_STATE_NEED_AUTH: + case NM_VPN_CONNECTION_STATE_CONNECT: + case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET: + new_ac_state = NM_ACTIVE_CONNECTION_STATE_ACTIVATING; + break; + case NM_VPN_CONNECTION_STATE_ACTIVATED: + new_ac_state = NM_ACTIVE_CONNECTION_STATE_ACTIVATED; + break; + default: + new_ac_state = NM_ACTIVE_CONNECTION_STATE_UNKNOWN; + break; + } + + if (new_ac_state != priv->state) { + priv->state = new_ac_state; + g_object_notify (G_OBJECT (connection), NM_ACTIVE_CONNECTION_STATE); + } + + /* The connection gets destroyed by the VPN manager when it enters the + * disconnected/failed state, but we need to keep it around for a bit + * to send out signals and handle the dispatcher. So ref it. + */ + g_object_ref (connection); + + g_signal_emit (connection, signals[VPN_STATE_CHANGED], 0, vpn_state, reason); + g_object_notify (G_OBJECT (connection), NM_VPN_CONNECTION_VPN_STATE); + + /* Call dispatcher after the event gets processed internally */ + switch (vpn_state) { + case NM_VPN_CONNECTION_STATE_ACTIVATED: + nm_utils_call_dispatcher ("vpn-up", + priv->connection, + priv->parent_dev, + ip_iface); + break; + case NM_VPN_CONNECTION_STATE_FAILED: + case NM_VPN_CONNECTION_STATE_DISCONNECTED: + if (old_vpn_state == NM_VPN_CONNECTION_STATE_ACTIVATED) { + nm_utils_call_dispatcher ("vpn-down", + priv->connection, + priv->parent_dev, + ip_iface); + } + break; + default: + break; + } + + g_free (ip_iface); + g_object_unref (connection); +} + +static void +device_state_changed (NMDevice *device, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason, + gpointer user_data) +{ + NMVPNConnection *connection = NM_VPN_CONNECTION (user_data); + + if (new_state <= NM_DEVICE_STATE_DISCONNECTED) { + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_DISCONNECTED, + NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); + } else if (new_state == NM_DEVICE_STATE_FAILED) { + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_FAILED, + NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); + } +} + +static void +device_ip4_config_changed (NMDevice *device, + GParamSpec *pspec, + gpointer user_data) +{ + NMVPNConnection *vpn = NM_VPN_CONNECTION (user_data); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (vpn); + + if ( (priv->vpn_state != NM_VPN_CONNECTION_STATE_ACTIVATED) + || !nm_device_get_ip4_config (device)) + return; + + if (priv->gw_route) + rtnl_route_put (priv->gw_route); + + /* Re-add the VPN gateway route */ + priv->gw_route = nm_system_add_ip4_vpn_gateway_route (priv->parent_dev, priv->ip4_config); +} + +NMVPNConnection * +nm_vpn_connection_new (NMConnection *connection, + NMActRequest *act_request, + NMDevice *parent_device) +{ + NMVPNConnection *self; + NMVPNConnectionPrivate *priv; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (NM_IS_ACT_REQUEST (act_request), NULL); + g_return_val_if_fail (NM_IS_DEVICE (parent_device), NULL); + + self = (NMVPNConnection *) g_object_new (NM_TYPE_VPN_CONNECTION, NULL); + if (!self) + return NULL; + + priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + + priv->connection = g_object_ref (connection); + priv->parent_dev = g_object_ref (parent_device); + priv->act_request = g_object_ref (act_request); + + priv->device_monitor = g_signal_connect (parent_device, "state-changed", + G_CALLBACK (device_state_changed), + self); + + priv->device_ip4 = g_signal_connect (parent_device, "notify::" NM_DEVICE_INTERFACE_IP4_CONFIG, + G_CALLBACK (device_ip4_config_changed), + self); + return self; +} + +static const char * +nm_vpn_connection_get_service (NMVPNConnection *connection) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + NMSettingVPN *setting; + + setting = (NMSettingVPN *) nm_connection_get_setting (priv->connection, NM_TYPE_SETTING_VPN); + return nm_setting_vpn_get_service_type (setting); +} + +static void +plugin_failed (DBusGProxy *proxy, + NMVPNPluginFailure plugin_failure, + gpointer user_data) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (user_data); + + nm_log_warn (LOGD_VPN, "VPN plugin failed: %d", plugin_failure); + + switch (plugin_failure) { + case NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED: + priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED; + break; + case NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG: + priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID; + break; + default: + priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_UNKNOWN; + } +} + +static void +plugin_state_changed (DBusGProxy *proxy, + NMVPNServiceState state, + gpointer user_data) +{ + NMVPNConnection *connection = NM_VPN_CONNECTION (user_data); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + nm_log_info (LOGD_VPN, "VPN plugin state changed: %d", state); + + if (state == NM_VPN_SERVICE_STATE_STOPPED) { + /* Clear connection secrets to ensure secrets get requested each time the + * connection is activated. + */ + nm_connection_clear_secrets (priv->connection); + + switch (nm_vpn_connection_get_vpn_state (connection)) { + case NM_VPN_CONNECTION_STATE_PREPARE: + case NM_VPN_CONNECTION_STATE_NEED_AUTH: + case NM_VPN_CONNECTION_STATE_CONNECT: + case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET: + case NM_VPN_CONNECTION_STATE_ACTIVATED: + nm_log_info (LOGD_VPN, "VPN plugin state change reason: %d", priv->failure_reason); + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_FAILED, + priv->failure_reason); + + /* Reset the failure reason */ + priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_UNKNOWN; + break; + default: + break; + } + } +} + +static const char * +ip_address_to_string (guint32 numeric) +{ + struct in_addr temp_addr; + static char buf[INET_ADDRSTRLEN + 1]; + + memset (&buf, '\0', sizeof (buf)); + temp_addr.s_addr = numeric; + + if (inet_ntop (AF_INET, &temp_addr, buf, INET_ADDRSTRLEN)) { + return buf; + } else { + nm_log_warn (LOGD_VPN, "error converting IP4 address 0x%X", + ntohl (temp_addr.s_addr)); + return NULL; + } +} + +static void +print_vpn_config (NMIP4Config *config, + guint32 internal_gw, + const char *tundev, + const char *banner) +{ + NMIP4Address *addr; + char *dns_domain = NULL; + guint32 num, i; + + g_return_if_fail (config != NULL); + + addr = nm_ip4_config_get_address (config, 0); + + nm_log_info (LOGD_VPN, "VPN Gateway: %s", ip_address_to_string (nm_ip4_address_get_gateway (addr))); + if (internal_gw) + nm_log_info (LOGD_VPN, "Internal Gateway: %s", ip_address_to_string (internal_gw)); + nm_log_info (LOGD_VPN, "Tunnel Device: %s", tundev); + nm_log_info (LOGD_VPN, "Internal IP4 Address: %s", ip_address_to_string (nm_ip4_address_get_address (addr))); + nm_log_info (LOGD_VPN, "Internal IP4 Prefix: %d", nm_ip4_address_get_prefix (addr)); + nm_log_info (LOGD_VPN, "Internal IP4 Point-to-Point Address: %s", + ip_address_to_string (nm_ip4_config_get_ptp_address (config))); + nm_log_info (LOGD_VPN, "Maximum Segment Size (MSS): %d", nm_ip4_config_get_mss (config)); + + num = nm_ip4_config_get_num_routes (config); + for (i = 0; i < num; i++) { + NMIP4Route *route; + + route = nm_ip4_config_get_route (config, i); + nm_log_info (LOGD_VPN, "Static Route: %s/%d Next Hop: %s", + ip_address_to_string (nm_ip4_route_get_dest (route)), + nm_ip4_route_get_prefix (route), + ip_address_to_string (nm_ip4_route_get_next_hop (route))); + } + + num = nm_ip4_config_get_num_nameservers (config); + for (i = 0; i < num; i++) { + nm_log_info (LOGD_VPN, "Internal IP4 DNS: %s", + ip_address_to_string (nm_ip4_config_get_nameserver (config, i))); + } + + if (nm_ip4_config_get_num_domains (config) > 0) + dns_domain = (char *) nm_ip4_config_get_domain (config, 0); + + nm_log_info (LOGD_VPN, "DNS Domain: '%s'", dns_domain ? dns_domain : "(none)"); + + if (banner && strlen (banner)) { + nm_log_info (LOGD_VPN, "Login Banner:"); + nm_log_info (LOGD_VPN, "-----------------------------------------"); + nm_log_info (LOGD_VPN, "%s", banner); + nm_log_info (LOGD_VPN, "-----------------------------------------"); + } +} + +static void +nm_vpn_connection_ip4_config_get (DBusGProxy *proxy, + GHashTable *config_hash, + gpointer user_data) +{ + NMVPNConnection *connection = NM_VPN_CONNECTION (user_data); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + NMSettingIP4Config *s_ip4; + NMIP4Address *addr; + NMIP4Config *config; + GValue *val; + int i; + guint32 vpn_ext_gw = 0; + + nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) reply received.", + nm_vpn_connection_get_name (connection)); + + g_source_remove (priv->ipconfig_timeout); + priv->ipconfig_timeout = 0; + + config = nm_ip4_config_new (); + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV); + if (val) + priv->tundev = g_strdup (g_value_get_string (val)); + else { + nm_log_err (LOGD_VPN, "invalid or missing tunnel device received!"); + goto error; + } + + addr = nm_ip4_address_new (); + nm_ip4_address_set_prefix (addr, 24); /* default to class C */ + + /* Internal address of the VPN subnet's gateway */ + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY); + if (val) + priv->ip4_internal_gw = g_value_get_uint (val); + + /* External world-visible address of the VPN server */ + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY); + if (val) { + nm_ip4_address_set_gateway (addr, g_value_get_uint (val)); + vpn_ext_gw = g_value_get_uint (val); + } + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS); + if (val) + nm_ip4_address_set_address (addr, g_value_get_uint (val)); + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_PTP); + if (val) + nm_ip4_config_set_ptp_address (config, g_value_get_uint (val)); + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX); + if (val) + nm_ip4_address_set_prefix (addr, g_value_get_uint (val)); + + if (nm_ip4_address_get_address (addr) && nm_ip4_address_get_prefix (addr)) { + nm_ip4_config_take_address (config, addr); + } else { + nm_log_err (LOGD_VPN, "invalid IP4 config received!"); + nm_ip4_address_unref (addr); + goto error; + } + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_DNS); + if (val) { + GArray *dns = (GArray *) g_value_get_boxed (val); + + for (i = 0; i < dns->len; i++) + nm_ip4_config_add_nameserver (config, g_array_index (dns, guint, i)); + } + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_NBNS); + if (val) { + GArray *nbns = (GArray *) g_value_get_boxed (val); + + for (i = 0; i < nbns->len; i++) + nm_ip4_config_add_wins (config, g_array_index (nbns, guint, i)); + } + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_MSS); + if (val) + nm_ip4_config_set_mss (config, g_value_get_uint (val)); + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_MTU); + if (val) + nm_ip4_config_set_mtu (config, g_value_get_uint (val)); + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_DOMAIN); + if (val) + nm_ip4_config_add_domain (config, g_value_get_string (val)); + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_BANNER); + if (val) { + if (priv->banner) + g_free (priv->banner); + priv->banner = g_strdup (g_value_get_string (val)); + } + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES); + if (val) { + GSList *routes; + GSList *iter; + + routes = nm_utils_ip4_routes_from_gvalue (val); + for (iter = routes; iter; iter = iter->next) { + NMIP4Route *route = iter->data; + + /* Ignore host routes to the VPN gateway since NM adds one itself + * below. Since NM knows more about the routing situation than + * the VPN server, we want to use the NM created route instead of + * whatever the server provides. + */ + if ( vpn_ext_gw + && nm_ip4_route_get_dest (route) == vpn_ext_gw + && nm_ip4_route_get_prefix (route) == 32) + continue; + + /* Otherwise accept the VPN-provided route */ + nm_ip4_config_take_route (config, route); + } + + g_slist_free (routes); + } + + print_vpn_config (config, priv->ip4_internal_gw, priv->tundev, priv->banner); + + /* Merge in user overrides from the NMConnection's IPv4 setting */ + s_ip4 = NM_SETTING_IP4_CONFIG (nm_connection_get_setting (priv->connection, NM_TYPE_SETTING_IP4_CONFIG)); + nm_utils_merge_ip4_config (config, s_ip4); + + nm_system_device_set_up_down_with_iface (priv->tundev, TRUE, NULL); + + if (nm_system_apply_ip4_config (priv->tundev, config, 0, NM_IP4_COMPARE_FLAG_ALL)) { + NMNamedManager *named_mgr; + + /* Add any explicit route to the VPN gateway through the parent device */ + priv->gw_route = nm_system_add_ip4_vpn_gateway_route (priv->parent_dev, config); + + /* Add the VPN to DNS */ + named_mgr = nm_named_manager_get (); + nm_named_manager_add_ip4_config (named_mgr, priv->tundev, config, NM_NAMED_IP_CONFIG_TYPE_VPN); + g_object_unref (named_mgr); + + priv->ip4_config = config; + + nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) complete.", + nm_vpn_connection_get_name (connection)); + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_ACTIVATED, + NM_VPN_CONNECTION_STATE_REASON_NONE); + return; + } + +error: + nm_log_warn (LOGD_VPN, "VPN connection '%s' did not receive valid IP config information.", + nm_vpn_connection_get_name (connection)); + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_FAILED, + NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID); + g_object_unref (config); +} + +static gboolean +nm_vpn_connection_ip_config_timeout (gpointer user_data) +{ + NMVPNConnection *connection = NM_VPN_CONNECTION (user_data); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + priv->ipconfig_timeout = 0; + + /* If the activation request's state is still IP_CONFIG_GET and we're + * in this timeout, cancel activation because it's taken too long. + */ + if (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_IP_CONFIG_GET) { + nm_log_warn (LOGD_VPN, "VPN connection '%s' (IP Config Get) timeout exceeded.", + nm_vpn_connection_get_name (connection)); + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_FAILED, + NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT); + } + + return FALSE; +} + +static void +nm_vpn_connection_connect_cb (DBusGProxy *proxy, GError *err, gpointer user_data) +{ + NMVPNConnection *connection = NM_VPN_CONNECTION (user_data); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + nm_log_info (LOGD_VPN, "VPN connection '%s' (Connect) reply received.", + nm_vpn_connection_get_name (connection)); + + if (err) { + nm_log_warn (LOGD_VPN, "VPN connection '%s' failed to connect: '%s'.", + nm_vpn_connection_get_name (connection), err->message); + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_FAILED, + NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED); + } else { + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_IP_CONFIG_GET, + NM_VPN_CONNECTION_STATE_REASON_NONE); + + /* 40 second timeout waiting for IP config signal from VPN service */ + priv->ipconfig_timeout = g_timeout_add_seconds (40, nm_vpn_connection_ip_config_timeout, connection); + } +} + +static void +really_activate (NMVPNConnection *connection) +{ + NMVPNConnectionPrivate *priv; + + g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); + g_return_if_fail (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_NEED_AUTH); + + priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + /* Ip4Config signal */ + dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, G_TYPE_VALUE, G_TYPE_INVALID); + dbus_g_proxy_add_signal (priv->proxy, "Ip4Config", + DBUS_TYPE_G_MAP_OF_VARIANT, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "Ip4Config", + G_CALLBACK (nm_vpn_connection_ip4_config_get), + connection, NULL); + + org_freedesktop_NetworkManager_VPN_Plugin_connect_async (priv->proxy, + nm_connection_to_hash (priv->connection), + nm_vpn_connection_connect_cb, + connection); + + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_CONNECT, + NM_VPN_CONNECTION_STATE_REASON_NONE); +} + +void +nm_vpn_connection_activate (NMVPNConnection *connection) +{ + NMVPNConnectionPrivate *priv; + NMDBusManager *dbus_mgr; + + g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); + g_return_if_fail (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_PREPARE); + + priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + dbus_mgr = nm_dbus_manager_get (); + priv->proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (dbus_mgr), + nm_vpn_connection_get_service (connection), + NM_VPN_DBUS_PLUGIN_PATH, + NM_VPN_DBUS_PLUGIN_INTERFACE); + g_object_unref (dbus_mgr); + + dbus_g_proxy_add_signal (priv->proxy, "Failure", G_TYPE_UINT, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "Failure", + G_CALLBACK (plugin_failed), + connection, NULL); + + /* StateChanged signal */ + dbus_g_proxy_add_signal (priv->proxy, "StateChanged", G_TYPE_UINT, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "StateChanged", + G_CALLBACK (plugin_state_changed), + connection, NULL); + + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_NEED_AUTH, + NM_VPN_CONNECTION_STATE_REASON_NONE); +} + +const char * +nm_vpn_connection_get_active_connection_path (NMVPNConnection *connection) +{ + g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL); + + return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ac_path; +} + +const char * +nm_vpn_connection_get_name (NMVPNConnection *connection) +{ + NMVPNConnectionPrivate *priv; + NMSettingConnection *setting; + + g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL); + + priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + setting = (NMSettingConnection *) nm_connection_get_setting (priv->connection, NM_TYPE_SETTING_CONNECTION); + + return nm_setting_connection_get_id (setting); +} + +NMConnection * +nm_vpn_connection_get_connection (NMVPNConnection *connection) +{ + g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL); + + return NM_VPN_CONNECTION_GET_PRIVATE (connection)->connection; +} + +NMVPNConnectionState +nm_vpn_connection_get_vpn_state (NMVPNConnection *connection) +{ + g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NM_VPN_CONNECTION_STATE_UNKNOWN); + + return NM_VPN_CONNECTION_GET_PRIVATE (connection)->vpn_state; +} + +const char * +nm_vpn_connection_get_banner (NMVPNConnection *connection) +{ + g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL); + + return NM_VPN_CONNECTION_GET_PRIVATE (connection)->banner; +} + +NMIP4Config * +nm_vpn_connection_get_ip4_config (NMVPNConnection *connection) +{ + g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL); + + return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip4_config; +} + +const char * +nm_vpn_connection_get_ip_iface (NMVPNConnection *connection) +{ + g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL); + + return NM_VPN_CONNECTION_GET_PRIVATE (connection)->tundev; +} + +NMDevice * +nm_vpn_connection_get_parent_device (NMVPNConnection *connection) +{ + g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL); + + return NM_VPN_CONNECTION_GET_PRIVATE (connection)->parent_dev; +} + +guint32 +nm_vpn_connection_get_ip4_internal_gateway (NMVPNConnection *connection) +{ + g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), 0); + + return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip4_internal_gw; +} + +void +nm_vpn_connection_fail (NMVPNConnection *connection, + NMVPNConnectionStateReason reason) +{ + g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); + + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_FAILED, + reason); +} + +void +nm_vpn_connection_disconnect (NMVPNConnection *connection, + NMVPNConnectionStateReason reason) +{ + g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); + + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_DISCONNECTED, + reason); +} + +/******************************************************************************/ + +static gboolean +secrets_update_setting (NMSecretsProviderInterface *interface, + const char *setting_name, + GHashTable *new) +{ + NMVPNConnection *self = NM_VPN_CONNECTION (interface); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + GError *error = NULL; + + g_return_val_if_fail (priv->connection != NULL, FALSE); + + if (strcmp (setting_name, NM_SETTING_VPN_SETTING_NAME)) + return FALSE; + + if (!nm_connection_update_secrets (priv->connection, NM_SETTING_VPN_SETTING_NAME, new, &error)) { + nm_log_warn (LOGD_VPN, "Failed to update VPN secrets: %d %s", + error ? error->code : -1, + error && error->message ? error->message : "(none)"); + g_clear_error (&error); + return FALSE; + } + return TRUE; +} + +static void +secrets_result (NMSecretsProviderInterface *interface, + const char *setting_name, + RequestSecretsCaller caller, + const GSList *updated, + GError *error) +{ + NMVPNConnection *self = NM_VPN_CONNECTION (interface); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + + g_return_if_fail (priv->connection != NULL); + g_return_if_fail (caller == SECRETS_CALLER_VPN); + + if (error) + nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); + else + really_activate (self); +} + +static void +secrets_provider_interface_init (NMSecretsProviderInterface *sp_interface_class) +{ + /* interface implementation */ + sp_interface_class->update_setting = secrets_update_setting; + sp_interface_class->result = secrets_result; +} + +static void +connection_need_secrets_cb (DBusGProxy *proxy, + char *setting_name, + GError *error, + gpointer user_data) +{ + NMVPNConnection *self = NM_VPN_CONNECTION (user_data); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + + if (error) { + nm_log_err (LOGD_VPN, "NeedSecrets failed: %s %s", + g_quark_to_string (error->domain), + error->message); + nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); + return; + } + + if (!setting_name || !strlen (setting_name)) { + /* No secrets required */ + really_activate (self); + return; + } + + /* Get the secrets the VPN plugin wants */ + if (!nm_secrets_provider_interface_get_secrets (NM_SECRETS_PROVIDER_INTERFACE (self), + priv->connection, + setting_name, + FALSE, + SECRETS_CALLER_VPN, + NULL, + NULL)) + nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); +} + +static void +call_need_secrets (NMVPNConnection *vpn_connection) +{ + NMVPNConnectionPrivate *priv; + GHashTable *settings; + + priv = NM_VPN_CONNECTION_GET_PRIVATE (vpn_connection); + settings = nm_connection_to_hash (priv->connection); + org_freedesktop_NetworkManager_VPN_Plugin_need_secrets_async (priv->proxy, + settings, + connection_need_secrets_cb, + vpn_connection); + g_hash_table_destroy (settings); +} + +static void +vpn_cleanup (NMVPNConnection *connection) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + if (priv->tundev) { + nm_system_device_set_up_down_with_iface (priv->tundev, FALSE, NULL); + /* FIXME: use AF_UNSPEC here when we have IPv6 support */ + nm_system_device_flush_routes_with_iface (priv->tundev, AF_INET); + nm_system_device_flush_addresses_with_iface (priv->tundev); + } + + if (priv->ip4_config) { + NMIP4Config *parent_config; + NMNamedManager *named_mgr; + + /* Remove attributes of the VPN's IP4 Config */ + named_mgr = nm_named_manager_get (); + nm_named_manager_remove_ip4_config (named_mgr, priv->tundev, priv->ip4_config); + g_object_unref (named_mgr); + + /* Remove any previously added VPN gateway host route */ + if (priv->gw_route) + rtnl_route_del (nm_netlink_get_default_handle (), priv->gw_route, 0); + + /* Reset routes and addresses of the currently active device */ + parent_config = nm_device_get_ip4_config (priv->parent_dev); + if (parent_config) { + if (!nm_system_apply_ip4_config (nm_device_get_ip_iface (priv->parent_dev), + nm_device_get_ip4_config (priv->parent_dev), + nm_device_get_priority (priv->parent_dev), + NM_IP4_COMPARE_FLAG_ADDRESSES | NM_IP4_COMPARE_FLAG_ROUTES)) { + nm_log_err (LOGD_VPN, "failed to re-apply VPN parent device addresses and routes."); + } + } + } + + if (priv->gw_route) { + rtnl_route_put (priv->gw_route); + priv->gw_route = NULL; + } + + if (priv->banner) { + g_free (priv->banner); + priv->banner = NULL; + } + + if (priv->tundev) { + g_free (priv->tundev); + priv->tundev = NULL; + } + + /* Clear out connection secrets to ensure that the settings service + * gets asked for them next time the connection is activated. + */ + if (priv->connection) + nm_connection_clear_secrets (priv->connection); +} + +static void +connection_state_changed (NMVPNConnection *connection, + NMVPNConnectionState state, + NMVPNConnectionStateReason reason) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + nm_secrets_provider_interface_cancel_get_secrets (NM_SECRETS_PROVIDER_INTERFACE (priv->act_request)); + + switch (state) { + case NM_VPN_CONNECTION_STATE_NEED_AUTH: + call_need_secrets (connection); + break; + case NM_VPN_CONNECTION_STATE_DISCONNECTED: + case NM_VPN_CONNECTION_STATE_FAILED: + if (priv->proxy) { + GError *err = NULL; + + org_freedesktop_NetworkManager_VPN_Plugin_disconnect (priv->proxy, &err); + if (err) { + nm_log_warn (LOGD_VPN, "error disconnecting VPN: %s", err->message); + g_error_free (err); + } + + g_object_unref (priv->proxy); + priv->proxy = NULL; + } + vpn_cleanup (connection); + break; + default: + break; + } +} + +static void +nm_vpn_connection_init (NMVPNConnection *connection) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + NMDBusManager *dbus_mgr; + + priv->state = NM_ACTIVE_CONNECTION_STATE_UNKNOWN; + priv->vpn_state = NM_VPN_CONNECTION_STATE_PREPARE; + priv->ac_path = nm_active_connection_get_next_object_path (); + + dbus_mgr = nm_dbus_manager_get (); + dbus_g_connection_register_g_object (nm_dbus_manager_get_connection (dbus_mgr), + priv->ac_path, + G_OBJECT (connection)); + g_object_unref (dbus_mgr); +} + +static void +dispose (GObject *object) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object); + + if (priv->disposed) { + G_OBJECT_CLASS (nm_vpn_connection_parent_class)->dispose (object); + return; + } + priv->disposed = TRUE; + + if (priv->gw_route) + rtnl_route_put (priv->gw_route); + + if (priv->device_ip4) + g_signal_handler_disconnect (priv->parent_dev, priv->device_ip4); + + if (priv->device_monitor) + g_signal_handler_disconnect (priv->parent_dev, priv->device_monitor); + + g_object_unref (priv->parent_dev); + + if (priv->ip4_config) + g_object_unref (priv->ip4_config); + + if (priv->ipconfig_timeout) + g_source_remove (priv->ipconfig_timeout); + + if (priv->proxy) + g_object_unref (priv->proxy); + + g_object_unref (priv->act_request); + g_object_unref (priv->connection); + + G_OBJECT_CLASS (nm_vpn_connection_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object); + + g_free (priv->banner); + g_free (priv->tundev); + g_free (priv->ac_path); + + G_OBJECT_CLASS (nm_vpn_connection_parent_class)->finalize (object); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_SERVICE_NAME: + nm_active_connection_scope_to_value (priv->connection, value); + break; + case PROP_CONNECTION: + g_value_set_boxed (value, nm_connection_get_path (priv->connection)); + break; + case PROP_SPECIFIC_OBJECT: + g_value_set_boxed (value, nm_act_request_get_active_connection_path (priv->act_request)); + break; + case PROP_DEVICES: + g_value_take_boxed (value, g_ptr_array_new ()); + break; + case PROP_STATE: + g_value_set_uint (value, priv->state); + break; + case PROP_DEFAULT: + g_value_set_boolean (value, priv->is_default); + break; + case PROP_DEFAULT6: + g_value_set_boolean (value, priv->is_default6); + break; + case PROP_VPN: + g_value_set_boolean (value, TRUE); + break; + case PROP_VPN_STATE: + g_value_set_uint (value, priv->vpn_state); + break; + case PROP_BANNER: + g_value_set_string (value, priv->banner ? priv->banner : ""); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_vpn_connection_class_init (NMVPNConnectionClass *connection_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (connection_class); + + g_type_class_add_private (connection_class, sizeof (NMVPNConnectionPrivate)); + + /* virtual methods */ + connection_class->vpn_state_changed = connection_state_changed; + object_class->get_property = get_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + + /* properties */ + g_object_class_install_property + (object_class, PROP_SERVICE_NAME, + g_param_spec_string (NM_ACTIVE_CONNECTION_SERVICE_NAME, + "Service name", + "Service name", + NULL, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_CONNECTION, + g_param_spec_boxed (NM_ACTIVE_CONNECTION_CONNECTION, + "Connection", + "Connection", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_SPECIFIC_OBJECT, + g_param_spec_boxed (NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT, + "Specific object", + "Specific object", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_DEVICES, + g_param_spec_boxed (NM_ACTIVE_CONNECTION_DEVICES, + "Devices", + "Devices", + DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_STATE, + g_param_spec_uint (NM_ACTIVE_CONNECTION_STATE, + "State", + "State", + NM_ACTIVE_CONNECTION_STATE_UNKNOWN, + NM_ACTIVE_CONNECTION_STATE_ACTIVATED, + NM_ACTIVE_CONNECTION_STATE_UNKNOWN, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_DEFAULT, + g_param_spec_boolean (NM_ACTIVE_CONNECTION_DEFAULT, + "Default", + "Is the default IPv4 active connection", + FALSE, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_DEFAULT6, + g_param_spec_boolean (NM_ACTIVE_CONNECTION_DEFAULT6, + "Default6", + "Is the default IPv6 active connection", + FALSE, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_VPN, + g_param_spec_boolean (NM_ACTIVE_CONNECTION_VPN, + "VPN", + "Is a VPN connection", + TRUE, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_VPN_STATE, + g_param_spec_uint (NM_VPN_CONNECTION_VPN_STATE, + "VpnState", + "Current VPN state", + NM_VPN_CONNECTION_STATE_UNKNOWN, + NM_VPN_CONNECTION_STATE_DISCONNECTED, + NM_VPN_CONNECTION_STATE_UNKNOWN, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_BANNER, + g_param_spec_string (NM_VPN_CONNECTION_BANNER, + "Banner", + "Login Banner", + NULL, + G_PARAM_READABLE)); + + /* signals */ + signals[VPN_STATE_CHANGED] = + g_signal_new ("vpn-state-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMVPNConnectionClass, vpn_state_changed), + NULL, NULL, + _nm_marshal_VOID__UINT_UINT, + G_TYPE_NONE, 2, + G_TYPE_UINT, G_TYPE_UINT); + + signals[PROPERTIES_CHANGED] = + nm_properties_changed_signal_new (object_class, + G_STRUCT_OFFSET (NMVPNConnectionClass, properties_changed)); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (object_class), + &dbus_glib_nm_vpn_connection_object_info); +} + diff --git a/src/vpn-manager/nm-vpn-connection.h b/src/vpn-manager/nm-vpn-connection.h new file mode 100644 index 00000000..d69674db --- /dev/null +++ b/src/vpn-manager/nm-vpn-connection.h @@ -0,0 +1,78 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2008 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#ifndef NM_VPN_CONNECTION_H +#define NM_VPN_CONNECTION_H + +#include <glib.h> +#include <glib-object.h> +#include "NetworkManagerVPN.h" +#include "nm-device.h" +#include "nm-activation-request.h" +#include "nm-secrets-provider-interface.h" + +#define NM_TYPE_VPN_CONNECTION (nm_vpn_connection_get_type ()) +#define NM_VPN_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_CONNECTION, NMVPNConnection)) +#define NM_VPN_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_VPN_CONNECTION, NMVPNConnectionClass)) +#define NM_IS_VPN_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_CONNECTION)) +#define NM_IS_VPN_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_VPN_CONNECTION)) +#define NM_VPN_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_VPN_CONNECTION, NMVPNConnectionClass)) + +#define NM_VPN_CONNECTION_VPN_STATE "vpn-state" +#define NM_VPN_CONNECTION_BANNER "banner" + +typedef struct { + GObject parent; +} NMVPNConnection; + +typedef struct { + GObjectClass parent; + + /* Signals */ + void (*vpn_state_changed) (NMVPNConnection *connection, + NMVPNConnectionState state, + NMVPNConnectionStateReason reason); + + void (*properties_changed) (NMVPNConnection *connection, GHashTable *properties); +} NMVPNConnectionClass; + +GType nm_vpn_connection_get_type (void); + +NMVPNConnection * nm_vpn_connection_new (NMConnection *connection, + NMActRequest *act_request, + NMDevice *parent_device); + +void nm_vpn_connection_activate (NMVPNConnection *connection); +NMConnection * nm_vpn_connection_get_connection (NMVPNConnection *connection); +const char * nm_vpn_connection_get_active_connection_path (NMVPNConnection *connection); +const char * nm_vpn_connection_get_name (NMVPNConnection *connection); +NMVPNConnectionState nm_vpn_connection_get_vpn_state (NMVPNConnection *connection); +const char * nm_vpn_connection_get_banner (NMVPNConnection *connection); +void nm_vpn_connection_fail (NMVPNConnection *connection, + NMVPNConnectionStateReason reason); +void nm_vpn_connection_disconnect (NMVPNConnection *connection, + NMVPNConnectionStateReason reason); +NMIP4Config * nm_vpn_connection_get_ip4_config (NMVPNConnection *connection); +const char * nm_vpn_connection_get_ip_iface (NMVPNConnection *connection); +NMDevice * nm_vpn_connection_get_parent_device (NMVPNConnection *connection); +guint32 nm_vpn_connection_get_ip4_internal_gateway (NMVPNConnection *connection); + +#endif /* NM_VPN_CONNECTION_H */ diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c new file mode 100644 index 00000000..221a8b54 --- /dev/null +++ b/src/vpn-manager/nm-vpn-manager.c @@ -0,0 +1,369 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2008 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#include <string.h> + +#include "nm-vpn-manager.h" +#include "nm-vpn-service.h" +#include "nm-vpn-connection.h" +#include "nm-setting-vpn.h" +#include "nm-dbus-manager.h" +#include "NetworkManagerVPN.h" +#include "nm-marshal.h" + +G_DEFINE_TYPE (NMVPNManager, nm_vpn_manager, G_TYPE_OBJECT) + +#define NM_VPN_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_MANAGER, NMVPNManagerPrivate)) + +typedef struct { + GSList *services; +} NMVPNManagerPrivate; + +enum { + CONNECTION_ACTIVATED, + CONNECTION_DEACTIVATED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +GQuark +nm_vpn_manager_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("nm-vpn-manager-error"); + return quark; +} + +/* This should really be standard. */ +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +GType +nm_vpn_manager_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + /* The base device for the VPN connection is not active. */ + ENUM_ENTRY (NM_VPN_MANAGER_ERROR_DEVICE_NOT_ACTIVE, "BaseDeviceNotActive"), + /* The requested VPN connection was invalid. */ + ENUM_ENTRY (NM_VPN_MANAGER_ERROR_CONNECTION_INVALID, "ConnectionInvalid"), + /* The VPN service required by this VPN connection did not exist or was invalid. */ + ENUM_ENTRY (NM_VPN_MANAGER_ERROR_SERVICE_INVALID, "ServiceInvalid"), + /* The VPN service required by this VPN connection could not be started. */ + ENUM_ENTRY (NM_VPN_MANAGER_ERROR_SERVICE_START_FAILED, "ServiceStartFailed"), + { 0, 0, 0 } + }; + etype = g_enum_register_static ("NMVPNManagerError", values); + } + return etype; +} + + + +static NMVPNService * +nm_vpn_manager_get_service (NMVPNManager *manager, const char *service_name) +{ + GSList *iter; + + for (iter = NM_VPN_MANAGER_GET_PRIVATE (manager)->services; iter; iter = iter->next) { + NMVPNService *service = NM_VPN_SERVICE (iter->data); + + if (!strcmp (service_name, nm_vpn_service_get_name (service))) + return g_object_ref (service); + } + + return NULL; +} + +static void +remove_service (gpointer data, GObject *service) +{ + NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (data); + + priv->services = g_slist_remove (priv->services, service); +} + +static void +nm_vpn_manager_add_service (NMVPNManager *manager, NMVPNService *service) +{ + NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (manager); + + priv->services = g_slist_prepend (priv->services, service); + g_object_weak_ref (G_OBJECT (service), remove_service, manager); +} + +static NMVPNConnection * +find_active_vpn_connection_by_connection (NMVPNManager *manager, NMConnection *connection) +{ + NMVPNManagerPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL); + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + priv = NM_VPN_MANAGER_GET_PRIVATE (manager); + for (iter = priv->services; iter; iter = g_slist_next (iter)) { + GSList *connections, *elt; + + connections = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data)); + for (elt = connections; elt; elt = g_slist_next (elt)) { + NMVPNConnection *vpn = NM_VPN_CONNECTION (elt->data); + + if (nm_vpn_connection_get_connection (vpn) == connection) + return vpn; + } + } + return NULL; +} + +static void +connection_vpn_state_changed (NMVPNConnection *connection, + NMVPNConnectionState state, + NMVPNConnectionStateReason reason, + gpointer user_data) +{ + NMVPNManager *manager = NM_VPN_MANAGER (user_data); + + switch (state) { + case NM_VPN_CONNECTION_STATE_ACTIVATED: + g_signal_emit (manager, signals[CONNECTION_ACTIVATED], 0, connection); + break; + case NM_VPN_CONNECTION_STATE_FAILED: + case NM_VPN_CONNECTION_STATE_DISCONNECTED: + g_signal_emit (manager, signals[CONNECTION_DEACTIVATED], 0, connection, state, reason); + break; + default: + break; + } +} + +NMVPNConnection * +nm_vpn_manager_activate_connection (NMVPNManager *manager, + NMConnection *connection, + NMActRequest *act_request, + NMDevice *device, + GError **error) +{ + NMSettingVPN *vpn_setting; + NMVPNService *service; + NMVPNConnection *vpn = NULL; + const char *service_type; + + g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL); + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (NM_IS_ACT_REQUEST (act_request), NULL); + g_return_val_if_fail (NM_IS_DEVICE (device), NULL); + g_return_val_if_fail (error != NULL, NULL); + g_return_val_if_fail (*error == NULL, NULL); + + if (nm_device_get_state (device) != NM_DEVICE_STATE_ACTIVATED) { + g_set_error (error, + NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_DEVICE_NOT_ACTIVE, + "%s", "The base device for the VPN connection was not active."); + return NULL; + } + + vpn_setting = (NMSettingVPN *) nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN); + if (!vpn_setting) { + g_set_error (error, + NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_CONNECTION_INVALID, + "%s", "The connection was not a VPN connection."); + return NULL; + } + + vpn = find_active_vpn_connection_by_connection (manager, connection); + if (vpn) { + nm_vpn_connection_disconnect (vpn, NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED); + vpn = NULL; + } + + service_type = nm_setting_vpn_get_service_type (vpn_setting); + service = nm_vpn_manager_get_service (manager, service_type); + if (!service) { + service = nm_vpn_service_new (service_type); + if (service) + nm_vpn_manager_add_service (manager, service); + } + + if (service) { + vpn = nm_vpn_service_activate (service, connection, act_request, device, error); + if (vpn) { + g_signal_connect (vpn, "vpn-state-changed", + G_CALLBACK (connection_vpn_state_changed), + manager); + } + } else { + g_set_error (error, + NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_SERVICE_INVALID, + "%s", "The VPN service was invalid."); + } + + return vpn; +} + +gboolean +nm_vpn_manager_deactivate_connection (NMVPNManager *manager, + const char *path, + NMVPNConnectionStateReason reason) +{ + NMVPNManagerPrivate *priv; + GSList *iter; + gboolean found = FALSE; + + g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + priv = NM_VPN_MANAGER_GET_PRIVATE (manager); + for (iter = priv->services; iter; iter = g_slist_next (iter)) { + GSList *connections, *elt; + + connections = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data)); + for (elt = connections; elt; elt = g_slist_next (elt)) { + NMVPNConnection *vpn = NM_VPN_CONNECTION (elt->data); + const char *vpn_path; + + vpn_path = nm_vpn_connection_get_active_connection_path (vpn); + if (!strcmp (path, vpn_path)) { + nm_vpn_connection_disconnect (vpn, reason); + found = TRUE; + } + } + } + + return found ? TRUE : FALSE; +} + +void +nm_vpn_manager_add_active_connections (NMVPNManager *manager, + NMConnection *filter, + GPtrArray *array) +{ + NMVPNManagerPrivate *priv; + GSList *iter; + + g_return_if_fail (NM_IS_VPN_MANAGER (manager)); + g_return_if_fail (array != NULL); + + priv = NM_VPN_MANAGER_GET_PRIVATE (manager); + for (iter = priv->services; iter; iter = g_slist_next (iter)) { + GSList *active, *elt; + + active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data)); + for (elt = active; elt; elt = g_slist_next (elt)) { + NMVPNConnection *vpn = NM_VPN_CONNECTION (elt->data); + const char *path; + + if (!filter || (nm_vpn_connection_get_connection (vpn) == filter)) { + path = nm_vpn_connection_get_active_connection_path (vpn); + g_ptr_array_add (array, g_strdup (path)); + } + } + } +} + +GSList * +nm_vpn_manager_get_active_connections (NMVPNManager *manager) +{ + NMVPNManagerPrivate *priv; + GSList *iter; + GSList *list = NULL; + + g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL); + + priv = NM_VPN_MANAGER_GET_PRIVATE (manager); + for (iter = priv->services; iter; iter = g_slist_next (iter)) { + GSList *active, *elt; + + active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data)); + for (elt = active; elt; elt = g_slist_next (elt)) + list = g_slist_append (list, g_object_ref (NM_VPN_CONNECTION (elt->data))); + } + + return list; +} + +NMVPNManager * +nm_vpn_manager_get (void) +{ + static NMVPNManager *singleton = NULL; + + if (!singleton) + singleton = NM_VPN_MANAGER (g_object_new (NM_TYPE_VPN_MANAGER, NULL)); + else + g_object_ref (singleton); + + g_assert (singleton); + return singleton; +} + +/******************************************************************************/ + +static void +nm_vpn_manager_init (NMVPNManager *manager) +{ +} + +static void +finalize (GObject *object) +{ + NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (object); + + g_slist_foreach (priv->services, (GFunc) g_object_unref, NULL); + + G_OBJECT_CLASS (nm_vpn_manager_parent_class)->finalize (object); +} + +static void +nm_vpn_manager_class_init (NMVPNManagerClass *manager_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (manager_class); + + g_type_class_add_private (manager_class, sizeof (NMVPNManagerPrivate)); + + /* virtual methods */ + object_class->finalize = finalize; + + /* signals */ + signals[CONNECTION_ACTIVATED] = + g_signal_new ("connection-activated", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, G_TYPE_OBJECT); + + signals[CONNECTION_DEACTIVATED] = + g_signal_new ("connection-deactivated", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMVPNManagerClass, connection_deactivated), + NULL, NULL, + _nm_marshal_VOID__OBJECT_UINT_UINT, + G_TYPE_NONE, 3, + G_TYPE_OBJECT, G_TYPE_UINT, G_TYPE_UINT); + + dbus_g_error_domain_register (NM_VPN_MANAGER_ERROR, NULL, NM_TYPE_VPN_MANAGER_ERROR); +} + diff --git a/src/vpn-manager/nm-vpn-manager.h b/src/vpn-manager/nm-vpn-manager.h new file mode 100644 index 00000000..d07aa250 --- /dev/null +++ b/src/vpn-manager/nm-vpn-manager.h @@ -0,0 +1,86 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2008 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#ifndef NM_VPN_MANAGER_H +#define NM_VPN_MANAGER_H + +#include <glib.h> +#include <glib-object.h> +#include "nm-vpn-connection.h" +#include "nm-activation-request.h" + +#define NM_TYPE_VPN_MANAGER (nm_vpn_manager_get_type ()) +#define NM_VPN_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_MANAGER, NMVPNManager)) +#define NM_VPN_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_VPN_MANAGER, NMVPNManagerClass)) +#define NM_IS_VPN_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_MANAGER)) +#define NM_IS_VPN_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_VPN_MANAGER)) +#define NM_VPN_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_VPN_MANAGER, NMVPNManagerClass)) + +typedef enum +{ + NM_VPN_MANAGER_ERROR_DEVICE_NOT_ACTIVE = 0, + NM_VPN_MANAGER_ERROR_CONNECTION_INVALID, + NM_VPN_MANAGER_ERROR_SERVICE_INVALID, + NM_VPN_MANAGER_ERROR_SERVICE_START_FAILED, +} NMVPNManagerError; + +#define NM_VPN_MANAGER_ERROR (nm_vpn_manager_error_quark ()) +#define NM_TYPE_VPN_MANAGER_ERROR (nm_vpn_manager_error_get_type ()) + +GQuark nm_vpn_manager_error_quark (void); +GType nm_vpn_manager_error_get_type (void); + + +typedef struct { + GObject parent; +} NMVPNManager; + +typedef struct { + GObjectClass parent; + + /* Signals */ + void (*connection_deactivated) (NMVPNManager *manager, + NMVPNConnection *connection, + NMVPNConnectionState state, + NMVPNConnectionStateReason reason); +} NMVPNManagerClass; + +GType nm_vpn_manager_get_type (void); + +NMVPNManager *nm_vpn_manager_get (void); + +NMVPNConnection *nm_vpn_manager_activate_connection (NMVPNManager *manager, + NMConnection *connection, + NMActRequest *act_request, + NMDevice *device, + GError **error); + +gboolean nm_vpn_manager_deactivate_connection (NMVPNManager *manager, + const char *path, + NMVPNConnectionStateReason reason); + +void nm_vpn_manager_add_active_connections (NMVPNManager *manager, + NMConnection *filter, + GPtrArray *list); + +GSList *nm_vpn_manager_get_active_connections (NMVPNManager *manager); + +#endif /* NM_VPN_VPN_MANAGER_H */ diff --git a/src/vpn-manager/nm-vpn-service.c b/src/vpn-manager/nm-vpn-service.c new file mode 100644 index 00000000..bdbb3774 --- /dev/null +++ b/src/vpn-manager/nm-vpn-service.c @@ -0,0 +1,478 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2008 Red Hat, Inc. + * Copyright (C) 2005 - 2008 Novell, Inc. + */ + +#include <glib.h> +#include <string.h> +#include <dbus/dbus.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <unistd.h> + +#include "nm-vpn-service.h" +#include "nm-dbus-manager.h" +#include "nm-logging.h" +#include "nm-vpn-manager.h" +#include "nm-glib-compat.h" + +G_DEFINE_TYPE (NMVPNService, nm_vpn_service, G_TYPE_OBJECT) + +typedef struct { + NMDBusManager *dbus_mgr; + char *name; + char *dbus_service; + char *program; + + GPid pid; + GSList *connections; + guint service_start_timeout; + guint service_child_watch; + gulong name_owner_id; +} NMVPNServicePrivate; + +#define NM_VPN_SERVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_SERVICE, NMVPNServicePrivate)) + +#define VPN_CONNECTION_GROUP "VPN Connection" + +static GKeyFile * +find_service_file (const char *name) +{ + GDir *dir; + const char *fn; + GKeyFile *key_file = NULL; + + dir = g_dir_open (VPN_NAME_FILES_DIR, 0, NULL); + if (!dir) + return NULL; + + while ((fn = g_dir_read_name (dir))) { + char *path; + gboolean found = FALSE; + + /* only parse filenames that end with .name */ + if (!g_str_has_suffix (fn, ".name")) + continue; + + key_file = g_key_file_new (); + path = g_build_filename (VPN_NAME_FILES_DIR, fn, NULL); + + if (g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, NULL)) { + gchar *val; + + val = g_key_file_get_string (key_file, VPN_CONNECTION_GROUP, "service", NULL); + if (val) { + if (!strcmp (val, name)) + found = TRUE; + g_free (val); + } + } + + g_free (path); + + if (found) + break; + + g_key_file_free (key_file); + key_file = NULL; + } + + g_dir_close (dir); + + return key_file; +} + +NMVPNService * +nm_vpn_service_new (const char *name) +{ + GKeyFile *key_file; + NMVPNService *service = NULL; + NMVPNServicePrivate *priv; + char *dbus_service = NULL; + char *program = NULL; + gboolean success = FALSE; + + g_return_val_if_fail (name != NULL, NULL); + + key_file = find_service_file (name); + if (!key_file) + return NULL; + + dbus_service = g_key_file_get_string (key_file, VPN_CONNECTION_GROUP, "service", NULL); + if (!dbus_service) + goto out; + + program = g_key_file_get_string (key_file, VPN_CONNECTION_GROUP, "program", NULL); + if (!program) + goto out; + + service = (NMVPNService *) g_object_new (NM_TYPE_VPN_SERVICE, NULL); + if (!service) + goto out; + + priv = NM_VPN_SERVICE_GET_PRIVATE (service); + + priv->name = g_strdup (name); + priv->dbus_service = dbus_service; + priv->program = program; + + success = TRUE; + + out: + g_key_file_free (key_file); + + if (!success) { + g_free (dbus_service); + g_free (program); + } + + return service; +} + +const char * +nm_vpn_service_get_name (NMVPNService *service) +{ + g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL); + + return NM_VPN_SERVICE_GET_PRIVATE (service)->name; +} + +static void +nm_vpn_service_connections_stop (NMVPNService *service, + gboolean fail, + NMVPNConnectionStateReason reason) +{ + NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); + GSList *iter, *copy; + + /* Copy because stopping the connection may remove it from the list + * in the the NMVPNService objects' VPN connection state handler. + */ + copy = g_slist_copy (priv->connections); + for (iter = copy; iter; iter = iter->next) { + if (fail) + nm_vpn_connection_fail (NM_VPN_CONNECTION (iter->data), reason); + else + nm_vpn_connection_disconnect (NM_VPN_CONNECTION (iter->data), reason); + } + g_slist_free (copy); +} + +/* + * nm_vpn_service_child_setup + * + * Set the process group ID of the newly forked process + * + */ +static void +nm_vpn_service_child_setup (gpointer user_data G_GNUC_UNUSED) +{ + /* We are in the child process at this point */ + pid_t pid = getpid (); + setpgid (pid, pid); +} + +static void +vpn_service_watch_cb (GPid pid, gint status, gpointer user_data) +{ + NMVPNService *service = NM_VPN_SERVICE (user_data); + NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); + + if (WIFEXITED (status)) { + guint err = WEXITSTATUS (status); + + if (err != 0) { + nm_log_warn (LOGD_VPN, "VPN service '%s' exited with error: %d", + nm_vpn_service_get_name (service), WSTOPSIG (status)); + } + } else if (WIFSTOPPED (status)) { + nm_log_warn (LOGD_VPN, "VPN service '%s' stopped unexpectedly with signal %d", + nm_vpn_service_get_name (service), WSTOPSIG (status)); + } else if (WIFSIGNALED (status)) { + nm_log_warn (LOGD_VPN, "VPN service '%s' died with signal %d", + nm_vpn_service_get_name (service), WTERMSIG (status)); + } else { + nm_log_warn (LOGD_VPN, "VPN service '%s' died from an unknown cause", + nm_vpn_service_get_name (service)); + } + + priv->pid = 0; + priv->service_child_watch = 0; + + nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED); +} + +static gboolean +nm_vpn_service_timeout (gpointer data) +{ + NMVPNService *service = NM_VPN_SERVICE (data); + + nm_log_warn (LOGD_VPN, "VPN service '%s' did not start in time, cancelling connections", + nm_vpn_service_get_name (service)); + + NM_VPN_SERVICE_GET_PRIVATE (service)->service_start_timeout = 0; + nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT); + + return FALSE; +} + +static gboolean +nm_vpn_service_daemon_exec (NMVPNService *service, GError **error) +{ + NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); + char *vpn_argv[2]; + gboolean success = FALSE; + GError *spawn_error = NULL; + + g_return_val_if_fail (NM_IS_VPN_SERVICE (service), FALSE); + g_return_val_if_fail (error != NULL, FALSE); + g_return_val_if_fail (*error == NULL, FALSE); + + vpn_argv[0] = priv->program; + vpn_argv[1] = NULL; + + success = g_spawn_async (NULL, vpn_argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, + nm_vpn_service_child_setup, NULL, &priv->pid, + &spawn_error); + if (success) { + nm_log_info (LOGD_VPN, "VPN service '%s' started (%s), PID %d", + nm_vpn_service_get_name (service), priv->dbus_service, priv->pid); + + priv->service_child_watch = g_child_watch_add (priv->pid, vpn_service_watch_cb, service); + priv->service_start_timeout = g_timeout_add_seconds (5, nm_vpn_service_timeout, service); + } else { + nm_log_warn (LOGD_VPN, "VPN service '%s': could not launch the VPN service. error: (%d) %s.", + nm_vpn_service_get_name (service), spawn_error->code, spawn_error->message); + + g_set_error (error, + NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_SERVICE_START_FAILED, + "%s", spawn_error ? spawn_error->message : "unknown g_spawn_async() error"); + + nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED); + if (spawn_error) + g_error_free (spawn_error); + } + + return success; +} + +static gboolean +destroy_service (gpointer data) +{ + g_object_unref (data); + + return FALSE; +} + +static void +connection_vpn_state_changed (NMVPNConnection *connection, + NMVPNConnectionState state, + NMVPNConnectionStateReason reason, + gpointer user_data) +{ + NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (user_data); + + switch (state) { + case NM_VPN_CONNECTION_STATE_FAILED: + case NM_VPN_CONNECTION_STATE_DISCONNECTED: + /* Remove the connection from our list */ + priv->connections = g_slist_remove (priv->connections, connection); + g_object_unref (connection); + + if (priv->connections == NULL) { + /* schedule a timeout (10 seconds) to destroy the service */ + g_timeout_add_seconds (10, destroy_service, user_data); + } + break; + default: + break; + } +} + +NMVPNConnection * +nm_vpn_service_activate (NMVPNService *service, + NMConnection *connection, + NMActRequest *act_request, + NMDevice *device, + GError **error) +{ + NMVPNConnection *vpn; + NMVPNServicePrivate *priv; + + g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL); + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (NM_IS_ACT_REQUEST (act_request), NULL); + g_return_val_if_fail (NM_IS_DEVICE (device), NULL); + g_return_val_if_fail (error != NULL, NULL); + g_return_val_if_fail (*error == NULL, NULL); + + priv = NM_VPN_SERVICE_GET_PRIVATE (service); + + vpn = nm_vpn_connection_new (connection, act_request, device); + g_signal_connect (vpn, "vpn-state-changed", + G_CALLBACK (connection_vpn_state_changed), + service); + + priv->connections = g_slist_prepend (priv->connections, vpn); + + if (nm_dbus_manager_name_has_owner (priv->dbus_mgr, priv->dbus_service)) { + // FIXME: fill in error when errors happen + nm_vpn_connection_activate (vpn); + } else if (priv->service_start_timeout == 0) { + nm_log_info (LOGD_VPN, "Starting VPN service '%s'...", + nm_vpn_service_get_name (service)); + if (!nm_vpn_service_daemon_exec (service, error)) + vpn = NULL; + } + + return vpn; +} + +GSList * +nm_vpn_service_get_active_connections (NMVPNService *service) +{ + g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL); + + return g_slist_copy (NM_VPN_SERVICE_GET_PRIVATE (service)->connections); +} + +static void +nm_vpn_service_name_owner_changed (NMDBusManager *mgr, + const char *name, + const char *old, + const char *new, + gpointer user_data) +{ + NMVPNService *service = NM_VPN_SERVICE (user_data); + NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); + gboolean old_owner_good; + gboolean new_owner_good; + + if (strcmp (name, priv->dbus_service)) + return; + + /* Service changed, no need to wait for the timeout any longer */ + if (priv->service_start_timeout) { + g_source_remove (priv->service_start_timeout); + priv->service_start_timeout = 0; + } + + old_owner_good = (old && (strlen (old) > 0)); + new_owner_good = (new && (strlen (new) > 0)); + + if (!old_owner_good && new_owner_good) { + /* service just appeared */ + GSList *iter; + + nm_log_info (LOGD_VPN, "VPN service '%s' appeared, activating connections", + nm_vpn_service_get_name (service)); + + for (iter = priv->connections; iter; iter = iter->next) + nm_vpn_connection_activate (NM_VPN_CONNECTION (iter->data)); + + } else if (old_owner_good && !new_owner_good) { + /* service went away */ + nm_log_info (LOGD_VPN, "VPN service '%s' disappeared, cancelling connections", + nm_vpn_service_get_name (service)); + nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED); + } +} + +/******************************************************************************/ + +static void +nm_vpn_service_init (NMVPNService *service) +{ + NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); + + priv->dbus_mgr = nm_dbus_manager_get (); + + priv->name_owner_id = g_signal_connect (priv->dbus_mgr, "name-owner-changed", + G_CALLBACK (nm_vpn_service_name_owner_changed), + service); +} + +static gboolean +ensure_killed (gpointer data) +{ + int pid = GPOINTER_TO_INT (data); + + if (kill (pid, 0) == 0) + kill (pid, SIGKILL); + + /* ensure the child is reaped */ + nm_log_dbg (LOGD_VPN, "waiting for VPN service pid %d to exit", pid); + waitpid (pid, NULL, 0); + nm_log_dbg (LOGD_VPN, "VPN service pid %d cleaned up", pid); + + return FALSE; +} + +static void +finalize (GObject *object) +{ + NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (object); + + if (priv->service_start_timeout) + g_source_remove (priv->service_start_timeout); + + nm_vpn_service_connections_stop (NM_VPN_SERVICE (object), + FALSE, + NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED); + + g_signal_handler_disconnect (priv->dbus_mgr, priv->name_owner_id); + + if (priv->service_child_watch) + g_source_remove (priv->service_child_watch); + + if (priv->pid) { + if (kill (priv->pid, SIGTERM) == 0) + g_timeout_add_seconds (2, ensure_killed, GINT_TO_POINTER (priv->pid)); + else { + kill (priv->pid, SIGKILL); + + /* ensure the child is reaped */ + nm_log_dbg (LOGD_VPN, "waiting for VPN service pid %d to exit", priv->pid); + waitpid (priv->pid, NULL, 0); + nm_log_dbg (LOGD_VPN, "VPN service pid %d cleaned up", priv->pid); + } + + priv->pid = 0; + } + + g_object_unref (priv->dbus_mgr); + + g_free (priv->name); + g_free (priv->dbus_service); + g_free (priv->program); + + G_OBJECT_CLASS (nm_vpn_service_parent_class)->finalize (object); +} + +static void +nm_vpn_service_class_init (NMVPNServiceClass *service_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (service_class); + + g_type_class_add_private (service_class, sizeof (NMVPNServicePrivate)); + + /* virtual methods */ + object_class->finalize = finalize; +} diff --git a/src/vpn-manager/nm-vpn-service.h b/src/vpn-manager/nm-vpn-service.h new file mode 100644 index 00000000..9c5e0eef --- /dev/null +++ b/src/vpn-manager/nm-vpn-service.h @@ -0,0 +1,60 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2008 Red Hat, Inc. + * Copyright (C) 2005 - 2008 Novell, Inc. + */ + +#ifndef NM_VPN_SERVICE_H +#define NM_VPN_SERVICE_H + +#include <glib.h> +#include <glib-object.h> +#include "nm-device.h" +#include "nm-vpn-connection.h" +#include "nm-activation-request.h" + +#define NM_TYPE_VPN_SERVICE (nm_vpn_service_get_type ()) +#define NM_VPN_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_SERVICE, NMVPNService)) +#define NM_VPN_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_VPN_SERVICE, NMVPNServiceClass)) +#define NM_IS_VPN_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_SERVICE)) +#define NM_IS_VPN_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_VPN_SERVICE)) +#define NM_VPN_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_VPN_SERVICE, NMVPNServiceClass)) + +typedef struct { + GObject parent; +} NMVPNService; + +typedef struct { + GObjectClass parent; +} NMVPNServiceClass; + +GType nm_vpn_service_get_type (void); + +NMVPNService * nm_vpn_service_new (const char *service_name); + +const char * nm_vpn_service_get_name (NMVPNService *service); + +NMVPNConnection * nm_vpn_service_activate (NMVPNService *service, + NMConnection *connection, + NMActRequest *act_request, + NMDevice *device, + GError **error); + +GSList * nm_vpn_service_get_active_connections (NMVPNService *service); + +#endif /* NM_VPN_VPN_SERVICE_H */ |