diff options
author | Yves-Alexis Perez <corsac@corsac.net> | 2012-06-28 21:16:07 +0200 |
---|---|---|
committer | Yves-Alexis Perez <corsac@corsac.net> | 2012-06-28 21:16:07 +0200 |
commit | b34738ed08c2227300d554b139e2495ca5da97d6 (patch) | |
tree | 62f33b52820f2e49f0e53c0f8c636312037c8054 /src/libhydra | |
parent | 0a9d51a49042a68daa15b0c74a2b7f152f52606b (diff) | |
download | vyos-strongswan-b34738ed08c2227300d554b139e2495ca5da97d6.tar.gz vyos-strongswan-b34738ed08c2227300d554b139e2495ca5da97d6.zip |
Imported Upstream version 4.6.4
Diffstat (limited to 'src/libhydra')
34 files changed, 2120 insertions, 873 deletions
diff --git a/src/libhydra/Android.mk b/src/libhydra/Android.mk index 2418e76ad..075f8dbcb 100644 --- a/src/libhydra/Android.mk +++ b/src/libhydra/Android.mk @@ -10,7 +10,7 @@ attributes/attribute_manager.c attributes/attribute_manager.h \ attributes/mem_pool.c attributes/mem_pool.h \ kernel/kernel_interface.c kernel/kernel_interface.h \ kernel/kernel_ipsec.c kernel/kernel_ipsec.h \ -kernel/kernel_net.h \ +kernel/kernel_net.c kernel/kernel_net.h \ kernel/kernel_listener.h # adding the plugin source files @@ -32,6 +32,8 @@ LOCAL_CFLAGS := $(strongswan_CFLAGS) LOCAL_MODULE := libhydra +LOCAL_MODULE_TAGS := optional + LOCAL_ARM_MODE := arm LOCAL_PRELINK_MODULE := false diff --git a/src/libhydra/Makefile.am b/src/libhydra/Makefile.am index d0698d0f5..1c7b3ba43 100644 --- a/src/libhydra/Makefile.am +++ b/src/libhydra/Makefile.am @@ -1,4 +1,4 @@ -lib_LTLIBRARIES = libhydra.la +ipseclib_LTLIBRARIES = libhydra.la libhydra_la_SOURCES = \ hydra.c hydra.h \ @@ -8,7 +8,7 @@ attributes/attribute_manager.c attributes/attribute_manager.h \ attributes/mem_pool.c attributes/mem_pool.h \ kernel/kernel_interface.c kernel/kernel_interface.h \ kernel/kernel_ipsec.c kernel/kernel_ipsec.h \ -kernel/kernel_net.h \ +kernel/kernel_net.c kernel/kernel_net.h \ kernel/kernel_listener.h libhydra_la_LIBADD = diff --git a/src/libhydra/Makefile.in b/src/libhydra/Makefile.in index 08c73b5e3..f452719dd 100644 --- a/src/libhydra/Makefile.in +++ b/src/libhydra/Makefile.in @@ -86,13 +86,13 @@ 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__installdirs = "$(DESTDIR)$(libdir)" -LTLIBRARIES = $(lib_LTLIBRARIES) +am__installdirs = "$(DESTDIR)$(ipseclibdir)" +LTLIBRARIES = $(ipseclib_LTLIBRARIES) libhydra_la_DEPENDENCIES = $(am__append_2) $(am__append_4) \ $(am__append_6) $(am__append_8) $(am__append_10) \ $(am__append_12) $(am__append_14) am_libhydra_la_OBJECTS = hydra.lo attributes.lo attribute_manager.lo \ - mem_pool.lo kernel_interface.lo kernel_ipsec.lo + mem_pool.lo kernel_interface.lo kernel_ipsec.lo kernel_net.lo libhydra_la_OBJECTS = $(am_libhydra_la_OBJECTS) DEFAULT_INCLUDES = -I.@am__isrc@ depcomp = $(SHELL) $(top_srcdir)/depcomp @@ -243,6 +243,9 @@ am__leading_dot = @am__leading_dot@ 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@ @@ -251,6 +254,7 @@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ c_plugins = @c_plugins@ +clearsilver_LIBS = @clearsilver_LIBS@ datadir = @datadir@ datarootdir = @datarootdir@ dbusservicedir = @dbusservicedir@ @@ -267,11 +271,13 @@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ +imcvdir = @imcvdir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ ipsecdir = @ipsecdir@ ipsecgroup = @ipsecgroup@ +ipseclibdir = @ipseclibdir@ ipsecuser = @ipsecuser@ libcharon_plugins = @libcharon_plugins@ libdir = @libdir@ @@ -315,6 +321,7 @@ sharedstatedir = @sharedstatedir@ soup_CFLAGS = @soup_CFLAGS@ soup_LIBS = @soup_LIBS@ srcdir = @srcdir@ +starter_plugins = @starter_plugins@ strongswan_conf = @strongswan_conf@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ @@ -325,7 +332,7 @@ top_srcdir = @top_srcdir@ urandom_device = @urandom_device@ xml_CFLAGS = @xml_CFLAGS@ xml_LIBS = @xml_LIBS@ -lib_LTLIBRARIES = libhydra.la +ipseclib_LTLIBRARIES = libhydra.la libhydra_la_SOURCES = \ hydra.c hydra.h \ attributes/attributes.c attributes/attributes.h \ @@ -334,7 +341,7 @@ attributes/attribute_manager.c attributes/attribute_manager.h \ attributes/mem_pool.c attributes/mem_pool.h \ kernel/kernel_interface.c kernel/kernel_interface.h \ kernel/kernel_ipsec.c kernel/kernel_ipsec.h \ -kernel/kernel_net.h \ +kernel/kernel_net.c kernel/kernel_net.h \ kernel/kernel_listener.h libhydra_la_LIBADD = $(am__append_2) $(am__append_4) $(am__append_6) \ @@ -392,39 +399,39 @@ $(top_srcdir)/configure: $(am__configure_deps) $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): -install-libLTLIBRARIES: $(lib_LTLIBRARIES) +install-ipseclibLTLIBRARIES: $(ipseclib_LTLIBRARIES) @$(NORMAL_INSTALL) - test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" - @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + test -z "$(ipseclibdir)" || $(MKDIR_P) "$(DESTDIR)$(ipseclibdir)" + @list='$(ipseclib_LTLIBRARIES)'; test -n "$(ipseclibdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ - echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ - $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(ipseclibdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(ipseclibdir)"; \ } -uninstall-libLTLIBRARIES: +uninstall-ipseclibLTLIBRARIES: @$(NORMAL_UNINSTALL) - @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + @list='$(ipseclib_LTLIBRARIES)'; test -n "$(ipseclibdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ - echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ - $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(ipseclibdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(ipseclibdir)/$$f"; \ done -clean-libLTLIBRARIES: - -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) - @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ +clean-ipseclibLTLIBRARIES: + -test -z "$(ipseclib_LTLIBRARIES)" || rm -f $(ipseclib_LTLIBRARIES) + @list='$(ipseclib_LTLIBRARIES)'; for p in $$list; do \ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ test "$$dir" != "$$p" || dir=.; \ echo "rm -f \"$${dir}/so_locations\""; \ rm -f "$${dir}/so_locations"; \ done libhydra.la: $(libhydra_la_OBJECTS) $(libhydra_la_DEPENDENCIES) - $(LINK) -rpath $(libdir) $(libhydra_la_OBJECTS) $(libhydra_la_LIBADD) $(LIBS) + $(LINK) -rpath $(ipseclibdir) $(libhydra_la_OBJECTS) $(libhydra_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -437,6 +444,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hydra.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_interface.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_ipsec.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_net.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mem_pool.Plo@am__quote@ .c.o: @@ -495,6 +503,13 @@ kernel_ipsec.lo: kernel/kernel_ipsec.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o kernel_ipsec.lo `test -f 'kernel/kernel_ipsec.c' || echo '$(srcdir)/'`kernel/kernel_ipsec.c +kernel_net.lo: kernel/kernel_net.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT kernel_net.lo -MD -MP -MF $(DEPDIR)/kernel_net.Tpo -c -o kernel_net.lo `test -f 'kernel/kernel_net.c' || echo '$(srcdir)/'`kernel/kernel_net.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/kernel_net.Tpo $(DEPDIR)/kernel_net.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='kernel/kernel_net.c' object='kernel_net.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o kernel_net.lo `test -f 'kernel/kernel_net.c' || echo '$(srcdir)/'`kernel/kernel_net.c + mostlyclean-libtool: -rm -f *.lo @@ -699,7 +714,7 @@ check: check-recursive all-am: Makefile $(LTLIBRARIES) installdirs: installdirs-recursive installdirs-am: - for dir in "$(DESTDIR)$(libdir)"; do \ + for dir in "$(DESTDIR)$(ipseclibdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive @@ -729,7 +744,7 @@ maintainer-clean-generic: @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive -clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ +clean-am: clean-generic clean-ipseclibLTLIBRARIES clean-libtool \ mostlyclean-am distclean: distclean-recursive @@ -750,13 +765,13 @@ info: info-recursive info-am: -install-data-am: +install-data-am: install-ipseclibLTLIBRARIES install-dvi: install-dvi-recursive install-dvi-am: -install-exec-am: install-libLTLIBRARIES +install-exec-am: install-html: install-html-recursive @@ -796,26 +811,26 @@ ps: ps-recursive ps-am: -uninstall-am: uninstall-libLTLIBRARIES +uninstall-am: uninstall-ipseclibLTLIBRARIES .MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \ install-am install-strip tags-recursive .PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ all all-am check check-am clean clean-generic \ - clean-libLTLIBRARIES clean-libtool ctags ctags-recursive \ + clean-ipseclibLTLIBRARIES clean-libtool ctags ctags-recursive \ distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ - install-info-am install-libLTLIBRARIES install-man install-pdf \ - install-pdf-am install-ps install-ps-am install-strip \ - installcheck installcheck-am installdirs installdirs-am \ - maintainer-clean maintainer-clean-generic mostlyclean \ - mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ - pdf pdf-am ps ps-am tags tags-recursive uninstall uninstall-am \ - uninstall-libLTLIBRARIES + install-info-am install-ipseclibLTLIBRARIES install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-recursive \ + uninstall uninstall-am uninstall-ipseclibLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/src/libhydra/attributes/attribute_manager.c b/src/libhydra/attributes/attribute_manager.c index 0d4cbda82..95520531e 100644 --- a/src/libhydra/attributes/attribute_manager.c +++ b/src/libhydra/attributes/attribute_manager.c @@ -59,12 +59,9 @@ typedef struct { host_t *vip; } enum_data_t; -/** - * Implementation of attribute_manager_t.acquire_address. - */ -static host_t* acquire_address(private_attribute_manager_t *this, - char *pool, identification_t *id, - host_t *requested) +METHOD(attribute_manager_t, acquire_address, host_t*, + private_attribute_manager_t *this, char *pool, identification_t *id, + host_t *requested) { enumerator_t *enumerator; attribute_provider_t *current; @@ -90,11 +87,9 @@ static host_t* acquire_address(private_attribute_manager_t *this, return host; } -/** - * Implementation of attribute_manager_t.release_address. - */ -static void release_address(private_attribute_manager_t *this, - char *pool, host_t *address, identification_t *id) +METHOD(attribute_manager_t, release_address, void, + private_attribute_manager_t *this, char *pool, host_t *address, + identification_t *id) { enumerator_t *enumerator; attribute_provider_t *current; @@ -129,12 +124,9 @@ static enumerator_t *responder_enum_create(attribute_provider_t *provider, data->id, data->vip); } -/** - * Implementation of attribute_manager_t.create_responder_enumerator - */ -static enumerator_t* create_responder_enumerator( - private_attribute_manager_t *this, char *pool, - identification_t *id, host_t *vip) +METHOD(attribute_manager_t, create_responder_enumerator, enumerator_t*, + private_attribute_manager_t *this, char *pool, identification_t *id, + host_t *vip) { enum_data_t *data = malloc_thing(enum_data_t); @@ -149,34 +141,26 @@ static enumerator_t* create_responder_enumerator( (void*)this->lock->unlock, this->lock); } -/** - * Implementation of attribute_manager_t.add_provider. - */ -static void add_provider(private_attribute_manager_t *this, - attribute_provider_t *provider) +METHOD(attribute_manager_t, add_provider, void, + private_attribute_manager_t *this, attribute_provider_t *provider) { this->lock->write_lock(this->lock); this->providers->insert_last(this->providers, provider); this->lock->unlock(this->lock); } -/** - * Implementation of attribute_manager_t.remove_provider. - */ -static void remove_provider(private_attribute_manager_t *this, - attribute_provider_t *provider) +METHOD(attribute_manager_t, remove_provider, void, + private_attribute_manager_t *this, attribute_provider_t *provider) { this->lock->write_lock(this->lock); this->providers->remove(this->providers, provider, NULL); this->lock->unlock(this->lock); } -/** - * Implementation of attribute_manager_t.handle - */ -static attribute_handler_t* handle(private_attribute_manager_t *this, - identification_t *server, attribute_handler_t *handler, - configuration_attribute_type_t type, chunk_t data) +METHOD(attribute_manager_t, handle, attribute_handler_t*, + private_attribute_manager_t *this, identification_t *server, + attribute_handler_t *handler, configuration_attribute_type_t type, + chunk_t data) { enumerator_t *enumerator; attribute_handler_t *current, *handled = NULL; @@ -217,13 +201,9 @@ static attribute_handler_t* handle(private_attribute_manager_t *this, return handled; } -/** - * Implementation of attribute_manager_t.release - */ -static void release(private_attribute_manager_t *this, - attribute_handler_t *handler, - identification_t *server, - configuration_attribute_type_t type, chunk_t data) +METHOD(attribute_manager_t, release, void, + private_attribute_manager_t *this, attribute_handler_t *handler, + identification_t *server, configuration_attribute_type_t type, chunk_t data) { enumerator_t *enumerator; attribute_handler_t *current; @@ -297,11 +277,8 @@ static void initiator_destroy(initiator_enumerator_t *this) free(this); } -/** - * Implementation of attribute_manager_t.create_initiator_enumerator - */ -static enumerator_t* create_initiator_enumerator( - private_attribute_manager_t *this, identification_t *id, host_t *vip) +METHOD(attribute_manager_t, create_initiator_enumerator, enumerator_t*, + private_attribute_manager_t *this, identification_t *id, host_t *vip) { initiator_enumerator_t *enumerator = malloc_thing(initiator_enumerator_t); @@ -318,32 +295,24 @@ static enumerator_t* create_initiator_enumerator( return &enumerator->public; } -/** - * Implementation of attribute_manager_t.add_handler - */ -static void add_handler(private_attribute_manager_t *this, - attribute_handler_t *handler) +METHOD(attribute_manager_t, add_handler, void, + private_attribute_manager_t *this, attribute_handler_t *handler) { this->lock->write_lock(this->lock); this->handlers->insert_last(this->handlers, handler); this->lock->unlock(this->lock); } -/** - * Implementation of attribute_manager_t.remove_handler - */ -static void remove_handler(private_attribute_manager_t *this, - attribute_handler_t *handler) +METHOD(attribute_manager_t, remove_handler, void, + private_attribute_manager_t *this, attribute_handler_t *handler) { this->lock->write_lock(this->lock); this->handlers->remove(this->handlers, handler, NULL); this->lock->unlock(this->lock); } -/** - * Implementation of attribute_manager_t.destroy - */ -static void destroy(private_attribute_manager_t *this) +METHOD(attribute_manager_t, destroy, void, + private_attribute_manager_t *this) { this->providers->destroy(this->providers); this->handlers->destroy(this->handlers); @@ -356,23 +325,26 @@ static void destroy(private_attribute_manager_t *this) */ attribute_manager_t *attribute_manager_create() { - private_attribute_manager_t *this = malloc_thing(private_attribute_manager_t); - - this->public.acquire_address = (host_t*(*)(attribute_manager_t*, char*, identification_t*,host_t*))acquire_address; - this->public.release_address = (void(*)(attribute_manager_t*, char *, host_t*, identification_t*))release_address; - this->public.create_responder_enumerator = (enumerator_t*(*)(attribute_manager_t*, char *name, identification_t*, host_t*))create_responder_enumerator; - this->public.add_provider = (void(*)(attribute_manager_t*, attribute_provider_t *provider))add_provider; - this->public.remove_provider = (void(*)(attribute_manager_t*, attribute_provider_t *provider))remove_provider; - this->public.handle = (attribute_handler_t*(*)(attribute_manager_t*,identification_t*, attribute_handler_t*, configuration_attribute_type_t, chunk_t))handle; - this->public.release = (void(*)(attribute_manager_t*, attribute_handler_t*, identification_t*, configuration_attribute_type_t, chunk_t))release; - this->public.create_initiator_enumerator = (enumerator_t*(*)(attribute_manager_t*, identification_t*, host_t*))create_initiator_enumerator; - this->public.add_handler = (void(*)(attribute_manager_t*, attribute_handler_t*))add_handler; - this->public.remove_handler = (void(*)(attribute_manager_t*, attribute_handler_t*))remove_handler; - this->public.destroy = (void(*)(attribute_manager_t*))destroy; - - this->providers = linked_list_create(); - this->handlers = linked_list_create(); - this->lock = rwlock_create(RWLOCK_TYPE_DEFAULT); + private_attribute_manager_t *this; + + INIT(this, + .public = { + .acquire_address = _acquire_address, + .release_address = _release_address, + .create_responder_enumerator = _create_responder_enumerator, + .add_provider = _add_provider, + .remove_provider = _remove_provider, + .handle = _handle, + .release = _release, + .create_initiator_enumerator = _create_initiator_enumerator, + .add_handler = _add_handler, + .remove_handler = _remove_handler, + .destroy = _destroy, + }, + .providers = linked_list_create(), + .handlers = linked_list_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + ); return &this->public; } diff --git a/src/libhydra/attributes/attributes.c b/src/libhydra/attributes/attributes.c index ea87109e2..d8490b7f5 100644 --- a/src/libhydra/attributes/attributes.c +++ b/src/libhydra/attributes/attributes.c @@ -17,7 +17,7 @@ #include "attributes.h" -ENUM_BEGIN(configuration_attribute_type_names, INTERNAL_IP4_ADDRESS, INTERNAL_IP6_PREFIX, +ENUM_BEGIN(configuration_attribute_type_names, INTERNAL_IP4_ADDRESS, HOME_AGENT_ADDRESS, "INTERNAL_IP4_ADDRESS", "INTERNAL_IP4_NETMASK", "INTERNAL_IP4_DNS", @@ -35,8 +35,9 @@ ENUM_BEGIN(configuration_attribute_type_names, INTERNAL_IP4_ADDRESS, INTERNAL_IP "INTERNAL_IP6_SUBNET", "MIP6_HOME_PREFIX", "INTERNAL_IP6_LINK", - "INTERNAL_IP6_PREFIX"); -ENUM_NEXT(configuration_attribute_type_names, XAUTH_TYPE, XAUTH_ANSWER, INTERNAL_IP6_PREFIX, + "INTERNAL_IP6_PREFIX", + "HOME_AGENT_ADDRESS"); +ENUM_NEXT(configuration_attribute_type_names, XAUTH_TYPE, XAUTH_ANSWER, HOME_AGENT_ADDRESS, "XAUTH_TYPE", "XAUTH_USER_NAME", "XAUTH_USER_PASSWORD", @@ -64,7 +65,7 @@ ENUM_NEXT(configuration_attribute_type_names, UNITY_BANNER, UNITY_DDNS_HOSTNAME, "UNITY_DDNS_HOSTNAME"); ENUM_END(configuration_attribute_type_names, UNITY_DDNS_HOSTNAME); -ENUM_BEGIN(configuration_attribute_type_short_names, INTERNAL_IP4_ADDRESS, INTERNAL_IP6_PREFIX, +ENUM_BEGIN(configuration_attribute_type_short_names, INTERNAL_IP4_ADDRESS, HOME_AGENT_ADDRESS, "ADDR", "MASK", "DNS", @@ -78,35 +79,36 @@ ENUM_BEGIN(configuration_attribute_type_short_names, INTERNAL_IP4_ADDRESS, INTER "NBNS6", "DHCP6", "SUBNET", - "SUPPORTED", + "SUP", "SUBNET6", "MIP6HPFX", "LINK6", - "PFX6"); -ENUM_NEXT(configuration_attribute_type_short_names, XAUTH_TYPE, XAUTH_ANSWER, INTERNAL_IP6_PREFIX, - "XAUTH_TYPE", - "XAUTH_USER_NAME", - "XAUTH_USER_PASSWORD", - "XAUTH_PASSCODE", - "XAUTH_MESSAGE", - "XAUTH_CHALLENGE", - "XAUTH_DOMAIN", - "XAUTH_STATUS", - "XAUTH_NEXT_PIN", - "XAUTH_ANSWER"); + "PFX6", + "HOA"); +ENUM_NEXT(configuration_attribute_type_short_names, XAUTH_TYPE, XAUTH_ANSWER, HOME_AGENT_ADDRESS, + "X_TYPE", + "X_USER_NAME", + "X_USER_PASSWORD", + "X_PASSCODE", + "X_MESSAGE", + "X_CHALLENGE", + "X_DOMAIN", + "X_STATUS", + "X_NEXT_PIN", + "X_ANSWER"); ENUM_NEXT(configuration_attribute_type_short_names, INTERNAL_IP4_SERVER, INTERNAL_IP6_SERVER, XAUTH_ANSWER, "SRV", "SRV6"); ENUM_NEXT(configuration_attribute_type_short_names, UNITY_BANNER, UNITY_DDNS_HOSTNAME, INTERNAL_IP6_SERVER, - "UNITY_BANNER", - "UNITY_SAVE_PASSWD", - "UNITY_DEF_DOMAIN", - "UNITY_SPLITDNS_NAME", - "UNITY_SPLIT_INCLUDE", - "UNITY_NATT_PORT", - "UNITY_LOCAL_LAN", - "UNITY_PFS", - "UNITY_FW_TYPE", - "UNITY_BACKUP_SERVERS", - "UNITY_DDNS_HOSTNAME"); + "U_BANNER", + "U_SAVE_PASSWD", + "U_DEF_DOMAIN", + "U_SPLITDNS_NAME", + "U_SPLIT_INCLUDE", + "U_NATT_PORT", + "U_LOCAL_LAN", + "U_PFS", + "U_FW_TYPE", + "U_BACKUP_SERVERS", + "U_DDNS_HOSTNAME"); ENUM_END(configuration_attribute_type_short_names, UNITY_DDNS_HOSTNAME); diff --git a/src/libhydra/attributes/attributes.h b/src/libhydra/attributes/attributes.h index 3a40ba367..8ff774b64 100644 --- a/src/libhydra/attributes/attributes.h +++ b/src/libhydra/attributes/attributes.h @@ -48,6 +48,7 @@ enum configuration_attribute_type_t { MIP6_HOME_PREFIX = 16, INTERNAL_IP6_LINK = 17, INTERNAL_IP6_PREFIX = 18, + HOME_AGENT_ADDRESS = 19, /* XAUTH attributes */ XAUTH_TYPE = 16520, XAUTH_USER_NAME = 16521, diff --git a/src/libhydra/kernel/kernel_interface.c b/src/libhydra/kernel/kernel_interface.c index 4b5b41f2b..573557506 100644 --- a/src/libhydra/kernel/kernel_interface.c +++ b/src/libhydra/kernel/kernel_interface.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2010 Tobias Brunner + * Copyright (C) 2008-2011 Tobias Brunner * Hochschule fuer Technik Rapperswil * Copyright (C) 2010 Martin Willi * Copyright (C) 2010 revosec AG @@ -34,6 +34,16 @@ struct private_kernel_interface_t { kernel_interface_t public; /** + * Registered IPsec constructor + */ + kernel_ipsec_constructor_t ipsec_constructor; + + /** + * Registered net constructor + */ + kernel_net_constructor_t net_constructor; + + /** * ipsec interface */ kernel_ipsec_t *ipsec; @@ -128,18 +138,28 @@ METHOD(kernel_interface_t, del_sa, status_t, return this->ipsec->del_sa(this->ipsec, src, dst, spi, protocol, cpi, mark); } +METHOD(kernel_interface_t, flush_sas, status_t, + private_kernel_interface_t *this) +{ + if (!this->ipsec) + { + return NOT_SUPPORTED; + } + return this->ipsec->flush_sas(this->ipsec); +} + METHOD(kernel_interface_t, add_policy, status_t, private_kernel_interface_t *this, host_t *src, host_t *dst, traffic_selector_t *src_ts, traffic_selector_t *dst_ts, policy_dir_t direction, policy_type_t type, ipsec_sa_cfg_t *sa, - mark_t mark, bool routed) + mark_t mark, policy_priority_t priority) { if (!this->ipsec) { return NOT_SUPPORTED; } return this->ipsec->add_policy(this->ipsec, src, dst, src_ts, dst_ts, - direction, type, sa, mark, routed); + direction, type, sa, mark, priority); } METHOD(kernel_interface_t, query_policy, status_t, @@ -157,15 +177,25 @@ METHOD(kernel_interface_t, query_policy, status_t, METHOD(kernel_interface_t, del_policy, status_t, private_kernel_interface_t *this, traffic_selector_t *src_ts, - traffic_selector_t *dst_ts, policy_dir_t direction, mark_t mark, - bool unrouted) + traffic_selector_t *dst_ts, policy_dir_t direction, u_int32_t reqid, + mark_t mark, policy_priority_t priority) { if (!this->ipsec) { return NOT_SUPPORTED; } return this->ipsec->del_policy(this->ipsec, src_ts, dst_ts, - direction, mark, unrouted); + direction, reqid, mark, priority); +} + +METHOD(kernel_interface_t, flush_policies, status_t, + private_kernel_interface_t *this) +{ + if (!this->ipsec) + { + return NOT_SUPPORTED; + } + return this->ipsec->flush_policies(this->ipsec); } METHOD(kernel_interface_t, get_source_addr, host_t*, @@ -310,7 +340,7 @@ METHOD(kernel_interface_t, get_address_by_ts, status_t, if (!found) { - DBG1(DBG_KNL, "no local address found in traffic selector %R", ts); + DBG2(DBG_KNL, "no local address found in traffic selector %R", ts); return FAILED; } @@ -324,6 +354,7 @@ METHOD(kernel_interface_t, add_ipsec_interface, void, { if (!this->ipsec) { + this->ipsec_constructor = constructor; this->ipsec = constructor(); } } @@ -331,7 +362,11 @@ METHOD(kernel_interface_t, add_ipsec_interface, void, METHOD(kernel_interface_t, remove_ipsec_interface, void, private_kernel_interface_t *this, kernel_ipsec_constructor_t constructor) { - /* TODO: replace if interface currently in use */ + if (constructor == this->ipsec_constructor) + { + this->ipsec->destroy(this->ipsec); + this->ipsec = NULL; + } } METHOD(kernel_interface_t, add_net_interface, void, @@ -339,6 +374,7 @@ METHOD(kernel_interface_t, add_net_interface, void, { if (!this->net) { + this->net_constructor = constructor; this->net = constructor(); } } @@ -346,7 +382,11 @@ METHOD(kernel_interface_t, add_net_interface, void, METHOD(kernel_interface_t, remove_net_interface, void, private_kernel_interface_t *this, kernel_net_constructor_t constructor) { - /* TODO: replace if interface currently in use */ + if (constructor == this->net_constructor) + { + this->net->destroy(this->net); + this->net = NULL; + } } METHOD(kernel_interface_t, add_listener, void, @@ -485,9 +525,11 @@ kernel_interface_t *kernel_interface_create() .update_sa = _update_sa, .query_sa = _query_sa, .del_sa = _del_sa, + .flush_sas = _flush_sas, .add_policy = _add_policy, .query_policy = _query_policy, .del_policy = _del_policy, + .flush_policies = _flush_policies, .get_source_addr = _get_source_addr, .get_nexthop = _get_nexthop, .get_interface = _get_interface, diff --git a/src/libhydra/kernel/kernel_interface.h b/src/libhydra/kernel/kernel_interface.h index 471a1d5d3..991cfafd0 100644 --- a/src/libhydra/kernel/kernel_interface.h +++ b/src/libhydra/kernel/kernel_interface.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2010 Tobias Brunner + * Copyright (C) 2006-2011 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -175,6 +175,13 @@ struct kernel_interface_t { mark_t mark); /** + * Flush all SAs from the SAD. + * + * @return SUCCESS if operation completed + */ + status_t (*flush_sas) (kernel_interface_t *this); + + /** * Add a policy to the SPD. * * A policy is always associated to an SA. Traffic which matches a @@ -188,7 +195,7 @@ struct kernel_interface_t { * @param type type of policy, POLICY_(IPSEC|PASS|DROP) * @param sa details about the SA(s) tied to this policy * @param mark mark for this policy - * @param routed TRUE, if this policy is routed in the kernel + * @param priority priority of this policy * @return SUCCESS if operation completed */ status_t (*add_policy) (kernel_interface_t *this, @@ -196,7 +203,8 @@ struct kernel_interface_t { traffic_selector_t *src_ts, traffic_selector_t *dst_ts, policy_dir_t direction, policy_type_t type, - ipsec_sa_cfg_t *sa, mark_t mark, bool routed); + ipsec_sa_cfg_t *sa, mark_t mark, + policy_priority_t priority); /** * Query the use time of a policy. @@ -228,15 +236,23 @@ struct kernel_interface_t { * @param src_ts traffic selector to match traffic source * @param dst_ts traffic selector to match traffic dest * @param direction direction of traffic, POLICY_(IN|OUT|FWD) + * @param reqid unique ID of the associated SA * @param mark optional mark - * @param unrouted TRUE, if this policy is unrouted from the kernel + * @param priority priority of the policy * @return SUCCESS if operation completed */ status_t (*del_policy) (kernel_interface_t *this, traffic_selector_t *src_ts, traffic_selector_t *dst_ts, - policy_dir_t direction, mark_t mark, - bool unrouted); + policy_dir_t direction, u_int32_t reqid, + mark_t mark, policy_priority_t priority); + + /** + * Flush all policies from the SPD. + * + * @return SUCCESS if operation completed + */ + status_t (*flush_policies) (kernel_interface_t *this); /** * Get our outgoing source address for a destination. diff --git a/src/libhydra/kernel/kernel_ipsec.c b/src/libhydra/kernel/kernel_ipsec.c index 383685426..9b38297cc 100644 --- a/src/libhydra/kernel/kernel_ipsec.c +++ b/src/libhydra/kernel/kernel_ipsec.c @@ -15,10 +15,14 @@ #include "kernel_ipsec.h" -ENUM(ipsec_mode_names, MODE_TRANSPORT, MODE_BEET, +#include <hydra.h> + +ENUM(ipsec_mode_names, MODE_TRANSPORT, MODE_DROP, "TRANSPORT", "TUNNEL", "BEET", + "PASS", + "DROP" ); ENUM(policy_dir_names, POLICY_IN, POLICY_FWD, @@ -35,3 +39,21 @@ ENUM(ipcomp_transform_names, IPCOMP_NONE, IPCOMP_LZJH, "IPCOMP_LZJH" ); +/** + * See header + */ +bool kernel_ipsec_register(plugin_t *plugin, plugin_feature_t *feature, + bool reg, void *data) +{ + if (reg) + { + hydra->kernel_interface->add_ipsec_interface(hydra->kernel_interface, + (kernel_ipsec_constructor_t)data); + } + else + { + hydra->kernel_interface->remove_ipsec_interface(hydra->kernel_interface, + (kernel_ipsec_constructor_t)data); + } + return TRUE; +} diff --git a/src/libhydra/kernel/kernel_ipsec.h b/src/libhydra/kernel/kernel_ipsec.h index ef36efd11..ddb63283c 100644 --- a/src/libhydra/kernel/kernel_ipsec.h +++ b/src/libhydra/kernel/kernel_ipsec.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2010 Tobias Brunner + * Copyright (C) 2006-2011 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -27,6 +27,7 @@ typedef enum ipsec_mode_t ipsec_mode_t; typedef enum policy_dir_t policy_dir_t; typedef enum policy_type_t policy_type_t; +typedef enum policy_priority_t policy_priority_t; typedef enum ipcomp_transform_t ipcomp_transform_t; typedef struct kernel_ipsec_t kernel_ipsec_t; typedef struct ipsec_sa_cfg_t ipsec_sa_cfg_t; @@ -36,6 +37,7 @@ typedef struct mark_t mark_t; #include <utils/host.h> #include <crypto/prf_plus.h> #include <selectors/traffic_selector.h> +#include <plugins/plugin.h> /** * Mode of an IPsec SA. @@ -47,6 +49,10 @@ enum ipsec_mode_t { MODE_TUNNEL, /** BEET mode, tunnel mode but fixed, bound inner addresses */ MODE_BEET, + /** passthrough policy for traffic without an IPsec SA */ + MODE_PASS, + /** drop policy discarding traffic */ + MODE_DROP }; /** @@ -86,6 +92,18 @@ enum policy_type_t { }; /** + * High-level priority of a policy. + */ +enum policy_priority_t { + /** Default priority */ + POLICY_PRIORITY_DEFAULT, + /** Priority for trap policies */ + POLICY_PRIORITY_ROUTED, + /** Priority for fallback drop policies */ + POLICY_PRIORITY_FALLBACK, +}; + +/** * IPComp transform IDs, as in RFC 4306 */ enum ipcomp_transform_t { @@ -288,6 +306,13 @@ struct kernel_ipsec_t { mark_t mark); /** + * Flush all SAs from the SAD. + * + * @return SUCCESS if operation completed + */ + status_t (*flush_sas) (kernel_ipsec_t *this); + + /** * Add a policy to the SPD. * * A policy is always associated to an SA. Traffic which matches a @@ -301,7 +326,7 @@ struct kernel_ipsec_t { * @param type type of policy, POLICY_(IPSEC|PASS|DROP) * @param sa details about the SA(s) tied to this policy * @param mark mark for this policy - * @param routed TRUE, if this policy is routed in the kernel + * @param priority priority of this policy * @return SUCCESS if operation completed */ status_t (*add_policy) (kernel_ipsec_t *this, @@ -309,7 +334,8 @@ struct kernel_ipsec_t { traffic_selector_t *src_ts, traffic_selector_t *dst_ts, policy_dir_t direction, policy_type_t type, - ipsec_sa_cfg_t *sa, mark_t mark, bool routed); + ipsec_sa_cfg_t *sa, mark_t mark, + policy_priority_t priority); /** * Query the use time of a policy. @@ -342,15 +368,23 @@ struct kernel_ipsec_t { * @param src_ts traffic selector to match traffic source * @param dst_ts traffic selector to match traffic dest * @param direction direction of traffic, POLICY_(IN|OUT|FWD) + * @param reqid unique ID of the associated SA * @param mark optional mark - * @param unrouted TRUE, if this policy is unrouted from the kernel + * @param priority priority of the policy * @return SUCCESS if operation completed */ status_t (*del_policy) (kernel_ipsec_t *this, traffic_selector_t *src_ts, traffic_selector_t *dst_ts, - policy_dir_t direction, mark_t mark, - bool unrouted); + policy_dir_t direction, u_int32_t reqid, + mark_t mark, policy_priority_t priority); + + /** + * Flush all policies from the SPD. + * + * @return SUCCESS if operation completed + */ + status_t (*flush_policies) (kernel_ipsec_t *this); /** * Install a bypass policy for the given socket. @@ -367,4 +401,18 @@ struct kernel_ipsec_t { void (*destroy) (kernel_ipsec_t *this); }; +/** + * Helper function to (un-)register IPsec kernel interfaces from plugin features. + * + * This function is a plugin_feature_callback_t and can be used with the + * PLUGIN_CALLBACK macro to register an IPsec kernel interface constructor. + * + * @param plugin plugin registering the kernel interface + * @param feature associated plugin feature + * @param reg TRUE to register, FALSE to unregister + * @param data data passed to callback, an kernel_ipsec_constructor_t + */ +bool kernel_ipsec_register(plugin_t *plugin, plugin_feature_t *feature, + bool reg, void *data); + #endif /** KERNEL_IPSEC_H_ @}*/ diff --git a/src/libhydra/kernel/kernel_listener.h b/src/libhydra/kernel/kernel_listener.h index 6f2dbd23b..5db297b6f 100644 --- a/src/libhydra/kernel/kernel_listener.h +++ b/src/libhydra/kernel/kernel_listener.h @@ -84,7 +84,7 @@ struct kernel_listener_t { policy_dir_t direction, host_t *local, host_t *remote); /** - * Hook called if changes in the networking layer occured (interfaces + * Hook called if changes in the networking layer occurred (interfaces * up/down, routes added/deleted etc.). * * @param address TRUE if address list, FALSE if routing changed diff --git a/src/libhydra/kernel/kernel_net.c b/src/libhydra/kernel/kernel_net.c new file mode 100644 index 000000000..0841ed803 --- /dev/null +++ b/src/libhydra/kernel/kernel_net.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2011 Martin Willi + * Copyright (C) 2011 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "kernel_net.h" + +#include <hydra.h> + +/** + * See header + */ +bool kernel_net_register(plugin_t *plugin, plugin_feature_t *feature, + bool reg, void *data) +{ + if (reg) + { + hydra->kernel_interface->add_net_interface(hydra->kernel_interface, + (kernel_net_constructor_t)data); + } + else + { + hydra->kernel_interface->remove_net_interface(hydra->kernel_interface, + (kernel_net_constructor_t)data); + } + return TRUE; +} diff --git a/src/libhydra/kernel/kernel_net.h b/src/libhydra/kernel/kernel_net.h index 69e01f43f..a89e76804 100644 --- a/src/libhydra/kernel/kernel_net.h +++ b/src/libhydra/kernel/kernel_net.h @@ -26,6 +26,7 @@ typedef struct kernel_net_t kernel_net_t; #include <utils/enumerator.h> #include <utils/host.h> +#include <plugins/plugin.h> /** * Interface to the network subsystem of the kernel. @@ -142,4 +143,18 @@ struct kernel_net_t { void (*destroy) (kernel_net_t *this); }; +/** + * Helper function to (un-)register net kernel interfaces from plugin features. + * + * This function is a plugin_feature_callback_t and can be used with the + * PLUGIN_CALLBACK macro to register an net kernel interface constructor. + * + * @param plugin plugin registering the kernel interface + * @param feature associated plugin feature + * @param reg TRUE to register, FALSE to unregister + * @param data data passed to callback, an kernel_net_constructor_t + */ +bool kernel_net_register(plugin_t *plugin, plugin_feature_t *feature, + bool reg, void *data); + #endif /** KERNEL_NET_H_ @}*/ diff --git a/src/libhydra/plugins/attr/Makefile.in b/src/libhydra/plugins/attr/Makefile.in index 250ac9539..1ceb93ef3 100644 --- a/src/libhydra/plugins/attr/Makefile.in +++ b/src/libhydra/plugins/attr/Makefile.in @@ -191,6 +191,9 @@ am__leading_dot = @am__leading_dot@ 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@ @@ -199,6 +202,7 @@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ c_plugins = @c_plugins@ +clearsilver_LIBS = @clearsilver_LIBS@ datadir = @datadir@ datarootdir = @datarootdir@ dbusservicedir = @dbusservicedir@ @@ -215,11 +219,13 @@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ +imcvdir = @imcvdir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ ipsecdir = @ipsecdir@ ipsecgroup = @ipsecgroup@ +ipseclibdir = @ipseclibdir@ ipsecuser = @ipsecuser@ libcharon_plugins = @libcharon_plugins@ libdir = @libdir@ @@ -263,6 +269,7 @@ sharedstatedir = @sharedstatedir@ soup_CFLAGS = @soup_CFLAGS@ soup_LIBS = @soup_LIBS@ srcdir = @srcdir@ +starter_plugins = @starter_plugins@ strongswan_conf = @strongswan_conf@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ diff --git a/src/libhydra/plugins/attr_sql/Makefile.in b/src/libhydra/plugins/attr_sql/Makefile.in index 80d497f59..4fe577f3b 100644 --- a/src/libhydra/plugins/attr_sql/Makefile.in +++ b/src/libhydra/plugins/attr_sql/Makefile.in @@ -204,6 +204,9 @@ am__leading_dot = @am__leading_dot@ 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@ @@ -212,6 +215,7 @@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ c_plugins = @c_plugins@ +clearsilver_LIBS = @clearsilver_LIBS@ datadir = @datadir@ datarootdir = @datarootdir@ dbusservicedir = @dbusservicedir@ @@ -228,11 +232,13 @@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ +imcvdir = @imcvdir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ ipsecdir = @ipsecdir@ ipsecgroup = @ipsecgroup@ +ipseclibdir = @ipseclibdir@ ipsecuser = @ipsecuser@ libcharon_plugins = @libcharon_plugins@ libdir = @libdir@ @@ -276,6 +282,7 @@ sharedstatedir = @sharedstatedir@ soup_CFLAGS = @soup_CFLAGS@ soup_LIBS = @soup_LIBS@ srcdir = @srcdir@ +starter_plugins = @starter_plugins@ strongswan_conf = @strongswan_conf@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ diff --git a/src/libhydra/plugins/attr_sql/pool.c b/src/libhydra/plugins/attr_sql/pool.c index e81a23ed9..a2000cffe 100644 --- a/src/libhydra/plugins/attr_sql/pool.c +++ b/src/libhydra/plugins/attr_sql/pool.c @@ -305,6 +305,11 @@ static void status(void) lease->enumerate(lease, &size); lease->destroy(lease); } + if (!size) + { /* empty pool */ + printf("%6d %11s %11s ", 0, "n/a", "n/a"); + goto next_pool; + } printf("%6d ", size); /* get number of online hosts */ lease = db->query(db, "SELECT COUNT(*) FROM addresses " @@ -316,7 +321,7 @@ static void status(void) lease->destroy(lease); } printf("%5d (%2d%%) ", online, online*100/size); - /* get number of online or valid lieases */ + /* get number of online or valid leases */ lease = db->query(db, "SELECT COUNT(*) FROM addresses " "WHERE addresses.pool = ? " "AND ((? AND acquired != 0) " @@ -330,6 +335,7 @@ static void status(void) } printf("%5d (%2d%%) ", used, used*100/size); +next_pool: printf("\n"); DESTROY_IF(start); DESTROY_IF(end); diff --git a/src/libhydra/plugins/attr_sql/sql_attribute.c b/src/libhydra/plugins/attr_sql/sql_attribute.c index 7f7bb190c..714bbcd72 100644 --- a/src/libhydra/plugins/attr_sql/sql_attribute.c +++ b/src/libhydra/plugins/attr_sql/sql_attribute.c @@ -38,7 +38,7 @@ struct private_sql_attribute_t { database_t *db; /** - * wheter to record lease history in lease table + * whether to record lease history in lease table */ bool history; }; @@ -232,12 +232,9 @@ static host_t* get_lease(private_sql_attribute_t *this, char *name, return NULL; } -/** - * Implementation of attribute_provider_t.acquire_address - */ -static host_t* acquire_address(private_sql_attribute_t *this, - char *names, identification_t *id, - host_t *requested) +METHOD(attribute_provider_t, acquire_address, host_t*, + private_sql_attribute_t *this, char *names, identification_t *id, + host_t *requested) { host_t *address = NULL; u_int identity, pool, timeout; @@ -302,11 +299,9 @@ static host_t* acquire_address(private_sql_attribute_t *this, return address; } -/** - * Implementation of attribute_provider_t.release_address - */ -static bool release_address(private_sql_attribute_t *this, - char *name, host_t *address, identification_t *id) +METHOD(attribute_provider_t, release_address, bool, + private_sql_attribute_t *this, char *name, host_t *address, + identification_t *id) { enumerator_t *enumerator; bool found = FALSE; @@ -343,11 +338,9 @@ static bool release_address(private_sql_attribute_t *this, return found; } -/** - * Implementation of sql_attribute_t.create_attribute_enumerator - */ -static enumerator_t* create_attribute_enumerator(private_sql_attribute_t *this, - char *names, identification_t *id, host_t *vip) +METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*, + private_sql_attribute_t *this, char *names, identification_t *id, + host_t *vip) { enumerator_t *attr_enumerator = NULL; @@ -444,10 +437,8 @@ static enumerator_t* create_attribute_enumerator(private_sql_attribute_t *this, return (attr_enumerator ? attr_enumerator : enumerator_create_empty()); } -/** - * Implementation of sql_attribute_t.destroy - */ -static void destroy(private_sql_attribute_t *this) +METHOD(sql_attribute_t, destroy, void, + private_sql_attribute_t *this) { free(this); } @@ -457,17 +448,22 @@ static void destroy(private_sql_attribute_t *this) */ sql_attribute_t *sql_attribute_create(database_t *db) { - private_sql_attribute_t *this = malloc_thing(private_sql_attribute_t); + private_sql_attribute_t *this; time_t now = time(NULL); - this->public.provider.acquire_address = (host_t*(*)(attribute_provider_t *this, char*, identification_t *, host_t *))acquire_address; - this->public.provider.release_address = (bool(*)(attribute_provider_t *this, char*,host_t *, identification_t*))release_address; - this->public.provider.create_attribute_enumerator = (enumerator_t*(*)(attribute_provider_t*, char *names, identification_t *id, host_t *host))create_attribute_enumerator; - this->public.destroy = (void(*)(sql_attribute_t*))destroy; - - this->db = db; - this->history = lib->settings->get_bool(lib->settings, - "libhydra.plugins.attr-sql.lease_history", TRUE); + INIT(this, + .public = { + .provider = { + .acquire_address = _acquire_address, + .release_address = _release_address, + .create_attribute_enumerator = _create_attribute_enumerator, + }, + .destroy = _destroy, + }, + .db = db, + .history = lib->settings->get_bool(lib->settings, + "libhydra.plugins.attr-sql.lease_history", TRUE), + ); /* close any "online" leases in the case we crashed */ if (this->history) diff --git a/src/libhydra/plugins/kernel_klips/Makefile.in b/src/libhydra/plugins/kernel_klips/Makefile.in index 5f6512b44..63f3e045b 100644 --- a/src/libhydra/plugins/kernel_klips/Makefile.in +++ b/src/libhydra/plugins/kernel_klips/Makefile.in @@ -195,6 +195,9 @@ am__leading_dot = @am__leading_dot@ 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@ @@ -203,6 +206,7 @@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ c_plugins = @c_plugins@ +clearsilver_LIBS = @clearsilver_LIBS@ datadir = @datadir@ datarootdir = @datarootdir@ dbusservicedir = @dbusservicedir@ @@ -219,11 +223,13 @@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ +imcvdir = @imcvdir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ ipsecdir = @ipsecdir@ ipsecgroup = @ipsecgroup@ +ipseclibdir = @ipseclibdir@ ipsecuser = @ipsecuser@ libcharon_plugins = @libcharon_plugins@ libdir = @libdir@ @@ -267,6 +273,7 @@ sharedstatedir = @sharedstatedir@ soup_CFLAGS = @soup_CFLAGS@ soup_LIBS = @soup_LIBS@ srcdir = @srcdir@ +starter_plugins = @starter_plugins@ strongswan_conf = @strongswan_conf@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ diff --git a/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c b/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c index ff4f0ed55..ceff8cdc9 100644 --- a/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c +++ b/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c @@ -1971,7 +1971,7 @@ METHOD(kernel_ipsec_t, add_policy, status_t, private_kernel_klips_ipsec_t *this, host_t *src, host_t *dst, traffic_selector_t *src_ts, traffic_selector_t *dst_ts, policy_dir_t direction, policy_type_t type, ipsec_sa_cfg_t *sa, - mark_t mark, bool routed) + mark_t mark, policy_priority_t priority) { unsigned char request[PFKEY_BUFFER_SIZE]; struct sadb_msg *msg, *out; @@ -2013,7 +2013,7 @@ METHOD(kernel_ipsec_t, add_policy, status_t, this->policies->insert_last(this->policies, policy); } - if (routed) + if (priority == POLICY_PRIORITY_ROUTED) { /* we install this as a %trap eroute in the kernel, later to be * triggered by packets matching the policy (-> ACQUIRE). */ @@ -2049,9 +2049,11 @@ METHOD(kernel_ipsec_t, add_policy, status_t, msg = (struct sadb_msg*)request; /* FIXME: SADB_X_SAFLAGS_INFLOW may be required, if we add an inbound policy for an IPIP SA */ - build_addflow(msg, satype, spi, routed ? NULL : src, routed ? NULL : dst, - policy->src.net, policy->src.mask, policy->dst.net, policy->dst.mask, - policy->src.proto, found != NULL); + build_addflow(msg, satype, spi, + priority == POLICY_PRIORITY_ROUTED ? NULL : src, + priority == POLICY_PRIORITY_ROUTED ? NULL : dst, + policy->src.net, policy->src.mask, policy->dst.net, + policy->dst.mask, policy->src.proto, found != NULL); this->mutex->unlock(this->mutex); @@ -2347,8 +2349,8 @@ METHOD(kernel_ipsec_t, query_policy, status_t, METHOD(kernel_ipsec_t, del_policy, status_t, private_kernel_klips_ipsec_t *this, traffic_selector_t *src_ts, - traffic_selector_t *dst_ts, policy_dir_t direction, mark_t mark, - bool unrouted) + traffic_selector_t *dst_ts, policy_dir_t direction, u_int32_t reqid, + mark_t mark, policy_priority_t priority) { unsigned char request[PFKEY_BUFFER_SIZE]; struct sadb_msg *msg = (struct sadb_msg*)request, *out; @@ -2382,7 +2384,8 @@ METHOD(kernel_ipsec_t, del_policy, status_t, policy_entry_destroy(policy); /* decrease appropriate counter */ - unrouted ? found->trapcount-- : found->activecount--; + priority == POLICY_PRIORITY_ROUTED ? found->trapcount-- + : found->activecount--; if (found->trapcount == 0) { @@ -2507,7 +2510,7 @@ static void init_ipsec_devices(private_kernel_klips_ipsec_t *this) } /** - * Register a socket for AQUIRE/EXPIRE messages + * Register a socket for ACQUIRE/EXPIRE messages */ static status_t register_pfkey_socket(private_kernel_klips_ipsec_t *this, u_int8_t satype) { @@ -2586,9 +2589,11 @@ kernel_klips_ipsec_t *kernel_klips_ipsec_create() .update_sa = _update_sa, .query_sa = _query_sa, .del_sa = _del_sa, + .flush_sas = (void*)return_failed, .add_policy = _add_policy, .query_policy = _query_policy, .del_policy = _del_policy, + .flush_policies = (void*)return_failed, .bypass_socket = _bypass_socket, .destroy = _destroy, }, @@ -2634,8 +2639,8 @@ kernel_klips_ipsec_t *kernel_klips_ipsec_create() return NULL; } - this->job = callback_job_create((callback_job_cb_t)receive_events, - this, NULL, NULL); + 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_klips/kernel_klips_plugin.c b/src/libhydra/plugins/kernel_klips/kernel_klips_plugin.c index 7fe47f630..ab02ba711 100644 --- a/src/libhydra/plugins/kernel_klips/kernel_klips_plugin.c +++ b/src/libhydra/plugins/kernel_klips/kernel_klips_plugin.c @@ -38,11 +38,20 @@ METHOD(plugin_t, get_name, char*, return "kernel-klips"; } +METHOD(plugin_t, get_features, int, + private_kernel_klips_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_CALLBACK(kernel_ipsec_register, kernel_klips_ipsec_create), + PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"), + }; + *features = f; + return countof(f); +} + METHOD(plugin_t, destroy, void, private_kernel_klips_plugin_t *this) { - hydra->kernel_interface->remove_ipsec_interface(hydra->kernel_interface, - (kernel_ipsec_constructor_t)kernel_klips_ipsec_create); free(this); } @@ -57,13 +66,11 @@ plugin_t *kernel_klips_plugin_create() .public = { .plugin = { .get_name = _get_name, - .reload = (void*)return_false, + .get_features = _get_features, .destroy = _destroy, }, }, ); - hydra->kernel_interface->add_ipsec_interface(hydra->kernel_interface, - (kernel_ipsec_constructor_t)kernel_klips_ipsec_create); return &this->public.plugin; } diff --git a/src/libhydra/plugins/kernel_netlink/Makefile.in b/src/libhydra/plugins/kernel_netlink/Makefile.in index 78dfb1b54..73dbdd0e3 100644 --- a/src/libhydra/plugins/kernel_netlink/Makefile.in +++ b/src/libhydra/plugins/kernel_netlink/Makefile.in @@ -196,6 +196,9 @@ am__leading_dot = @am__leading_dot@ 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@ @@ -204,6 +207,7 @@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ c_plugins = @c_plugins@ +clearsilver_LIBS = @clearsilver_LIBS@ datadir = @datadir@ datarootdir = @datarootdir@ dbusservicedir = @dbusservicedir@ @@ -220,11 +224,13 @@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ +imcvdir = @imcvdir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ ipsecdir = @ipsecdir@ ipsecgroup = @ipsecgroup@ +ipseclibdir = @ipseclibdir@ ipsecuser = @ipsecuser@ libcharon_plugins = @libcharon_plugins@ libdir = @libdir@ @@ -268,6 +274,7 @@ sharedstatedir = @sharedstatedir@ soup_CFLAGS = @soup_CFLAGS@ soup_LIBS = @soup_LIBS@ srcdir = @srcdir@ +starter_plugins = @starter_plugins@ strongswan_conf = @strongswan_conf@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c index 8b2a1aa77..b2cf778be 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-2010 Tobias Brunner + * Copyright (C) 2006-2011 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2008 Andreas Steffen * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser @@ -40,32 +40,32 @@ #include <threading/thread.h> #include <threading/mutex.h> #include <utils/hashtable.h> +#include <utils/linked_list.h> #include <processing/jobs/callback_job.h> -/** required for Linux 2.6.26 kernel and later */ +/** Required for Linux 2.6.26 kernel and later */ #ifndef XFRM_STATE_AF_UNSPEC -#define XFRM_STATE_AF_UNSPEC 32 +#define XFRM_STATE_AF_UNSPEC 32 #endif -/** from linux/in.h */ +/** From linux/in.h */ #ifndef IP_XFRM_POLICY #define IP_XFRM_POLICY 17 #endif -/* missing on uclibc */ +/** Missing on uclibc */ #ifndef IPV6_XFRM_POLICY #define IPV6_XFRM_POLICY 34 #endif /*IPV6_XFRM_POLICY*/ -/** default priority of installed policies */ -#define PRIO_LOW 1024 -#define PRIO_HIGH 512 +/** Default priority of installed policies */ +#define PRIO_BASE 512 -/** default replay window size, if not set using charon.replay_window */ +/** Default replay window size, if not set using charon.replay_window */ #define DEFAULT_REPLAY_WINDOW 32 /** - * map the limit for bytes and packets to XFRM_INF per default + * Map the limit for bytes and packets to XFRM_INF by default */ #define XFRM_LIMIT(x) ((x) == 0 ? XFRM_INF : (x)) @@ -75,17 +75,19 @@ #define XFRMNLGRP(x) (1<<(XFRMNLGRP_##x-1)) /** - * returns a pointer to the first rtattr following the nlmsghdr *nlh and the + * Returns a pointer to the first rtattr following the nlmsghdr *nlh and the * 'usual' netlink data x like 'struct xfrm_usersa_info' */ -#define XFRM_RTA(nlh, x) ((struct rtattr*)(NLMSG_DATA(nlh) + NLMSG_ALIGN(sizeof(x)))) +#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 !!! + * 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))) +#define XFRM_RTA_NEXT(rta) ((struct rtattr*)(((char*)(rta)) + \ + RTA_ALIGN((rta)->rta_len))) /** - * returns the total size of attached rta data + * Returns the total size of attached rta data * (after 'usual' netlink data x like 'struct xfrm_usersa_info') */ #define XFRM_PAYLOAD(nlh, x) NLMSG_PAYLOAD(nlh, sizeof(x)) @@ -133,7 +135,7 @@ ENUM(xfrm_msg_names, XFRM_MSG_NEWSA, XFRM_MSG_MAPPING, "XFRM_MSG_MAPPING" ); -ENUM(xfrm_attr_type_names, XFRMA_UNSPEC, XFRMA_KMADDRESS, +ENUM(xfrm_attr_type_names, XFRMA_UNSPEC, XFRMA_REPLAY_ESN_VAL, "XFRMA_UNSPEC", "XFRMA_ALG_AUTH", "XFRMA_ALG_CRYPT", @@ -153,7 +155,11 @@ ENUM(xfrm_attr_type_names, XFRMA_UNSPEC, XFRMA_KMADDRESS, "XFRMA_POLICY_TYPE", "XFRMA_MIGRATE", "XFRMA_ALG_AEAD", - "XFRMA_KMADDRESS" + "XFRMA_KMADDRESS", + "XFRMA_ALG_AUTH_TRUNC", + "XFRMA_MARK", + "XFRMA_TFCPAD", + "XFRMA_REPLAY_ESN_VAL", ); #define END_OF_LIST -1 @@ -196,7 +202,9 @@ static kernel_algorithm_t encryption_algs[] = { */ static kernel_algorithm_t integrity_algs[] = { {AUTH_HMAC_MD5_96, "md5" }, + {AUTH_HMAC_MD5_128, "hmac(md5)" }, {AUTH_HMAC_SHA1_96, "sha1" }, + {AUTH_HMAC_SHA1_160, "hmac(sha1)" }, {AUTH_HMAC_SHA2_256_96, "sha256" }, {AUTH_HMAC_SHA2_256_128, "hmac(sha256)" }, {AUTH_HMAC_SHA2_384_192, "hmac(sha384)" }, @@ -234,10 +242,72 @@ static char* lookup_algorithm(kernel_algorithm_t *list, int ikev2) return NULL; } +typedef struct private_kernel_netlink_ipsec_t private_kernel_netlink_ipsec_t; + +/** + * Private variables and functions of kernel_netlink class. + */ +struct private_kernel_netlink_ipsec_t { + /** + * Public part of the kernel_netlink_t object + */ + kernel_netlink_ipsec_t public; + + /** + * Mutex to lock access to installed policies + */ + mutex_t *mutex; + + /** + * Hash table of installed policies (policy_entry_t) + */ + hashtable_t *policies; + + /** + * Hash table of IPsec SAs using policies (ipsec_sa_t) + */ + hashtable_t *sas; + + /** + * Job receiving netlink events + */ + callback_job_t *job; + + /** + * Netlink xfrm socket (IPsec) + */ + netlink_socket_t *socket_xfrm; + + /** + * Netlink xfrm socket to receive acquire and expire events + */ + int socket_xfrm_events; + + /** + * Whether to install routes along policies + */ + bool install_routes; + + /** + * Whether to track the history of a policy + */ + bool policy_history; + + /** + * Size of the replay window, in packets + */ + u_int32_t replay_window; + + /** + * Size of the replay window bitmap, in bytes + */ + u_int32_t replay_bmp; +}; + typedef struct route_entry_t route_entry_t; /** - * installed routing entry + * Installed routing entry */ struct route_entry_t { /** Name of the interface the route is bound to */ @@ -246,7 +316,7 @@ struct route_entry_t { /** Source ip of the route */ host_t *src_ip; - /** gateway for this route */ + /** Gateway for this route */ host_t *gateway; /** Destination net */ @@ -257,7 +327,7 @@ struct route_entry_t { }; /** - * destroy an route_entry_t object + * Destroy a route_entry_t object */ static void route_entry_destroy(route_entry_t *this) { @@ -268,30 +338,226 @@ static void route_entry_destroy(route_entry_t *this) free(this); } +/** + * Compare two route_entry_t objects + */ +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) && + chunk_equals(a->dst_net, b->dst_net) && a->prefixlen == b->prefixlen; +} + +typedef struct ipsec_sa_t ipsec_sa_t; + +/** + * IPsec SA assigned to a policy. + */ +struct ipsec_sa_t { + /** Source address of this SA */ + host_t *src; + + /** Destination address of this SA */ + host_t *dst; + + /** Optional mark */ + mark_t mark; + + /** Description of this SA */ + ipsec_sa_cfg_t cfg; + + /** Reference count for this SA */ + refcount_t refcount; +}; + +/** + * Hash function for ipsec_sa_t objects + */ +static u_int ipsec_sa_hash(ipsec_sa_t *sa) +{ + return chunk_hash_inc(sa->src->get_address(sa->src), + chunk_hash_inc(sa->dst->get_address(sa->dst), + chunk_hash_inc(chunk_from_thing(sa->mark), + chunk_hash(chunk_from_thing(sa->cfg))))); +} + +/** + * Equality function for ipsec_sa_t objects + */ +static bool ipsec_sa_equals(ipsec_sa_t *sa, ipsec_sa_t *other_sa) +{ + return sa->src->ip_equals(sa->src, other_sa->src) && + sa->dst->ip_equals(sa->dst, other_sa->dst) && + memeq(&sa->mark, &other_sa->mark, sizeof(mark_t)) && + memeq(&sa->cfg, &other_sa->cfg, sizeof(ipsec_sa_cfg_t)); +} + +/** + * Allocate or reference an IPsec SA object + */ +static ipsec_sa_t *ipsec_sa_create(private_kernel_netlink_ipsec_t *this, + host_t *src, host_t *dst, mark_t mark, + ipsec_sa_cfg_t *cfg) +{ + ipsec_sa_t *sa, *found; + INIT(sa, + .src = src, + .dst = dst, + .mark = mark, + .cfg = *cfg, + ); + found = this->sas->get(this->sas, sa); + if (!found) + { + sa->src = src->clone(src); + sa->dst = dst->clone(dst); + this->sas->put(this->sas, sa, sa); + } + else + { + free(sa); + sa = found; + } + ref_get(&sa->refcount); + return sa; +} + +/** + * Release and destroy an IPsec SA object + */ +static void ipsec_sa_destroy(private_kernel_netlink_ipsec_t *this, + ipsec_sa_t *sa) +{ + if (ref_put(&sa->refcount)) + { + this->sas->remove(this->sas, sa); + DESTROY_IF(sa->src); + DESTROY_IF(sa->dst); + free(sa); + } +} + +typedef struct policy_sa_t policy_sa_t; +typedef struct policy_sa_fwd_t policy_sa_fwd_t; + +/** + * Mapping between a policy and an IPsec SA. + */ +struct policy_sa_t { + /** Priority assigned to the policy when installed with this SA */ + u_int32_t priority; + + /** Type of the policy */ + policy_type_t type; + + /** Assigned SA */ + ipsec_sa_t *sa; +}; + +/** + * For forward policies we also cache the traffic selectors in order to install + * the route. + */ +struct policy_sa_fwd_t { + /** Generic interface */ + policy_sa_t generic; + + /** Source traffic selector of this policy */ + traffic_selector_t *src_ts; + + /** Destination traffic selector of this policy */ + traffic_selector_t *dst_ts; +}; + +/** + * Create a policy_sa(_fwd)_t object + */ +static policy_sa_t *policy_sa_create(private_kernel_netlink_ipsec_t *this, + policy_dir_t dir, policy_type_t type, host_t *src, host_t *dst, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts, mark_t mark, + ipsec_sa_cfg_t *cfg) +{ + policy_sa_t *policy; + + if (dir == POLICY_FWD) + { + policy_sa_fwd_t *fwd; + INIT(fwd, + .src_ts = src_ts->clone(src_ts), + .dst_ts = dst_ts->clone(dst_ts), + ); + policy = &fwd->generic; + } + else + { + INIT(policy, .priority = 0); + } + policy->type = type; + policy->sa = ipsec_sa_create(this, src, dst, mark, cfg); + return policy; +} + +/** + * Destroy a policy_sa(_fwd)_t object + */ +static void policy_sa_destroy(policy_sa_t *policy, policy_dir_t *dir, + private_kernel_netlink_ipsec_t *this) +{ + if (*dir == POLICY_FWD) + { + policy_sa_fwd_t *fwd = (policy_sa_fwd_t*)policy; + fwd->src_ts->destroy(fwd->src_ts); + fwd->dst_ts->destroy(fwd->dst_ts); + } + ipsec_sa_destroy(this, policy->sa); + free(policy); +} + typedef struct policy_entry_t policy_entry_t; /** - * installed kernel policy. + * Installed kernel policy. */ struct policy_entry_t { - /** direction of this policy: in, out, forward */ + /** Direction of this policy: in, out, forward */ u_int8_t direction; - /** parameters of installed policy */ + /** Parameters of installed policy */ struct xfrm_selector sel; - /** optional mark */ + /** Optional mark */ u_int32_t mark; - /** associated route installed for this policy */ + /** Associated route installed for this policy */ route_entry_t *route; - /** by how many CHILD_SA's this policy is used */ - u_int refcount; + /** List of SAs this policy is used by, ordered by priority */ + linked_list_t *used_by; }; /** + * Destroy a policy_entry_t object + */ +static void policy_entry_destroy(private_kernel_netlink_ipsec_t *this, + policy_entry_t *policy) +{ + if (policy->route) + { + route_entry_destroy(policy->route); + } + if (policy->used_by) + { + policy->used_by->invoke_function(policy->used_by, + (linked_list_invoke_t)policy_sa_destroy, + &policy->direction, this); + policy->used_by->destroy(policy->used_by); + } + free(policy); +} + +/** * Hash function for policy_entry_t objects */ static u_int policy_hash(policy_entry_t *key) @@ -311,60 +577,35 @@ static bool policy_equals(policy_entry_t *key, policy_entry_t *other_key) key->direction == other_key->direction; } -typedef struct private_kernel_netlink_ipsec_t private_kernel_netlink_ipsec_t; - /** - * Private variables and functions of kernel_netlink class. + * Calculate the priority of a policy */ -struct private_kernel_netlink_ipsec_t { - /** - * Public part of the kernel_netlink_t object. - */ - kernel_netlink_ipsec_t public; - - /** - * mutex to lock access to various lists - */ - mutex_t *mutex; - - /** - * Hash table of installed policies (policy_entry_t) - */ - hashtable_t *policies; - - /** - * job receiving netlink events - */ - callback_job_t *job; - - /** - * Netlink xfrm socket (IPsec) - */ - netlink_socket_t *socket_xfrm; - - /** - * netlink xfrm socket to receive acquire and expire events - */ - int socket_xfrm_events; - - /** - * whether to install routes along policies - */ - bool install_routes; - - /** - * Size of the replay window, in packets - */ - u_int32_t replay_window; - - /** - * Size of the replay window bitmap, in bytes - */ - u_int32_t replay_bmp; -}; +static inline u_int32_t get_priority(policy_entry_t *policy, + policy_priority_t prio) +{ + u_int32_t priority = PRIO_BASE; + switch (prio) + { + case POLICY_PRIORITY_FALLBACK: + priority <<= 1; + /* fall-through */ + case POLICY_PRIORITY_ROUTED: + priority <<= 1; + /* fall-through */ + case POLICY_PRIORITY_DEFAULT: + break; + } + /* calculate priority based on selector size, small size = high prio */ + priority -= policy->sel.prefixlen_s; + priority -= policy->sel.prefixlen_d; + priority <<= 2; /* make some room for the two flags */ + priority += policy->sel.sport_mask || policy->sel.dport_mask ? 0 : 2; + priority += policy->sel.proto ? 0 : 1; + return priority; +} /** - * convert the general ipsec mode to the one defined in xfrm.h + * Convert the general ipsec mode to the one defined in xfrm.h */ static u_int8_t mode2kernel(ipsec_mode_t mode) { @@ -382,7 +623,7 @@ static u_int8_t mode2kernel(ipsec_mode_t mode) } /** - * convert a host_t to a struct xfrm_address + * Convert a host_t to a struct xfrm_address */ static void host2xfrm(host_t *host, xfrm_address_t *xfrm) { @@ -391,7 +632,7 @@ static void host2xfrm(host_t *host, xfrm_address_t *xfrm) } /** - * convert a struct xfrm_address to a host_t + * Convert a struct xfrm_address to a host_t */ static host_t* xfrm2host(int family, xfrm_address_t *xfrm, u_int16_t port) { @@ -412,7 +653,7 @@ static host_t* xfrm2host(int family, xfrm_address_t *xfrm, u_int16_t port) } /** - * convert a traffic selector address range to subnet and its mask. + * Convert a traffic selector address range to subnet and its mask. */ static void ts2subnet(traffic_selector_t* ts, xfrm_address_t *net, u_int8_t *mask) @@ -427,12 +668,12 @@ static void ts2subnet(traffic_selector_t* ts, } /** - * convert a traffic selector port range to port/portmask + * Convert a traffic selector port range to port/portmask */ static void ts2ports(traffic_selector_t* ts, u_int16_t *port, u_int16_t *mask) { - /* linux does not seem to accept complex portmasks. Only + /* Linux does not seem to accept complex portmasks. Only * any or a specific port is allowed. We set to any, if we have * a port range, or to a specific, if we have one port only. */ @@ -454,7 +695,7 @@ static void ts2ports(traffic_selector_t* ts, } /** - * convert a pair of traffic_selectors to a xfrm_selector + * Convert a pair of traffic_selectors to an xfrm_selector */ static struct xfrm_selector ts2selector(traffic_selector_t *src, traffic_selector_t *dst) @@ -476,7 +717,7 @@ static struct xfrm_selector ts2selector(traffic_selector_t *src, } /** - * convert a xfrm_selector to a src|dst traffic_selector + * Convert an xfrm_selector to a src|dst traffic_selector */ static traffic_selector_t* selector2ts(struct xfrm_selector *sel, bool src) { @@ -525,16 +766,17 @@ static traffic_selector_t* selector2ts(struct xfrm_selector *sel, bool src) } /** - * process a XFRM_MSG_ACQUIRE from kernel + * Process a XFRM_MSG_ACQUIRE from kernel */ -static void process_acquire(private_kernel_netlink_ipsec_t *this, struct nlmsghdr *hdr) +static void process_acquire(private_kernel_netlink_ipsec_t *this, + struct nlmsghdr *hdr) { - u_int32_t reqid = 0; - int proto = 0; - traffic_selector_t *src_ts, *dst_ts; struct xfrm_user_acquire *acquire; struct rtattr *rta; size_t rtasize; + traffic_selector_t *src_ts, *dst_ts; + u_int32_t reqid = 0; + int proto = 0; acquire = (struct xfrm_user_acquire*)NLMSG_DATA(hdr); rta = XFRM_RTA(hdr, struct xfrm_user_acquire); @@ -549,7 +791,6 @@ static void process_acquire(private_kernel_netlink_ipsec_t *this, struct nlmsghd if (rta->rta_type == XFRMA_TMPL) { struct xfrm_user_tmpl* tmpl; - tmpl = (struct xfrm_user_tmpl*)RTA_DATA(rta); reqid = tmpl->reqid; proto = tmpl->id.proto; @@ -574,13 +815,14 @@ static void process_acquire(private_kernel_netlink_ipsec_t *this, struct nlmsghd } /** - * process a XFRM_MSG_EXPIRE from kernel + * Process a XFRM_MSG_EXPIRE from kernel */ -static void process_expire(private_kernel_netlink_ipsec_t *this, struct nlmsghdr *hdr) +static void process_expire(private_kernel_netlink_ipsec_t *this, + struct nlmsghdr *hdr) { - u_int8_t protocol; - u_int32_t spi, reqid; struct xfrm_user_expire *expire; + u_int32_t spi, reqid; + u_int8_t protocol; expire = (struct xfrm_user_expire*)NLMSG_DATA(hdr); protocol = expire->state.id.proto; @@ -601,17 +843,18 @@ static void process_expire(private_kernel_netlink_ipsec_t *this, struct nlmsghdr } /** - * process a XFRM_MSG_MIGRATE from kernel + * Process a XFRM_MSG_MIGRATE from kernel */ -static void process_migrate(private_kernel_netlink_ipsec_t *this, struct nlmsghdr *hdr) +static void process_migrate(private_kernel_netlink_ipsec_t *this, + struct nlmsghdr *hdr) { + struct xfrm_userpolicy_id *policy_id; + struct rtattr *rta; + size_t rtasize; traffic_selector_t *src_ts, *dst_ts; host_t *local = NULL, *remote = NULL; host_t *old_src = NULL, *old_dst = NULL; host_t *new_src = NULL, *new_dst = NULL; - struct xfrm_userpolicy_id *policy_id; - struct rtattr *rta; - size_t rtasize; u_int32_t reqid = 0; policy_dir_t dir; @@ -650,7 +893,7 @@ static void process_migrate(private_kernel_netlink_ipsec_t *this, struct nlmsghd new_dst = xfrm2host(migrate->new_family, &migrate->new_daddr, 0); reqid = migrate->reqid; DBG2(DBG_KNL, " migrate %H...%H to %H...%H, reqid {%u}", - old_src, old_dst, new_src, new_dst, reqid); + old_src, old_dst, new_src, new_dst, reqid); DESTROY_IF(old_src); DESTROY_IF(old_dst); DESTROY_IF(new_src); @@ -674,14 +917,13 @@ static void process_migrate(private_kernel_netlink_ipsec_t *this, struct nlmsghd } /** - * process a XFRM_MSG_MAPPING from kernel + * Process a XFRM_MSG_MAPPING from kernel */ static void process_mapping(private_kernel_netlink_ipsec_t *this, struct nlmsghdr *hdr) { - u_int32_t spi, reqid; struct xfrm_user_mapping *mapping; - host_t *host; + u_int32_t spi, reqid; mapping = (struct xfrm_user_mapping*)NLMSG_DATA(hdr); spi = mapping->id.spi; @@ -691,6 +933,7 @@ static void process_mapping(private_kernel_netlink_ipsec_t *this, if (mapping->id.proto == IPPROTO_ESP) { + host_t *host; host = xfrm2host(mapping->id.family, &mapping->new_saddr, mapping->new_sport); if (host) @@ -757,7 +1000,8 @@ static job_requeue_t receive_events(private_kernel_netlink_ipsec_t *this) process_mapping(this, hdr); break; default: - DBG1(DBG_KNL, "received unknown event from xfrm event socket: %d", hdr->nlmsg_type); + DBG1(DBG_KNL, "received unknown event from xfrm event " + "socket: %d", hdr->nlmsg_type); break; } hdr = NLMSG_NEXT(hdr, len); @@ -769,8 +1013,8 @@ static job_requeue_t receive_events(private_kernel_netlink_ipsec_t *this) * Get an SPI for a specific protocol from the kernel. */ static status_t get_spi_internal(private_kernel_netlink_ipsec_t *this, - host_t *src, host_t *dst, u_int8_t proto, u_int32_t min, u_int32_t max, - u_int32_t reqid, u_int32_t *spi) + host_t *src, host_t *dst, u_int8_t proto, u_int32_t min, u_int32_t max, + u_int32_t reqid, u_int32_t *spi) { netlink_buf_t request; struct nlmsghdr *hdr, *out; @@ -811,7 +1055,6 @@ static status_t get_spi_internal(private_kernel_netlink_ipsec_t *this, case NLMSG_ERROR: { struct nlmsgerr *err = NLMSG_DATA(hdr); - DBG1(DBG_KNL, "allocating SPI failed: %s (%d)", strerror(-err->error), -err->error); break; @@ -843,14 +1086,13 @@ METHOD(kernel_ipsec_t, get_spi, status_t, DBG2(DBG_KNL, "getting SPI for reqid {%u}", reqid); if (get_spi_internal(this, src, dst, protocol, - 0xc0000000, 0xcFFFFFFF, reqid, spi) != SUCCESS) + 0xc0000000, 0xcFFFFFFF, reqid, spi) != SUCCESS) { DBG1(DBG_KNL, "unable to get SPI for reqid {%u}", reqid); return FAILED; } DBG2(DBG_KNL, "got SPI %.8x for reqid {%u}", ntohl(*spi), reqid); - return SUCCESS; } @@ -862,8 +1104,8 @@ METHOD(kernel_ipsec_t, get_cpi, status_t, DBG2(DBG_KNL, "getting CPI for reqid {%u}", reqid); - if (get_spi_internal(this, src, dst, - IPPROTO_COMP, 0x100, 0xEFFF, reqid, &received_spi) != SUCCESS) + if (get_spi_internal(this, src, dst, IPPROTO_COMP, + 0x100, 0xEFFF, reqid, &received_spi) != SUCCESS) { DBG1(DBG_KNL, "unable to get CPI for reqid {%u}", reqid); return FAILED; @@ -872,7 +1114,6 @@ METHOD(kernel_ipsec_t, get_cpi, status_t, *cpi = htons((u_int16_t)ntohl(received_spi)); DBG2(DBG_KNL, "got CPI %.4x for reqid {%u}", ntohs(*cpi), reqid); - return SUCCESS; } @@ -896,9 +1137,9 @@ METHOD(kernel_ipsec_t, add_sa, status_t, if (ipcomp != IPCOMP_NONE && cpi != 0) { 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); + 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); ipcomp = IPCOMP_NONE; /* use transport mode ESP SA, IPComp uses tunnel mode */ mode = MODE_TRANSPORT; @@ -908,8 +1149,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t, 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); + DBG2(DBG_KNL, "adding SAD entry with SPI %.8x and reqid {%u} (mark " + "%u/0x%8x)", ntohl(spi), reqid, mark.value, mark.mask); } else { @@ -990,7 +1231,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t, 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); + 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)) { @@ -1039,6 +1281,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t, if (int_alg != AUTH_UNDEFINED) { + u_int trunc_len = 0; + alg_name = lookup_algorithm(integrity_algs, int_alg); if (alg_name == NULL) { @@ -1049,14 +1293,29 @@ METHOD(kernel_ipsec_t, add_sa, status_t, DBG2(DBG_KNL, " using integrity algorithm %N with key size %d", integrity_algorithm_names, int_alg, int_key.len * 8); - if (int_alg == AUTH_HMAC_SHA2_256_128) + switch (int_alg) + { + case AUTH_HMAC_MD5_128: + case AUTH_HMAC_SHA2_256_128: + trunc_len = 128; + break; + case AUTH_HMAC_SHA1_160: + trunc_len = 160; + break; + default: + break; + } + + if (trunc_len) { struct xfrm_algo_auth* algo; /* the kernel uses SHA256 with 96 bit truncation by default, - * use specified truncation size supported by newer kernels */ + * 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); + 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)) @@ -1066,7 +1325,7 @@ METHOD(kernel_ipsec_t, add_sa, status_t, algo = (struct xfrm_algo_auth*)RTA_DATA(rthdr); algo->alg_key_len = int_key.len * 8; - algo->alg_trunc_len = 128; + algo->alg_trunc_len = trunc_len; strcpy(algo->alg_name, alg_name); memcpy(algo->alg_key, int_key.ptr, int_key.len); } @@ -1137,14 +1396,15 @@ METHOD(kernel_ipsec_t, add_sa, status_t, tmpl->encap_dport = htons(dst->get_port(dst)); memset(&tmpl->encap_oa, 0, sizeof (xfrm_address_t)); /* encap_oa could probably be derived from the - * traffic selectors [rfc4306, p39]. In the netlink kernel implementation - * pluto does the same as we do here but it uses encap_oa in the - * pfkey implementation. BUT as /usr/src/linux/net/key/af_key.c indicates - * the kernel ignores it anyway + * traffic selectors [rfc4306, p39]. In the netlink kernel + * implementation pluto does the same as we do here but it uses + * encap_oa in the pfkey implementation. + * BUT as /usr/src/linux/net/key/af_key.c indicates the kernel ignores + * it anyway * -> does that mean that NAT-T encap doesn't work in transport mode? * 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. */ + * (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); } @@ -1207,10 +1467,13 @@ METHOD(kernel_ipsec_t, add_sa, status_t, /* 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", + this->replay_window); rthdr = XFRM_RTA_NEXT(rthdr); if (esn) { + DBG2(DBG_KNL, " using extended sequence numbers (ESN)"); sa->flags |= XFRM_STATE_ESN; } } @@ -1261,7 +1524,7 @@ static void get_replay_state(private_kernel_netlink_ipsec_t *this, memset(&request, 0, sizeof(request)); DBG2(DBG_KNL, "querying replay state from SAD entry with SPI %.8x", - ntohl(spi)); + ntohl(spi)); hdr = (struct nlmsghdr*)request; hdr->nlmsg_flags = NLM_F_REQUEST; @@ -1291,8 +1554,9 @@ static void get_replay_state(private_kernel_netlink_ipsec_t *this, case NLMSG_ERROR: { struct nlmsgerr *err = NLMSG_DATA(hdr); - DBG1(DBG_KNL, "querying replay state from SAD entry failed: %s (%d)", - strerror(-err->error), -err->error); + DBG1(DBG_KNL, "querying replay state from SAD entry " + "failed: %s (%d)", strerror(-err->error), + -err->error); break; } default: @@ -1500,7 +1764,8 @@ METHOD(kernel_ipsec_t, del_sa, status_t, } else { - DBG1(DBG_KNL, "unable to delete SAD entry with SPI %.8x", ntohl(spi)); + DBG1(DBG_KNL, "unable to delete SAD entry with SPI %.8x", + ntohl(spi)); } return FAILED; } @@ -1596,12 +1861,13 @@ METHOD(kernel_ipsec_t, update_sa, status_t, /* delete the old SA (without affecting the IPComp SA) */ if (del_sa(this, src, dst, spi, protocol, 0, mark) != SUCCESS) { - DBG1(DBG_KNL, "unable to delete old SAD entry with SPI %.8x", ntohl(spi)); + DBG1(DBG_KNL, "unable to delete old SAD entry with SPI %.8x", + ntohl(spi)); goto failed; } DBG2(DBG_KNL, "updating SAD entry with SPI %.8x from %#H..%#H to %#H..%#H", - ntohl(spi), src, dst, new_src, new_dst); + 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))); @@ -1695,7 +1961,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t, else { DBG1(DBG_KNL, "unable to copy replay state from old SAD entry " - "with SPI %.8x", ntohl(spi)); + "with SPI %.8x", ntohl(spi)); } if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS) @@ -1709,77 +1975,62 @@ failed: free(replay); free(replay_esn); memwipe(out, len); + memwipe(request, sizeof(request)); free(out); return status; } -METHOD(kernel_ipsec_t, add_policy, status_t, - private_kernel_netlink_ipsec_t *this, host_t *src, host_t *dst, - traffic_selector_t *src_ts, traffic_selector_t *dst_ts, - policy_dir_t direction, policy_type_t type, ipsec_sa_cfg_t *sa, - mark_t mark, bool routed) +METHOD(kernel_ipsec_t, flush_sas, status_t, + private_kernel_netlink_ipsec_t *this) { - policy_entry_t *current, *policy; - bool found = FALSE; netlink_buf_t request; - struct xfrm_userpolicy_info *policy_info; struct nlmsghdr *hdr; - int i; + struct xfrm_usersa_flush *flush; - /* create a policy */ - policy = malloc_thing(policy_entry_t); - memset(policy, 0, sizeof(policy_entry_t)); - policy->sel = ts2selector(src_ts, dst_ts); - policy->mark = mark.value & mark.mask; - policy->direction = direction; + memset(&request, 0, sizeof(request)); - /* find the policy, which matches EXACTLY */ - this->mutex->lock(this->mutex); - current = this->policies->get(this->policies, policy); - if (current) - { - /* use existing policy */ - current->refcount++; - 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 - { - DBG2(DBG_KNL, "policy %R === %R %N " - "already exists, increasing refcount", - src_ts, dst_ts, policy_dir_names, direction); - } - free(policy); - policy = current; - found = TRUE; - } - else - { /* apply the new one, if we have no such policy */ - this->policies->put(this->policies, policy, policy); - policy->refcount = 1; - } + DBG2(DBG_KNL, "flushing all SAD entries"); - if (mark.value) - { - DBG2(DBG_KNL, "adding policy %R === %R %N (mark %u/0x%8x)", - src_ts, dst_ts, policy_dir_names, direction, - mark.value, mark.mask); - } - else + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = XFRM_MSG_FLUSHSA; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_flush)); + + flush = (struct xfrm_usersa_flush*)NLMSG_DATA(hdr); + flush->proto = IPSEC_PROTO_ANY; + + if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS) { - DBG2(DBG_KNL, "adding policy %R === %R %N", - src_ts, dst_ts, policy_dir_names, direction); + DBG1(DBG_KNL, "unable to flush SAD entries"); + return FAILED; } + return SUCCESS; +} + +/** + * Add or update a policy in the kernel. + * + * Note: The mutex has to be locked when entering this function + * and is unlocked here in any case. + */ +static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, + policy_entry_t *policy, policy_sa_t *mapping, bool update) +{ + netlink_buf_t request; + policy_entry_t clone; + ipsec_sa_t *ipsec = mapping->sa; + struct xfrm_userpolicy_info *policy_info; + struct nlmsghdr *hdr; + int i; + + /* clone the policy so we are able to check it out again later */ + memcpy(&clone, policy, sizeof(policy_entry_t)); memset(&request, 0, sizeof(request)); hdr = (struct nlmsghdr*)request; hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; - hdr->nlmsg_type = found ? XFRM_MSG_UPDPOLICY : XFRM_MSG_NEWPOLICY; + hdr->nlmsg_type = update ? XFRM_MSG_UPDPOLICY : XFRM_MSG_NEWPOLICY; hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_info)); policy_info = (struct xfrm_userpolicy_info*)NLMSG_DATA(hdr); @@ -1787,18 +2038,10 @@ METHOD(kernel_ipsec_t, add_policy, status_t, policy_info->dir = policy->direction; /* calculate priority based on selector size, small size = high prio */ - policy_info->priority = routed ? PRIO_LOW : PRIO_HIGH; - policy_info->priority -= policy->sel.prefixlen_s; - policy_info->priority -= policy->sel.prefixlen_d; - policy_info->priority <<= 2; /* make some room for the two flags */ - policy_info->priority += policy->sel.sport_mask || - policy->sel.dport_mask ? 0 : 2; - policy_info->priority += policy->sel.proto ? 0 : 1; - - policy_info->action = type != POLICY_DROP ? XFRM_POLICY_ALLOW - : XFRM_POLICY_BLOCK; + policy_info->priority = mapping->priority; + policy_info->action = mapping->type != POLICY_DROP ? XFRM_POLICY_ALLOW + : XFRM_POLICY_BLOCK; policy_info->share = XFRM_SHARE_ANY; - this->mutex->unlock(this->mutex); /* policies don't expire */ policy_info->lft.soft_byte_limit = XFRM_INF; @@ -1812,18 +2055,18 @@ METHOD(kernel_ipsec_t, add_policy, status_t, struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_userpolicy_info); - if (type == POLICY_IPSEC) + if (mapping->type == POLICY_IPSEC) { struct xfrm_user_tmpl *tmpl = (struct xfrm_user_tmpl*)RTA_DATA(rthdr); struct { u_int8_t proto; bool use; } protos[] = { - { IPPROTO_COMP, sa->ipcomp.transform != IPCOMP_NONE }, - { IPPROTO_ESP, sa->esp.use }, - { IPPROTO_AH, sa->ah.use }, + { IPPROTO_COMP, ipsec->cfg.ipcomp.transform != IPCOMP_NONE }, + { IPPROTO_ESP, ipsec->cfg.esp.use }, + { IPPROTO_AH, ipsec->cfg.ah.use }, }; - ipsec_mode_t proto_mode = sa->mode; + ipsec_mode_t proto_mode = ipsec->cfg.mode; rthdr->rta_type = XFRMA_TMPL; rthdr->rta_len = 0; /* actual length is set below */ @@ -1839,21 +2082,22 @@ METHOD(kernel_ipsec_t, add_policy, status_t, hdr->nlmsg_len += RTA_ALIGN(RTA_LENGTH(sizeof(struct xfrm_user_tmpl))); if (hdr->nlmsg_len > sizeof(request)) { + this->mutex->unlock(this->mutex); return FAILED; } - tmpl->reqid = sa->reqid; + tmpl->reqid = ipsec->cfg.reqid; tmpl->id.proto = protos[i].proto; tmpl->aalgos = tmpl->ealgos = tmpl->calgos = ~0; tmpl->mode = mode2kernel(proto_mode); tmpl->optional = protos[i].proto == IPPROTO_COMP && - direction != POLICY_OUT; - tmpl->family = src->get_family(src); + policy->direction != POLICY_OUT; + tmpl->family = ipsec->src->get_family(ipsec->src); if (proto_mode == MODE_TUNNEL) { /* only for tunnel mode */ - host2xfrm(src, &tmpl->saddr); - host2xfrm(dst, &tmpl->id.daddr); + host2xfrm(ipsec->src, &tmpl->saddr); + host2xfrm(ipsec->dst, &tmpl->id.daddr); } tmpl++; @@ -1865,7 +2109,7 @@ METHOD(kernel_ipsec_t, add_policy, status_t, rthdr = XFRM_RTA_NEXT(rthdr); } - if (mark.value) + if (ipsec->mark.value) { struct xfrm_mark *mrk; @@ -1875,71 +2119,110 @@ METHOD(kernel_ipsec_t, add_policy, status_t, 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; + mrk->v = ipsec->mark.value; + mrk->m = ipsec->mark.mask; } + this->mutex->unlock(this->mutex); if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS) { - DBG1(DBG_KNL, "unable to add policy %R === %R %N", src_ts, dst_ts, - policy_dir_names, direction); return FAILED; } + /* find the policy again */ + this->mutex->lock(this->mutex); + policy = this->policies->get(this->policies, &clone); + if (!policy || + policy->used_by->find_first(policy->used_by, + NULL, (void**)&mapping) != SUCCESS) + { /* policy or mapping is already gone, ignore */ + this->mutex->unlock(this->mutex); + return SUCCESS; + } + /* install a route, if: - * - we are NOT updating a policy * - this is a forward policy (to just get one for each child) * - we are in tunnel/BEET mode * - routing is not disabled via strongswan.conf */ - if (policy->route == NULL && direction == POLICY_FWD && - sa->mode != MODE_TRANSPORT && this->install_routes) + if (policy->direction == POLICY_FWD && + ipsec->cfg.mode != MODE_TRANSPORT && this->install_routes) { route_entry_t *route = malloc_thing(route_entry_t); + policy_sa_fwd_t *fwd = (policy_sa_fwd_t*)mapping; if (hydra->kernel_interface->get_address_by_ts(hydra->kernel_interface, - dst_ts, &route->src_ip) == SUCCESS) + fwd->dst_ts, &route->src_ip) == SUCCESS) { - /* get the nexthop to src (src as we are in POLICY_FWD).*/ + /* get the nexthop to src (src as we are in POLICY_FWD) */ route->gateway = hydra->kernel_interface->get_nexthop( - hydra->kernel_interface, src); + hydra->kernel_interface, ipsec->src); /* install route via outgoing interface */ route->if_name = hydra->kernel_interface->get_interface( - hydra->kernel_interface, dst); + hydra->kernel_interface, 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) + if (!route->if_name) + { + this->mutex->unlock(this->mutex); + route_entry_destroy(route); + return SUCCESS; + } + + if (policy->route) { - DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s", - src_ts, route->gateway, route->src_ip, route->if_name); - switch (hydra->kernel_interface->add_route( - hydra->kernel_interface, route->dst_net, - route->prefixlen, route->gateway, - route->src_ip, route->if_name)) + 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; + } + /* uninstall previously installed route */ + if (hydra->kernel_interface->del_route(hydra->kernel_interface, + old->dst_net, old->prefixlen, old->gateway, + old->src_ip, old->if_name) != SUCCESS) { - default: - DBG1(DBG_KNL, "unable to install source route for %H", - route->src_ip); - /* FALL */ - case ALREADY_DONE: - /* route exists, do not uninstall */ - route_entry_destroy(route); - break; - case SUCCESS: - /* cache the installed route */ - policy->route = route; - break; + DBG1(DBG_KNL, "error uninstalling route installed with " + "policy %R === %R %N", fwd->src_ts, + fwd->dst_ts, policy_dir_names, + policy->direction); } + route_entry_destroy(old); + policy->route = NULL; } - else + + DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s", + fwd->src_ts, route->gateway, route->src_ip, route->if_name); + switch (hydra->kernel_interface->add_route( + hydra->kernel_interface, route->dst_net, + route->prefixlen, route->gateway, + route->src_ip, route->if_name)) { - route_entry_destroy(route); + default: + DBG1(DBG_KNL, "unable to install source route for %H", + route->src_ip); + /* FALL */ + case ALREADY_DONE: + /* route exists, do not uninstall */ + route_entry_destroy(route); + break; + case SUCCESS: + /* cache the installed route */ + policy->route = route; + break; } } else @@ -1947,6 +2230,110 @@ METHOD(kernel_ipsec_t, add_policy, status_t, free(route); } } + this->mutex->unlock(this->mutex); + return SUCCESS; +} + +METHOD(kernel_ipsec_t, add_policy, status_t, + private_kernel_netlink_ipsec_t *this, host_t *src, host_t *dst, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts, + policy_dir_t direction, policy_type_t type, ipsec_sa_cfg_t *sa, + mark_t mark, policy_priority_t priority) +{ + policy_entry_t *policy, *current; + policy_sa_t *assigned_sa, *current_sa; + enumerator_t *enumerator; + bool found = FALSE, update = TRUE; + + /* create a policy */ + INIT(policy, + .sel = ts2selector(src_ts, dst_ts), + .mark = mark.value & mark.mask, + .direction = direction, + ); + + /* find the policy, which matches EXACTLY */ + this->mutex->lock(this->mutex); + 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 + { + DBG2(DBG_KNL, "policy %R === %R %N " + "already exists, increasing refcount", + src_ts, dst_ts, policy_dir_names, direction); + } + policy_entry_destroy(this, policy); + policy = current; + found = TRUE; + } + else + { /* use the new one, if we have no such policy */ + policy->used_by = linked_list_create(); + this->policies->put(this->policies, policy, policy); + } + + /* cache the assigned IPsec SA */ + assigned_sa = policy_sa_create(this, direction, type, src, dst, src_ts, + dst_ts, mark, sa); + assigned_sa->priority = get_priority(policy, priority); + + if (this->policy_history) + { /* insert the SA according to its priority */ + enumerator = policy->used_by->create_enumerator(policy->used_by); + while (enumerator->enumerate(enumerator, (void**)¤t_sa)) + { + if (current_sa->priority >= assigned_sa->priority) + { + break; + } + update = FALSE; + } + policy->used_by->insert_before(policy->used_by, enumerator, + assigned_sa); + enumerator->destroy(enumerator); + } + else + { /* simply insert it last and only update if it is not installed yet */ + policy->used_by->insert_last(policy->used_by, assigned_sa); + update = !found; + } + + if (!update) + { /* we don't update the policy if the priority is lower than that of + * the currently installed one */ + this->mutex->unlock(this->mutex); + 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); + } + + if (add_policy_internal(this, policy, assigned_sa, found) != SUCCESS) + { + DBG1(DBG_KNL, "unable to %s policy %R === %R %N", + found ? "update" : "add", src_ts, dst_ts, + policy_dir_names, direction); + return FAILED; + } return SUCCESS; } @@ -2018,7 +2405,7 @@ METHOD(kernel_ipsec_t, query_policy, status_t, { struct nlmsgerr *err = NLMSG_DATA(hdr); DBG1(DBG_KNL, "querying policy failed: %s (%d)", - strerror(-err->error), -err->error); + strerror(-err->error), -err->error); break; } default: @@ -2055,14 +2442,17 @@ METHOD(kernel_ipsec_t, query_policy, status_t, METHOD(kernel_ipsec_t, del_policy, status_t, private_kernel_netlink_ipsec_t *this, traffic_selector_t *src_ts, - traffic_selector_t *dst_ts, policy_dir_t direction, mark_t mark, - bool unrouted) + traffic_selector_t *dst_ts, policy_dir_t direction, u_int32_t reqid, + mark_t mark, policy_priority_t prio) { - policy_entry_t *current, policy, *to_delete = NULL; - route_entry_t *route; + policy_entry_t *current, policy; + enumerator_t *enumerator; + policy_sa_t *mapping; netlink_buf_t request; struct nlmsghdr *hdr; struct xfrm_userpolicy_id *policy_id; + bool is_installed = TRUE; + u_int32_t priority; if (mark.value) { @@ -2085,21 +2475,7 @@ 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) - { - to_delete = current; - if (--to_delete->refcount > 0) - { - /* is used by more SAs, keep in kernel */ - DBG2(DBG_KNL, "policy still used by another CHILD_SA, not removed"); - this->mutex->unlock(this->mutex); - return SUCCESS; - } - /* remove if last reference */ - this->policies->remove(this->policies, to_delete); - } - this->mutex->unlock(this->mutex); - if (!to_delete) + if (!current) { if (mark.value) { @@ -2112,9 +2488,65 @@ METHOD(kernel_ipsec_t, del_policy, status_t, DBG1(DBG_KNL, "deleting policy %R === %R %N failed, not found", src_ts, dst_ts, policy_dir_names, direction); } + this->mutex->unlock(this->mutex); return NOT_FOUND; } + if (this->policy_history) + { /* remove mapping to SA by reqid and priority */ + priority = get_priority(current, prio); + enumerator = current->used_by->create_enumerator(current->used_by); + while (enumerator->enumerate(enumerator, (void**)&mapping)) + { + if (reqid == mapping->sa->cfg.reqid && + priority == mapping->priority) + { + current->used_by->remove_at(current->used_by, enumerator); + policy_sa_destroy(mapping, &direction, this); + break; + } + is_installed = FALSE; + } + enumerator->destroy(enumerator); + } + else + { /* remove one of the SAs but don't update the policy */ + current->used_by->remove_last(current->used_by, (void**)&mapping); + policy_sa_destroy(mapping, &direction, this); + is_installed = FALSE; + } + + if (current->used_by->get_count(current->used_by) > 0) + { /* policy is used by more SAs, keep in kernel */ + DBG2(DBG_KNL, "policy still used by another CHILD_SA, not removed"); + if (!is_installed) + { /* no need to update as the policy was not installed for this SA */ + this->mutex->unlock(this->mutex); + 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); + } + + current->used_by->get_first(current->used_by, (void**)&mapping); + if (add_policy_internal(this, current, mapping, TRUE) != SUCCESS) + { + DBG1(DBG_KNL, "unable to update policy %R === %R %N", + src_ts, dst_ts, policy_dir_names, direction); + return FAILED; + } + return SUCCESS; + } + memset(&request, 0, sizeof(request)); hdr = (struct nlmsghdr*)request; @@ -2123,7 +2555,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t, hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_id)); policy_id = (struct xfrm_userpolicy_id*)NLMSG_DATA(hdr); - policy_id->sel = to_delete->sel; + policy_id->sel = current->sel; policy_id->dir = direction; if (mark.value) @@ -2136,6 +2568,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t, hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); if (hdr->nlmsg_len > sizeof(request)) { + this->mutex->unlock(this->mutex); return FAILED; } @@ -2144,15 +2577,29 @@ METHOD(kernel_ipsec_t, del_policy, status_t, mrk->m = mark.mask; } - route = to_delete->route; - free(to_delete); + if (current->route) + { + route_entry_t *route = current->route; + if (hydra->kernel_interface->del_route(hydra->kernel_interface, + route->dst_net, route->prefixlen, route->gateway, + route->src_ip, route->if_name) != SUCCESS) + { + DBG1(DBG_KNL, "error uninstalling route installed with " + "policy %R === %R %N", src_ts, dst_ts, + policy_dir_names, direction); + } + } + + this->policies->remove(this->policies, current); + policy_entry_destroy(this, current); + this->mutex->unlock(this->mutex); if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS) { 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%8x)", src_ts, dst_ts, policy_dir_names, direction, mark.value, mark.mask); } else @@ -2162,22 +2609,36 @@ METHOD(kernel_ipsec_t, del_policy, status_t, } return FAILED; } + return SUCCESS; +} + +METHOD(kernel_ipsec_t, flush_policies, status_t, + private_kernel_netlink_ipsec_t *this) +{ + netlink_buf_t request; + struct nlmsghdr *hdr; + + memset(&request, 0, sizeof(request)); - if (route) + DBG2(DBG_KNL, "flushing all policies from SPD"); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = XFRM_MSG_FLUSHPOLICY; + hdr->nlmsg_len = NLMSG_LENGTH(0); /* no data associated */ + + /* by adding an rtattr of type XFRMA_POLICY_TYPE we could restrict this + * to main or sub policies (default is main) */ + + if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS) { - if (hydra->kernel_interface->del_route(hydra->kernel_interface, - route->dst_net, route->prefixlen, route->gateway, - route->src_ip, route->if_name) != SUCCESS) - { - DBG1(DBG_KNL, "error uninstalling route installed with " - "policy %R === %R %N", src_ts, dst_ts, - policy_dir_names, direction); - } - route_entry_destroy(route); + DBG1(DBG_KNL, "unable to flush SPD entries"); + return FAILED; } return SUCCESS; } + METHOD(kernel_ipsec_t, bypass_socket, bool, private_kernel_netlink_ipsec_t *this, int fd, int family) { @@ -2206,14 +2667,14 @@ METHOD(kernel_ipsec_t, bypass_socket, bool, if (setsockopt(fd, sol, ipsec_policy, &policy, sizeof(policy)) < 0) { DBG1(DBG_KNL, "unable to set IPSEC_POLICY on socket: %s", - strerror(errno)); + strerror(errno)); return FALSE; } policy.dir = XFRM_POLICY_IN; if (setsockopt(fd, sol, ipsec_policy, &policy, sizeof(policy)) < 0) { DBG1(DBG_KNL, "unable to set IPSEC_POLICY on socket: %s", - strerror(errno)); + strerror(errno)); return FALSE; } return TRUE; @@ -2237,10 +2698,11 @@ METHOD(kernel_ipsec_t, destroy, void, enumerator = this->policies->create_enumerator(this->policies); while (enumerator->enumerate(enumerator, &policy, &policy)) { - free(policy); + policy_entry_destroy(this, policy); } enumerator->destroy(enumerator); this->policies->destroy(this->policies); + this->sas->destroy(this->sas); this->mutex->destroy(this->mutex); free(this); } @@ -2263,16 +2725,21 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() .update_sa = _update_sa, .query_sa = _query_sa, .del_sa = _del_sa, + .flush_sas = _flush_sas, .add_policy = _add_policy, .query_policy = _query_policy, .del_policy = _del_policy, + .flush_policies = _flush_policies, .bypass_socket = _bypass_socket, .destroy = _destroy, }, }, .policies = hashtable_create((hashtable_hash_t)policy_hash, (hashtable_equals_t)policy_equals, 32), + .sas = hashtable_create((hashtable_hash_t)ipsec_sa_hash, + (hashtable_equals_t)ipsec_sa_equals, 32), .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .policy_history = TRUE, .install_routes = lib->settings->get_bool(lib->settings, "%s.install_routes", TRUE, hydra->daemon), .replay_window = lib->settings->get_int(lib->settings, @@ -2285,6 +2752,8 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() 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; } /* disable lifetimes for allocated SPIs in kernel */ @@ -2321,8 +2790,8 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() destroy(this); return NULL; } - this->job = callback_job_create((callback_job_cb_t)receive_events, - this, NULL, NULL); + 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 8315ed310..cce0ff402 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c @@ -193,16 +193,16 @@ struct private_kernel_netlink_net_t { */ static int get_vip_refcount(private_kernel_netlink_net_t *this, host_t* ip) { - iterator_t *ifaces, *addrs; + enumerator_t *ifaces, *addrs; iface_entry_t *iface; addr_entry_t *addr; int refcount = 0; - ifaces = this->ifaces->create_iterator(this->ifaces, TRUE); - while (ifaces->iterate(ifaces, (void**)&iface)) + ifaces = this->ifaces->create_enumerator(this->ifaces); + while (ifaces->enumerate(ifaces, (void**)&iface)) { - addrs = iface->addrs->create_iterator(iface->addrs, TRUE); - while (addrs->iterate(addrs, (void**)&addr)) + 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)) @@ -375,9 +375,13 @@ static void process_link(private_kernel_netlink_net_t *this, { if (current->ifindex == msg->ifi_index) { - /* we do not remove it, as an address may be added to a - * "down" interface and we wan't to know that. */ - current->flags = msg->ifi_flags; + if (event) + { + update = TRUE; + DBG1(DBG_KNL, "interface %s deleted", current->ifname); + } + this->ifaces->remove_at(this->ifaces, enumerator); + iface_entry_destroy(current); break; } } @@ -904,7 +908,7 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest, struct rtattr *rta; size_t rtasize; chunk_t rta_gtw, rta_src, rta_dst; - u_int32_t rta_oif = 0; + u_int32_t rta_oif = 0, rta_table; host_t *new_src, *new_gtw; bool cont = FALSE; uintptr_t table; @@ -913,6 +917,7 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest, 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) @@ -932,6 +937,14 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest, 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); } @@ -942,7 +955,7 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest, enumerator = this->rt_exclude->create_enumerator(this->rt_exclude); while (enumerator->enumerate(enumerator, &table)) { - if (table == msg->rtm_table) + if (table == rta_table) { cont = TRUE; break; @@ -954,7 +967,7 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest, continue; } if (this->routing_table != 0 && - msg->rtm_table == this->routing_table) + rta_table == this->routing_table) { /* route is from our own ipsec routing table */ continue; } @@ -1529,7 +1542,7 @@ kernel_netlink_net_t *kernel_netlink_net_create() return NULL; } addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | - RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_ROUTE | RTMGRP_LINK; + 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"); @@ -1537,8 +1550,8 @@ kernel_netlink_net_t *kernel_netlink_net_create() return NULL; } - this->job = callback_job_create((callback_job_cb_t)receive_events, - this, NULL, NULL); + 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); 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 779466472..0eb00dadf 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_plugin.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_plugin.c @@ -39,13 +39,22 @@ METHOD(plugin_t, get_name, char*, return "kernel-netlink"; } +METHOD(plugin_t, get_features, int, + private_kernel_netlink_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_CALLBACK(kernel_ipsec_register, kernel_netlink_ipsec_create), + PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"), + PLUGIN_CALLBACK(kernel_net_register, kernel_netlink_net_create), + PLUGIN_PROVIDE(CUSTOM, "kernel-net"), + }; + *features = f; + return countof(f); +} + METHOD(plugin_t, destroy, void, private_kernel_netlink_plugin_t *this) { - hydra->kernel_interface->remove_ipsec_interface(hydra->kernel_interface, - (kernel_ipsec_constructor_t)kernel_netlink_ipsec_create); - hydra->kernel_interface->remove_net_interface(hydra->kernel_interface, - (kernel_net_constructor_t)kernel_netlink_net_create); free(this); } @@ -60,15 +69,11 @@ plugin_t *kernel_netlink_plugin_create() .public = { .plugin = { .get_name = _get_name, - .reload = (void*)return_false, + .get_features = _get_features, .destroy = _destroy, }, }, ); - hydra->kernel_interface->add_ipsec_interface(hydra->kernel_interface, - (kernel_ipsec_constructor_t)kernel_netlink_ipsec_create); - hydra->kernel_interface->add_net_interface(hydra->kernel_interface, - (kernel_net_constructor_t)kernel_netlink_net_create); return &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 c26fd2e51..dad3fb68e 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c @@ -61,11 +61,9 @@ struct private_netlink_socket_t { */ extern enum_name_t *xfrm_msg_names; -/** - * Implementation of netlink_socket_t.send - */ -static status_t netlink_send(private_netlink_socket_t *this, struct nlmsghdr *in, - struct nlmsghdr **out, size_t *out_len) +METHOD(netlink_socket_t, netlink_send, status_t, + private_netlink_socket_t *this, struct nlmsghdr *in, struct nlmsghdr **out, + size_t *out_len) { int len, addr_len; struct sockaddr_nl addr; @@ -182,10 +180,8 @@ static status_t netlink_send(private_netlink_socket_t *this, struct nlmsghdr *in return SUCCESS; } -/** - * Implementation of netlink_socket_t.send_ack. - */ -static status_t netlink_send_ack(private_netlink_socket_t *this, struct nlmsghdr *in) +METHOD(netlink_socket_t, netlink_send_ack, status_t, + private_netlink_socket_t *this, struct nlmsghdr *in) { struct nlmsghdr *out, *hdr; size_t len; @@ -231,10 +227,8 @@ static status_t netlink_send_ack(private_netlink_socket_t *this, struct nlmsghdr return FAILED; } -/** - * Implementation of netlink_socket_t.destroy. - */ -static void destroy(private_netlink_socket_t *this) +METHOD(netlink_socket_t, destroy, void, + private_netlink_socket_t *this) { if (this->socket > 0) { @@ -249,22 +243,23 @@ static void destroy(private_netlink_socket_t *this) */ netlink_socket_t *netlink_socket_create(int protocol) { - private_netlink_socket_t *this = malloc_thing(private_netlink_socket_t); + private_netlink_socket_t *this; struct sockaddr_nl addr; - /* public functions */ - this->public.send = (status_t(*)(netlink_socket_t*,struct nlmsghdr*, struct nlmsghdr**, size_t*))netlink_send; - this->public.send_ack = (status_t(*)(netlink_socket_t*,struct nlmsghdr*))netlink_send_ack; - this->public.destroy = (void(*)(netlink_socket_t*))destroy; - - /* private members */ - this->seq = 200; - this->mutex = mutex_create(MUTEX_TYPE_DEFAULT); + INIT(this, + .public = { + .send = _netlink_send, + .send_ack = _netlink_send_ack, + .destroy = _destroy, + }, + .seq = 200, + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .protocol = protocol, + ); memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; - this->protocol = protocol; this->socket = socket(AF_NETLINK, SOCK_RAW, protocol); if (this->socket < 0) { diff --git a/src/libhydra/plugins/kernel_pfkey/Makefile.in b/src/libhydra/plugins/kernel_pfkey/Makefile.in index 251483017..14c924b6f 100644 --- a/src/libhydra/plugins/kernel_pfkey/Makefile.in +++ b/src/libhydra/plugins/kernel_pfkey/Makefile.in @@ -195,6 +195,9 @@ am__leading_dot = @am__leading_dot@ 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@ @@ -203,6 +206,7 @@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ c_plugins = @c_plugins@ +clearsilver_LIBS = @clearsilver_LIBS@ datadir = @datadir@ datarootdir = @datarootdir@ dbusservicedir = @dbusservicedir@ @@ -219,11 +223,13 @@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ +imcvdir = @imcvdir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ ipsecdir = @ipsecdir@ ipsecgroup = @ipsecgroup@ +ipseclibdir = @ipseclibdir@ ipsecuser = @ipsecuser@ libcharon_plugins = @libcharon_plugins@ libdir = @libdir@ @@ -267,6 +273,7 @@ sharedstatedir = @sharedstatedir@ soup_CFLAGS = @soup_CFLAGS@ soup_LIBS = @soup_LIBS@ srcdir = @srcdir@ +starter_plugins = @starter_plugins@ strongswan_conf = @strongswan_conf@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index b252b7092..da10edffe 100644 --- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2010 Tobias Brunner + * Copyright (C) 2008-2011 Tobias Brunner * Copyright (C) 2008 Andreas Steffen * Hochschule fuer Technik Rapperswil * @@ -58,6 +58,7 @@ #include <debug.h> #include <utils/host.h> #include <utils/linked_list.h> +#include <utils/hashtable.h> #include <threading/thread.h> #include <threading/mutex.h> #include <processing/jobs/callback_job.h> @@ -99,8 +100,7 @@ #endif /** default priority of installed policies */ -#define PRIO_LOW 1024 -#define PRIO_HIGH 512 +#define PRIO_BASE 512 #ifdef __APPLE__ /** from xnu/bsd/net/pfkeyv2.h */ @@ -163,6 +163,11 @@ struct private_kernel_pfkey_ipsec_t linked_list_t *policies; /** + * Hash table of IPsec SAs using policies (ipsec_sa_t) + */ + hashtable_t *sas; + + /** * whether to install routes along policies */ bool install_routes; @@ -199,19 +204,19 @@ typedef struct route_entry_t route_entry_t; * installed routing entry */ struct route_entry_t { - /** Name of the interface the route is bound to */ + /** name of the interface the route is bound to */ char *if_name; - /** Source ip of the route */ + /** source ip of the route */ host_t *src_ip; /** gateway for this route */ host_t *gateway; - /** Destination net */ + /** destination net */ chunk_t dst_net; - /** Destination net prefixlen */ + /** destination net prefixlen */ u_int8_t prefixlen; }; @@ -227,57 +232,222 @@ static void route_entry_destroy(route_entry_t *this) free(this); } +/** + * compare two route_entry_t objects + */ +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) && + chunk_equals(a->dst_net, b->dst_net) && a->prefixlen == b->prefixlen; +} + +typedef struct ipsec_sa_t ipsec_sa_t; + +/** + * IPsec SA assigned to a policy. + */ +struct ipsec_sa_t { + /** Source address of this SA */ + host_t *src; + + /** Destination address of this SA */ + host_t *dst; + + /** Description of this SA */ + ipsec_sa_cfg_t cfg; + + /** Reference count for this SA */ + refcount_t refcount; +}; + +/** + * Hash function for ipsec_sa_t objects + */ +static u_int ipsec_sa_hash(ipsec_sa_t *sa) +{ + return chunk_hash_inc(sa->src->get_address(sa->src), + chunk_hash_inc(sa->dst->get_address(sa->dst), + chunk_hash(chunk_from_thing(sa->cfg)))); +} + +/** + * Equality function for ipsec_sa_t objects + */ +static bool ipsec_sa_equals(ipsec_sa_t *sa, ipsec_sa_t *other_sa) +{ + return sa->src->ip_equals(sa->src, other_sa->src) && + sa->dst->ip_equals(sa->dst, other_sa->dst) && + memeq(&sa->cfg, &other_sa->cfg, sizeof(ipsec_sa_cfg_t)); +} + +/** + * Allocate or reference an IPsec SA object + */ +static ipsec_sa_t *ipsec_sa_create(private_kernel_pfkey_ipsec_t *this, + host_t *src, host_t *dst, + ipsec_sa_cfg_t *cfg) +{ + ipsec_sa_t *sa, *found; + INIT(sa, + .src = src, + .dst = dst, + .cfg = *cfg, + ); + found = this->sas->get(this->sas, sa); + if (!found) + { + sa->src = src->clone(src); + sa->dst = dst->clone(dst); + this->sas->put(this->sas, sa, sa); + } + else + { + free(sa); + sa = found; + } + ref_get(&sa->refcount); + return sa; +} + +/** + * Release and destroy an IPsec SA object + */ +static void ipsec_sa_destroy(private_kernel_pfkey_ipsec_t *this, + ipsec_sa_t *sa) +{ + if (ref_put(&sa->refcount)) + { + this->sas->remove(this->sas, sa); + DESTROY_IF(sa->src); + DESTROY_IF(sa->dst); + free(sa); + } +} + +typedef struct policy_sa_t policy_sa_t; +typedef struct policy_sa_fwd_t policy_sa_fwd_t; + +/** + * Mapping between a policy and an IPsec SA. + */ +struct policy_sa_t { + /** Priority assigned to the policy when installed with this SA */ + u_int32_t priority; + + /** Type of the policy */ + policy_type_t type; + + /** Assigned SA */ + ipsec_sa_t *sa; +}; + +/** + * For forward policies we also cache the traffic selectors in order to install + * the route. + */ +struct policy_sa_fwd_t { + /** Generic interface */ + policy_sa_t generic; + + /** Source traffic selector of this policy */ + traffic_selector_t *src_ts; + + /** Destination traffic selector of this policy */ + traffic_selector_t *dst_ts; +}; + +/** + * Create a policy_sa(_fwd)_t object + */ +static policy_sa_t *policy_sa_create(private_kernel_pfkey_ipsec_t *this, + policy_dir_t dir, policy_type_t type, host_t *src, host_t *dst, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts, ipsec_sa_cfg_t *cfg) +{ + policy_sa_t *policy; + + if (dir == POLICY_FWD) + { + policy_sa_fwd_t *fwd; + INIT(fwd, + .src_ts = src_ts->clone(src_ts), + .dst_ts = dst_ts->clone(dst_ts), + ); + policy = &fwd->generic; + } + else + { + INIT(policy, .priority = 0); + } + policy->type = type; + policy->sa = ipsec_sa_create(this, src, dst, cfg); + return policy; +} + +/** + * Destroy a policy_sa(_fwd)_t object + */ +static void policy_sa_destroy(policy_sa_t *policy, policy_dir_t *dir, + private_kernel_pfkey_ipsec_t *this) +{ + if (*dir == POLICY_FWD) + { + policy_sa_fwd_t *fwd = (policy_sa_fwd_t*)policy; + fwd->src_ts->destroy(fwd->src_ts); + fwd->dst_ts->destroy(fwd->dst_ts); + } + ipsec_sa_destroy(this, policy->sa); + free(policy); +} + typedef struct policy_entry_t policy_entry_t; /** * installed kernel policy. */ struct policy_entry_t { - - /** reqid of this policy */ - u_int32_t reqid; - - /** index assigned by the kernel */ + /** Index assigned by the kernel */ u_int32_t index; - /** direction of this policy: in, out, forward */ + /** Direction of this policy: in, out, forward */ u_int8_t direction; - /** parameters of installed policy */ + /** Parameters of installed policy */ struct { - /** subnet and port */ + /** Subnet and port */ host_t *net; - /** subnet mask */ + /** Subnet mask */ u_int8_t mask; - /** protocol */ + /** Protocol */ u_int8_t proto; } src, dst; - /** associated route installed for this policy */ + /** Associated route installed for this policy */ route_entry_t *route; - /** by how many CHILD_SA's this policy is used */ - u_int refcount; + /** List of SAs this policy is used by, ordered by priority */ + linked_list_t *used_by; }; /** - * create a policy_entry_t object + * Create a policy_entry_t object */ static policy_entry_t *create_policy_entry(traffic_selector_t *src_ts, - traffic_selector_t *dst_ts, policy_dir_t dir, u_int32_t reqid) + traffic_selector_t *dst_ts, + policy_dir_t dir) { - policy_entry_t *policy = malloc_thing(policy_entry_t); - policy->reqid = reqid; - policy->index = 0; - policy->direction = dir; - policy->route = NULL; - policy->refcount = 0; + policy_entry_t *policy; + INIT(policy, + .direction = dir, + ); src_ts->to_subnet(src_ts, &policy->src.net, &policy->src.mask); dst_ts->to_subnet(dst_ts, &policy->dst.net, &policy->dst.mask); /* src or dest proto may be "any" (0), use more restrictive one */ - policy->src.proto = max(src_ts->get_protocol(src_ts), dst_ts->get_protocol(dst_ts)); + policy->src.proto = max(src_ts->get_protocol(src_ts), + dst_ts->get_protocol(dst_ts)); policy->src.proto = policy->src.proto ? policy->src.proto : IPSEC_PROTO_ANY; policy->dst.proto = policy->src.proto; @@ -285,23 +455,32 @@ static policy_entry_t *create_policy_entry(traffic_selector_t *src_ts, } /** - * destroy a policy_entry_t object + * Destroy a policy_entry_t object */ -static void policy_entry_destroy(policy_entry_t *this) +static void policy_entry_destroy(policy_entry_t *policy, + private_kernel_pfkey_ipsec_t *this) { - DESTROY_IF(this->src.net); - DESTROY_IF(this->dst.net); - if (this->route) + if (policy->route) { - route_entry_destroy(this->route); + route_entry_destroy(policy->route); } - free(this); + if (policy->used_by) + { + policy->used_by->invoke_function(policy->used_by, + (linked_list_invoke_t)policy_sa_destroy, + &policy->direction, this); + policy->used_by->destroy(policy->used_by); + } + DESTROY_IF(policy->src.net); + DESTROY_IF(policy->dst.net); + free(policy); } /** * compares two policy_entry_t */ -static inline bool policy_entry_equals(policy_entry_t *current, policy_entry_t *policy) +static inline bool policy_entry_equals(policy_entry_t *current, + policy_entry_t *policy) { return current->direction == policy->direction && current->src.proto == policy->src.proto && @@ -315,11 +494,41 @@ static inline bool policy_entry_equals(policy_entry_t *current, policy_entry_t * /** * compare the given kernel index with that of a policy */ -static inline bool policy_entry_match_byindex(policy_entry_t *current, u_int32_t *index) +static inline bool policy_entry_match_byindex(policy_entry_t *current, + u_int32_t *index) { return current->index == *index; } +/** + * Calculate the priority of a policy + */ +static inline u_int32_t get_priority(policy_entry_t *policy, + policy_priority_t prio) +{ + u_int32_t priority = PRIO_BASE; + switch (prio) + { + case POLICY_PRIORITY_FALLBACK: + priority <<= 1; + /* fall-through */ + case POLICY_PRIORITY_ROUTED: + priority <<= 1; + /* fall-through */ + case POLICY_PRIORITY_DEFAULT: + break; + } + /* calculate priority based on selector size, small size = high prio */ + priority -= policy->src.mask; + priority -= policy->dst.mask; + priority <<= 2; /* make some room for the two flags */ + priority += policy->src.net->get_port(policy->src.net) || + policy->dst.net->get_port(policy->dst.net) ? + 0 : 2; + priority += policy->src.proto != IPSEC_PROTO_ANY ? 0 : 1; + return priority; +} + typedef struct pfkey_msg_t pfkey_msg_t; struct pfkey_msg_t @@ -470,6 +679,23 @@ static u_int8_t dir2kernel(policy_dir_t dir) } } +/** + * convert the policy type to the one defined in ipsec.h + */ +static inline u_int16_t type2kernel(policy_type_t type) +{ + switch (type) + { + case POLICY_IPSEC: + return IPSEC_POLICY_IPSEC; + case POLICY_PASS: + return IPSEC_POLICY_NONE; + case POLICY_DROP: + return IPSEC_POLICY_DISCARD; + } + return type; +} + #ifdef SADB_X_MIGRATE /** * convert the policy direction in ipsec.h to the general one. @@ -665,19 +891,19 @@ static void add_encap_ext(struct sadb_msg *msg, host_t *src, host_t *dst) nat_type = (struct sadb_x_nat_t_type*)PFKEY_EXT_ADD_NEXT(msg); nat_type->sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE; - nat_type->sadb_x_nat_t_type_len = PFKEY_LEN(sizeof(struct sadb_x_nat_t_type)); + nat_type->sadb_x_nat_t_type_len = PFKEY_LEN(sizeof(*nat_type)); nat_type->sadb_x_nat_t_type_type = UDP_ENCAP_ESPINUDP; PFKEY_EXT_ADD(msg, nat_type); nat_port = (struct sadb_x_nat_t_port*)PFKEY_EXT_ADD_NEXT(msg); nat_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT; - nat_port->sadb_x_nat_t_port_len = PFKEY_LEN(sizeof(struct sadb_x_nat_t_port)); + nat_port->sadb_x_nat_t_port_len = PFKEY_LEN(sizeof(*nat_port)); nat_port->sadb_x_nat_t_port_port = htons(src->get_port(src)); PFKEY_EXT_ADD(msg, nat_port); nat_port = (struct sadb_x_nat_t_port*)PFKEY_EXT_ADD_NEXT(msg); nat_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT; - nat_port->sadb_x_nat_t_port_len = PFKEY_LEN(sizeof(struct sadb_x_nat_t_port)); + nat_port->sadb_x_nat_t_port_len = PFKEY_LEN(sizeof(*nat_port)); nat_port->sadb_x_nat_t_port_port = htons(dst->get_port(dst)); PFKEY_EXT_ADD(msg, nat_port); } @@ -694,9 +920,11 @@ static traffic_selector_t* sadb_address2ts(struct sadb_address *address) /* The Linux 2.6 kernel does not set the protocol and port information * in the src and dst sadb_address extensions of the SADB_ACQUIRE message. */ - host = host_create_from_sockaddr((sockaddr_t*)&address[1]) ; - ts = traffic_selector_create_from_subnet(host, address->sadb_address_prefixlen, - address->sadb_address_proto, host->get_port(host)); + host = host_create_from_sockaddr((sockaddr_t*)&address[1]); + ts = traffic_selector_create_from_subnet(host, + address->sadb_address_prefixlen, + address->sadb_address_proto, + host->get_port(host)); return ts; } @@ -729,7 +957,8 @@ static status_t parse_pfkey_message(struct sadb_msg *msg, pfkey_msg_t *out) if ((ext->sadb_ext_type > SADB_EXT_MAX) || (!ext->sadb_ext_type)) { - DBG1(DBG_KNL, "type of PF_KEY extension (%d) is invalid", ext->sadb_ext_type); + DBG1(DBG_KNL, "type of PF_KEY extension (%d) is invalid", + ext->sadb_ext_type); break; } @@ -784,7 +1013,8 @@ static status_t pfkey_send_socket(private_kernel_pfkey_ipsec_t *this, int socket continue; } this->mutex_pfkey->unlock(this->mutex_pfkey); - DBG1(DBG_KNL, "error sending to PF_KEY socket: %s", strerror(errno)); + DBG1(DBG_KNL, "error sending to PF_KEY socket: %s", + strerror(errno)); return FAILED; } break; @@ -804,7 +1034,8 @@ static status_t pfkey_send_socket(private_kernel_pfkey_ipsec_t *this, int socket /* interrupted, try again */ continue; } - DBG1(DBG_KNL, "error reading from PF_KEY socket: %s", strerror(errno)); + DBG1(DBG_KNL, "error reading from PF_KEY socket: %s", + strerror(errno)); this->mutex_pfkey->unlock(this->mutex_pfkey); return FAILED; } @@ -817,7 +1048,8 @@ static status_t pfkey_send_socket(private_kernel_pfkey_ipsec_t *this, int socket } if (msg->sadb_msg_len > len / PFKEY_ALIGNMENT) { - DBG1(DBG_KNL, "buffer was too small to receive the complete PF_KEY message"); + DBG1(DBG_KNL, "buffer was too small to receive the complete PF_KEY " + "message"); this->mutex_pfkey->unlock(this->mutex_pfkey); return FAILED; } @@ -829,7 +1061,8 @@ static status_t pfkey_send_socket(private_kernel_pfkey_ipsec_t *this, int socket if (msg->sadb_msg_seq != this->seq) { DBG1(DBG_KNL, "received PF_KEY message with unexpected sequence " - "number, was %d expected %d", msg->sadb_msg_seq, this->seq); + "number, was %d expected %d", msg->sadb_msg_seq, + this->seq); if (msg->sadb_msg_seq == 0) { /* FreeBSD and Mac OS X do this for the response to @@ -849,8 +1082,8 @@ static status_t pfkey_send_socket(private_kernel_pfkey_ipsec_t *this, int socket if (msg->sadb_msg_type != in->sadb_msg_type) { DBG2(DBG_KNL, "received PF_KEY message of wrong type, " - "was %d expected %d, ignoring", - msg->sadb_msg_type, in->sadb_msg_type); + "was %d expected %d, ignoring", msg->sadb_msg_type, + in->sadb_msg_type); } break; } @@ -860,7 +1093,6 @@ static status_t pfkey_send_socket(private_kernel_pfkey_ipsec_t *this, int socket memcpy(*out, buf, len); this->mutex_pfkey->unlock(this->mutex_pfkey); - return SUCCESS; } @@ -868,7 +1100,8 @@ static status_t pfkey_send_socket(private_kernel_pfkey_ipsec_t *this, int socket * Send a message to the default PF_KEY socket and handle the response. */ static status_t pfkey_send(private_kernel_pfkey_ipsec_t *this, - struct sadb_msg *in, struct sadb_msg **out, size_t *out_len) + struct sadb_msg *in, struct sadb_msg **out, + size_t *out_len) { return pfkey_send_socket(this, this->socket, in, out, out_len); } @@ -876,12 +1109,14 @@ static status_t pfkey_send(private_kernel_pfkey_ipsec_t *this, /** * Process a SADB_ACQUIRE message from the kernel */ -static void process_acquire(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* msg) +static void process_acquire(private_kernel_pfkey_ipsec_t *this, + struct sadb_msg* msg) { pfkey_msg_t response; u_int32_t index, reqid = 0; traffic_selector_t *src_ts, *dst_ts; policy_entry_t *policy; + policy_sa_t *sa; switch (msg->sadb_msg_satype) { @@ -904,18 +1139,21 @@ static void process_acquire(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* index = response.x_policy->sadb_x_policy_id; this->mutex->lock(this->mutex); if (this->policies->find_first(this->policies, - (linked_list_match_t)policy_entry_match_byindex, (void**)&policy, &index) == SUCCESS) + (linked_list_match_t)policy_entry_match_byindex, + (void**)&policy, &index) == SUCCESS && + policy->used_by->get_first(policy->used_by, (void**)&sa) == SUCCESS) { - reqid = policy->reqid; + reqid = sa->sa->cfg.reqid; } else { - DBG1(DBG_KNL, "received an SADB_ACQUIRE with policy id %d but no" - " matching policy found", index); + DBG1(DBG_KNL, "received an SADB_ACQUIRE with policy id %d but no " + "matching policy found", index); } + this->mutex->unlock(this->mutex); + src_ts = sadb_address2ts(response.src); dst_ts = sadb_address2ts(response.dst); - this->mutex->unlock(this->mutex); hydra->kernel_interface->acquire(hydra->kernel_interface, reqid, src_ts, dst_ts); @@ -924,7 +1162,8 @@ static void process_acquire(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* /** * Process a SADB_EXPIRE message from the kernel */ -static void process_expire(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* msg) +static void process_expire(private_kernel_pfkey_ipsec_t *this, + struct sadb_msg* msg) { pfkey_msg_t response; u_int8_t protocol; @@ -946,8 +1185,8 @@ static void process_expire(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* if (protocol != IPPROTO_ESP && protocol != IPPROTO_AH) { - DBG2(DBG_KNL, "ignoring SADB_EXPIRE for SA with SPI %.8x and reqid {%u} " - "which is not a CHILD_SA", ntohl(spi), reqid); + DBG2(DBG_KNL, "ignoring SADB_EXPIRE for SA with SPI %.8x and " + "reqid {%u} which is not a CHILD_SA", ntohl(spi), reqid); return; } @@ -959,7 +1198,8 @@ static void process_expire(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* /** * Process a SADB_X_MIGRATE message from the kernel */ -static void process_migrate(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* msg) +static void process_migrate(private_kernel_pfkey_ipsec_t *this, + struct sadb_msg* msg) { pfkey_msg_t response; traffic_selector_t *src_ts, *dst_ts; @@ -1014,10 +1254,12 @@ static void process_migrate(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* /** * Process a SADB_X_NAT_T_NEW_MAPPING message from the kernel */ -static void process_mapping(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* msg) +static void process_mapping(private_kernel_pfkey_ipsec_t *this, + struct sadb_msg* msg) { pfkey_msg_t response; u_int32_t spi, reqid; + sockaddr_t *sa; host_t *host; DBG2(DBG_KNL, "received an SADB_X_NAT_T_NEW_MAPPING"); @@ -1038,30 +1280,33 @@ static void process_mapping(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* spi = response.sa->sadb_sa_spi; reqid = response.x_sa2->sadb_x_sa2_reqid; - if (satype2proto(msg->sadb_msg_satype) == IPPROTO_ESP) + if (satype2proto(msg->sadb_msg_satype) != IPPROTO_ESP) + { + return; + } + + sa = (sockaddr_t*)(response.dst + 1); + switch (sa->sa_family) { - sockaddr_t *sa = (sockaddr_t*)(response.dst + 1); - switch (sa->sa_family) + case AF_INET: { - case AF_INET: - { - struct sockaddr_in *sin = (struct sockaddr_in*)sa; - sin->sin_port = htons(response.x_natt_dport->sadb_x_nat_t_port_port); - } - case AF_INET6: - { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)sa; - sin6->sin6_port = htons(response.x_natt_dport->sadb_x_nat_t_port_port); - } - default: - break; + struct sockaddr_in *sin = (struct sockaddr_in*)sa; + sin->sin_port = htons(response.x_natt_dport->sadb_x_nat_t_port_port); } - host = host_create_from_sockaddr(sa); - if (host) + case AF_INET6: { - hydra->kernel_interface->mapping(hydra->kernel_interface, reqid, - spi, host); + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)sa; + sin6->sin6_port = htons(response.x_natt_dport->sadb_x_nat_t_port_port); } + default: + break; + } + + host = host_create_from_sockaddr(sa); + if (host) + { + hydra->kernel_interface->mapping(hydra->kernel_interface, reqid, + spi, host); } } #endif /*SADB_X_NAT_T_NEW_MAPPING*/ @@ -1073,8 +1318,8 @@ static job_requeue_t receive_events(private_kernel_pfkey_ipsec_t *this) { unsigned char buf[PFKEY_BUFFER_SIZE]; struct sadb_msg *msg = (struct sadb_msg*)buf; - int len; bool oldstate; + int len; oldstate = thread_cancelability(TRUE); len = recvfrom(this->socket_events, buf, sizeof(buf), 0, NULL, 0); @@ -1109,7 +1354,8 @@ static job_requeue_t receive_events(private_kernel_pfkey_ipsec_t *this) } if (msg->sadb_msg_len > len / PFKEY_ALIGNMENT) { - DBG1(DBG_KNL, "buffer was too small to receive the complete PF_KEY message"); + DBG1(DBG_KNL, "buffer was too small to receive the complete " + "PF_KEY message"); return JOB_REQUEUE_DIRECT; } @@ -1179,7 +1425,7 @@ METHOD(kernel_ipsec_t, get_spi, status_t, if (out->sadb_msg_errno) { DBG1(DBG_KNL, "allocating SPI failed: %s (%d)", - strerror(out->sadb_msg_errno), out->sadb_msg_errno); + strerror(out->sadb_msg_errno), out->sadb_msg_errno); } else if (parse_pfkey_message(out, &response) == SUCCESS) { @@ -1222,7 +1468,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t, memset(&request, 0, sizeof(request)); - 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}", + ntohl(spi), reqid); msg = (struct sadb_msg*)request; msg->sadb_msg_version = PF_KEY_V2; @@ -1287,11 +1534,11 @@ METHOD(kernel_ipsec_t, add_sa, status_t, if (!sa->sadb_sa_encrypt) { DBG1(DBG_KNL, "algorithm %N not supported by kernel!", - encryption_algorithm_names, enc_alg); + encryption_algorithm_names, enc_alg); return FAILED; } DBG2(DBG_KNL, " using encryption algorithm %N with key size %d", - encryption_algorithm_names, enc_alg, enc_key.len * 8); + encryption_algorithm_names, enc_alg, enc_key.len * 8); key = (struct sadb_key*)PFKEY_EXT_ADD_NEXT(msg); key->sadb_key_exttype = SADB_EXT_KEY_ENCRYPT; @@ -1307,11 +1554,11 @@ METHOD(kernel_ipsec_t, add_sa, status_t, if (!sa->sadb_sa_auth) { DBG1(DBG_KNL, "algorithm %N not supported by kernel!", - integrity_algorithm_names, int_alg); + integrity_algorithm_names, int_alg); return FAILED; } DBG2(DBG_KNL, " using integrity algorithm %N with key size %d", - integrity_algorithm_names, int_alg, int_key.len * 8); + integrity_algorithm_names, int_alg, int_key.len * 8); key = (struct sadb_key*)PFKEY_EXT_ADD_NEXT(msg); key->sadb_key_exttype = SADB_EXT_KEY_AUTH; @@ -1368,8 +1615,8 @@ METHOD(kernel_ipsec_t, update_sa, status_t, if (!src->ip_equals(src, new_src) || !dst->ip_equals(dst, new_dst)) { - DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x: address changes" - " are not supported", ntohl(spi)); + DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x: address " + "changes are not supported", ntohl(spi)); return NOT_SUPPORTED; } @@ -1396,27 +1643,27 @@ METHOD(kernel_ipsec_t, update_sa, status_t, if (pfkey_send(this, msg, &out, &len) != SUCCESS) { - DBG1(DBG_KNL, "unable to query SAD entry with SPI %.8x", - ntohl(spi)); + DBG1(DBG_KNL, "unable to query SAD entry with SPI %.8x", ntohl(spi)); return FAILED; } else if (out->sadb_msg_errno) { DBG1(DBG_KNL, "unable to query SAD entry with SPI %.8x: %s (%d)", - ntohl(spi), strerror(out->sadb_msg_errno), out->sadb_msg_errno); + ntohl(spi), strerror(out->sadb_msg_errno), + out->sadb_msg_errno); free(out); return FAILED; } else if (parse_pfkey_message(out, &response) != SUCCESS) { - DBG1(DBG_KNL, "unable to query SAD entry with SPI %.8x: parsing response " - "from kernel failed", ntohl(spi)); + DBG1(DBG_KNL, "unable to query SAD entry with SPI %.8x: parsing " + "response from kernel failed", ntohl(spi)); free(out); return FAILED; } DBG2(DBG_KNL, "updating SAD entry with SPI %.8x from %#H..%#H to %#H..%#H", - ntohl(spi), src, dst, new_src, new_dst); + ntohl(spi), src, dst, new_src, new_dst); memset(&request, 0, sizeof(request)); @@ -1476,7 +1723,8 @@ METHOD(kernel_ipsec_t, update_sa, status_t, else if (out->sadb_msg_errno) { DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x: %s (%d)", - ntohl(spi), strerror(out->sadb_msg_errno), out->sadb_msg_errno); + ntohl(spi), strerror(out->sadb_msg_errno), + out->sadb_msg_errno); free(out); return FAILED; } @@ -1525,7 +1773,8 @@ METHOD(kernel_ipsec_t, query_sa, status_t, else if (out->sadb_msg_errno) { DBG1(DBG_KNL, "unable to query SAD entry with SPI %.8x: %s (%d)", - ntohl(spi), strerror(out->sadb_msg_errno), out->sadb_msg_errno); + ntohl(spi), strerror(out->sadb_msg_errno), + out->sadb_msg_errno); free(out); return FAILED; } @@ -1580,7 +1829,8 @@ METHOD(kernel_ipsec_t, del_sa, status_t, else if (out->sadb_msg_errno) { DBG1(DBG_KNL, "unable to delete SAD entry with SPI %.8x: %s (%d)", - ntohl(spi), strerror(out->sadb_msg_errno), out->sadb_msg_errno); + ntohl(spi), strerror(out->sadb_msg_errno), + out->sadb_msg_errno); free(out); return FAILED; } @@ -1590,57 +1840,60 @@ METHOD(kernel_ipsec_t, del_sa, status_t, return SUCCESS; } -METHOD(kernel_ipsec_t, add_policy, status_t, - private_kernel_pfkey_ipsec_t *this, host_t *src, host_t *dst, - traffic_selector_t *src_ts, traffic_selector_t *dst_ts, - policy_dir_t direction, policy_type_t type, ipsec_sa_cfg_t *sa, - mark_t mark, bool routed) +METHOD(kernel_ipsec_t, flush_sas, status_t, + private_kernel_pfkey_ipsec_t *this) { unsigned char request[PFKEY_BUFFER_SIZE]; struct sadb_msg *msg, *out; - struct sadb_x_policy *pol; - struct sadb_x_ipsecrequest *req; - policy_entry_t *policy, *found = NULL; - pfkey_msg_t response; size_t len; - if (dir2kernel(direction) == IPSEC_DIR_INVALID) - { - /* FWD policies are not supported on all platforms */ - return SUCCESS; - } + memset(&request, 0, sizeof(request)); - /* create a policy */ - policy = create_policy_entry(src_ts, dst_ts, direction, sa->reqid); + DBG2(DBG_KNL, "flushing all SAD entries"); - /* find a matching policy */ - this->mutex->lock(this->mutex); - if (this->policies->find_first(this->policies, - (linked_list_match_t)policy_entry_equals, (void**)&found, policy) == SUCCESS) + msg = (struct sadb_msg*)request; + msg->sadb_msg_version = PF_KEY_V2; + msg->sadb_msg_type = SADB_FLUSH; + msg->sadb_msg_satype = SADB_SATYPE_UNSPEC; + msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg)); + + if (pfkey_send(this, msg, &out, &len) != SUCCESS) { - /* use existing policy */ - found->refcount++; - DBG2(DBG_KNL, "policy %R === %R %N already exists, increasing " - "refcount", src_ts, dst_ts, - policy_dir_names, direction); - policy_entry_destroy(policy); - policy = found; + DBG1(DBG_KNL, "unable to flush SAD entries"); + return FAILED; } - else + else if (out->sadb_msg_errno) { - /* apply the new one, if we have no such policy */ - this->policies->insert_last(this->policies, policy); - policy->refcount = 1; + DBG1(DBG_KNL, "unable to flush SAD entries: %s (%d)", + strerror(out->sadb_msg_errno), out->sadb_msg_errno); + free(out); + return FAILED; } + free(out); + return SUCCESS; +} - memset(&request, 0, sizeof(request)); +/** + * Add or update a policy in the kernel. + * + * Note: The mutex has to be locked when entering this function. + */ +static status_t add_policy_internal(private_kernel_pfkey_ipsec_t *this, + policy_entry_t *policy, policy_sa_t *mapping, bool update) +{ + unsigned char request[PFKEY_BUFFER_SIZE]; + struct sadb_msg *msg, *out; + struct sadb_x_policy *pol; + struct sadb_x_ipsecrequest *req; + ipsec_sa_t *ipsec = mapping->sa; + pfkey_msg_t response; + size_t len; - DBG2(DBG_KNL, "adding policy %R === %R %N", src_ts, dst_ts, - policy_dir_names, direction); + memset(&request, 0, sizeof(request)); msg = (struct sadb_msg*)request; msg->sadb_msg_version = PF_KEY_V2; - msg->sadb_msg_type = found ? SADB_X_SPDUPDATE : SADB_X_SPDADD; + msg->sadb_msg_type = update ? SADB_X_SPDUPDATE : SADB_X_SPDADD; msg->sadb_msg_satype = 0; msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg)); @@ -1648,32 +1901,27 @@ METHOD(kernel_ipsec_t, add_policy, status_t, pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY; pol->sadb_x_policy_len = PFKEY_LEN(sizeof(struct sadb_x_policy)); pol->sadb_x_policy_id = 0; - pol->sadb_x_policy_dir = dir2kernel(direction); - pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC; + pol->sadb_x_policy_dir = dir2kernel(policy->direction); + pol->sadb_x_policy_type = type2kernel(mapping->type); #ifdef HAVE_STRUCT_SADB_X_POLICY_SADB_X_POLICY_PRIORITY - /* calculate priority based on selector size, small size = high prio */ - pol->sadb_x_policy_priority = routed ? PRIO_LOW : PRIO_HIGH; - pol->sadb_x_policy_priority -= policy->src.mask; - pol->sadb_x_policy_priority -= policy->dst.mask; - pol->sadb_x_policy_priority <<= 2; /* make some room for the flags */ - pol->sadb_x_policy_priority += policy->src.net->get_port(policy->src.net) || - policy->dst.net->get_port(policy->dst.net) ? 0 : 2; - pol->sadb_x_policy_priority += policy->src.proto != IPSEC_PROTO_ANY ? 0 : 1; + pol->sadb_x_policy_priority = mapping->priority; #endif - /* one or more sadb_x_ipsecrequest extensions are added to the sadb_x_policy extension */ + /* one or more sadb_x_ipsecrequest extensions are added to the + * sadb_x_policy extension */ req = (struct sadb_x_ipsecrequest*)(pol + 1); - req->sadb_x_ipsecrequest_proto = sa->esp.use ? IPPROTO_ESP : IPPROTO_AH; - /* !!! the length of this struct MUST be in octets instead of 64 bit words */ + req->sadb_x_ipsecrequest_proto = ipsec->cfg.esp.use ? IPPROTO_ESP + : IPPROTO_AH; + /* !!! the length here MUST be in octets instead of 64 bit words */ req->sadb_x_ipsecrequest_len = sizeof(struct sadb_x_ipsecrequest); - req->sadb_x_ipsecrequest_mode = mode2kernel(sa->mode); - req->sadb_x_ipsecrequest_reqid = sa->reqid; + req->sadb_x_ipsecrequest_mode = mode2kernel(ipsec->cfg.mode); + req->sadb_x_ipsecrequest_reqid = ipsec->cfg.reqid; req->sadb_x_ipsecrequest_level = IPSEC_LEVEL_UNIQUE; - if (sa->mode == MODE_TUNNEL) + if (ipsec->cfg.mode == MODE_TUNNEL) { - len = hostcpy(req + 1, src); + len = hostcpy(req + 1, ipsec->src); req->sadb_x_ipsecrequest_len += len; - len = hostcpy((char*)(req + 1) + len, dst); + len = hostcpy((char*)(req + 1) + len, ipsec->dst); req->sadb_x_ipsecrequest_len += len; } @@ -1701,33 +1949,31 @@ METHOD(kernel_ipsec_t, add_policy, status_t, if (pfkey_send(this, msg, &out, &len) != SUCCESS) { - DBG1(DBG_KNL, "unable to add policy %R === %R %N", src_ts, dst_ts, - policy_dir_names, direction); return FAILED; } else if (out->sadb_msg_errno) { - DBG1(DBG_KNL, "unable to add policy %R === %R %N: %s (%d)", src_ts, dst_ts, - policy_dir_names, direction, - strerror(out->sadb_msg_errno), out->sadb_msg_errno); + DBG1(DBG_KNL, "unable to %s policy: %s (%d)", + update ? "update" : "add", strerror(out->sadb_msg_errno), + out->sadb_msg_errno); free(out); return FAILED; } else if (parse_pfkey_message(out, &response) != SUCCESS) { - DBG1(DBG_KNL, "unable to add policy %R === %R %N: parsing response " - "from kernel failed", src_ts, dst_ts, policy_dir_names, direction); + DBG1(DBG_KNL, "unable to %s policy: parsing response from kernel " + "failed", update ? "update" : "add"); free(out); return FAILED; } - this->mutex->lock(this->mutex); - /* we try to find the policy again and update the kernel index */ - if (this->policies->find_last(this->policies, NULL, (void**)&policy) != SUCCESS) + this->mutex->lock(this->mutex); + if (this->policies->find_last(this->policies, NULL, + (void**)&policy) != SUCCESS) { - DBG2(DBG_KNL, "unable to update index, the policy %R === %R %N is " - "already gone, ignoring", src_ts, dst_ts, policy_dir_names, direction); + DBG2(DBG_KNL, "unable to update index, the policy is already gone, " + "ignoring"); this->mutex->unlock(this->mutex); free(out); return SUCCESS; @@ -1736,53 +1982,83 @@ METHOD(kernel_ipsec_t, add_policy, status_t, free(out); /* install a route, if: - * - we are NOT updating a policy * - this is a forward policy (to just get one for each child) * - we are in tunnel mode - * - we are not using IPv6 (does not work correctly yet!) * - routing is not disabled via strongswan.conf */ - if (policy->route == NULL && direction == POLICY_FWD && - sa->mode != MODE_TRANSPORT && src->get_family(src) != AF_INET6 && - this->install_routes) + if (policy->direction == POLICY_FWD && + ipsec->cfg.mode != MODE_TRANSPORT && this->install_routes) { route_entry_t *route = malloc_thing(route_entry_t); + policy_sa_fwd_t *fwd = (policy_sa_fwd_t*)mapping; if (hydra->kernel_interface->get_address_by_ts(hydra->kernel_interface, - dst_ts, &route->src_ip) == SUCCESS) + fwd->dst_ts, &route->src_ip) == SUCCESS) { /* get the nexthop to src (src as we are in POLICY_FWD).*/ route->gateway = hydra->kernel_interface->get_nexthop( - hydra->kernel_interface, src); + hydra->kernel_interface, ipsec->src); + /* install route via outgoing interface */ route->if_name = hydra->kernel_interface->get_interface( - hydra->kernel_interface, dst); - route->dst_net = chunk_clone(policy->src.net->get_address(policy->src.net)); + hydra->kernel_interface, ipsec->dst); + route->dst_net = chunk_clone(policy->src.net->get_address( + policy->src.net)); route->prefixlen = policy->src.mask; - if (route->if_name) + if (!route->if_name) { - switch (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; + } + + if (policy->route) + { + 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; + } + /* uninstall previously installed route */ + if (hydra->kernel_interface->del_route(hydra->kernel_interface, + old->dst_net, old->prefixlen, old->gateway, + old->src_ip, old->if_name) != SUCCESS) { - default: - DBG1(DBG_KNL, "unable to install source route for %H", - route->src_ip); - /* FALL */ - case ALREADY_DONE: - /* route exists, do not uninstall */ - route_entry_destroy(route); - break; - case SUCCESS: - /* cache the installed route */ - policy->route = route; - break; + DBG1(DBG_KNL, "error uninstalling route installed with " + "policy %R === %R %N", fwd->src_ts, + fwd->dst_ts, policy_dir_names, + policy->direction); } + route_entry_destroy(old); + policy->route = NULL; } - else + + DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s", + fwd->src_ts, route->gateway, route->src_ip, route->if_name); + switch (hydra->kernel_interface->add_route( + hydra->kernel_interface, route->dst_net, + route->prefixlen, route->gateway, + route->src_ip, route->if_name)) { - route_entry_destroy(route); + default: + DBG1(DBG_KNL, "unable to install source route for %H", + route->src_ip); + /* FALL */ + case ALREADY_DONE: + /* route exists, do not uninstall */ + route_entry_destroy(route); + break; + case SUCCESS: + /* cache the installed route */ + policy->route = route; + break; } } else @@ -1790,9 +2066,82 @@ METHOD(kernel_ipsec_t, add_policy, status_t, free(route); } } - this->mutex->unlock(this->mutex); + return SUCCESS; +} + +METHOD(kernel_ipsec_t, add_policy, status_t, + private_kernel_pfkey_ipsec_t *this, host_t *src, host_t *dst, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts, + policy_dir_t direction, policy_type_t type, ipsec_sa_cfg_t *sa, + mark_t mark, policy_priority_t priority) +{ + policy_entry_t *policy, *found = NULL; + policy_sa_t *assigned_sa, *current_sa; + enumerator_t *enumerator; + bool update = TRUE; + + if (dir2kernel(direction) == IPSEC_DIR_INVALID) + { /* FWD policies are not supported on all platforms */ + return SUCCESS; + } + + /* create a policy */ + policy = create_policy_entry(src_ts, dst_ts, direction); + + /* find a matching policy */ + this->mutex->lock(this->mutex); + if (this->policies->find_first(this->policies, + (linked_list_match_t)policy_entry_equals, + (void**)&found, policy) == SUCCESS) + { /* use existing policy */ + DBG2(DBG_KNL, "policy %R === %R %N already exists, increasing " + "refcount", src_ts, dst_ts, policy_dir_names, direction); + policy_entry_destroy(policy, this); + policy = found; + } + else + { /* use the new one, if we have no such policy */ + this->policies->insert_last(this->policies, policy); + policy->used_by = linked_list_create(); + } + /* cache the assigned IPsec SA */ + assigned_sa = policy_sa_create(this, direction, type, src, dst, src_ts, + dst_ts, sa); + assigned_sa->priority = get_priority(policy, priority); + + /* insert the SA according to its priority */ + enumerator = policy->used_by->create_enumerator(policy->used_by); + while (enumerator->enumerate(enumerator, (void**)¤t_sa)) + { + if (current_sa->priority >= assigned_sa->priority) + { + break; + } + update = FALSE; + } + policy->used_by->insert_before(policy->used_by, enumerator, assigned_sa); + enumerator->destroy(enumerator); + + if (!update) + { /* we don't update the policy if the priority is lower than that of the + * currently installed one */ + this->mutex->unlock(this->mutex); + return SUCCESS; + } + + DBG2(DBG_KNL, "%s policy %R === %R %N", + found ? "updating" : "adding", src_ts, dst_ts, + policy_dir_names, direction); + + if (add_policy_internal(this, policy, assigned_sa, found) != SUCCESS) + { + DBG1(DBG_KNL, "unable to %s policy %R === %R %N", + found ? "update" : "add", src_ts, dst_ts, + policy_dir_names, direction); + return FAILED; + } return SUCCESS; } @@ -1809,8 +2158,7 @@ METHOD(kernel_ipsec_t, query_policy, status_t, size_t len; if (dir2kernel(direction) == IPSEC_DIR_INVALID) - { - /* FWD policies are not supported on all platforms */ + { /* FWD policies are not supported on all platforms */ return NOT_FOUND; } @@ -1818,20 +2166,21 @@ METHOD(kernel_ipsec_t, query_policy, status_t, policy_dir_names, direction); /* create a policy */ - policy = create_policy_entry(src_ts, dst_ts, direction, 0); + policy = create_policy_entry(src_ts, dst_ts, direction); /* find a matching policy */ this->mutex->lock(this->mutex); if (this->policies->find_first(this->policies, - (linked_list_match_t)policy_entry_equals, (void**)&found, policy) != SUCCESS) + (linked_list_match_t)policy_entry_equals, + (void**)&found, policy) != SUCCESS) { DBG1(DBG_KNL, "querying policy %R === %R %N failed, not found", src_ts, dst_ts, policy_dir_names, direction); - policy_entry_destroy(policy); + policy_entry_destroy(policy, this); this->mutex->unlock(this->mutex); return NOT_FOUND; } - policy_entry_destroy(policy); + policy_entry_destroy(policy, this); policy = found; memset(&request, 0, sizeof(request)); @@ -1874,17 +2223,19 @@ METHOD(kernel_ipsec_t, query_policy, status_t, else if (parse_pfkey_message(out, &response) != SUCCESS) { DBG1(DBG_KNL, "unable to query policy %R === %R %N: parsing response " - "from kernel failed", src_ts, dst_ts, policy_dir_names, direction); + "from kernel failed", src_ts, dst_ts, policy_dir_names, + direction); free(out); return FAILED; } else if (response.lft_current == NULL) { DBG1(DBG_KNL, "unable to query policy %R === %R %N: kernel reports no " - "use time", src_ts, dst_ts, policy_dir_names, direction); + "use time", src_ts, dst_ts, policy_dir_names, direction); free(out); return FAILED; } + /* we need the monotonic time, but the kernel returns system time. */ if (response.lft_current->sadb_lifetime_usetime) { @@ -1896,25 +2247,26 @@ METHOD(kernel_ipsec_t, query_policy, status_t, *use_time = 0; } free(out); - return SUCCESS; } METHOD(kernel_ipsec_t, del_policy, status_t, private_kernel_pfkey_ipsec_t *this, traffic_selector_t *src_ts, - traffic_selector_t *dst_ts, policy_dir_t direction, mark_t mark, - bool unrouted) + traffic_selector_t *dst_ts, policy_dir_t direction, u_int32_t reqid, + mark_t mark, policy_priority_t prio) { unsigned char request[PFKEY_BUFFER_SIZE]; struct sadb_msg *msg, *out; struct sadb_x_policy *pol; policy_entry_t *policy, *found = NULL; - route_entry_t *route; + policy_sa_t *mapping; + enumerator_t *enumerator; + bool is_installed = TRUE; + u_int32_t priority; size_t len; if (dir2kernel(direction) == IPSEC_DIR_INVALID) - { - /* FWD policies are not supported on all platforms */ + { /* FWD policies are not supported on all platforms */ return SUCCESS; } @@ -1922,35 +2274,59 @@ METHOD(kernel_ipsec_t, del_policy, status_t, policy_dir_names, direction); /* create a policy */ - policy = create_policy_entry(src_ts, dst_ts, direction, 0); + policy = create_policy_entry(src_ts, dst_ts, direction); /* find a matching policy */ this->mutex->lock(this->mutex); if (this->policies->find_first(this->policies, - (linked_list_match_t)policy_entry_equals, (void**)&found, policy) == SUCCESS) - { - if (--found->refcount > 0) - { - /* is used by more SAs, keep in kernel */ - DBG2(DBG_KNL, "policy still used by another CHILD_SA, not removed"); - policy_entry_destroy(policy); - this->mutex->unlock(this->mutex); - return SUCCESS; - } - /* remove if last reference */ - this->policies->remove(this->policies, found, NULL); - policy_entry_destroy(policy); - policy = found; - } - else + (linked_list_match_t)policy_entry_equals, + (void**)&found, policy) != SUCCESS) { DBG1(DBG_KNL, "deleting policy %R === %R %N failed, not found", src_ts, dst_ts, policy_dir_names, direction); - policy_entry_destroy(policy); + policy_entry_destroy(policy, this); this->mutex->unlock(this->mutex); return NOT_FOUND; } - this->mutex->unlock(this->mutex); + policy_entry_destroy(policy, this); + policy = found; + + /* remove mapping to SA by reqid and priority */ + priority = get_priority(policy, prio); + enumerator = policy->used_by->create_enumerator(policy->used_by); + while (enumerator->enumerate(enumerator, (void**)&mapping)) + { + if (reqid == mapping->sa->cfg.reqid && priority == mapping->priority) + { + policy->used_by->remove_at(policy->used_by, enumerator); + break; + } + is_installed = FALSE; + } + enumerator->destroy(enumerator); + + if (policy->used_by->get_count(policy->used_by) > 0) + { /* policy is used by more SAs, keep in kernel */ + DBG2(DBG_KNL, "policy still used by another CHILD_SA, not removed"); + policy_sa_destroy(mapping, &direction, this); + + if (!is_installed) + { /* no need to update as the policy was not installed for this SA */ + this->mutex->unlock(this->mutex); + return SUCCESS; + } + + DBG2(DBG_KNL, "updating policy %R === %R %N", src_ts, dst_ts, + policy_dir_names, direction); + policy->used_by->get_first(policy->used_by, (void**)&mapping); + if (add_policy_internal(this, policy, mapping, TRUE) != SUCCESS) + { + DBG1(DBG_KNL, "unable to update policy %R === %R %N", + src_ts, dst_ts, policy_dir_names, direction); + return FAILED; + } + return SUCCESS; + } memset(&request, 0, sizeof(request)); @@ -1964,7 +2340,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t, pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY; pol->sadb_x_policy_len = PFKEY_LEN(sizeof(struct sadb_x_policy)); pol->sadb_x_policy_dir = dir2kernel(direction); - pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC; + pol->sadb_x_policy_type = type2kernel(mapping->type); PFKEY_EXT_ADD(msg, pol); add_addr_ext(msg, policy->src.net, SADB_EXT_ADDRESS_SRC, policy->src.proto, @@ -1972,9 +2348,23 @@ METHOD(kernel_ipsec_t, del_policy, status_t, add_addr_ext(msg, policy->dst.net, SADB_EXT_ADDRESS_DST, policy->dst.proto, policy->dst.mask); - route = policy->route; - policy->route = NULL; - policy_entry_destroy(policy); + if (policy->route) + { + route_entry_t *route = policy->route; + if (hydra->kernel_interface->del_route(hydra->kernel_interface, + route->dst_net, route->prefixlen, route->gateway, + route->src_ip, route->if_name) != SUCCESS) + { + DBG1(DBG_KNL, "error uninstalling route installed with " + "policy %R === %R %N", src_ts, dst_ts, + policy_dir_names, direction); + } + } + + this->policies->remove(this->policies, found, NULL); + policy_sa_destroy(mapping, &direction, this); + policy_entry_destroy(policy, this); + this->mutex->unlock(this->mutex); if (pfkey_send(this, msg, &out, &len) != SUCCESS) { @@ -1991,25 +2381,44 @@ METHOD(kernel_ipsec_t, del_policy, status_t, return FAILED; } free(out); + return SUCCESS; +} + +METHOD(kernel_ipsec_t, flush_policies, status_t, + private_kernel_pfkey_ipsec_t *this) +{ + unsigned char request[PFKEY_BUFFER_SIZE]; + struct sadb_msg *msg, *out; + size_t len; + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "flushing all policies from SPD"); - if (route) + msg = (struct sadb_msg*)request; + msg->sadb_msg_version = PF_KEY_V2; + msg->sadb_msg_type = SADB_X_SPDFLUSH; + msg->sadb_msg_satype = SADB_SATYPE_UNSPEC; + msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg)); + + if (pfkey_send(this, msg, &out, &len) != SUCCESS) { - if (hydra->kernel_interface->del_route(hydra->kernel_interface, - route->dst_net, route->prefixlen, route->gateway, - route->src_ip, route->if_name) != SUCCESS) - { - DBG1(DBG_KNL, "error uninstalling route installed with " - "policy %R === %R %N", src_ts, dst_ts, - policy_dir_names, direction); - } - route_entry_destroy(route); + DBG1(DBG_KNL, "unable to flush SPD entries"); + return FAILED; } - + else if (out->sadb_msg_errno) + { + DBG1(DBG_KNL, "unable to flush SPD entries: %s (%d)", + strerror(out->sadb_msg_errno), out->sadb_msg_errno); + free(out); + return FAILED; + } + free(out); return SUCCESS; } /** - * Register a socket for AQUIRE/EXPIRE messages + * Register a socket for ACQUIRE/EXPIRE messages */ static status_t register_pfkey_socket(private_kernel_pfkey_ipsec_t *this, u_int8_t satype) @@ -2075,14 +2484,14 @@ METHOD(kernel_ipsec_t, bypass_socket, bool, if (setsockopt(fd, sol, ipsec_policy, &policy, sizeof(policy)) < 0) { DBG1(DBG_KNL, "unable to set IPSEC_POLICY on socket: %s", - strerror(errno)); + strerror(errno)); return FALSE; } policy.sadb_x_policy_dir = IPSEC_DIR_INBOUND; if (setsockopt(fd, sol, ipsec_policy, &policy, sizeof(policy)) < 0) { DBG1(DBG_KNL, "unable to set IPSEC_POLICY on socket: %s", - strerror(errno)); + strerror(errno)); return FALSE; } return TRUE; @@ -2103,7 +2512,11 @@ METHOD(kernel_ipsec_t, destroy, void, { close(this->socket_events); } - this->policies->destroy_function(this->policies, (void*)policy_entry_destroy); + this->policies->invoke_function(this->policies, + (linked_list_invoke_t)policy_entry_destroy, + this); + this->policies->destroy(this->policies); + this->sas->destroy(this->sas); this->mutex->destroy(this->mutex); this->mutex_pfkey->destroy(this->mutex_pfkey); free(this); @@ -2125,14 +2538,18 @@ kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create() .update_sa = _update_sa, .query_sa = _query_sa, .del_sa = _del_sa, + .flush_sas = _flush_sas, .add_policy = _add_policy, .query_policy = _query_policy, .del_policy = _del_policy, + .flush_policies = _flush_policies, .bypass_socket = _bypass_socket, .destroy = _destroy, }, }, .policies = linked_list_create(), + .sas = hashtable_create((hashtable_hash_t)ipsec_sa_hash, + (hashtable_equals_t)ipsec_sa_equals, 32), .mutex = mutex_create(MUTEX_TYPE_DEFAULT), .mutex_pfkey = mutex_create(MUTEX_TYPE_DEFAULT), .install_routes = lib->settings->get_bool(lib->settings, @@ -2172,8 +2589,8 @@ kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create() return NULL; } - this->job = callback_job_create((callback_job_cb_t)receive_events, - this, NULL, NULL); + 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_pfkey/kernel_pfkey_plugin.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_plugin.c index 842511181..894175402 100644 --- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_plugin.c +++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_plugin.c @@ -38,11 +38,20 @@ METHOD(plugin_t, get_name, char*, return "kernel-pfkey"; } +METHOD(plugin_t, get_features, int, + private_kernel_pfkey_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_CALLBACK(kernel_ipsec_register, kernel_pfkey_ipsec_create), + PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"), + }; + *features = f; + return countof(f); +} + METHOD(plugin_t, destroy, void, private_kernel_pfkey_plugin_t *this) { - hydra->kernel_interface->remove_ipsec_interface(hydra->kernel_interface, - (kernel_ipsec_constructor_t)kernel_pfkey_ipsec_create); free(this); } @@ -57,13 +66,11 @@ plugin_t *kernel_pfkey_plugin_create() .public = { .plugin = { .get_name = _get_name, - .reload = (void*)return_false, + .get_features = _get_features, .destroy = _destroy, }, }, ); - hydra->kernel_interface->add_ipsec_interface(hydra->kernel_interface, - (kernel_ipsec_constructor_t)kernel_pfkey_ipsec_create); return &this->public.plugin; } diff --git a/src/libhydra/plugins/kernel_pfroute/Makefile.in b/src/libhydra/plugins/kernel_pfroute/Makefile.in index b7e12561d..1412db0ec 100644 --- a/src/libhydra/plugins/kernel_pfroute/Makefile.in +++ b/src/libhydra/plugins/kernel_pfroute/Makefile.in @@ -195,6 +195,9 @@ am__leading_dot = @am__leading_dot@ 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@ @@ -203,6 +206,7 @@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ c_plugins = @c_plugins@ +clearsilver_LIBS = @clearsilver_LIBS@ datadir = @datadir@ datarootdir = @datarootdir@ dbusservicedir = @dbusservicedir@ @@ -219,11 +223,13 @@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ +imcvdir = @imcvdir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ ipsecdir = @ipsecdir@ ipsecgroup = @ipsecgroup@ +ipseclibdir = @ipseclibdir@ ipsecuser = @ipsecuser@ libcharon_plugins = @libcharon_plugins@ libdir = @libdir@ @@ -267,6 +273,7 @@ sharedstatedir = @sharedstatedir@ soup_CFLAGS = @soup_CFLAGS@ soup_LIBS = @soup_LIBS@ srcdir = @srcdir@ +starter_plugins = @starter_plugins@ strongswan_conf = @strongswan_conf@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c index fca46bfd2..5464568df 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -710,8 +710,8 @@ kernel_pfroute_net_t *kernel_pfroute_net_create() return NULL; } - this->job = callback_job_create((callback_job_cb_t)receive_events, - this, NULL, NULL); + 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); if (init_address_list(this) != SUCCESS) diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_plugin.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_plugin.c index 680caa5d0..09068b33e 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_plugin.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_plugin.c @@ -38,11 +38,20 @@ METHOD(plugin_t, get_name, char*, return "kernel-pfroute"; } +METHOD(plugin_t, get_features, int, + private_kernel_pfroute_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_CALLBACK(kernel_net_register, kernel_pfroute_net_create), + PLUGIN_PROVIDE(CUSTOM, "kernel-net"), + }; + *features = f; + return countof(f); +} + METHOD(plugin_t, destroy, void, private_kernel_pfroute_plugin_t *this) { - hydra->kernel_interface->remove_net_interface(hydra->kernel_interface, - (kernel_net_constructor_t)kernel_pfroute_net_create); free(this); } @@ -57,13 +66,11 @@ plugin_t *kernel_pfroute_plugin_create() .public = { .plugin = { .get_name = _get_name, - .reload = (void*)return_false, + .get_features = _get_features, .destroy = _destroy, }, }, ); - hydra->kernel_interface->add_net_interface(hydra->kernel_interface, - (kernel_net_constructor_t)kernel_pfroute_net_create); return &this->public.plugin; } diff --git a/src/libhydra/plugins/resolve/Makefile.in b/src/libhydra/plugins/resolve/Makefile.in index d3cda309a..41846ffe0 100644 --- a/src/libhydra/plugins/resolve/Makefile.in +++ b/src/libhydra/plugins/resolve/Makefile.in @@ -194,6 +194,9 @@ am__leading_dot = @am__leading_dot@ 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@ @@ -202,6 +205,7 @@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ c_plugins = @c_plugins@ +clearsilver_LIBS = @clearsilver_LIBS@ datadir = @datadir@ datarootdir = @datarootdir@ dbusservicedir = @dbusservicedir@ @@ -218,11 +222,13 @@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ +imcvdir = @imcvdir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ ipsecdir = @ipsecdir@ ipsecgroup = @ipsecgroup@ +ipseclibdir = @ipseclibdir@ ipsecuser = @ipsecuser@ libcharon_plugins = @libcharon_plugins@ libdir = @libdir@ @@ -266,6 +272,7 @@ sharedstatedir = @sharedstatedir@ soup_CFLAGS = @soup_CFLAGS@ soup_LIBS = @soup_LIBS@ srcdir = @srcdir@ +starter_plugins = @starter_plugins@ strongswan_conf = @strongswan_conf@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ diff --git a/src/libhydra/plugins/resolve/resolve_handler.c b/src/libhydra/plugins/resolve/resolve_handler.c index feb2fd05a..011ebbaaf 100644 --- a/src/libhydra/plugins/resolve/resolve_handler.c +++ b/src/libhydra/plugins/resolve/resolve_handler.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2012 Tobias Brunner * Copyright (C) 2009 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -15,12 +16,20 @@ #include "resolve_handler.h" +#include <sys/types.h> +#include <sys/stat.h> #include <unistd.h> #include <hydra.h> #include <debug.h> #include <threading/mutex.h> +/* path to resolvconf executable */ +#define RESOLVCONF_EXEC "/sbin/resolvconf" + +/* default prefix used for resolvconf interfaces (should have high prio) */ +#define RESOLVCONF_PREFIX "lo.inet.ipsec." + typedef struct private_resolve_handler_t private_resolve_handler_t; /** @@ -39,49 +48,40 @@ struct private_resolve_handler_t { char *file; /** + * use resolvconf instead of writing directly to resolv.conf + */ + bool use_resolvconf; + + /** + * prefix to be used for interface names sent to resolvconf + */ + char *iface_prefix; + + /** * Mutex to access file exclusively */ mutex_t *mutex; }; /** - * Implementation of attribute_handler_t.handle + * Writes the given nameserver to resolv.conf */ -static bool handle(private_resolve_handler_t *this, identification_t *server, - configuration_attribute_type_t type, chunk_t data) +static bool write_nameserver(private_resolve_handler_t *this, + identification_t *server, host_t *addr) { FILE *in, *out; char buf[1024]; - host_t *addr; size_t len; bool handled = FALSE; - switch (type) - { - case INTERNAL_IP4_DNS: - addr = host_create_from_chunk(AF_INET, data, 0); - break; - case INTERNAL_IP6_DNS: - addr = host_create_from_chunk(AF_INET6, data, 0); - break; - default: - return FALSE; - } - - if (!addr || addr->is_anyaddr(addr)) - { - DESTROY_IF(addr); - return FALSE; - } - this->mutex->lock(this->mutex); - in = fopen(this->file, "r"); /* allows us to stream from in to out */ unlink(this->file); out = fopen(this->file, "w"); if (out) { - fprintf(out, "nameserver %H # by strongSwan, from %Y\n", addr, server); + fprintf(out, "nameserver %H # by strongSwan, from %Y\n", addr, + server); DBG1(DBG_IKE, "installing DNS server %H to %s", addr, this->file); handled = TRUE; @@ -99,40 +99,17 @@ static bool handle(private_resolve_handler_t *this, identification_t *server, { fclose(in); } - this->mutex->unlock(this->mutex); - addr->destroy(addr); - - if (!handled) - { - DBG1(DBG_IKE, "adding DNS server failed", this->file); - } return handled; } /** - * Implementation of attribute_handler_t.release + * Removes the given nameserver from resolv.conf */ -static void release(private_resolve_handler_t *this, identification_t *server, - configuration_attribute_type_t type, chunk_t data) +static void remove_nameserver(private_resolve_handler_t *this, + identification_t *server, host_t *addr) { FILE *in, *out; char line[1024], matcher[512]; - host_t *addr; - int family; - - switch (type) - { - case INTERNAL_IP4_DNS: - family = AF_INET; - break; - case INTERNAL_IP6_DNS: - family = AF_INET6; - break; - default: - return; - } - - this->mutex->lock(this->mutex); in = fopen(this->file, "r"); if (in) @@ -142,7 +119,6 @@ static void release(private_resolve_handler_t *this, identification_t *server, out = fopen(this->file, "w"); if (out) { - addr = host_create_from_chunk(family, data, 0); snprintf(matcher, sizeof(matcher), "nameserver %H # by strongSwan, from %Y\n", addr, server); @@ -160,13 +136,129 @@ static void release(private_resolve_handler_t *this, identification_t *server, fputs(line, out); } } - addr->destroy(addr); fclose(out); } fclose(in); } +} +/** + * Add or remove the given nameserver by invoking resolvconf. + */ +static bool invoke_resolvconf(private_resolve_handler_t *this, + identification_t *server, host_t *addr, + bool install) +{ + char cmd[128]; + + /* we use the nameserver's IP address as part of the interface name to + * make them unique */ + if (snprintf(cmd, sizeof(cmd), "%s %s %s%H", RESOLVCONF_EXEC, + install ? "-a" : "-d", this->iface_prefix, addr) >= sizeof(cmd)) + { + return FALSE; + } + + if (install) + { + FILE *out; + + out = popen(cmd, "w"); + if (!out) + { + return FALSE; + } + DBG1(DBG_IKE, "installing DNS server %H via resolvconf", addr); + fprintf(out, "nameserver %H # by strongSwan, from %Y\n", addr, + server); + if (ferror(out) || pclose(out)) + { + return FALSE; + } + } + else + { + ignore_result(system(cmd)); + } + return TRUE; +} + +METHOD(attribute_handler_t, handle, bool, + private_resolve_handler_t *this, identification_t *server, + configuration_attribute_type_t type, chunk_t data) +{ + host_t *addr; + bool handled; + + switch (type) + { + case INTERNAL_IP4_DNS: + addr = host_create_from_chunk(AF_INET, data, 0); + break; + case INTERNAL_IP6_DNS: + addr = host_create_from_chunk(AF_INET6, data, 0); + break; + default: + return FALSE; + } + + if (!addr || addr->is_anyaddr(addr)) + { + DESTROY_IF(addr); + return FALSE; + } + + this->mutex->lock(this->mutex); + if (this->use_resolvconf) + { + handled = invoke_resolvconf(this, server, addr, TRUE); + } + else + { + handled = write_nameserver(this, server, addr); + } this->mutex->unlock(this->mutex); + addr->destroy(addr); + + if (!handled) + { + DBG1(DBG_IKE, "adding DNS server failed"); + } + return handled; +} + +METHOD(attribute_handler_t, release, void, + private_resolve_handler_t *this, identification_t *server, + configuration_attribute_type_t type, chunk_t data) +{ + host_t *addr; + int family; + + switch (type) + { + case INTERNAL_IP4_DNS: + family = AF_INET; + break; + case INTERNAL_IP6_DNS: + family = AF_INET6; + break; + default: + return; + } + addr = host_create_from_chunk(family, data, 0); + + this->mutex->lock(this->mutex); + if (this->use_resolvconf) + { + invoke_resolvconf(this, server, addr, FALSE); + } + else + { + remove_nameserver(this, server, addr); + } + this->mutex->unlock(this->mutex); + + addr->destroy(addr); } /** @@ -179,11 +271,9 @@ typedef struct { host_t *vip; } attribute_enumerator_t; -/** - * Implementation of create_attribute_enumerator().enumerate() - */ static bool attribute_enumerate(attribute_enumerator_t *this, - configuration_attribute_type_t *type, chunk_t *data) + configuration_attribute_type_t *type, + chunk_t *data) { switch (this->vip->get_family(this->vip)) { @@ -202,11 +292,8 @@ static bool attribute_enumerate(attribute_enumerator_t *this, return TRUE; } -/** - * Implementation of attribute_handler_t.create_attribute_enumerator - */ -static enumerator_t* create_attribute_enumerator(private_resolve_handler_t *this, - identification_t *server, host_t *vip) +METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t*, + private_resolve_handler_t *this, identification_t *server, host_t *vip) { if (vip) { @@ -222,10 +309,8 @@ static enumerator_t* create_attribute_enumerator(private_resolve_handler_t *this return enumerator_create_empty(); } -/** - * Implementation of resolve_handler_t.destroy. - */ -static void destroy(private_resolve_handler_t *this) +METHOD(resolve_handler_t, destroy, void, + private_resolve_handler_t *this) { this->mutex->destroy(this->mutex); free(this); @@ -236,16 +321,30 @@ static void destroy(private_resolve_handler_t *this) */ resolve_handler_t *resolve_handler_create() { - private_resolve_handler_t *this = malloc_thing(private_resolve_handler_t); + private_resolve_handler_t *this; + struct stat st; - this->public.handler.handle = (bool(*)(attribute_handler_t*, identification_t*, configuration_attribute_type_t, chunk_t))handle; - this->public.handler.release = (void(*)(attribute_handler_t*, identification_t*, configuration_attribute_type_t, chunk_t))release; - this->public.handler.create_attribute_enumerator = (enumerator_t*(*)(attribute_handler_t*, identification_t *server, host_t *vip))create_attribute_enumerator; - this->public.destroy = (void(*)(resolve_handler_t*))destroy; + INIT(this, + .public = { + .handler = { + .handle = _handle, + .release = _release, + .create_attribute_enumerator = _create_attribute_enumerator, + }, + .destroy = _destroy, + }, + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .file = lib->settings->get_str(lib->settings, "%s.plugins.resolve.file", + RESOLV_CONF, hydra->daemon), + ); - this->mutex = mutex_create(MUTEX_TYPE_DEFAULT); - this->file = lib->settings->get_str(lib->settings, - "%s.plugins.resolve.file", RESOLV_CONF, hydra->daemon); + if (stat(RESOLVCONF_EXEC, &st) == 0) + { + this->use_resolvconf = TRUE; + this->iface_prefix = lib->settings->get_str(lib->settings, + "%s.plugins.resolve.resolvconf.iface_prefix", + RESOLVCONF_PREFIX, hydra->daemon); + } return &this->public; } diff --git a/src/libhydra/plugins/resolve/resolve_plugin.c b/src/libhydra/plugins/resolve/resolve_plugin.c index d23d36127..f95827ed9 100644 --- a/src/libhydra/plugins/resolve/resolve_plugin.c +++ b/src/libhydra/plugins/resolve/resolve_plugin.c @@ -31,7 +31,7 @@ struct private_resolve_plugin_t { resolve_plugin_t public; /** - * The registerd DNS attribute handler + * The registered DNS attribute handler */ resolve_handler_t *handler; }; |