diff options
| author | Yves-Alexis Perez <corsac@debian.org> | 2013-10-17 21:23:38 +0200 |
|---|---|---|
| committer | Yves-Alexis Perez <corsac@debian.org> | 2013-10-17 21:23:38 +0200 |
| commit | 9d37ad77ef660b92ea51b69d74e14f931d2a04e2 (patch) | |
| tree | d6bbb4a5fed1959f8675df9ee7c03713b543fcc9 /src/libhydra/plugins/kernel_netlink | |
| parent | 104f57d4b0fb6d7547d6898352eaa5fb4b222010 (diff) | |
| parent | e5ee4e7fcdd58b7d86bf1b458da2c63e8e19627b (diff) | |
| download | vyos-strongswan-9d37ad77ef660b92ea51b69d74e14f931d2a04e2.tar.gz vyos-strongswan-9d37ad77ef660b92ea51b69d74e14f931d2a04e2.zip | |
Merge tag 'v5.1.0-1' into sid
tag strongSwan 5.1.0-1
Diffstat (limited to 'src/libhydra/plugins/kernel_netlink')
7 files changed, 1810 insertions, 1018 deletions
diff --git a/src/libhydra/plugins/kernel_netlink/Makefile.am b/src/libhydra/plugins/kernel_netlink/Makefile.am index 1ad379421..ad573523e 100644 --- a/src/libhydra/plugins/kernel_netlink/Makefile.am +++ b/src/libhydra/plugins/kernel_netlink/Makefile.am @@ -1,10 +1,12 @@ +AM_CPPFLAGS = \ + -I${linux_headers} \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libhydra \ + -DROUTING_TABLE=${routing_table} \ + -DROUTING_TABLE_PRIO=${routing_table_prio} -INCLUDES = -I${linux_headers} -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra - -AM_CFLAGS = -rdynamic \ --DROUTING_TABLE=${routing_table} \ --DROUTING_TABLE_PRIO=${routing_table_prio} +AM_CFLAGS = \ + -rdynamic if MONOLITHIC noinst_LTLIBRARIES = libstrongswan-kernel-netlink.la diff --git a/src/libhydra/plugins/kernel_netlink/Makefile.in b/src/libhydra/plugins/kernel_netlink/Makefile.in index 73dbdd0e3..9cb988c8d 100644 --- a/src/libhydra/plugins/kernel_netlink/Makefile.in +++ b/src/libhydra/plugins/kernel_netlink/Makefile.in @@ -1,9 +1,9 @@ -# Makefile.in generated by automake 1.11.1 from Makefile.am. +# Makefile.in generated by automake 1.11.6 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. +# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 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. @@ -16,6 +16,23 @@ @SET_MAKE@ VPATH = @srcdir@ +am__make_dryrun = \ + { \ + am__dry=no; \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \ + | grep '^AM OK$$' >/dev/null || am__dry=yes;; \ + *) \ + for am__flg in $$MAKEFLAGS; do \ + case $$am__flg in \ + *=*|--*) ;; \ + *n*) am__dry=yes; break;; \ + esac; \ + done;; \ + esac; \ + test $$am__dry = yes; \ + } pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -45,10 +62,11 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/config/libtool.m4 \ $(top_srcdir)/m4/macros/with.m4 \ $(top_srcdir)/m4/macros/enable-disable.m4 \ $(top_srcdir)/m4/macros/add-plugin.m4 \ - $(top_srcdir)/configure.in + $(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 = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; @@ -72,6 +90,12 @@ am__nobase_list = $(am__nobase_strip_setup); \ am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } am__installdirs = "$(DESTDIR)$(plugindir)" LTLIBRARIES = $(noinst_LTLIBRARIES) $(plugin_LTLIBRARIES) libstrongswan_kernel_netlink_la_LIBADD = @@ -80,49 +104,77 @@ am_libstrongswan_kernel_netlink_la_OBJECTS = kernel_netlink_plugin.lo \ kernel_netlink_shared.lo libstrongswan_kernel_netlink_la_OBJECTS = \ $(am_libstrongswan_kernel_netlink_la_OBJECTS) -libstrongswan_kernel_netlink_la_LINK = $(LIBTOOL) --tag=CC \ +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +libstrongswan_kernel_netlink_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) \ $(libstrongswan_kernel_netlink_la_LDFLAGS) $(LDFLAGS) -o $@ @MONOLITHIC_FALSE@am_libstrongswan_kernel_netlink_la_rpath = -rpath \ @MONOLITHIC_FALSE@ $(plugindir) @MONOLITHIC_TRUE@am_libstrongswan_kernel_netlink_la_rpath = -DEFAULT_INCLUDES = -I.@am__isrc@ +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) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ CCLD = $(CC) -LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ - --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ - $(LDFLAGS) -o $@ +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; SOURCES = $(libstrongswan_kernel_netlink_la_SOURCES) DIST_SOURCES = $(libstrongswan_kernel_netlink_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ALLOCA = @ALLOCA@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ +BFDLIB = @BFDLIB@ BTLIB = @BTLIB@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ +CHECK_CFLAGS = @CHECK_CFLAGS@ +CHECK_LIBS = @CHECK_LIBS@ +COVERAGE_CFLAGS = @COVERAGE_CFLAGS@ +COVERAGE_LDFLAGS = @COVERAGE_LDFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLIB = @DLLIB@ +DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ @@ -131,13 +183,16 @@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ +GENHTML = @GENHTML@ GPERF = @GPERF@ +GPRBUILD = @GPRBUILD@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LCOV = @LCOV@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ @@ -150,6 +205,7 @@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MYSQLCFLAG = @MYSQLCFLAG@ MYSQLCONFIG = @MYSQLCONFIG@ @@ -177,11 +233,13 @@ RANLIB = @RANLIB@ RTLIB = @RTLIB@ RUBY = @RUBY@ RUBYINCLUDE = @RUBYINCLUDE@ +RUBYLIB = @RUBYLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOCKLIB = @SOCKLIB@ STRIP = @STRIP@ +UNWINDLIB = @UNWINDLIB@ VERSION = @VERSION@ YACC = @YACC@ YFLAGS = @YFLAGS@ @@ -189,6 +247,7 @@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ @@ -197,8 +256,6 @@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ attest_plugins = @attest_plugins@ -axis2c_CFLAGS = @axis2c_CFLAGS@ -axis2c_LIBS = @axis2c_LIBS@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ @@ -207,14 +264,19 @@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ c_plugins = @c_plugins@ +charon_natt_port = @charon_natt_port@ +charon_plugins = @charon_plugins@ +charon_udp_port = @charon_udp_port@ clearsilver_LIBS = @clearsilver_LIBS@ +cmd_plugins = @cmd_plugins@ datadir = @datadir@ datarootdir = @datarootdir@ dbusservicedir = @dbusservicedir@ -default_pkcs11 = @default_pkcs11@ +dev_headers = @dev_headers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ +fips_mode = @fips_mode@ gtk_CFLAGS = @gtk_CFLAGS@ gtk_LIBS = @gtk_LIBS@ h_plugins = @h_plugins@ @@ -228,17 +290,17 @@ imcvdir = @imcvdir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ +ipsec_script = @ipsec_script@ +ipsec_script_upper = @ipsec_script_upper@ ipsecdir = @ipsecdir@ ipsecgroup = @ipsecgroup@ ipseclibdir = @ipseclibdir@ ipsecuser = @ipsecuser@ -libcharon_plugins = @libcharon_plugins@ libdir = @libdir@ libexecdir = @libexecdir@ linux_headers = @linux_headers@ localedir = @localedir@ localstatedir = @localstatedir@ -lt_ECHO = @lt_ECHO@ maemo_CFLAGS = @maemo_CFLAGS@ maemo_LIBS = @maemo_LIBS@ manager_plugins = @manager_plugins@ @@ -248,16 +310,15 @@ mkdir_p = @mkdir_p@ nm_CFLAGS = @nm_CFLAGS@ nm_LIBS = @nm_LIBS@ nm_ca_dir = @nm_ca_dir@ +nm_plugins = @nm_plugins@ oldincludedir = @oldincludedir@ openac_plugins = @openac_plugins@ -p_plugins = @p_plugins@ pcsclite_CFLAGS = @pcsclite_CFLAGS@ pcsclite_LIBS = @pcsclite_LIBS@ pdfdir = @pdfdir@ piddir = @piddir@ pki_plugins = @pki_plugins@ plugindir = @plugindir@ -pluto_plugins = @pluto_plugins@ pool_plugins = @pool_plugins@ prefix = @prefix@ program_transform_name = @program_transform_name@ @@ -285,12 +346,15 @@ top_srcdir = @top_srcdir@ urandom_device = @urandom_device@ xml_CFLAGS = @xml_CFLAGS@ xml_LIBS = @xml_LIBS@ -INCLUDES = -I${linux_headers} -I$(top_srcdir)/src/libstrongswan \ - -I$(top_srcdir)/src/libhydra +AM_CPPFLAGS = \ + -I${linux_headers} \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libhydra \ + -DROUTING_TABLE=${routing_table} \ + -DROUTING_TABLE_PRIO=${routing_table_prio} -AM_CFLAGS = -rdynamic \ --DROUTING_TABLE=${routing_table} \ --DROUTING_TABLE_PRIO=${routing_table_prio} +AM_CFLAGS = \ + -rdynamic @MONOLITHIC_TRUE@noinst_LTLIBRARIES = libstrongswan-kernel-netlink.la @MONOLITHIC_FALSE@plugin_LTLIBRARIES = libstrongswan-kernel-netlink.la @@ -346,7 +410,6 @@ clean-noinstLTLIBRARIES: done install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES) @$(NORMAL_INSTALL) - test -z "$(plugindir)" || $(MKDIR_P) "$(DESTDIR)$(plugindir)" @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ @@ -354,6 +417,8 @@ install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES) else :; fi; \ done; \ test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(plugindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(plugindir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(plugindir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(plugindir)"; \ } @@ -375,8 +440,8 @@ clean-pluginLTLIBRARIES: echo "rm -f \"$${dir}/so_locations\""; \ rm -f "$${dir}/so_locations"; \ done -libstrongswan-kernel-netlink.la: $(libstrongswan_kernel_netlink_la_OBJECTS) $(libstrongswan_kernel_netlink_la_DEPENDENCIES) - $(libstrongswan_kernel_netlink_la_LINK) $(am_libstrongswan_kernel_netlink_la_rpath) $(libstrongswan_kernel_netlink_la_OBJECTS) $(libstrongswan_kernel_netlink_la_LIBADD) $(LIBS) +libstrongswan-kernel-netlink.la: $(libstrongswan_kernel_netlink_la_OBJECTS) $(libstrongswan_kernel_netlink_la_DEPENDENCIES) $(EXTRA_libstrongswan_kernel_netlink_la_DEPENDENCIES) + $(AM_V_CCLD)$(libstrongswan_kernel_netlink_la_LINK) $(am_libstrongswan_kernel_netlink_la_rpath) $(libstrongswan_kernel_netlink_la_OBJECTS) $(libstrongswan_kernel_netlink_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -390,25 +455,25 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_netlink_shared.Plo@am__quote@ .c.o: -@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(COMPILE) -c $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< .c.obj: -@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` .c.lo: -@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo @@ -515,10 +580,15 @@ install-am: all-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 + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi mostlyclean-generic: clean-generic: diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c index b2cf778be..b34fa149c 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2011 Tobias Brunner + * Copyright (C) 2006-2012 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2008 Andreas Steffen * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser @@ -36,12 +36,10 @@ #include "kernel_netlink_shared.h" #include <hydra.h> -#include <debug.h> -#include <threading/thread.h> +#include <utils/debug.h> #include <threading/mutex.h> -#include <utils/hashtable.h> -#include <utils/linked_list.h> -#include <processing/jobs/callback_job.h> +#include <collections/hashtable.h> +#include <collections/linked_list.h> /** Required for Linux 2.6.26 kernel and later */ #ifndef XFRM_STATE_AF_UNSPEC @@ -58,6 +56,20 @@ #define IPV6_XFRM_POLICY 34 #endif /*IPV6_XFRM_POLICY*/ +/* from linux/udp.h */ +#ifndef UDP_ENCAP +#define UDP_ENCAP 100 +#endif + +#ifndef UDP_ENCAP_ESPINUDP +#define UDP_ENCAP_ESPINUDP 2 +#endif + +/* this is not defined on some platforms */ +#ifndef SOL_UDP +#define SOL_UDP IPPROTO_UDP +#endif + /** Default priority of installed policies */ #define PRIO_BASE 512 @@ -81,12 +93,6 @@ #define XFRM_RTA(nlh, x) ((struct rtattr*)(NLMSG_DATA(nlh) + \ NLMSG_ALIGN(sizeof(x)))) /** - * Returns a pointer to the next rtattr following rta. - * !!! Do not use this to parse messages. Use RTA_NEXT and RTA_OK instead !!! - */ -#define XFRM_RTA_NEXT(rta) ((struct rtattr*)(((char*)(rta)) + \ - RTA_ALIGN((rta)->rta_len))) -/** * Returns the total size of attached rta data * (after 'usual' netlink data x like 'struct xfrm_usersa_info') */ @@ -162,8 +168,6 @@ ENUM(xfrm_attr_type_names, XFRMA_UNSPEC, XFRMA_REPLAY_ESN_VAL, "XFRMA_REPLAY_ESN_VAL", ); -#define END_OF_LIST -1 - /** * Algorithms for encryption */ @@ -194,7 +198,6 @@ static kernel_algorithm_t encryption_algs[] = { /* {ENCR_CAMELLIA_CCM_ICV16, "***" }, */ {ENCR_SERPENT_CBC, "serpent" }, {ENCR_TWOFISH_CBC, "twofish" }, - {END_OF_LIST, NULL } }; /** @@ -212,7 +215,6 @@ static kernel_algorithm_t integrity_algs[] = { /* {AUTH_DES_MAC, "***" }, */ /* {AUTH_KPDK_MD5, "***" }, */ {AUTH_AES_XCBC_96, "xcbc(aes)" }, - {END_OF_LIST, NULL } }; /** @@ -223,21 +225,45 @@ static kernel_algorithm_t compression_algs[] = { {IPCOMP_DEFLATE, "deflate" }, {IPCOMP_LZS, "lzs" }, {IPCOMP_LZJH, "lzjh" }, - {END_OF_LIST, NULL } }; /** * Look up a kernel algorithm name and its key size */ -static char* lookup_algorithm(kernel_algorithm_t *list, int ikev2) +static char* lookup_algorithm(transform_type_t type, int ikev2) { - while (list->ikev2 != END_OF_LIST) + kernel_algorithm_t *list; + int i, count; + char *name; + + switch (type) + { + case ENCRYPTION_ALGORITHM: + list = encryption_algs; + count = countof(encryption_algs); + break; + case INTEGRITY_ALGORITHM: + list = integrity_algs; + count = countof(integrity_algs); + break; + case COMPRESSION_ALGORITHM: + list = compression_algs; + count = countof(compression_algs); + break; + default: + return NULL; + } + for (i = 0; i < count; i++) { - if (list->ikev2 == ikev2) + if (list[i].ikev2 == ikev2) { - return list->name; + return list[i].name; } - list++; + } + if (hydra->kernel_interface->lookup_algorithm(hydra->kernel_interface, + ikev2, type, NULL, &name)) + { + return name; } return NULL; } @@ -269,11 +295,6 @@ struct private_kernel_netlink_ipsec_t { hashtable_t *sas; /** - * Job receiving netlink events - */ - callback_job_t *job; - - /** * Netlink xfrm socket (IPsec) */ netlink_socket_t *socket_xfrm; @@ -294,12 +315,12 @@ struct private_kernel_netlink_ipsec_t { bool policy_history; /** - * Size of the replay window, in packets + * Size of the replay window, in packets (= bits) */ u_int32_t replay_window; /** - * Size of the replay window bitmap, in bytes + * Size of the replay window bitmap, in number of __u32 blocks */ u_int32_t replay_bmp; }; @@ -344,8 +365,8 @@ static void route_entry_destroy(route_entry_t *this) static bool route_entry_equals(route_entry_t *a, route_entry_t *b) { return a->if_name && b->if_name && streq(a->if_name, b->if_name) && - a->src_ip->equals(a->src_ip, b->src_ip) && - a->gateway->equals(a->gateway, b->gateway) && + a->src_ip->ip_equals(a->src_ip, b->src_ip) && + a->gateway->ip_equals(a->gateway, b->gateway) && chunk_equals(a->dst_net, b->dst_net) && a->prefixlen == b->prefixlen; } @@ -535,6 +556,9 @@ struct policy_entry_t { /** List of SAs this policy is used by, ordered by priority */ linked_list_t *used_by; + + /** reqid for this policy */ + u_int32_t reqid; }; /** @@ -562,9 +586,8 @@ static void policy_entry_destroy(private_kernel_netlink_ipsec_t *this, */ static u_int policy_hash(policy_entry_t *key) { - chunk_t chunk = chunk_create((void*)&key->sel, - sizeof(struct xfrm_selector) + sizeof(u_int32_t)); - return chunk_hash(chunk); + chunk_t chunk = chunk_from_thing(key->sel); + return chunk_hash_inc(chunk, chunk_hash(chunk_from_thing(key->mark))); } /** @@ -572,8 +595,8 @@ static u_int policy_hash(policy_entry_t *key) */ static bool policy_equals(policy_entry_t *key, policy_entry_t *other_key) { - return memeq(&key->sel, &other_key->sel, - sizeof(struct xfrm_selector) + sizeof(u_int32_t)) && + return memeq(&key->sel, &other_key->sel, sizeof(struct xfrm_selector)) && + key->mark == other_key->mark && key->direction == other_key->direction; } @@ -760,7 +783,7 @@ static traffic_selector_t* selector2ts(struct xfrm_selector *sel, bool src) if (host) { return traffic_selector_create_from_subnet(host, prefixlen, - sel->proto, port); + sel->proto, port, port ?: 65535); } return NULL; } @@ -947,40 +970,37 @@ static void process_mapping(private_kernel_netlink_ipsec_t *this, /** * Receives events from kernel */ -static job_requeue_t receive_events(private_kernel_netlink_ipsec_t *this) +static bool receive_events(private_kernel_netlink_ipsec_t *this, int fd, + watcher_event_t event) { char response[1024]; struct nlmsghdr *hdr = (struct nlmsghdr*)response; struct sockaddr_nl addr; socklen_t addr_len = sizeof(addr); int len; - bool oldstate; - - oldstate = thread_cancelability(TRUE); - len = recvfrom(this->socket_xfrm_events, response, sizeof(response), 0, - (struct sockaddr*)&addr, &addr_len); - thread_cancelability(oldstate); + len = recvfrom(this->socket_xfrm_events, response, sizeof(response), + MSG_DONTWAIT, (struct sockaddr*)&addr, &addr_len); if (len < 0) { switch (errno) { case EINTR: /* interrupted, try again */ - return JOB_REQUEUE_DIRECT; + return TRUE; case EAGAIN: /* no data ready, select again */ - return JOB_REQUEUE_DIRECT; + return TRUE; default: DBG1(DBG_KNL, "unable to receive from xfrm event socket"); sleep(1); - return JOB_REQUEUE_FAIR; + return TRUE; } } if (addr.nl_pid != 0) { /* not from kernel. not interested, try another one */ - return JOB_REQUEUE_DIRECT; + return TRUE; } while (NLMSG_OK(hdr, len)) @@ -1006,7 +1026,13 @@ static job_requeue_t receive_events(private_kernel_netlink_ipsec_t *this) } hdr = NLMSG_NEXT(hdr, len); } - return JOB_REQUEUE_DIRECT; + return TRUE; +} + +METHOD(kernel_ipsec_t, get_features, kernel_feature_t, + private_kernel_netlink_ipsec_t *this) +{ + return KERNEL_ESP_V3_TFC; } /** @@ -1117,12 +1143,32 @@ METHOD(kernel_ipsec_t, get_cpi, status_t, return SUCCESS; } +/** + * Add a XFRM mark to message if required + */ +static bool add_mark(struct nlmsghdr *hdr, int buflen, mark_t mark) +{ + if (mark.value) + { + struct xfrm_mark *xmrk; + + xmrk = netlink_reserve(hdr, buflen, XFRMA_MARK, sizeof(*xmrk)); + if (!xmrk) + { + return FALSE; + } + xmrk->v = mark.value; + xmrk->m = mark.mask; + } + return TRUE; +} + METHOD(kernel_ipsec_t, add_sa, status_t, private_kernel_netlink_ipsec_t *this, host_t *src, host_t *dst, u_int32_t spi, u_int8_t protocol, u_int32_t reqid, mark_t mark, u_int32_t tfc, lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key, u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp, - u_int16_t cpi, bool encap, bool esn, bool inbound, + u_int16_t cpi, bool initiator, bool encap, bool esn, bool inbound, traffic_selector_t* src_ts, traffic_selector_t* dst_ts) { netlink_buf_t request; @@ -1139,7 +1185,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t, lifetime_cfg_t lft = {{0,0,0},{0,0,0},{0,0,0}}; add_sa(this, src, dst, htonl(ntohs(cpi)), IPPROTO_COMP, reqid, mark, tfc, &lft, ENCR_UNDEFINED, chunk_empty, AUTH_UNDEFINED, - chunk_empty, mode, ipcomp, 0, FALSE, FALSE, inbound, NULL, NULL); + chunk_empty, mode, ipcomp, 0, initiator, FALSE, FALSE, inbound, + NULL, NULL); ipcomp = IPCOMP_NONE; /* use transport mode ESP SA, IPComp uses tunnel mode */ mode = MODE_TRANSPORT; @@ -1147,16 +1194,9 @@ METHOD(kernel_ipsec_t, add_sa, status_t, memset(&request, 0, sizeof(request)); - if (mark.value) - { - DBG2(DBG_KNL, "adding SAD entry with SPI %.8x and reqid {%u} (mark " - "%u/0x%8x)", ntohl(spi), reqid, mark.value, mark.mask); - } - else - { - DBG2(DBG_KNL, "adding SAD entry with SPI %.8x and reqid {%u}", - ntohl(spi), reqid); - } + DBG2(DBG_KNL, "adding SAD entry with SPI %.8x and reqid {%u} (mark " + "%u/0x%08x)", ntohl(spi), reqid, mark.value, mark.mask); + hdr = (struct nlmsghdr*)request; hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; hdr->nlmsg_type = inbound ? XFRM_MSG_UPDSA : XFRM_MSG_NEWSA; @@ -1179,6 +1219,12 @@ METHOD(kernel_ipsec_t, add_sa, status_t, if(src_ts && dst_ts) { sa->sel = ts2selector(src_ts, dst_ts); + /* don't install proto/port on SA. This would break + * potential secondary SAs for the same address using a + * different prot/port. */ + sa->sel.proto = 0; + sa->sel.dport = sa->sel.dport_mask = 0; + sa->sel.sport = sa->sel.sport_mask = 0; } break; default: @@ -1196,8 +1242,6 @@ METHOD(kernel_ipsec_t, add_sa, status_t, sa->lft.soft_use_expires_seconds = 0; sa->lft.hard_use_expires_seconds = 0; - struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_usersa_info); - switch (enc_alg) { case ENCR_UNDEFINED: @@ -1220,39 +1264,34 @@ METHOD(kernel_ipsec_t, add_sa, status_t, { struct xfrm_algo_aead *algo; - alg_name = lookup_algorithm(encryption_algs, enc_alg); + alg_name = lookup_algorithm(ENCRYPTION_ALGORITHM, enc_alg); if (alg_name == NULL) { DBG1(DBG_KNL, "algorithm %N not supported by kernel!", - encryption_algorithm_names, enc_alg); - goto failed; + encryption_algorithm_names, enc_alg); + goto failed; } DBG2(DBG_KNL, " using encryption algorithm %N with key size %d", encryption_algorithm_names, enc_alg, enc_key.len * 8); - rthdr->rta_type = XFRMA_ALG_AEAD; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo_aead) + - enc_key.len); - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) + algo = netlink_reserve(hdr, sizeof(request), XFRMA_ALG_AEAD, + sizeof(*algo) + enc_key.len); + if (!algo) { goto failed; } - - algo = (struct xfrm_algo_aead*)RTA_DATA(rthdr); algo->alg_key_len = enc_key.len * 8; algo->alg_icv_len = icv_size; - strcpy(algo->alg_name, alg_name); + strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name)); + algo->alg_name[sizeof(algo->alg_name) - 1] = '\0'; memcpy(algo->alg_key, enc_key.ptr, enc_key.len); - - rthdr = XFRM_RTA_NEXT(rthdr); break; } default: { struct xfrm_algo *algo; - alg_name = lookup_algorithm(encryption_algs, enc_alg); + alg_name = lookup_algorithm(ENCRYPTION_ALGORITHM, enc_alg); if (alg_name == NULL) { DBG1(DBG_KNL, "algorithm %N not supported by kernel!", @@ -1262,20 +1301,16 @@ METHOD(kernel_ipsec_t, add_sa, status_t, DBG2(DBG_KNL, " using encryption algorithm %N with key size %d", encryption_algorithm_names, enc_alg, enc_key.len * 8); - rthdr->rta_type = XFRMA_ALG_CRYPT; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + enc_key.len); - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) + algo = netlink_reserve(hdr, sizeof(request), XFRMA_ALG_CRYPT, + sizeof(*algo) + enc_key.len); + if (!algo) { goto failed; } - - algo = (struct xfrm_algo*)RTA_DATA(rthdr); algo->alg_key_len = enc_key.len * 8; - strcpy(algo->alg_name, alg_name); + strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name)); + algo->alg_name[sizeof(algo->alg_name) - 1] = '\0'; memcpy(algo->alg_key, enc_key.ptr, enc_key.len); - - rthdr = XFRM_RTA_NEXT(rthdr); } } @@ -1283,7 +1318,7 @@ METHOD(kernel_ipsec_t, add_sa, status_t, { u_int trunc_len = 0; - alg_name = lookup_algorithm(integrity_algs, int_alg); + alg_name = lookup_algorithm(INTEGRITY_ALGORITHM, int_alg); if (alg_name == NULL) { DBG1(DBG_KNL, "algorithm %N not supported by kernel!", @@ -1313,47 +1348,40 @@ METHOD(kernel_ipsec_t, add_sa, status_t, /* the kernel uses SHA256 with 96 bit truncation by default, * use specified truncation size supported by newer kernels. * also use this for untruncated MD5 and SHA1. */ - rthdr->rta_type = XFRMA_ALG_AUTH_TRUNC; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo_auth) + - int_key.len); - - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) + algo = netlink_reserve(hdr, sizeof(request), XFRMA_ALG_AUTH_TRUNC, + sizeof(*algo) + int_key.len); + if (!algo) { goto failed; } - - algo = (struct xfrm_algo_auth*)RTA_DATA(rthdr); algo->alg_key_len = int_key.len * 8; algo->alg_trunc_len = trunc_len; - strcpy(algo->alg_name, alg_name); + strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name)); + algo->alg_name[sizeof(algo->alg_name) - 1] = '\0'; memcpy(algo->alg_key, int_key.ptr, int_key.len); } else { struct xfrm_algo* algo; - rthdr->rta_type = XFRMA_ALG_AUTH; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + int_key.len); - - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) + algo = netlink_reserve(hdr, sizeof(request), XFRMA_ALG_AUTH, + sizeof(*algo) + int_key.len); + if (!algo) { goto failed; } - - algo = (struct xfrm_algo*)RTA_DATA(rthdr); algo->alg_key_len = int_key.len * 8; - strcpy(algo->alg_name, alg_name); + strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name)); + algo->alg_name[sizeof(algo->alg_name) - 1] = '\0'; memcpy(algo->alg_key, int_key.ptr, int_key.len); } - rthdr = XFRM_RTA_NEXT(rthdr); } if (ipcomp != IPCOMP_NONE) { - rthdr->rta_type = XFRMA_ALG_COMP; - alg_name = lookup_algorithm(compression_algs, ipcomp); + struct xfrm_algo* algo; + + alg_name = lookup_algorithm(COMPRESSION_ALGORITHM, ipcomp); if (alg_name == NULL) { DBG1(DBG_KNL, "algorithm %N not supported by kernel!", @@ -1363,34 +1391,26 @@ METHOD(kernel_ipsec_t, add_sa, status_t, DBG2(DBG_KNL, " using compression algorithm %N", ipcomp_transform_names, ipcomp); - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo)); - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) + algo = netlink_reserve(hdr, sizeof(request), XFRMA_ALG_COMP, + sizeof(*algo)); + if (!algo) { goto failed; } - - struct xfrm_algo* algo = (struct xfrm_algo*)RTA_DATA(rthdr); algo->alg_key_len = 0; - strcpy(algo->alg_name, alg_name); - - rthdr = XFRM_RTA_NEXT(rthdr); + strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name)); + algo->alg_name[sizeof(algo->alg_name) - 1] = '\0'; } if (encap) { struct xfrm_encap_tmpl *tmpl; - rthdr->rta_type = XFRMA_ENCAP; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_encap_tmpl)); - - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) + tmpl = netlink_reserve(hdr, sizeof(request), XFRMA_ENCAP, sizeof(*tmpl)); + if (!tmpl) { goto failed; } - - tmpl = (struct xfrm_encap_tmpl*)RTA_DATA(rthdr); tmpl->encap_type = UDP_ENCAP_ESPINUDP; tmpl->encap_sport = htons(src->get_port(src)); tmpl->encap_dport = htons(dst->get_port(dst)); @@ -1405,44 +1425,24 @@ METHOD(kernel_ipsec_t, add_sa, status_t, * No. The reason the kernel ignores NAT-OA is that it recomputes * (or, rather, just ignores) the checksum. If packets pass the IPsec * checks it marks them "checksum ok" so OA isn't needed. */ - rthdr = XFRM_RTA_NEXT(rthdr); } - if (mark.value) + if (!add_mark(hdr, sizeof(request), mark)) { - struct xfrm_mark *mrk; - - rthdr->rta_type = XFRMA_MARK; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_mark)); - - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) - { - goto failed; - } - - mrk = (struct xfrm_mark*)RTA_DATA(rthdr); - mrk->v = mark.value; - mrk->m = mark.mask; - rthdr = XFRM_RTA_NEXT(rthdr); + goto failed; } if (tfc) { u_int32_t *tfcpad; - rthdr->rta_type = XFRMA_TFCPAD; - rthdr->rta_len = RTA_LENGTH(sizeof(u_int32_t)); - - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) + tfcpad = netlink_reserve(hdr, sizeof(request), XFRMA_TFCPAD, + sizeof(*tfcpad)); + if (!tfcpad) { goto failed; } - - tfcpad = (u_int32_t*)RTA_DATA(rthdr); *tfcpad = tfc; - rthdr = XFRM_RTA_NEXT(rthdr); } if (protocol != IPPROTO_COMP) @@ -1453,24 +1453,18 @@ METHOD(kernel_ipsec_t, add_sa, status_t, * XFRMA_REPLAY_ESN_VAL attribute to configure a bitmap */ struct xfrm_replay_state_esn *replay; - rthdr->rta_type = XFRMA_REPLAY_ESN_VAL; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_replay_state_esn) + - (this->replay_window + 7) / 8); - - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) + replay = netlink_reserve(hdr, sizeof(request), XFRMA_REPLAY_ESN_VAL, + sizeof(*replay) + (this->replay_window + 7) / 8); + if (!replay) { goto failed; } - - replay = (struct xfrm_replay_state_esn*)RTA_DATA(rthdr); /* bmp_len contains number uf __u32's */ replay->bmp_len = this->replay_bmp; replay->replay_window = this->replay_window; - DBG2(DBG_KNL, " using replay window of %u bytes", + DBG2(DBG_KNL, " using replay window of %u packets", this->replay_window); - rthdr = XFRM_RTA_NEXT(rthdr); if (esn) { DBG2(DBG_KNL, " using extended sequence numbers (ESN)"); @@ -1479,7 +1473,9 @@ METHOD(kernel_ipsec_t, add_sa, status_t, } else { - sa->replay_window = DEFAULT_REPLAY_WINDOW; + DBG2(DBG_KNL, " using replay window of %u packets", + this->replay_window); + sa->replay_window = this->replay_window; } } @@ -1488,7 +1484,7 @@ METHOD(kernel_ipsec_t, add_sa, status_t, if (mark.value) { DBG1(DBG_KNL, "unable to add SAD entry with SPI %.8x " - "(mark %u/0x%8x)", ntohl(spi), mark.value, mark.mask); + "(mark %u/0x%08x)", ntohl(spi), mark.value, mark.mask); } else { @@ -1510,7 +1506,8 @@ failed: * Allocates into one the replay state structure we get from the kernel. */ static void get_replay_state(private_kernel_netlink_ipsec_t *this, - u_int32_t spi, u_int8_t protocol, host_t *dst, + u_int32_t spi, u_int8_t protocol, + host_t *dst, mark_t mark, struct xfrm_replay_state_esn **replay_esn, struct xfrm_replay_state **replay) { @@ -1539,6 +1536,11 @@ static void get_replay_state(private_kernel_netlink_ipsec_t *this, aevent_id->sa_id.proto = protocol; aevent_id->sa_id.family = dst->get_family(dst); + if (!add_mark(hdr, sizeof(request), mark)) + { + return; + } + if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) { hdr = out; @@ -1597,7 +1599,8 @@ static void get_replay_state(private_kernel_netlink_ipsec_t *this, METHOD(kernel_ipsec_t, query_sa, status_t, private_kernel_netlink_ipsec_t *this, host_t *src, host_t *dst, - u_int32_t spi, u_int8_t protocol, mark_t mark, u_int64_t *bytes) + u_int32_t spi, u_int8_t protocol, mark_t mark, + u_int64_t *bytes, u_int64_t *packets, u_int32_t *time) { netlink_buf_t request; struct nlmsghdr *out = NULL, *hdr; @@ -1608,15 +1611,9 @@ METHOD(kernel_ipsec_t, query_sa, status_t, memset(&request, 0, sizeof(request)); - if (mark.value) - { - DBG2(DBG_KNL, "querying SAD entry with SPI %.8x (mark %u/0x%8x)", - ntohl(spi), mark.value, mark.mask); - } - else - { - DBG2(DBG_KNL, "querying SAD entry with SPI %.8x", ntohl(spi)); - } + DBG2(DBG_KNL, "querying SAD entry with SPI %.8x (mark %u/0x%08x)", + ntohl(spi), mark.value, mark.mask); + hdr = (struct nlmsghdr*)request; hdr->nlmsg_flags = NLM_F_REQUEST; hdr->nlmsg_type = XFRM_MSG_GETSA; @@ -1628,22 +1625,9 @@ METHOD(kernel_ipsec_t, query_sa, status_t, sa_id->proto = protocol; sa_id->family = dst->get_family(dst); - if (mark.value) + if (!add_mark(hdr, sizeof(request), mark)) { - struct xfrm_mark *mrk; - struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_usersa_id); - - rthdr->rta_type = XFRMA_MARK; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_mark)); - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) - { - return FAILED; - } - - mrk = (struct xfrm_mark*)RTA_DATA(rthdr); - mrk->v = mark.value; - mrk->m = mark.mask; + return FAILED; } if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) @@ -1665,7 +1649,7 @@ METHOD(kernel_ipsec_t, query_sa, status_t, if (mark.value) { DBG1(DBG_KNL, "querying SAD entry with SPI %.8x " - "(mark %u/0x%8x) failed: %s (%d)", + "(mark %u/0x%08x) failed: %s (%d)", ntohl(spi), mark.value, mark.mask, strerror(-err->error), -err->error); } @@ -1693,7 +1677,20 @@ METHOD(kernel_ipsec_t, query_sa, status_t, } else { - *bytes = sa->curlft.bytes; + if (bytes) + { + *bytes = sa->curlft.bytes; + } + if (packets) + { + *packets = sa->curlft.packets; + } + if (time) + { /* curlft contains an "use" time, but that contains a timestamp + * of the first use, not the last. Last use time must be queried + * on the policy on Linux */ + *time = 0; + } status = SUCCESS; } memwipe(out, len); @@ -1717,15 +1714,9 @@ METHOD(kernel_ipsec_t, del_sa, status_t, memset(&request, 0, sizeof(request)); - if (mark.value) - { - DBG2(DBG_KNL, "deleting SAD entry with SPI %.8x (mark %u/0x%8x)", - ntohl(spi), mark.value, mark.mask); - } - else - { - DBG2(DBG_KNL, "deleting SAD entry with SPI %.8x", ntohl(spi)); - } + DBG2(DBG_KNL, "deleting SAD entry with SPI %.8x (mark %u/0x%08x)", + ntohl(spi), mark.value, mark.mask); + hdr = (struct nlmsghdr*)request; hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; hdr->nlmsg_type = XFRM_MSG_DELSA; @@ -1737,48 +1728,32 @@ METHOD(kernel_ipsec_t, del_sa, status_t, sa_id->proto = protocol; sa_id->family = dst->get_family(dst); - if (mark.value) + if (!add_mark(hdr, sizeof(request), mark)) { - struct xfrm_mark *mrk; - struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_usersa_id); - - rthdr->rta_type = XFRMA_MARK; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_mark)); - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) - { - return FAILED; - } - - mrk = (struct xfrm_mark*)RTA_DATA(rthdr); - mrk->v = mark.value; - mrk->m = mark.mask; - } - - if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS) - { - if (mark.value) - { - DBG1(DBG_KNL, "unable to delete SAD entry with SPI %.8x " - "(mark %u/0x%8x)", ntohl(spi), mark.value, mark.mask); - } - else - { - DBG1(DBG_KNL, "unable to delete SAD entry with SPI %.8x", - ntohl(spi)); - } return FAILED; } - if (mark.value) - { - DBG2(DBG_KNL, "deleted SAD entry with SPI %.8x (mark %u/0x%8x)", - ntohl(spi), mark.value, mark.mask); - } - else + + switch (this->socket_xfrm->send_ack(this->socket_xfrm, hdr)) { - DBG2(DBG_KNL, "deleted SAD entry with SPI %.8x", ntohl(spi)); + case SUCCESS: + DBG2(DBG_KNL, "deleted SAD entry with SPI %.8x (mark %u/0x%08x)", + ntohl(spi), mark.value, mark.mask); + return SUCCESS; + case NOT_FOUND: + return NOT_FOUND; + default: + if (mark.value) + { + DBG1(DBG_KNL, "unable to delete SAD entry with SPI %.8x " + "(mark %u/0x%08x)", ntohl(spi), mark.value, mark.mask); + } + else + { + DBG1(DBG_KNL, "unable to delete SAD entry with SPI %.8x", + ntohl(spi)); + } + return FAILED; } - return SUCCESS; } METHOD(kernel_ipsec_t, update_sa, status_t, @@ -1787,7 +1762,6 @@ METHOD(kernel_ipsec_t, update_sa, status_t, bool old_encap, bool new_encap, mark_t mark) { netlink_buf_t request; - u_char *pos; struct nlmsghdr *hdr, *out = NULL; struct xfrm_usersa_id *sa_id; struct xfrm_usersa_info *out_sa = NULL, *sa; @@ -1822,6 +1796,11 @@ METHOD(kernel_ipsec_t, update_sa, status_t, sa_id->proto = protocol; sa_id->family = dst->get_family(dst); + if (!add_mark(hdr, sizeof(request), mark)) + { + return FAILED; + } + if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) { hdr = out; @@ -1856,7 +1835,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t, goto failed; } - get_replay_state(this, spi, protocol, dst, &replay_esn, &replay); + get_replay_state(this, spi, protocol, dst, mark, &replay_esn, &replay); /* delete the old SA (without affecting the IPComp SA) */ if (del_sa(this, src, dst, spi, protocol, 0, mark) != SUCCESS) @@ -1870,11 +1849,11 @@ METHOD(kernel_ipsec_t, update_sa, status_t, ntohl(spi), src, dst, new_src, new_dst); /* copy over the SA from out to request */ hdr = (struct nlmsghdr*)request; - memcpy(hdr, out, min(out->nlmsg_len, sizeof(request))); hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; hdr->nlmsg_type = XFRM_MSG_NEWSA; hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_info)); sa = NLMSG_DATA(hdr); + memcpy(sa, NLMSG_DATA(out), sizeof(struct xfrm_usersa_info)); sa->family = new_dst->get_family(new_dst); if (!src->ip_equals(src, new_src)) @@ -1888,75 +1867,60 @@ METHOD(kernel_ipsec_t, update_sa, status_t, rta = XFRM_RTA(out, struct xfrm_usersa_info); rtasize = XFRM_PAYLOAD(out, struct xfrm_usersa_info); - pos = (u_char*)XFRM_RTA(hdr, struct xfrm_usersa_info); - while(RTA_OK(rta, rtasize)) + while (RTA_OK(rta, rtasize)) { /* copy all attributes, but not XFRMA_ENCAP if we are disabling it */ if (rta->rta_type != XFRMA_ENCAP || new_encap) { if (rta->rta_type == XFRMA_ENCAP) { /* update encap tmpl */ - tmpl = (struct xfrm_encap_tmpl*)RTA_DATA(rta); + tmpl = RTA_DATA(rta); tmpl->encap_sport = ntohs(new_src->get_port(new_src)); tmpl->encap_dport = ntohs(new_dst->get_port(new_dst)); } - memcpy(pos, rta, rta->rta_len); - pos += RTA_ALIGN(rta->rta_len); - hdr->nlmsg_len += RTA_ALIGN(rta->rta_len); + netlink_add_attribute(hdr, rta->rta_type, + chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta)), + sizeof(request)); } rta = RTA_NEXT(rta, rtasize); } - rta = (struct rtattr*)pos; if (tmpl == NULL && new_encap) { /* add tmpl if we are enabling it */ - rta->rta_type = XFRMA_ENCAP; - rta->rta_len = RTA_LENGTH(sizeof(struct xfrm_encap_tmpl)); - - hdr->nlmsg_len += RTA_ALIGN(rta->rta_len); - if (hdr->nlmsg_len > sizeof(request)) + tmpl = netlink_reserve(hdr, sizeof(request), XFRMA_ENCAP, sizeof(*tmpl)); + if (!tmpl) { goto failed; } - - tmpl = (struct xfrm_encap_tmpl*)RTA_DATA(rta); tmpl->encap_type = UDP_ENCAP_ESPINUDP; tmpl->encap_sport = ntohs(new_src->get_port(new_src)); tmpl->encap_dport = ntohs(new_dst->get_port(new_dst)); memset(&tmpl->encap_oa, 0, sizeof (xfrm_address_t)); - - rta = XFRM_RTA_NEXT(rta); } if (replay_esn) { - rta->rta_type = XFRMA_REPLAY_ESN_VAL; - rta->rta_len = RTA_LENGTH(sizeof(struct xfrm_replay_state_esn) + - this->replay_bmp); + struct xfrm_replay_state_esn *state; - hdr->nlmsg_len += RTA_ALIGN(rta->rta_len); - if (hdr->nlmsg_len > sizeof(request)) + state = netlink_reserve(hdr, sizeof(request), XFRMA_REPLAY_ESN_VAL, + sizeof(*state) + this->replay_bmp); + if (!state) { goto failed; } - memcpy(RTA_DATA(rta), replay_esn, - sizeof(struct xfrm_replay_state_esn) + this->replay_bmp); - - rta = XFRM_RTA_NEXT(rta); + memcpy(state, replay_esn, sizeof(*state) + this->replay_bmp); } else if (replay) { - rta->rta_type = XFRMA_REPLAY_VAL; - rta->rta_len = RTA_LENGTH(sizeof(struct xfrm_replay_state)); + struct xfrm_replay_state *state; - hdr->nlmsg_len += RTA_ALIGN(rta->rta_len); - if (hdr->nlmsg_len > sizeof(request)) + state = netlink_reserve(hdr, sizeof(request), XFRMA_REPLAY_VAL, + sizeof(*state)); + if (!state) { goto failed; } - memcpy(RTA_DATA(rta), replay, sizeof(replay)); - - rta = XFRM_RTA_NEXT(rta); + memcpy(state, replay, sizeof(*state)); } else { @@ -2053,11 +2017,9 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, policy_info->lft.soft_use_expires_seconds = 0; policy_info->lft.hard_use_expires_seconds = 0; - struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_userpolicy_info); - if (mapping->type == POLICY_IPSEC) { - struct xfrm_user_tmpl *tmpl = (struct xfrm_user_tmpl*)RTA_DATA(rthdr); + struct xfrm_user_tmpl *tmpl; struct { u_int8_t proto; bool use; @@ -2067,26 +2029,30 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, { IPPROTO_AH, ipsec->cfg.ah.use }, }; ipsec_mode_t proto_mode = ipsec->cfg.mode; - - rthdr->rta_type = XFRMA_TMPL; - rthdr->rta_len = 0; /* actual length is set below */ + int count = 0; for (i = 0; i < countof(protos); i++) { - if (!protos[i].use) + if (protos[i].use) { - continue; + count++; } + } + tmpl = netlink_reserve(hdr, sizeof(request), XFRMA_TMPL, + count * sizeof(*tmpl)); + if (!tmpl) + { + this->mutex->unlock(this->mutex); + return FAILED; + } - rthdr->rta_len += RTA_LENGTH(sizeof(struct xfrm_user_tmpl)); - hdr->nlmsg_len += RTA_ALIGN(RTA_LENGTH(sizeof(struct xfrm_user_tmpl))); - if (hdr->nlmsg_len > sizeof(request)) + for (i = 0; i < countof(protos); i++) + { + if (!protos[i].use) { - this->mutex->unlock(this->mutex); - return FAILED; + continue; } - - tmpl->reqid = ipsec->cfg.reqid; + tmpl->reqid = policy->reqid; tmpl->id.proto = protos[i].proto; tmpl->aalgos = tmpl->ealgos = tmpl->calgos = ~0; tmpl->mode = mode2kernel(proto_mode); @@ -2094,7 +2060,7 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, policy->direction != POLICY_OUT; tmpl->family = ipsec->src->get_family(ipsec->src); - if (proto_mode == MODE_TUNNEL) + if (proto_mode == MODE_TUNNEL || proto_mode == MODE_BEET) { /* only for tunnel mode */ host2xfrm(ipsec->src, &tmpl->saddr); host2xfrm(ipsec->dst, &tmpl->id.daddr); @@ -2105,27 +2071,12 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, /* use transport mode for other SAs */ proto_mode = MODE_TRANSPORT; } - - rthdr = XFRM_RTA_NEXT(rthdr); } - if (ipsec->mark.value) + if (!add_mark(hdr, sizeof(request), ipsec->mark)) { - struct xfrm_mark *mrk; - - rthdr->rta_type = XFRMA_MARK; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_mark)); - - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) - { - this->mutex->unlock(this->mutex); - return FAILED; - } - - mrk = (struct xfrm_mark*)RTA_DATA(rthdr); - mrk->v = ipsec->mark.value; - mrk->m = ipsec->mark.mask; + this->mutex->unlock(this->mutex); + return FAILED; } this->mutex->unlock(this->mutex); @@ -2147,29 +2098,41 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, /* install a route, if: * - this is a forward policy (to just get one for each child) - * - we are in tunnel/BEET mode + * - we are in tunnel/BEET mode or install a bypass policy * - routing is not disabled via strongswan.conf */ - if (policy->direction == POLICY_FWD && - ipsec->cfg.mode != MODE_TRANSPORT && this->install_routes) + if (policy->direction == POLICY_FWD && this->install_routes && + (mapping->type != POLICY_IPSEC || ipsec->cfg.mode != MODE_TRANSPORT)) { - route_entry_t *route = malloc_thing(route_entry_t); policy_sa_fwd_t *fwd = (policy_sa_fwd_t*)mapping; + route_entry_t *route; + host_t *iface; + + INIT(route, + .prefixlen = policy->sel.prefixlen_s, + ); if (hydra->kernel_interface->get_address_by_ts(hydra->kernel_interface, - fwd->dst_ts, &route->src_ip) == SUCCESS) + fwd->dst_ts, &route->src_ip, NULL) == SUCCESS) { /* get the nexthop to src (src as we are in POLICY_FWD) */ route->gateway = hydra->kernel_interface->get_nexthop( - hydra->kernel_interface, ipsec->src); - /* install route via outgoing interface */ - route->if_name = hydra->kernel_interface->get_interface( - hydra->kernel_interface, ipsec->dst); + hydra->kernel_interface, ipsec->src, + ipsec->dst); route->dst_net = chunk_alloc(policy->sel.family == AF_INET ? 4 : 16); memcpy(route->dst_net.ptr, &policy->sel.saddr, route->dst_net.len); - route->prefixlen = policy->sel.prefixlen_s; - if (!route->if_name) + /* get the interface to install the route for. If we have a local + * address, use it. Otherwise (for shunt policies) use the + * routes source address. */ + iface = ipsec->dst; + if (iface->is_anyaddr(iface)) + { + iface = route->src_ip; + } + /* install route via outgoing interface */ + if (!hydra->kernel_interface->get_interface(hydra->kernel_interface, + iface, &route->if_name)) { this->mutex->unlock(this->mutex); route_entry_destroy(route); @@ -2180,12 +2143,7 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, { route_entry_t *old = policy->route; if (route_entry_equals(old, route)) - { /* keep previously installed route. since it might have - * still been removed by an address change, we install it - * again but ignore the result */ - hydra->kernel_interface->add_route(hydra->kernel_interface, - route->dst_net, route->prefixlen, route->gateway, - route->src_ip, route->if_name); + { this->mutex->unlock(this->mutex); route_entry_destroy(route); return SUCCESS; @@ -2250,6 +2208,7 @@ METHOD(kernel_ipsec_t, add_policy, status_t, .sel = ts2selector(src_ts, dst_ts), .mark = mark.value & mark.mask, .direction = direction, + .reqid = sa->reqid, ); /* find the policy, which matches EXACTLY */ @@ -2257,20 +2216,21 @@ METHOD(kernel_ipsec_t, add_policy, status_t, current = this->policies->get(this->policies, policy); if (current) { - /* use existing policy */ - if (mark.value) - { - DBG2(DBG_KNL, "policy %R === %R %N (mark %u/0x%8x) " - "already exists, increasing refcount", - src_ts, dst_ts, policy_dir_names, direction, - mark.value, mark.mask); - } - else + if (current->reqid != sa->reqid) { - DBG2(DBG_KNL, "policy %R === %R %N " - "already exists, increasing refcount", - src_ts, dst_ts, policy_dir_names, direction); + DBG1(DBG_CFG, "unable to install policy %R === %R %N (mark " + "%u/0x%08x) for reqid %u, the same policy for reqid %u exists", + src_ts, dst_ts, policy_dir_names, direction, + mark.value, mark.mask, sa->reqid, current->reqid); + policy_entry_destroy(this, policy); + this->mutex->unlock(this->mutex); + return INVALID_STATE; } + /* use existing policy */ + DBG2(DBG_KNL, "policy %R === %R %N (mark %u/0x%08x) " + "already exists, increasing refcount", + src_ts, dst_ts, policy_dir_names, direction, + mark.value, mark.mask); policy_entry_destroy(this, policy); policy = current; found = TRUE; @@ -2314,18 +2274,9 @@ METHOD(kernel_ipsec_t, add_policy, status_t, return SUCCESS; } - if (mark.value) - { - DBG2(DBG_KNL, "%s policy %R === %R %N (mark %u/0x%8x)", - found ? "updating" : "adding", src_ts, dst_ts, - policy_dir_names, direction, mark.value, mark.mask); - } - else - { - DBG2(DBG_KNL, "%s policy %R === %R %N", - found ? "updating" : "adding", src_ts, dst_ts, - policy_dir_names, direction); - } + DBG2(DBG_KNL, "%s policy %R === %R %N (mark %u/0x%08x)", + found ? "updating" : "adding", src_ts, dst_ts, + policy_dir_names, direction, mark.value, mark.mask); if (add_policy_internal(this, policy, assigned_sa, found) != SUCCESS) { @@ -2350,17 +2301,10 @@ METHOD(kernel_ipsec_t, query_policy, status_t, memset(&request, 0, sizeof(request)); - if (mark.value) - { - DBG2(DBG_KNL, "querying policy %R === %R %N (mark %u/0x%8x)", - src_ts, dst_ts, policy_dir_names, direction, - mark.value, mark.mask); - } - else - { - DBG2(DBG_KNL, "querying policy %R === %R %N", src_ts, dst_ts, - policy_dir_names, direction); - } + DBG2(DBG_KNL, "querying policy %R === %R %N (mark %u/0x%08x)", + src_ts, dst_ts, policy_dir_names, direction, + mark.value, mark.mask); + hdr = (struct nlmsghdr*)request; hdr->nlmsg_flags = NLM_F_REQUEST; hdr->nlmsg_type = XFRM_MSG_GETPOLICY; @@ -2370,23 +2314,9 @@ METHOD(kernel_ipsec_t, query_policy, status_t, policy_id->sel = ts2selector(src_ts, dst_ts); policy_id->dir = direction; - if (mark.value) + if (!add_mark(hdr, sizeof(request), mark)) { - struct xfrm_mark *mrk; - struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_userpolicy_id); - - rthdr->rta_type = XFRMA_MARK; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_mark)); - - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) - { - return FAILED; - } - - mrk = (struct xfrm_mark*)RTA_DATA(rthdr); - mrk->v = mark.value; - mrk->m = mark.mask; + return FAILED; } if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) @@ -2454,17 +2384,9 @@ METHOD(kernel_ipsec_t, del_policy, status_t, bool is_installed = TRUE; u_int32_t priority; - if (mark.value) - { - DBG2(DBG_KNL, "deleting policy %R === %R %N (mark %u/0x%8x)", - src_ts, dst_ts, policy_dir_names, direction, - mark.value, mark.mask); - } - else - { - DBG2(DBG_KNL, "deleting policy %R === %R %N", - src_ts, dst_ts, policy_dir_names, direction); - } + DBG2(DBG_KNL, "deleting policy %R === %R %N (mark %u/0x%08x)", + src_ts, dst_ts, policy_dir_names, direction, + mark.value, mark.mask); /* create a policy */ memset(&policy, 0, sizeof(policy_entry_t)); @@ -2475,11 +2397,11 @@ METHOD(kernel_ipsec_t, del_policy, status_t, /* find the policy */ this->mutex->lock(this->mutex); current = this->policies->get(this->policies, &policy); - if (!current) + if (!current || current->reqid != reqid) { if (mark.value) { - DBG1(DBG_KNL, "deleting policy %R === %R %N (mark %u/0x%8x) " + DBG1(DBG_KNL, "deleting policy %R === %R %N (mark %u/0x%08x) " "failed, not found", src_ts, dst_ts, policy_dir_names, direction, mark.value, mark.mask); } @@ -2498,8 +2420,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t, enumerator = current->used_by->create_enumerator(current->used_by); while (enumerator->enumerate(enumerator, (void**)&mapping)) { - if (reqid == mapping->sa->cfg.reqid && - priority == mapping->priority) + if (priority == mapping->priority) { current->used_by->remove_at(current->used_by, enumerator); policy_sa_destroy(mapping, &direction, this); @@ -2525,17 +2446,9 @@ METHOD(kernel_ipsec_t, del_policy, status_t, return SUCCESS; } - if (mark.value) - { - DBG2(DBG_KNL, "updating policy %R === %R %N (mark %u/0x%8x)", - src_ts, dst_ts, policy_dir_names, direction, - mark.value, mark.mask); - } - else - { - DBG2(DBG_KNL, "updating policy %R === %R %N", - src_ts, dst_ts, policy_dir_names, direction); - } + DBG2(DBG_KNL, "updating policy %R === %R %N (mark %u/0x%08x)", + src_ts, dst_ts, policy_dir_names, direction, + mark.value, mark.mask); current->used_by->get_first(current->used_by, (void**)&mapping); if (add_policy_internal(this, current, mapping, TRUE) != SUCCESS) @@ -2558,23 +2471,9 @@ METHOD(kernel_ipsec_t, del_policy, status_t, policy_id->sel = current->sel; policy_id->dir = direction; - if (mark.value) + if (!add_mark(hdr, sizeof(request), mark)) { - struct xfrm_mark *mrk; - struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_userpolicy_id); - - rthdr->rta_type = XFRMA_MARK; - rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_mark)); - hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); - if (hdr->nlmsg_len > sizeof(request)) - { - this->mutex->unlock(this->mutex); - return FAILED; - } - - mrk = (struct xfrm_mark*)RTA_DATA(rthdr); - mrk->v = mark.value; - mrk->m = mark.mask; + return FAILED; } if (current->route) @@ -2599,7 +2498,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t, if (mark.value) { DBG1(DBG_KNL, "unable to delete policy %R === %R %N " - "(mark %u/0x%8x)", src_ts, dst_ts, policy_dir_names, + "(mark %u/0x%08x)", src_ts, dst_ts, policy_dir_names, direction, mark.value, mark.mask); } else @@ -2680,18 +2579,28 @@ METHOD(kernel_ipsec_t, bypass_socket, bool, return TRUE; } +METHOD(kernel_ipsec_t, enable_udp_decap, bool, + private_kernel_netlink_ipsec_t *this, int fd, int family, u_int16_t port) +{ + int type = UDP_ENCAP_ESPINUDP; + + if (setsockopt(fd, SOL_UDP, UDP_ENCAP, &type, sizeof(type)) < 0) + { + DBG1(DBG_KNL, "unable to set UDP_ENCAP: %s", strerror(errno)); + return FALSE; + } + return TRUE; +} + METHOD(kernel_ipsec_t, destroy, void, private_kernel_netlink_ipsec_t *this) { enumerator_t *enumerator; policy_entry_t *policy; - if (this->job) - { - this->job->cancel(this->job); - } if (this->socket_xfrm_events > 0) { + lib->watcher->remove(lib->watcher, this->socket_xfrm_events); close(this->socket_xfrm_events); } DESTROY_IF(this->socket_xfrm); @@ -2713,12 +2622,13 @@ METHOD(kernel_ipsec_t, destroy, void, kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() { private_kernel_netlink_ipsec_t *this; - struct sockaddr_nl addr; + bool register_for_events = TRUE; int fd; INIT(this, .public = { .interface = { + .get_features = _get_features, .get_spi = _get_spi, .get_cpi = _get_cpi, .add_sa = _add_sa, @@ -2731,6 +2641,7 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() .del_policy = _del_policy, .flush_policies = _flush_policies, .bypass_socket = _bypass_socket, + .enable_udp_decap = _enable_udp_decap, .destroy = _destroy, }, }, @@ -2749,16 +2660,14 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() this->replay_bmp = (this->replay_window + sizeof(u_int32_t) * 8 - 1) / (sizeof(u_int32_t) * 8); - if (streq(hydra->daemon, "pluto")) - { /* no routes for pluto, they are installed via updown script */ - this->install_routes = FALSE; - /* no policy history for pluto */ - this->policy_history = FALSE; + if (streq(hydra->daemon, "starter")) + { /* starter has no threads, so we do not register for kernel events */ + register_for_events = FALSE; } /* disable lifetimes for allocated SPIs in kernel */ fd = open("/proc/sys/net/core/xfrm_acq_expires", O_WRONLY); - if (fd) + if (fd > 0) { ignore_result(write(fd, "165", 3)); close(fd); @@ -2771,29 +2680,32 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() return NULL; } - memset(&addr, 0, sizeof(addr)); - addr.nl_family = AF_NETLINK; - - /* create and bind XFRM socket for ACQUIRE, EXPIRE, MIGRATE & MAPPING */ - this->socket_xfrm_events = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); - if (this->socket_xfrm_events <= 0) - { - DBG1(DBG_KNL, "unable to create XFRM event socket"); - destroy(this); - return NULL; - } - addr.nl_groups = XFRMNLGRP(ACQUIRE) | XFRMNLGRP(EXPIRE) | - XFRMNLGRP(MIGRATE) | XFRMNLGRP(MAPPING); - if (bind(this->socket_xfrm_events, (struct sockaddr*)&addr, sizeof(addr))) + if (register_for_events) { - DBG1(DBG_KNL, "unable to bind XFRM event socket"); - destroy(this); - return NULL; + struct sockaddr_nl addr; + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + + /* create and bind XFRM socket for ACQUIRE, EXPIRE, MIGRATE & MAPPING */ + this->socket_xfrm_events = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); + if (this->socket_xfrm_events <= 0) + { + DBG1(DBG_KNL, "unable to create XFRM event socket"); + destroy(this); + return NULL; + } + addr.nl_groups = XFRMNLGRP(ACQUIRE) | XFRMNLGRP(EXPIRE) | + XFRMNLGRP(MIGRATE) | XFRMNLGRP(MAPPING); + if (bind(this->socket_xfrm_events, (struct sockaddr*)&addr, sizeof(addr))) + { + DBG1(DBG_KNL, "unable to bind XFRM event socket"); + destroy(this); + return NULL; + } + lib->watcher->add(lib->watcher, this->socket_xfrm_events, WATCHER_READ, + (watcher_cb_t)receive_events, this); } - this->job = callback_job_create_with_prio((callback_job_cb_t)receive_events, - this, NULL, NULL, JOB_PRIO_CRITICAL); - lib->processor->queue_job(lib->processor, (job_t*)this->job); return &this->public; } - diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c index cce0ff402..e129ab131 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Tobias Brunner + * Copyright (C) 2008-2013 Tobias Brunner * Copyright (C) 2005-2008 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -38,6 +38,7 @@ */ #include <sys/socket.h> +#include <sys/utsname.h> #include <linux/netlink.h> #include <linux/rtnetlink.h> #include <unistd.h> @@ -48,34 +49,50 @@ #include "kernel_netlink_shared.h" #include <hydra.h> -#include <debug.h> -#include <threading/thread.h> -#include <threading/condvar.h> +#include <utils/debug.h> #include <threading/mutex.h> -#include <utils/linked_list.h> +#include <threading/rwlock.h> +#include <threading/rwlock_condvar.h> +#include <threading/spinlock.h> +#include <collections/hashtable.h> +#include <collections/linked_list.h> #include <processing/jobs/callback_job.h> /** delay before firing roam events (ms) */ #define ROAM_DELAY 100 +/** delay before reinstalling routes (ms) */ +#define ROUTE_DELAY 100 + +/** maximum recursion when searching for addresses in get_route() */ +#define MAX_ROUTE_RECURSION 2 + +#ifndef ROUTING_TABLE +#define ROUTING_TABLE 0 +#endif + +#ifndef ROUTING_TABLE_PRIO +#define ROUTING_TABLE_PRIO 0 +#endif + typedef struct addr_entry_t addr_entry_t; /** - * IP address in an inface_entry_t + * IP address in an iface_entry_t */ struct addr_entry_t { - /** The ip address */ + /** the ip address */ host_t *ip; - /** virtual IP managed by us */ - bool virtual; - /** scope of the address */ u_char scope; - /** Number of times this IP is used, if virtual */ + /** number of times this IP is used, if virtual (i.e. managed by us) */ u_int refcount; + + /** TRUE once it is installed, if virtual */ + bool installed; }; /** @@ -105,6 +122,9 @@ struct iface_entry_t { /** list of addresses as host_t */ linked_list_t *addrs; + + /** TRUE if usable by config */ + bool usable; }; /** @@ -116,6 +136,212 @@ static void iface_entry_destroy(iface_entry_t *this) free(this); } +/** + * find an interface entry by index + */ +static bool iface_entry_by_index(iface_entry_t *this, int *ifindex) +{ + return this->ifindex == *ifindex; +} + +/** + * find an interface entry by name + */ +static bool iface_entry_by_name(iface_entry_t *this, char *ifname) +{ + return streq(this->ifname, ifname); +} + +/** + * check if an interface is up + */ +static inline bool iface_entry_up(iface_entry_t *iface) +{ + return (iface->flags & IFF_UP) == IFF_UP; +} + +/** + * check if an interface is up and usable + */ +static inline bool iface_entry_up_and_usable(iface_entry_t *iface) +{ + return iface->usable && iface_entry_up(iface); +} + +typedef struct addr_map_entry_t addr_map_entry_t; + +/** + * Entry that maps an IP address to an interface entry + */ +struct addr_map_entry_t { + /** The IP address */ + host_t *ip; + + /** The address entry for this IP address */ + addr_entry_t *addr; + + /** The interface this address is installed on */ + iface_entry_t *iface; +}; + +/** + * Hash a addr_map_entry_t object, all entries with the same IP address + * are stored in the same bucket + */ +static u_int addr_map_entry_hash(addr_map_entry_t *this) +{ + return chunk_hash(this->ip->get_address(this->ip)); +} + +/** + * Compare two addr_map_entry_t objects, two entries are equal if they are + * installed on the same interface + */ +static bool addr_map_entry_equals(addr_map_entry_t *a, addr_map_entry_t *b) +{ + return a->iface->ifindex == b->iface->ifindex && + a->ip->ip_equals(a->ip, b->ip); +} + +/** + * Used with get_match this finds an address entry if it is installed on + * an up and usable interface + */ +static bool addr_map_entry_match_up_and_usable(addr_map_entry_t *a, + addr_map_entry_t *b) +{ + return iface_entry_up_and_usable(b->iface) && + a->ip->ip_equals(a->ip, b->ip); +} + +/** + * Used with get_match this finds an address entry if it is installed on + * any active local interface + */ +static bool addr_map_entry_match_up(addr_map_entry_t *a, addr_map_entry_t *b) +{ + return iface_entry_up(b->iface) && a->ip->ip_equals(a->ip, b->ip); +} + +/** + * Used with get_match this finds an address entry if it is installed on + * any local interface + */ +static bool addr_map_entry_match(addr_map_entry_t *a, addr_map_entry_t *b) +{ + return a->ip->ip_equals(a->ip, b->ip); +} + +typedef struct route_entry_t route_entry_t; + +/** + * Installed routing entry + */ +struct route_entry_t { + /** Name of the interface the route is bound to */ + char *if_name; + + /** Source ip of the route */ + host_t *src_ip; + + /** Gateway for this route */ + host_t *gateway; + + /** Destination net */ + chunk_t dst_net; + + /** Destination net prefixlen */ + u_int8_t prefixlen; +}; + +/** + * Clone a route_entry_t object. + */ +static route_entry_t *route_entry_clone(route_entry_t *this) +{ + route_entry_t *route; + + INIT(route, + .if_name = strdup(this->if_name), + .src_ip = this->src_ip->clone(this->src_ip), + .gateway = this->gateway ? this->gateway->clone(this->gateway) : NULL, + .dst_net = chunk_clone(this->dst_net), + .prefixlen = this->prefixlen, + ); + return route; +} + +/** + * Destroy a route_entry_t object + */ +static void route_entry_destroy(route_entry_t *this) +{ + free(this->if_name); + DESTROY_IF(this->src_ip); + DESTROY_IF(this->gateway); + chunk_free(&this->dst_net); + free(this); +} + +/** + * Hash a route_entry_t object + */ +static u_int route_entry_hash(route_entry_t *this) +{ + return chunk_hash_inc(chunk_from_thing(this->prefixlen), + chunk_hash(this->dst_net)); +} + +/** + * Compare two route_entry_t objects + */ +static bool route_entry_equals(route_entry_t *a, route_entry_t *b) +{ + if (a->if_name && b->if_name && streq(a->if_name, b->if_name) && + a->src_ip->ip_equals(a->src_ip, b->src_ip) && + chunk_equals(a->dst_net, b->dst_net) && a->prefixlen == b->prefixlen) + { + return (!a->gateway && !b->gateway) || (a->gateway && b->gateway && + a->gateway->ip_equals(a->gateway, b->gateway)); + } + return FALSE; +} + +typedef struct net_change_t net_change_t; + +/** + * Queued network changes + */ +struct net_change_t { + /** Name of the interface that got activated (or an IP appeared on) */ + char *if_name; +}; + +/** + * Destroy a net_change_t object + */ +static void net_change_destroy(net_change_t *this) +{ + free(this->if_name); + free(this); +} + +/** + * Hash a net_change_t object + */ +static u_int net_change_hash(net_change_t *this) +{ + return chunk_hash(chunk_create(this->if_name, strlen(this->if_name))); +} + +/** + * Compare two net_change_t objects + */ +static bool net_change_equals(net_change_t *a, net_change_t *b) +{ + return streq(a->if_name, b->if_name); +} + typedef struct private_kernel_netlink_net_t private_kernel_netlink_net_t; /** @@ -128,14 +354,14 @@ struct private_kernel_netlink_net_t { kernel_netlink_net_t public; /** - * mutex to lock access to various lists + * lock to access various lists and maps */ - mutex_t *mutex; + rwlock_t *lock; /** * condition variable to signal virtual IP add/removal */ - condvar_t *condvar; + rwlock_condvar_t *condvar; /** * Cached list of interfaces and its addresses (iface_entry_t) @@ -143,9 +369,14 @@ struct private_kernel_netlink_net_t { linked_list_t *ifaces; /** - * job receiving netlink events + * Map for IP addresses to iface_entry_t objects (addr_map_entry_t) + */ + hashtable_t *addrs; + + /** + * Map for virtual IP addresses to iface_entry_t objects (addr_map_entry_t) */ - callback_job_t *job; + hashtable_t *vips; /** * netlink rt socket (routing) @@ -158,9 +389,14 @@ struct private_kernel_netlink_net_t { int socket_events; /** - * time of the last roam event + * earliest time of the next roam event */ - timeval_t last_roam; + timeval_t next_roam; + + /** + * lock to check and update roam event time + */ + spinlock_t *roam_lock; /** * routing table to install routes @@ -173,89 +409,288 @@ struct private_kernel_netlink_net_t { int routing_table_prio; /** + * installed routes + */ + hashtable_t *routes; + + /** + * mutex for routes + */ + mutex_t *routes_lock; + + /** + * interface changes which may trigger route reinstallation + */ + hashtable_t *net_changes; + + /** + * mutex for route reinstallation triggers + */ + mutex_t *net_changes_lock; + + /** + * time of last route reinstallation + */ + timeval_t last_route_reinstall; + + /** * whether to react to RTM_NEWROUTE or RTM_DELROUTE events */ bool process_route; /** + * whether to trigger roam events + */ + bool roam_events; + + /** * whether to actually install virtual IPs */ bool install_virtual_ip; /** + * the name of the interface virtual IP addresses are installed on + */ + char *install_virtual_ip_on; + + /** + * whether preferred source addresses can be specified for IPv6 routes + */ + bool rta_prefsrc_for_ipv6; + + /** * list with routing tables to be excluded from route lookup */ linked_list_t *rt_exclude; }; /** - * get the refcount of a virtual ip + * Forward declaration */ -static int get_vip_refcount(private_kernel_netlink_net_t *this, host_t* ip) +static status_t manage_srcroute(private_kernel_netlink_net_t *this, + int nlmsg_type, int flags, chunk_t dst_net, + u_int8_t prefixlen, host_t *gateway, + host_t *src_ip, char *if_name); + +/** + * Clear the queued network changes. + */ +static void net_changes_clear(private_kernel_netlink_net_t *this) { - enumerator_t *ifaces, *addrs; - iface_entry_t *iface; - addr_entry_t *addr; - int refcount = 0; + enumerator_t *enumerator; + net_change_t *change; - ifaces = this->ifaces->create_enumerator(this->ifaces); - while (ifaces->enumerate(ifaces, (void**)&iface)) + enumerator = this->net_changes->create_enumerator(this->net_changes); + while (enumerator->enumerate(enumerator, NULL, (void**)&change)) { - addrs = iface->addrs->create_enumerator(iface->addrs); - while (addrs->enumerate(addrs, (void**)&addr)) - { - if (addr->virtual && (iface->flags & IFF_UP) && - ip->ip_equals(ip, addr->ip)) + this->net_changes->remove_at(this->net_changes, enumerator); + net_change_destroy(change); + } + enumerator->destroy(enumerator); +} + +/** + * Act upon queued network changes. + */ +static job_requeue_t reinstall_routes(private_kernel_netlink_net_t *this) +{ + enumerator_t *enumerator; + route_entry_t *route; + + this->net_changes_lock->lock(this->net_changes_lock); + this->routes_lock->lock(this->routes_lock); + + enumerator = this->routes->create_enumerator(this->routes); + while (enumerator->enumerate(enumerator, NULL, (void**)&route)) + { + net_change_t *change, lookup = { + .if_name = route->if_name, + }; + /* check if a change for the outgoing interface is queued */ + change = this->net_changes->get(this->net_changes, &lookup); + if (!change) + { /* in case src_ip is not on the outgoing interface */ + if (this->public.interface.get_interface(&this->public.interface, + route->src_ip, &lookup.if_name)) { - refcount = addr->refcount; - break; + if (!streq(lookup.if_name, route->if_name)) + { + change = this->net_changes->get(this->net_changes, &lookup); + } + free(lookup.if_name); } } - addrs->destroy(addrs); - if (refcount) + if (change) { - break; + manage_srcroute(this, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, + route->dst_net, route->prefixlen, route->gateway, + route->src_ip, route->if_name); } } - ifaces->destroy(ifaces); + enumerator->destroy(enumerator); + this->routes_lock->unlock(this->routes_lock); - return refcount; + net_changes_clear(this); + this->net_changes_lock->unlock(this->net_changes_lock); + return JOB_REQUEUE_NONE; +} + +/** + * Queue route reinstallation caused by network changes for a given interface. + * + * The route reinstallation is delayed for a while and only done once for + * several calls during this delay, in order to avoid doing it too often. + * The interface name is freed. + */ +static void queue_route_reinstall(private_kernel_netlink_net_t *this, + char *if_name) +{ + net_change_t *update, *found; + timeval_t now; + job_t *job; + + INIT(update, + .if_name = if_name + ); + + this->net_changes_lock->lock(this->net_changes_lock); + found = this->net_changes->put(this->net_changes, update, update); + if (found) + { + net_change_destroy(found); + } + time_monotonic(&now); + if (timercmp(&now, &this->last_route_reinstall, >)) + { + timeval_add_ms(&now, ROUTE_DELAY); + this->last_route_reinstall = now; + + job = (job_t*)callback_job_create((callback_job_cb_t)reinstall_routes, + this, NULL, NULL); + lib->scheduler->schedule_job_ms(lib->scheduler, job, ROUTE_DELAY); + } + this->net_changes_lock->unlock(this->net_changes_lock); +} + +/** + * check if the given IP is known as virtual IP and currently installed + * + * this function will also return TRUE if the virtual IP entry disappeared. + * in that case the returned entry will be NULL. + * + * this->lock must be held when calling this function + */ +static bool is_vip_installed_or_gone(private_kernel_netlink_net_t *this, + host_t *ip, addr_map_entry_t **entry) +{ + addr_map_entry_t lookup = { + .ip = ip, + }; + + *entry = this->vips->get_match(this->vips, &lookup, + (void*)addr_map_entry_match); + if (*entry == NULL) + { /* the virtual IP disappeared */ + return TRUE; + } + return (*entry)->addr->installed; +} + +/** + * check if the given IP is known as virtual IP + * + * this->lock must be held when calling this function + */ +static bool is_known_vip(private_kernel_netlink_net_t *this, host_t *ip) +{ + addr_map_entry_t lookup = { + .ip = ip, + }; + + return this->vips->get_match(this->vips, &lookup, + (void*)addr_map_entry_match) != NULL; +} + +/** + * Add an address map entry + */ +static void addr_map_entry_add(hashtable_t *map, addr_entry_t *addr, + iface_entry_t *iface) +{ + addr_map_entry_t *entry; + + INIT(entry, + .ip = addr->ip, + .addr = addr, + .iface = iface, + ); + entry = map->put(map, entry, entry); + free(entry); +} + +/** + * Remove an address map entry + */ +static void addr_map_entry_remove(hashtable_t *map, addr_entry_t *addr, + iface_entry_t *iface) +{ + addr_map_entry_t *entry, lookup = { + .ip = addr->ip, + .addr = addr, + .iface = iface, + }; + + entry = map->remove(map, &lookup); + free(entry); } /** * get the first non-virtual ip address on the given interface. + * if a candidate address is given, we first search for that address and if not + * found return the address as above. * returned host is a clone, has to be freed by caller. + * + * this->lock must be held when calling this function */ static host_t *get_interface_address(private_kernel_netlink_net_t *this, - int ifindex, int family) + int ifindex, int family, host_t *candidate) { - enumerator_t *ifaces, *addrs; iface_entry_t *iface; + enumerator_t *addrs; addr_entry_t *addr; host_t *ip = NULL; - this->mutex->lock(this->mutex); - ifaces = this->ifaces->create_enumerator(this->ifaces); - while (ifaces->enumerate(ifaces, &iface)) + if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_index, + (void**)&iface, &ifindex) == SUCCESS) { - if (iface->ifindex == ifindex) - { + if (iface->usable) + { /* only use interfaces not excluded by config */ addrs = iface->addrs->create_enumerator(iface->addrs); while (addrs->enumerate(addrs, &addr)) { - if (!addr->virtual && addr->ip->get_family(addr->ip) == family) + if (addr->refcount) + { /* ignore virtual IP addresses */ + continue; + } + if (addr->ip->get_family(addr->ip) == family) { - ip = addr->ip->clone(addr->ip); - break; + if (!candidate || candidate->ip_equals(candidate, addr->ip)) + { /* stop at the first address if we don't search for a + * candidate or if the candidate matches */ + ip = addr->ip; + break; + } + else if (!ip) + { /* store the first address as fallback if candidate is + * not found */ + ip = addr->ip; + } } } addrs->destroy(addrs); - break; } } - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); - return ip; + return ip ? ip->clone(ip) : NULL; } /** @@ -276,22 +711,61 @@ static void fire_roam_event(private_kernel_netlink_net_t *this, bool address) timeval_t now; job_t *job; + if (!this->roam_events) + { + return; + } + time_monotonic(&now); - if (timercmp(&now, &this->last_roam, >)) + this->roam_lock->lock(this->roam_lock); + if (!timercmp(&now, &this->next_roam, >)) { - now.tv_usec += ROAM_DELAY * 1000; - while (now.tv_usec > 1000000) - { - now.tv_sec++; - now.tv_usec -= 1000000; - } - this->last_roam = now; + this->roam_lock->unlock(this->roam_lock); + return; + } + timeval_add_ms(&now, ROAM_DELAY); + this->next_roam = now; + this->roam_lock->unlock(this->roam_lock); + + job = (job_t*)callback_job_create((callback_job_cb_t)roam_event, + (void*)(uintptr_t)(address ? 1 : 0), + NULL, NULL); + lib->scheduler->schedule_job_ms(lib->scheduler, job, ROAM_DELAY); +} + +/** + * check if an interface with a given index is up and usable + * + * this->lock must be locked when calling this function + */ +static bool is_interface_up_and_usable(private_kernel_netlink_net_t *this, + int index) +{ + iface_entry_t *iface; + + if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_index, + (void**)&iface, &index) == SUCCESS) + { + return iface_entry_up_and_usable(iface); + } + return FALSE; +} - job = (job_t*)callback_job_create((callback_job_cb_t)roam_event, - (void*)(uintptr_t)(address ? 1 : 0), - NULL, NULL); - lib->scheduler->schedule_job_ms(lib->scheduler, job, ROAM_DELAY); +/** + * unregister the current addr_entry_t from the hashtable it is stored in + * + * this->lock must be locked when calling this function + */ +static void addr_entry_unregister(addr_entry_t *addr, iface_entry_t *iface, + private_kernel_netlink_net_t *this) +{ + if (addr->refcount) + { + addr_map_entry_remove(this->vips, addr, iface); + this->condvar->broadcast(this->condvar); + return; } + addr_map_entry_remove(this->addrs, addr, iface); } /** @@ -306,9 +780,9 @@ static void process_link(private_kernel_netlink_net_t *this, enumerator_t *enumerator; iface_entry_t *current, *entry = NULL; char *name = NULL; - bool update = FALSE; + bool update = FALSE, update_routes = FALSE; - while(RTA_OK(rta, rtasize)) + while (RTA_OK(rta, rtasize)) { switch (rta->rta_type) { @@ -323,40 +797,30 @@ static void process_link(private_kernel_netlink_net_t *this, name = "(unknown)"; } - this->mutex->lock(this->mutex); + this->lock->write_lock(this->lock); switch (hdr->nlmsg_type) { case RTM_NEWLINK: { - if (msg->ifi_flags & IFF_LOOPBACK) - { /* ignore loopback interfaces */ - break; - } - enumerator = this->ifaces->create_enumerator(this->ifaces); - while (enumerator->enumerate(enumerator, ¤t)) + if (this->ifaces->find_first(this->ifaces, + (void*)iface_entry_by_index, (void**)&entry, + &msg->ifi_index) != SUCCESS) { - if (current->ifindex == msg->ifi_index) - { - entry = current; - break; - } - } - enumerator->destroy(enumerator); - if (!entry) - { - entry = malloc_thing(iface_entry_t); - entry->ifindex = msg->ifi_index; - entry->flags = 0; - entry->addrs = linked_list_create(); + INIT(entry, + .ifindex = msg->ifi_index, + .addrs = linked_list_create(), + .usable = hydra->kernel_interface->is_interface_usable( + hydra->kernel_interface, name), + ); this->ifaces->insert_last(this->ifaces, entry); } strncpy(entry->ifname, name, IFNAMSIZ); entry->ifname[IFNAMSIZ-1] = '\0'; - if (event) + if (event && entry->usable) { if (!(entry->flags & IFF_UP) && (msg->ifi_flags & IFF_UP)) { - update = TRUE; + update = update_routes = TRUE; DBG1(DBG_KNL, "interface %s activated", name); } if ((entry->flags & IFF_UP) && !(msg->ifi_flags & IFF_UP)) @@ -375,12 +839,16 @@ static void process_link(private_kernel_netlink_net_t *this, { if (current->ifindex == msg->ifi_index) { - if (event) + if (event && current->usable) { update = TRUE; DBG1(DBG_KNL, "interface %s deleted", current->ifname); } + /* TODO: move virtual IPs installed on this interface to + * another interface? */ this->ifaces->remove_at(this->ifaces, enumerator); + current->addrs->invoke_function(current->addrs, + (void*)addr_entry_unregister, current, this); iface_entry_destroy(current); break; } @@ -389,9 +857,13 @@ static void process_link(private_kernel_netlink_net_t *this, break; } } - this->mutex->unlock(this->mutex); + this->lock->unlock(this->lock); + + if (update_routes && event) + { + queue_route_reinstall(this, strdup(name)); + } - /* send an update to all IKE_SAs */ if (update && event) { fire_roam_event(this, TRUE); @@ -408,13 +880,12 @@ static void process_addr(private_kernel_netlink_net_t *this, struct rtattr *rta = IFA_RTA(msg); size_t rtasize = IFA_PAYLOAD (hdr); host_t *host = NULL; - enumerator_t *ifaces, *addrs; iface_entry_t *iface; - addr_entry_t *addr; chunk_t local = chunk_empty, address = chunk_empty; + char *route_ifname = NULL; bool update = FALSE, found = FALSE, changed = FALSE; - while(RTA_OK(rta, rtasize)) + while (RTA_OK(rta, rtasize)) { switch (rta->rta_type) { @@ -447,65 +918,92 @@ static void process_addr(private_kernel_netlink_net_t *this, return; } - this->mutex->lock(this->mutex); - ifaces = this->ifaces->create_enumerator(this->ifaces); - while (ifaces->enumerate(ifaces, &iface)) + this->lock->write_lock(this->lock); + if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_index, + (void**)&iface, &msg->ifa_index) == SUCCESS) { - if (iface->ifindex == msg->ifa_index) + addr_map_entry_t *entry, lookup = { + .ip = host, + .iface = iface, + }; + addr_entry_t *addr; + + entry = this->vips->get(this->vips, &lookup); + if (entry) { - addrs = iface->addrs->create_enumerator(iface->addrs); - while (addrs->enumerate(addrs, &addr)) + if (hdr->nlmsg_type == RTM_NEWADDR) + { /* mark as installed and signal waiting threads */ + entry->addr->installed = TRUE; + } + else + { /* the address was already marked as uninstalled */ + addr = entry->addr; + iface->addrs->remove(iface->addrs, addr, NULL); + addr_map_entry_remove(this->vips, addr, iface); + addr_entry_destroy(addr); + } + /* no roam events etc. for virtual IPs */ + this->condvar->broadcast(this->condvar); + this->lock->unlock(this->lock); + host->destroy(host); + return; + } + entry = this->addrs->get(this->addrs, &lookup); + if (entry) + { + if (hdr->nlmsg_type == RTM_DELADDR) { - if (host->ip_equals(host, addr->ip)) + found = TRUE; + addr = entry->addr; + iface->addrs->remove(iface->addrs, addr, NULL); + if (iface->usable) { - found = TRUE; - if (hdr->nlmsg_type == RTM_DELADDR) - { - iface->addrs->remove_at(iface->addrs, addrs); - if (!addr->virtual) - { - changed = TRUE; - DBG1(DBG_KNL, "%H disappeared from %s", - host, iface->ifname); - } - addr_entry_destroy(addr); - } - else if (hdr->nlmsg_type == RTM_NEWADDR && addr->virtual) - { - addr->refcount = 1; - } + changed = TRUE; + DBG1(DBG_KNL, "%H disappeared from %s", host, + iface->ifname); } + addr_map_entry_remove(this->addrs, addr, iface); + addr_entry_destroy(addr); } - addrs->destroy(addrs); - + } + else + { if (hdr->nlmsg_type == RTM_NEWADDR) { - if (!found) + found = TRUE; + changed = TRUE; + route_ifname = strdup(iface->ifname); + INIT(addr, + .ip = host->clone(host), + .scope = msg->ifa_scope, + ); + iface->addrs->insert_last(iface->addrs, addr); + addr_map_entry_add(this->addrs, addr, iface); + if (event && iface->usable) { - found = TRUE; - changed = TRUE; - addr = malloc_thing(addr_entry_t); - addr->ip = host->clone(host); - addr->virtual = FALSE; - addr->refcount = 1; - addr->scope = msg->ifa_scope; - - iface->addrs->insert_last(iface->addrs, addr); - if (event) - { - DBG1(DBG_KNL, "%H appeared on %s", host, iface->ifname); - } + DBG1(DBG_KNL, "%H appeared on %s", host, iface->ifname); } } - if (found && (iface->flags & IFF_UP)) - { - update = TRUE; - } - break; + } + if (found && (iface->flags & IFF_UP)) + { + update = TRUE; + } + if (!iface->usable) + { /* ignore events for interfaces excluded by config */ + update = changed = FALSE; } } - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); + this->lock->unlock(this->lock); + + if (update && event && route_ifname) + { + queue_route_reinstall(this, route_ifname); + } + else + { + free(route_ifname); + } host->destroy(host); /* send an update to all IKE_SAs */ @@ -532,6 +1030,10 @@ static void process_route(private_kernel_netlink_net_t *this, struct nlmsghdr *h { return; } + else if (msg->rtm_flags & RTM_F_CLONED) + { /* ignore cached routes, seem to be created a lot for IPv6 */ + return; + } while (RTA_OK(rta, rtasize)) { @@ -551,59 +1053,62 @@ static void process_route(private_kernel_netlink_net_t *this, struct nlmsghdr *h } rta = RTA_NEXT(rta, rtasize); } + this->lock->read_lock(this->lock); + if (rta_oif && !is_interface_up_and_usable(this, rta_oif)) + { /* ignore route changes for interfaces that are ignored or down */ + this->lock->unlock(this->lock); + DESTROY_IF(host); + return; + } if (!host && rta_oif) { - host = get_interface_address(this, rta_oif, msg->rtm_family); + host = get_interface_address(this, rta_oif, msg->rtm_family, NULL); } - if (host) - { - this->mutex->lock(this->mutex); - if (!get_vip_refcount(this, host)) - { /* ignore routes added for virtual IPs */ - fire_roam_event(this, FALSE); - } - this->mutex->unlock(this->mutex); - host->destroy(host); + if (!host || is_known_vip(this, host)) + { /* ignore routes added for virtual IPs */ + this->lock->unlock(this->lock); + DESTROY_IF(host); + return; } + this->lock->unlock(this->lock); + fire_roam_event(this, FALSE); + host->destroy(host); } /** * Receives events from kernel */ -static job_requeue_t receive_events(private_kernel_netlink_net_t *this) +static bool receive_events(private_kernel_netlink_net_t *this, int fd, + watcher_event_t event) { char response[1024]; struct nlmsghdr *hdr = (struct nlmsghdr*)response; struct sockaddr_nl addr; socklen_t addr_len = sizeof(addr); int len; - bool oldstate; - - oldstate = thread_cancelability(TRUE); - len = recvfrom(this->socket_events, response, sizeof(response), 0, - (struct sockaddr*)&addr, &addr_len); - thread_cancelability(oldstate); + len = recvfrom(this->socket_events, response, sizeof(response), + MSG_DONTWAIT, (struct sockaddr*)&addr, &addr_len); if (len < 0) { switch (errno) { case EINTR: /* interrupted, try again */ - return JOB_REQUEUE_DIRECT; + return TRUE; case EAGAIN: /* no data ready, select again */ - return JOB_REQUEUE_DIRECT; + return TRUE; default: DBG1(DBG_KNL, "unable to receive from rt event socket"); sleep(1); - return JOB_REQUEUE_FAIR; + return TRUE; } } if (addr.nl_pid != 0) { /* not from kernel. not interested, try another one */ - return JOB_REQUEUE_DIRECT; + return TRUE; } while (NLMSG_OK(hdr, len)) @@ -614,12 +1119,10 @@ static job_requeue_t receive_events(private_kernel_netlink_net_t *this) case RTM_NEWADDR: case RTM_DELADDR: process_addr(this, hdr, TRUE); - this->condvar->broadcast(this->condvar); break; case RTM_NEWLINK: case RTM_DELLINK: process_link(this, hdr, TRUE); - this->condvar->broadcast(this->condvar); break; case RTM_NEWROUTE: case RTM_DELROUTE: @@ -633,16 +1136,14 @@ static job_requeue_t receive_events(private_kernel_netlink_net_t *this) } hdr = NLMSG_NEXT(hdr, len); } - return JOB_REQUEUE_DIRECT; + return TRUE; } /** enumerator over addresses */ typedef struct { private_kernel_netlink_net_t* this; - /** whether to enumerate down interfaces */ - bool include_down_ifaces; - /** whether to enumerate virtual ip addresses */ - bool include_virtual_ips; + /** which addresses to enumerate */ + kernel_address_type_t which; } address_enumerator_t; /** @@ -650,7 +1151,7 @@ typedef struct { */ static void address_enumerator_destroy(address_enumerator_t *data) { - data->this->mutex->unlock(data->this->mutex); + data->this->lock->unlock(data->this->lock); free(data); } @@ -660,10 +1161,14 @@ static void address_enumerator_destroy(address_enumerator_t *data) static bool filter_addresses(address_enumerator_t *data, addr_entry_t** in, host_t** out) { - if (!data->include_virtual_ips && (*in)->virtual) + if (!(data->which & ADDR_TYPE_VIRTUAL) && (*in)->refcount) { /* skip virtual interfaces added by us */ return FALSE; } + if (!(data->which & ADDR_TYPE_REGULAR) && !(*in)->refcount) + { /* address is regular, but not requested */ + return FALSE; + } if ((*in)->scope >= RT_SCOPE_LINK) { /* skip addresses with a unusable scope */ return FALSE; @@ -689,7 +1194,15 @@ static enumerator_t *create_iface_enumerator(iface_entry_t *iface, static bool filter_interfaces(address_enumerator_t *data, iface_entry_t** in, iface_entry_t** out) { - if (!data->include_down_ifaces && !((*in)->flags & IFF_UP)) + if (!(data->which & ADDR_TYPE_IGNORED) && !(*in)->usable) + { /* skip interfaces excluded by config */ + return FALSE; + } + if (!(data->which & ADDR_TYPE_LOOPBACK) && ((*in)->flags & IFF_LOOPBACK)) + { /* ignore loopback devices */ + return FALSE; + } + if (!(data->which & ADDR_TYPE_DOWN) && !((*in)->flags & IFF_UP)) { /* skip interfaces not up */ return FALSE; } @@ -698,15 +1211,16 @@ static bool filter_interfaces(address_enumerator_t *data, iface_entry_t** in, } METHOD(kernel_net_t, create_address_enumerator, enumerator_t*, - private_kernel_netlink_net_t *this, - bool include_down_ifaces, bool include_virtual_ips) + private_kernel_netlink_net_t *this, kernel_address_type_t which) { - address_enumerator_t *data = malloc_thing(address_enumerator_t); - data->this = this; - data->include_down_ifaces = include_down_ifaces; - data->include_virtual_ips = include_virtual_ips; + address_enumerator_t *data; + + INIT(data, + .this = this, + .which = which, + ); - this->mutex->lock(this->mutex); + this->lock->read_lock(this->lock); return enumerator_create_nested( enumerator_create_filter( this->ifaces->create_enumerator(this->ifaces), @@ -715,47 +1229,53 @@ METHOD(kernel_net_t, create_address_enumerator, enumerator_t*, (void*)address_enumerator_destroy); } -METHOD(kernel_net_t, get_interface_name, char*, - private_kernel_netlink_net_t *this, host_t* ip) +METHOD(kernel_net_t, get_interface_name, bool, + private_kernel_netlink_net_t *this, host_t* ip, char **name) { - enumerator_t *ifaces, *addrs; - iface_entry_t *iface; - addr_entry_t *addr; - char *name = NULL; - - DBG2(DBG_KNL, "getting interface name for %H", ip); + addr_map_entry_t *entry, lookup = { + .ip = ip, + }; - this->mutex->lock(this->mutex); - ifaces = this->ifaces->create_enumerator(this->ifaces); - while (ifaces->enumerate(ifaces, &iface)) + if (ip->is_anyaddr(ip)) + { + return FALSE; + } + this->lock->read_lock(this->lock); + /* first try to find it on an up and usable interface */ + entry = this->addrs->get_match(this->addrs, &lookup, + (void*)addr_map_entry_match_up_and_usable); + if (entry) { - addrs = iface->addrs->create_enumerator(iface->addrs); - while (addrs->enumerate(addrs, &addr)) - { - if (ip->ip_equals(ip, addr->ip)) - { - name = strdup(iface->ifname); - break; - } - } - addrs->destroy(addrs); if (name) { - break; + *name = strdup(entry->iface->ifname); + DBG2(DBG_KNL, "%H is on interface %s", ip, *name); } + this->lock->unlock(this->lock); + return TRUE; } - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); - - if (name) + /* in a second step, consider virtual IPs installed by us */ + entry = this->vips->get_match(this->vips, &lookup, + (void*)addr_map_entry_match_up_and_usable); + if (entry) { - DBG2(DBG_KNL, "%H is on interface %s", ip, name); + if (name) + { + *name = strdup(entry->iface->ifname); + DBG2(DBG_KNL, "virtual IP %H is on interface %s", ip, *name); + } + this->lock->unlock(this->lock); + return TRUE; } - else + /* maybe it is installed on an ignored interface */ + entry = this->addrs->get_match(this->addrs, &lookup, + (void*)addr_map_entry_match_up); + if (!entry) { - DBG2(DBG_KNL, "%H is not a local address", ip); + DBG2(DBG_KNL, "%H is not a local address or the interface is down", ip); } - return name; + this->lock->unlock(this->lock); + return FALSE; } /** @@ -763,24 +1283,18 @@ METHOD(kernel_net_t, get_interface_name, char*, */ static int get_interface_index(private_kernel_netlink_net_t *this, char* name) { - enumerator_t *ifaces; iface_entry_t *iface; int ifindex = 0; DBG2(DBG_KNL, "getting iface index for %s", name); - this->mutex->lock(this->mutex); - ifaces = this->ifaces->create_enumerator(this->ifaces); - while (ifaces->enumerate(ifaces, &iface)) + this->lock->read_lock(this->lock); + if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_name, + (void**)&iface, name) == SUCCESS) { - if (streq(name, iface->ifname)) - { - ifindex = iface->ifindex; - break; - } + ifindex = iface->ifindex; } - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); + this->lock->unlock(this->lock); if (ifindex == 0) { @@ -790,29 +1304,6 @@ static int get_interface_index(private_kernel_netlink_net_t *this, char* name) } /** - * Check if an interface with a given index is up - */ -static bool is_interface_up(private_kernel_netlink_net_t *this, int index) -{ - enumerator_t *ifaces; - iface_entry_t *iface; - /* default to TRUE for interface we do not monitor (e.g. lo) */ - bool up = TRUE; - - ifaces = this->ifaces->create_enumerator(this->ifaces); - while (ifaces->enumerate(ifaces, &iface)) - { - if (iface->ifindex == index) - { - up = iface->flags & IFF_UP; - break; - } - } - ifaces->destroy(ifaces); - return up; -} - -/** * check if an address (chunk) addr is in subnet (net with net_len net bits) */ static bool addr_in_subnet(chunk_t addr, chunk_t net, int net_len) @@ -849,32 +1340,124 @@ static bool addr_in_subnet(chunk_t addr, chunk_t net, int net_len) } /** + * Store information about a route retrieved via RTNETLINK + */ +typedef struct { + chunk_t gtw; + chunk_t src; + chunk_t dst; + host_t *src_host; + u_int8_t dst_len; + u_int32_t table; + u_int32_t oif; +} rt_entry_t; + +/** + * Free a route entry + */ +static void rt_entry_destroy(rt_entry_t *this) +{ + DESTROY_IF(this->src_host); + free(this); +} + +/** + * Parse route received with RTM_NEWROUTE. The given rt_entry_t object will be + * reused if not NULL. + * + * Returned chunks point to internal data of the Netlink message. + */ +static rt_entry_t *parse_route(struct nlmsghdr *hdr, rt_entry_t *route) +{ + struct rtattr *rta; + struct rtmsg *msg; + size_t rtasize; + + msg = (struct rtmsg*)(NLMSG_DATA(hdr)); + rta = RTM_RTA(msg); + rtasize = RTM_PAYLOAD(hdr); + + if (route) + { + route->gtw = chunk_empty; + route->src = chunk_empty; + route->dst = chunk_empty; + route->dst_len = msg->rtm_dst_len; + route->table = msg->rtm_table; + route->oif = 0; + } + else + { + INIT(route, + .dst_len = msg->rtm_dst_len, + .table = msg->rtm_table, + ); + } + + while (RTA_OK(rta, rtasize)) + { + switch (rta->rta_type) + { + case RTA_PREFSRC: + route->src = chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta)); + break; + case RTA_GATEWAY: + route->gtw = chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta)); + break; + case RTA_DST: + route->dst = chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta)); + break; + case RTA_OIF: + if (RTA_PAYLOAD(rta) == sizeof(route->oif)) + { + route->oif = *(u_int32_t*)RTA_DATA(rta); + } + break; +#ifdef HAVE_RTA_TABLE + case RTA_TABLE: + if (RTA_PAYLOAD(rta) == sizeof(route->table)) + { + route->table = *(u_int32_t*)RTA_DATA(rta); + } + break; +#endif /* HAVE_RTA_TABLE*/ + } + rta = RTA_NEXT(rta, rtasize); + } + return route; +} + +/** * Get a route: If "nexthop", the nexthop is returned. source addr otherwise. */ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest, - bool nexthop, host_t *candidate) + bool nexthop, host_t *candidate, u_int recursion) { netlink_buf_t request; struct nlmsghdr *hdr, *out, *current; struct rtmsg *msg; chunk_t chunk; size_t len; - int best = -1; + linked_list_t *routes; + rt_entry_t *route = NULL, *best = NULL; enumerator_t *enumerator; - host_t *src = NULL, *gtw = NULL; + host_t *addr = NULL; - DBG2(DBG_KNL, "getting address to reach %H", dest); + if (recursion > MAX_ROUTE_RECURSION) + { + return NULL; + } memset(&request, 0, sizeof(request)); hdr = (struct nlmsghdr*)request; hdr->nlmsg_flags = NLM_F_REQUEST; - if (dest->get_family(dest) == AF_INET) - { - /* We dump all addresses for IPv4, as we want to ignore IPsec specific - * routes installed by us. But the kernel does not return source - * addresses in a IPv6 dump, so fall back to get() for v6 routes. */ - hdr->nlmsg_flags |= NLM_F_ROOT | NLM_F_DUMP; + if (dest->get_family(dest) == AF_INET || this->rta_prefsrc_for_ipv6 || + this->routing_table) + { /* kernels prior to 3.0 do not support RTA_PREFSRC for IPv6 routes. + * as we want to ignore routes with virtual IPs we cannot use DUMP + * if these routes are not installed in a separate table */ + hdr->nlmsg_flags |= NLM_F_DUMP; } hdr->nlmsg_type = RTM_GETROUTE; hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); @@ -891,10 +1474,12 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest, if (this->socket->send(this->socket, hdr, &out, &len) != SUCCESS) { - DBG1(DBG_KNL, "getting address to %H failed", dest); + DBG2(DBG_KNL, "getting %s to reach %H failed", + nexthop ? "nexthop" : "address", dest); return NULL; } - this->mutex->lock(this->mutex); + routes = linked_list_create(); + this->lock->read_lock(this->lock); for (current = out; NLMSG_OK(current, len); current = NLMSG_NEXT(current, len)) @@ -905,132 +1490,53 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest, break; case RTM_NEWROUTE: { - struct rtattr *rta; - size_t rtasize; - chunk_t rta_gtw, rta_src, rta_dst; - u_int32_t rta_oif = 0, rta_table; - host_t *new_src, *new_gtw; - bool cont = FALSE; + rt_entry_t *other; uintptr_t table; - rta_gtw = rta_src = rta_dst = chunk_empty; - msg = (struct rtmsg*)(NLMSG_DATA(current)); - rta = RTM_RTA(msg); - rtasize = RTM_PAYLOAD(current); - rta_table = msg->rtm_table; - while (RTA_OK(rta, rtasize)) - { - switch (rta->rta_type) - { - case RTA_PREFSRC: - rta_src = chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta)); - break; - case RTA_GATEWAY: - rta_gtw = chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta)); - break; - case RTA_DST: - rta_dst = chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta)); - break; - case RTA_OIF: - if (RTA_PAYLOAD(rta) == sizeof(rta_oif)) - { - rta_oif = *(u_int32_t*)RTA_DATA(rta); - } - break; -#ifdef HAVE_RTA_TABLE - case RTA_TABLE: - if (RTA_PAYLOAD(rta) == sizeof(rta_table)) - { - rta_table = *(u_int32_t*)RTA_DATA(rta); - } - break; -#endif /* HAVE_RTA_TABLE*/ - } - rta = RTA_NEXT(rta, rtasize); - } - if (msg->rtm_dst_len <= best) - { /* not better than a previous one */ - continue; - } - enumerator = this->rt_exclude->create_enumerator(this->rt_exclude); - while (enumerator->enumerate(enumerator, &table)) - { - if (table == rta_table) - { - cont = TRUE; - break; - } - } - enumerator->destroy(enumerator); - if (cont) - { + route = parse_route(current, route); + + table = (uintptr_t)route->table; + if (this->rt_exclude->find_first(this->rt_exclude, NULL, + (void**)&table) == SUCCESS) + { /* route is from an excluded routing table */ continue; } if (this->routing_table != 0 && - rta_table == this->routing_table) + route->table == this->routing_table) { /* route is from our own ipsec routing table */ continue; } - if (rta_oif && !is_interface_up(this, rta_oif)) + if (route->oif && !is_interface_up_and_usable(this, route->oif)) { /* interface is down */ continue; } - if (!addr_in_subnet(chunk, rta_dst, msg->rtm_dst_len)) + if (!addr_in_subnet(chunk, route->dst, route->dst_len)) { /* route destination does not contain dest */ continue; } - - if (nexthop) - { - /* nexthop lookup, return gateway if any */ - DESTROY_IF(gtw); - gtw = host_create_from_chunk(msg->rtm_family, rta_gtw, 0); - best = msg->rtm_dst_len; - continue; - } - if (rta_src.ptr) - { /* got a source address */ - new_src = host_create_from_chunk(msg->rtm_family, rta_src, 0); - if (new_src) - { - if (get_vip_refcount(this, new_src)) - { /* skip source address if it is installed by us */ - new_src->destroy(new_src); - } - else - { - DESTROY_IF(src); - src = new_src; - best = msg->rtm_dst_len; - } - } - continue; - } - if (rta_oif) - { /* no src or gtw, but an interface. Get address from it. */ - new_src = get_interface_address(this, rta_oif, - msg->rtm_family); - if (new_src) - { - DESTROY_IF(src); - src = new_src; - best = msg->rtm_dst_len; + if (route->src.ptr) + { /* verify source address, if any */ + host_t *src = host_create_from_chunk(msg->rtm_family, + route->src, 0); + if (src && is_known_vip(this, src)) + { /* ignore routes installed by us */ + src->destroy(src); + continue; } - continue; + route->src_host = src; } - if (rta_gtw.ptr) - { /* no source, but a gateway. Lookup source to reach gtw. */ - new_gtw = host_create_from_chunk(msg->rtm_family, rta_gtw, 0); - new_src = get_route(this, new_gtw, FALSE, candidate); - new_gtw->destroy(new_gtw); - if (new_src) + /* insert route, sorted by decreasing network prefix */ + enumerator = routes->create_enumerator(routes); + while (enumerator->enumerate(enumerator, &other)) + { + if (route->dst_len > other->dst_len) { - DESTROY_IF(src); - src = new_src; - best = msg->rtm_dst_len; + break; } - continue; } + routes->insert_before(routes, enumerator, route); + enumerator->destroy(enumerator); + route = NULL; continue; } default: @@ -1038,30 +1544,127 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest, } break; } - free(out); - this->mutex->unlock(this->mutex); + if (route) + { + rt_entry_destroy(route); + } + + /* now we have a list of routes matching dest, sorted by net prefix. + * we will look for source addresses for these routes and select the one + * with the preferred source address, if possible */ + enumerator = routes->create_enumerator(routes); + while (enumerator->enumerate(enumerator, &route)) + { + if (route->src_host) + { /* got a source address with the route, if no preferred source + * is given or it matches we are done, as this is the best route */ + if (!candidate || candidate->ip_equals(candidate, route->src_host)) + { + best = route; + break; + } + else if (route->oif) + { /* no match yet, maybe it is assigned to the same interface */ + host_t *src = get_interface_address(this, route->oif, + msg->rtm_family, candidate); + if (src && src->ip_equals(src, candidate)) + { + route->src_host->destroy(route->src_host); + route->src_host = src; + best = route; + break; + } + DESTROY_IF(src); + } + /* no luck yet with the source address. if this is the best (first) + * route we store it as fallback in case we don't find a route with + * the preferred source */ + best = best ?: route; + continue; + } + if (route->oif) + { /* no src, but an interface - get address from it */ + route->src_host = get_interface_address(this, route->oif, + msg->rtm_family, candidate); + if (route->src_host) + { /* we handle this address the same as the one above */ + if (!candidate || + candidate->ip_equals(candidate, route->src_host)) + { + best = route; + break; + } + best = best ?: route; + continue; + } + } + if (route->gtw.ptr) + { /* no src, no iface, but a gateway - lookup src to reach gtw */ + host_t *gtw; + + gtw = host_create_from_chunk(msg->rtm_family, route->gtw, 0); + if (gtw && !gtw->ip_equals(gtw, dest)) + { + route->src_host = get_route(this, gtw, FALSE, candidate, + recursion + 1); + } + DESTROY_IF(gtw); + if (route->src_host) + { /* more of the same */ + if (!candidate || + candidate->ip_equals(candidate, route->src_host)) + { + best = route; + break; + } + best = best ?: route; + } + } + } + enumerator->destroy(enumerator); if (nexthop) + { /* nexthop lookup, return gateway if any */ + if (best || routes->get_first(routes, (void**)&best) == SUCCESS) + { + addr = host_create_from_chunk(msg->rtm_family, best->gtw, 0); + } + addr = addr ?: dest->clone(dest); + } + else { - if (gtw) + if (best) { - return gtw; + addr = best->src_host->clone(best->src_host); } - return dest->clone(dest); } - return src; + this->lock->unlock(this->lock); + routes->destroy_function(routes, (void*)rt_entry_destroy); + free(out); + + if (addr) + { + DBG2(DBG_KNL, "using %H as %s to reach %H", addr, + nexthop ? "nexthop" : "address", dest); + } + else if (!recursion) + { + DBG2(DBG_KNL, "no %s found to reach %H", + nexthop ? "nexthop" : "address", dest); + } + return addr; } METHOD(kernel_net_t, get_source_addr, host_t*, private_kernel_netlink_net_t *this, host_t *dest, host_t *src) { - return get_route(this, dest, FALSE, src); + return get_route(this, dest, FALSE, src, 0); } METHOD(kernel_net_t, get_nexthop, host_t*, - private_kernel_netlink_net_t *this, host_t *dest) + private_kernel_netlink_net_t *this, host_t *dest, host_t *src) { - return get_route(this, dest, TRUE, NULL); + return get_route(this, dest, TRUE, src, 0); } /** @@ -1069,7 +1672,7 @@ METHOD(kernel_net_t, get_nexthop, host_t*, * By setting the appropriate nlmsg_type, the ip will be set or unset. */ static status_t manage_ipaddr(private_kernel_netlink_net_t *this, int nlmsg_type, - int flags, int if_index, host_t *ip) + int flags, int if_index, host_t *ip, int prefix) { netlink_buf_t request; struct nlmsghdr *hdr; @@ -1088,7 +1691,7 @@ static status_t manage_ipaddr(private_kernel_netlink_net_t *this, int nlmsg_type msg = (struct ifaddrmsg*)NLMSG_DATA(hdr); msg->ifa_family = ip->get_family(ip); msg->ifa_flags = 0; - msg->ifa_prefixlen = 8 * chunk.len; + msg->ifa_prefixlen = prefix < 0 ? chunk.len * 8 : prefix; msg->ifa_scope = RT_SCOPE_UNIVERSE; msg->ifa_index = if_index; @@ -1098,89 +1701,111 @@ static status_t manage_ipaddr(private_kernel_netlink_net_t *this, int nlmsg_type } METHOD(kernel_net_t, add_ip, status_t, - private_kernel_netlink_net_t *this, host_t *virtual_ip, host_t *iface_ip) + private_kernel_netlink_net_t *this, host_t *virtual_ip, int prefix, + char *iface_name) { - iface_entry_t *iface; - addr_entry_t *addr; - enumerator_t *addrs, *ifaces; - int ifindex; + addr_map_entry_t *entry, lookup = { + .ip = virtual_ip, + }; + iface_entry_t *iface = NULL; if (!this->install_virtual_ip) { /* disabled by config */ return SUCCESS; } - DBG2(DBG_KNL, "adding virtual IP %H", virtual_ip); - - this->mutex->lock(this->mutex); - ifaces = this->ifaces->create_enumerator(this->ifaces); - while (ifaces->enumerate(ifaces, &iface)) - { - bool iface_found = FALSE; - - addrs = iface->addrs->create_enumerator(iface->addrs); - while (addrs->enumerate(addrs, &addr)) - { - if (iface_ip->ip_equals(iface_ip, addr->ip)) + this->lock->write_lock(this->lock); + /* the virtual IP might actually be installed as regular IP, in which case + * we don't track it as virtual IP */ + entry = this->addrs->get_match(this->addrs, &lookup, + (void*)addr_map_entry_match); + if (!entry) + { /* otherwise it might already be installed as virtual IP */ + entry = this->vips->get_match(this->vips, &lookup, + (void*)addr_map_entry_match); + if (entry) + { /* the vip we found can be in one of three states: 1) installed and + * ready, 2) just added by another thread, but not yet confirmed to + * be installed by the kernel, 3) just deleted, but not yet gone. + * Then while we wait below, several things could happen (as we + * release the lock). For instance, the interface could disappear, + * or the IP is finally deleted, and it reappears on a different + * interface. All these cases are handled by the call below. */ + while (!is_vip_installed_or_gone(this, virtual_ip, &entry)) { - iface_found = TRUE; + this->condvar->wait(this->condvar, this->lock); } - else if (virtual_ip->ip_equals(virtual_ip, addr->ip)) + if (entry) { - addr->refcount++; - DBG2(DBG_KNL, "virtual IP %H already installed on %s", - virtual_ip, iface->ifname); - addrs->destroy(addrs); - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); - return SUCCESS; + entry->addr->refcount++; } } - addrs->destroy(addrs); - - if (iface_found) + } + if (entry) + { + DBG2(DBG_KNL, "virtual IP %H is already installed on %s", virtual_ip, + entry->iface->ifname); + this->lock->unlock(this->lock); + return SUCCESS; + } + /* try to find the target interface, either by config or via src ip */ + if (!this->install_virtual_ip_on || + this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_name, + (void**)&iface, this->install_virtual_ip_on) != SUCCESS) + { + if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_name, + (void**)&iface, iface_name) != SUCCESS) + { /* if we don't find the requested interface we just use the first */ + this->ifaces->get_first(this->ifaces, (void**)&iface); + } + } + if (iface) + { + addr_entry_t *addr; + + INIT(addr, + .ip = virtual_ip->clone(virtual_ip), + .refcount = 1, + .scope = RT_SCOPE_UNIVERSE, + ); + iface->addrs->insert_last(iface->addrs, addr); + addr_map_entry_add(this->vips, addr, iface); + if (manage_ipaddr(this, RTM_NEWADDR, NLM_F_CREATE | NLM_F_EXCL, + iface->ifindex, virtual_ip, prefix) == SUCCESS) { - ifindex = iface->ifindex; - addr = malloc_thing(addr_entry_t); - addr->ip = virtual_ip->clone(virtual_ip); - addr->refcount = 0; - addr->virtual = TRUE; - addr->scope = RT_SCOPE_UNIVERSE; - iface->addrs->insert_last(iface->addrs, addr); - - if (manage_ipaddr(this, RTM_NEWADDR, NLM_F_CREATE | NLM_F_EXCL, - ifindex, virtual_ip) == SUCCESS) - { - while (get_vip_refcount(this, virtual_ip) == 0) - { /* wait until address appears */ - this->condvar->wait(this->condvar, this->mutex); - } - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); + while (!is_vip_installed_or_gone(this, virtual_ip, &entry)) + { /* wait until address appears */ + this->condvar->wait(this->condvar, this->lock); + } + if (entry) + { /* we fail if the interface got deleted in the meantime */ + DBG2(DBG_KNL, "virtual IP %H installed on %s", virtual_ip, + entry->iface->ifname); + this->lock->unlock(this->lock); + /* during IKEv1 reauthentication, children get moved from + * old the new SA before the virtual IP is available. This + * kills the route for our virtual IP, reinstall. */ + queue_route_reinstall(this, strdup(entry->iface->ifname)); return SUCCESS; } - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); - DBG1(DBG_KNL, "adding virtual IP %H failed", virtual_ip); - return FAILED; } + this->lock->unlock(this->lock); + DBG1(DBG_KNL, "adding virtual IP %H failed", virtual_ip); + return FAILED; } - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); - - DBG1(DBG_KNL, "interface address %H not found, unable to install" - "virtual IP %H", iface_ip, virtual_ip); + this->lock->unlock(this->lock); + DBG1(DBG_KNL, "no interface available, unable to install virtual IP %H", + virtual_ip); return FAILED; } METHOD(kernel_net_t, del_ip, status_t, - private_kernel_netlink_net_t *this, host_t *virtual_ip) + private_kernel_netlink_net_t *this, host_t *virtual_ip, int prefix, + bool wait) { - iface_entry_t *iface; - addr_entry_t *addr; - enumerator_t *addrs, *ifaces; - status_t status; - int ifindex; + addr_map_entry_t *entry, lookup = { + .ip = virtual_ip, + }; if (!this->install_virtual_ip) { /* disabled by config */ @@ -1189,60 +1814,61 @@ METHOD(kernel_net_t, del_ip, status_t, DBG2(DBG_KNL, "deleting virtual IP %H", virtual_ip); - this->mutex->lock(this->mutex); - ifaces = this->ifaces->create_enumerator(this->ifaces); - while (ifaces->enumerate(ifaces, &iface)) - { - addrs = iface->addrs->create_enumerator(iface->addrs); - while (addrs->enumerate(addrs, &addr)) + this->lock->write_lock(this->lock); + entry = this->vips->get_match(this->vips, &lookup, + (void*)addr_map_entry_match); + if (!entry) + { /* we didn't install this IP as virtual IP */ + entry = this->addrs->get_match(this->addrs, &lookup, + (void*)addr_map_entry_match); + if (entry) { - if (virtual_ip->ip_equals(virtual_ip, addr->ip)) + DBG2(DBG_KNL, "not deleting existing IP %H on %s", virtual_ip, + entry->iface->ifname); + this->lock->unlock(this->lock); + return SUCCESS; + } + DBG2(DBG_KNL, "virtual IP %H not cached, unable to delete", virtual_ip); + this->lock->unlock(this->lock); + return FAILED; + } + if (entry->addr->refcount == 1) + { + status_t status; + + /* we set this flag so that threads calling add_ip will block and wait + * until the entry is gone, also so we can wait below */ + entry->addr->installed = FALSE; + status = manage_ipaddr(this, RTM_DELADDR, 0, entry->iface->ifindex, + virtual_ip, prefix); + if (status == SUCCESS && wait) + { /* wait until the address is really gone */ + while (is_known_vip(this, virtual_ip)) { - ifindex = iface->ifindex; - if (addr->refcount == 1) - { - status = manage_ipaddr(this, RTM_DELADDR, 0, - ifindex, virtual_ip); - if (status == SUCCESS) - { /* wait until the address is really gone */ - while (get_vip_refcount(this, virtual_ip) > 0) - { - this->condvar->wait(this->condvar, this->mutex); - } - } - addrs->destroy(addrs); - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); - return status; - } - else - { - addr->refcount--; - } - DBG2(DBG_KNL, "virtual IP %H used by other SAs, not deleting", - virtual_ip); - addrs->destroy(addrs); - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); - return SUCCESS; + this->condvar->wait(this->condvar, this->lock); } } - addrs->destroy(addrs); + this->lock->unlock(this->lock); + return status; } - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); - - DBG2(DBG_KNL, "virtual IP %H not cached, unable to delete", virtual_ip); - return FAILED; + else + { + entry->addr->refcount--; + } + DBG2(DBG_KNL, "virtual IP %H used by other SAs, not deleting", + virtual_ip); + this->lock->unlock(this->lock); + return SUCCESS; } /** * Manages source routes in the routing table. * By setting the appropriate nlmsg_type, the route gets added or removed. */ -static status_t manage_srcroute(private_kernel_netlink_net_t *this, int nlmsg_type, - int flags, chunk_t dst_net, u_int8_t prefixlen, - host_t *gateway, host_t *src_ip, char *if_name) +static status_t manage_srcroute(private_kernel_netlink_net_t *this, + int nlmsg_type, int flags, chunk_t dst_net, + u_int8_t prefixlen, host_t *gateway, + host_t *src_ip, char *if_name) { netlink_buf_t request; struct nlmsghdr *hdr; @@ -1306,16 +1932,56 @@ METHOD(kernel_net_t, add_route, status_t, private_kernel_netlink_net_t *this, chunk_t dst_net, u_int8_t prefixlen, host_t *gateway, host_t *src_ip, char *if_name) { - return manage_srcroute(this, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, - dst_net, prefixlen, gateway, src_ip, if_name); + status_t status; + route_entry_t *found, route = { + .dst_net = dst_net, + .prefixlen = prefixlen, + .gateway = gateway, + .src_ip = src_ip, + .if_name = if_name, + }; + + this->routes_lock->lock(this->routes_lock); + found = this->routes->get(this->routes, &route); + if (found) + { + this->routes_lock->unlock(this->routes_lock); + return ALREADY_DONE; + } + found = route_entry_clone(&route); + this->routes->put(this->routes, found, found); + status = manage_srcroute(this, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, + dst_net, prefixlen, gateway, src_ip, if_name); + this->routes_lock->unlock(this->routes_lock); + return status; } METHOD(kernel_net_t, del_route, status_t, private_kernel_netlink_net_t *this, chunk_t dst_net, u_int8_t prefixlen, host_t *gateway, host_t *src_ip, char *if_name) { - return manage_srcroute(this, RTM_DELROUTE, 0, dst_net, prefixlen, - gateway, src_ip, if_name); + status_t status; + route_entry_t *found, route = { + .dst_net = dst_net, + .prefixlen = prefixlen, + .gateway = gateway, + .src_ip = src_ip, + .if_name = if_name, + }; + + this->routes_lock->lock(this->routes_lock); + found = this->routes->get(this->routes, &route); + if (!found) + { + this->routes_lock->unlock(this->routes_lock); + return NOT_FOUND; + } + this->routes->remove(this->routes, found); + route_entry_destroy(found); + status = manage_srcroute(this, RTM_DELROUTE, 0, dst_net, prefixlen, + gateway, src_ip, if_name); + this->routes_lock->unlock(this->routes_lock); + return status; } /** @@ -1331,7 +1997,7 @@ static status_t init_address_list(private_kernel_netlink_net_t *this) iface_entry_t *iface; addr_entry_t *addr; - DBG1(DBG_KNL, "listening on interfaces:"); + DBG2(DBG_KNL, "known interfaces and IP addresses:"); memset(&request, 0, sizeof(request)); @@ -1389,23 +2055,23 @@ static status_t init_address_list(private_kernel_netlink_net_t *this) } free(out); - this->mutex->lock(this->mutex); + this->lock->read_lock(this->lock); ifaces = this->ifaces->create_enumerator(this->ifaces); while (ifaces->enumerate(ifaces, &iface)) { - if (iface->flags & IFF_UP) + if (iface_entry_up_and_usable(iface)) { - DBG1(DBG_KNL, " %s", iface->ifname); + DBG2(DBG_KNL, " %s", iface->ifname); addrs = iface->addrs->create_enumerator(iface->addrs); while (addrs->enumerate(addrs, (void**)&addr)) { - DBG1(DBG_KNL, " %H", addr->ip); + DBG2(DBG_KNL, " %H", addr->ip); } addrs->destroy(addrs); } } ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); + this->lock->unlock(this->lock); return SUCCESS; } @@ -1443,9 +2109,59 @@ static status_t manage_rule(private_kernel_netlink_net_t *this, int nlmsg_type, return this->socket->send_ack(this->socket, hdr); } +/** + * check for kernel features (currently only via version number) + */ +static void check_kernel_features(private_kernel_netlink_net_t *this) +{ + struct utsname utsname; + int a, b, c; + + if (uname(&utsname) == 0) + { + switch(sscanf(utsname.release, "%d.%d.%d", &a, &b, &c)) + { + case 3: + if (a == 2) + { + DBG2(DBG_KNL, "detected Linux %d.%d.%d, no support for " + "RTA_PREFSRC for IPv6 routes", a, b, c); + break; + } + /* fall-through */ + case 2: + /* only 3.x+ uses two part version numbers */ + this->rta_prefsrc_for_ipv6 = TRUE; + break; + default: + break; + } + } +} + +/** + * Destroy an address to iface map + */ +static void addr_map_destroy(hashtable_t *map) +{ + enumerator_t *enumerator; + addr_map_entry_t *addr; + + enumerator = map->create_enumerator(map); + while (enumerator->enumerate(enumerator, NULL, (void**)&addr)) + { + free(addr); + } + enumerator->destroy(enumerator); + map->destroy(map); +} + METHOD(kernel_net_t, destroy, void, private_kernel_netlink_net_t *this) { + enumerator_t *enumerator; + route_entry_t *route; + if (this->routing_table) { manage_rule(this, RTM_DELRULE, AF_INET, this->routing_table, @@ -1453,19 +2169,35 @@ METHOD(kernel_net_t, destroy, void, manage_rule(this, RTM_DELRULE, AF_INET6, this->routing_table, this->routing_table_prio); } - if (this->job) - { - this->job->cancel(this->job); - } if (this->socket_events > 0) { + lib->watcher->remove(lib->watcher, this->socket_events); close(this->socket_events); } + enumerator = this->routes->create_enumerator(this->routes); + while (enumerator->enumerate(enumerator, NULL, (void**)&route)) + { + manage_srcroute(this, RTM_DELROUTE, 0, route->dst_net, route->prefixlen, + route->gateway, route->src_ip, route->if_name); + route_entry_destroy(route); + } + enumerator->destroy(enumerator); + this->routes->destroy(this->routes); + this->routes_lock->destroy(this->routes_lock); DESTROY_IF(this->socket); + + net_changes_clear(this); + this->net_changes->destroy(this->net_changes); + this->net_changes_lock->destroy(this->net_changes_lock); + + addr_map_destroy(this->addrs); + addr_map_destroy(this->vips); + this->ifaces->destroy_function(this->ifaces, (void*)iface_entry_destroy); this->rt_exclude->destroy(this->rt_exclude); + this->roam_lock->destroy(this->roam_lock); this->condvar->destroy(this->condvar); - this->mutex->destroy(this->mutex); + this->lock->destroy(this->lock); free(this); } @@ -1475,8 +2207,8 @@ METHOD(kernel_net_t, destroy, void, kernel_netlink_net_t *kernel_netlink_net_create() { private_kernel_netlink_net_t *this; - struct sockaddr_nl addr; enumerator_t *enumerator; + bool register_for_events = TRUE; char *exclude; INIT(this, @@ -1495,9 +2227,22 @@ kernel_netlink_net_t *kernel_netlink_net_create() }, .socket = netlink_socket_create(NETLINK_ROUTE), .rt_exclude = linked_list_create(), + .routes = hashtable_create((hashtable_hash_t)route_entry_hash, + (hashtable_equals_t)route_entry_equals, 16), + .net_changes = hashtable_create( + (hashtable_hash_t)net_change_hash, + (hashtable_equals_t)net_change_equals, 16), + .addrs = hashtable_create( + (hashtable_hash_t)addr_map_entry_hash, + (hashtable_equals_t)addr_map_entry_equals, 16), + .vips = hashtable_create((hashtable_hash_t)addr_map_entry_hash, + (hashtable_equals_t)addr_map_entry_equals, 16), + .routes_lock = mutex_create(MUTEX_TYPE_DEFAULT), + .net_changes_lock = mutex_create(MUTEX_TYPE_DEFAULT), .ifaces = linked_list_create(), - .mutex = mutex_create(MUTEX_TYPE_RECURSIVE), - .condvar = condvar_create(CONDVAR_TYPE_DEFAULT), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + .condvar = rwlock_condvar_create(), + .roam_lock = spinlock_create(), .routing_table = lib->settings->get_int(lib->settings, "%s.routing_table", ROUTING_TABLE, hydra->daemon), .routing_table_prio = lib->settings->get_int(lib->settings, @@ -1506,8 +2251,20 @@ kernel_netlink_net_t *kernel_netlink_net_create() "%s.process_route", TRUE, hydra->daemon), .install_virtual_ip = lib->settings->get_bool(lib->settings, "%s.install_virtual_ip", TRUE, hydra->daemon), + .install_virtual_ip_on = lib->settings->get_str(lib->settings, + "%s.install_virtual_ip_on", NULL, hydra->daemon), + .roam_events = lib->settings->get_bool(lib->settings, + "%s.plugins.kernel-netlink.roam_events", TRUE, hydra->daemon), ); - timerclear(&this->last_roam); + timerclear(&this->last_route_reinstall); + timerclear(&this->next_roam); + + check_kernel_features(this); + + if (streq(hydra->daemon, "starter")) + { /* starter has no threads, so we do not register for kernel events */ + register_for_events = FALSE; + } exclude = lib->settings->get_str(lib->settings, "%s.ignore_routing_tables", NULL, hydra->daemon); @@ -1530,29 +2287,33 @@ kernel_netlink_net_t *kernel_netlink_net_create() enumerator->destroy(enumerator); } - memset(&addr, 0, sizeof(addr)); - addr.nl_family = AF_NETLINK; - - /* create and bind RT socket for events (address/interface/route changes) */ - this->socket_events = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (this->socket_events < 0) + if (register_for_events) { - DBG1(DBG_KNL, "unable to create RT event socket"); - destroy(this); - return NULL; - } - addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | - RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_LINK; - if (bind(this->socket_events, (struct sockaddr*)&addr, sizeof(addr))) - { - DBG1(DBG_KNL, "unable to bind RT event socket"); - destroy(this); - return NULL; - } + struct sockaddr_nl addr; + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; - this->job = callback_job_create_with_prio((callback_job_cb_t)receive_events, - this, NULL, NULL, JOB_PRIO_CRITICAL); - lib->processor->queue_job(lib->processor, (job_t*)this->job); + /* create and bind RT socket for events (address/interface/route changes) */ + this->socket_events = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (this->socket_events < 0) + { + DBG1(DBG_KNL, "unable to create RT event socket"); + destroy(this); + return NULL; + } + addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | + RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_LINK; + if (bind(this->socket_events, (struct sockaddr*)&addr, sizeof(addr))) + { + DBG1(DBG_KNL, "unable to bind RT event socket"); + destroy(this); + return NULL; + } + + lib->watcher->add(lib->watcher, this->socket_events, WATCHER_READ, + (watcher_cb_t)receive_events, this); + } if (init_address_list(this) != SUCCESS) { diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_plugin.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_plugin.c index 0eb00dadf..8d5a0d5e8 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_plugin.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_plugin.c @@ -65,6 +65,14 @@ plugin_t *kernel_netlink_plugin_create() { private_kernel_netlink_plugin_t *this; + if (!lib->caps->keep(lib->caps, CAP_NET_ADMIN)) + { /* required to bind/use XFRM sockets / create/modify routing tables, but + * not if only the read-only parts of kernel-netlink-net are used, so + * we don't fail here */ + DBG1(DBG_KNL, "kernel-netlink plugin might require CAP_NET_ADMIN " + "capability"); + } + INIT(this, .public = { .plugin = { diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c index dad3fb68e..fd00c23af 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c @@ -21,7 +21,7 @@ #include "kernel_netlink_shared.h" -#include <debug.h> +#include <utils/debug.h> #include <threading/mutex.h> typedef struct private_netlink_socket_t private_netlink_socket_t; @@ -206,6 +206,11 @@ METHOD(netlink_socket_t, netlink_send_ack, status_t, free(out); return ALREADY_DONE; } + if (-err->error == ESRCH) + { /* do not report missing entries */ + free(out); + return NOT_FOUND; + } DBG1(DBG_KNL, "received netlink error: %s (%d)", strerror(-err->error), -err->error); free(out); @@ -287,7 +292,7 @@ void netlink_add_attribute(struct nlmsghdr *hdr, int rta_type, chunk_t data, { struct rtattr *rta; - if (NLMSG_ALIGN(hdr->nlmsg_len) + RTA_ALIGN(data.len) > buflen) + if (NLMSG_ALIGN(hdr->nlmsg_len) + RTA_LENGTH(data.len) > buflen) { DBG1(DBG_KNL, "unable to add attribute, buffer too small"); return; @@ -299,3 +304,24 @@ void netlink_add_attribute(struct nlmsghdr *hdr, int rta_type, chunk_t data, memcpy(RTA_DATA(rta), data.ptr, data.len); hdr->nlmsg_len = NLMSG_ALIGN(hdr->nlmsg_len) + rta->rta_len; } + +/** + * Described in header. + */ +void* netlink_reserve(struct nlmsghdr *hdr, int buflen, int type, int len) +{ + struct rtattr *rta; + + if (NLMSG_ALIGN(hdr->nlmsg_len) + RTA_LENGTH(len) > buflen) + { + DBG1(DBG_KNL, "unable to add attribute, buffer too small"); + return NULL; + } + + rta = ((void*)hdr) + NLMSG_ALIGN(hdr->nlmsg_len); + rta->rta_type = type; + rta->rta_len = RTA_LENGTH(len); + hdr->nlmsg_len = NLMSG_ALIGN(hdr->nlmsg_len) + rta->rta_len; + + return RTA_DATA(rta); +} diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.h b/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.h index dfd27a21a..8be935bc3 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.h +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.h @@ -42,7 +42,8 @@ struct netlink_socket_t { * @param out received netlink message * @param out_len length of the received message */ - status_t (*send)(netlink_socket_t *this, struct nlmsghdr *in, struct nlmsghdr **out, size_t *out_len); + status_t (*send)(netlink_socket_t *this, struct nlmsghdr *in, + struct nlmsghdr **out, size_t *out_len); /** * Send a netlink message and wait for its acknowledge. @@ -67,11 +68,23 @@ netlink_socket_t *netlink_socket_create(int protocol); /** * Creates an rtattr and adds it to the given netlink message. * - * @param hdr netlink message - * @param rta_type type of the rtattr - * @param data data to add to the rtattr - * @param buflen length of the netlink message buffer + * @param hdr netlink message + * @param rta_type type of the rtattr + * @param data data to add to the rtattr + * @param buflen length of the netlink message buffer */ -void netlink_add_attribute(struct nlmsghdr *hdr, int rta_type, chunk_t data, size_t buflen); +void netlink_add_attribute(struct nlmsghdr *hdr, int rta_type, chunk_t data, + size_t buflen); + +/** + * Reserve space in a netlink message for given size and type, returning buffer. + * + * @param hdr netlink message + * @param buflen size of full netlink buffer + * @param type RTA type + * @param len length of RTA data + * @return buffer to len bytes of attribute data, NULL on error + */ +void* netlink_reserve(struct nlmsghdr *hdr, int buflen, int type, int len); #endif /* KERNEL_NETLINK_SHARED_H_ */ |
