diff options
Diffstat (limited to 'src/libcharon/tests')
27 files changed, 5975 insertions, 24 deletions
diff --git a/src/libcharon/tests/Makefile.am b/src/libcharon/tests/Makefile.am index 0589269aa..b8670246b 100644 --- a/src/libcharon/tests/Makefile.am +++ b/src/libcharon/tests/Makefile.am @@ -1,8 +1,9 @@ -TESTS = libcharon_tests +TESTS = libcharon_tests exchange_tests check_PROGRAMS = $(TESTS) libcharon_tests_SOURCES = \ + suites/test_proposal.c \ suites/test_ike_cfg.c \ suites/test_mem_pool.c \ suites/test_message_chapoly.c \ @@ -21,3 +22,34 @@ libcharon_tests_LDADD = \ $(top_builddir)/src/libcharon/libcharon.la \ $(top_builddir)/src/libstrongswan/libstrongswan.la \ $(top_builddir)/src/libstrongswan/tests/libtest.la + + +exchange_tests_SOURCES = \ + suites/test_child_create.c \ + suites/test_child_delete.c \ + suites/test_child_rekey.c \ + suites/test_ike_delete.c \ + suites/test_ike_rekey.c \ + utils/exchange_test_asserts.h utils/exchange_test_asserts.c \ + utils/exchange_test_helper.h utils/exchange_test_helper.c \ + utils/job_asserts.h \ + utils/mock_dh.h utils/mock_dh.c \ + utils/mock_ipsec.h utils/mock_ipsec.c \ + utils/mock_nonce_gen.h utils/mock_nonce_gen.c \ + utils/mock_sender.h utils/mock_sender.c \ + utils/sa_asserts.h \ + exchange_tests.h exchange_tests.c + +exchange_tests_CFLAGS = \ + -I$(top_srcdir)/src/libcharon \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libstrongswan/tests \ + -DPLUGINDIR=\""$(abs_top_builddir)/src/libstrongswan/plugins\"" \ + -DPLUGINS=\""${s_plugins}\"" \ + @COVERAGE_CFLAGS@ + +exchange_tests_LDFLAGS = @COVERAGE_LDFLAGS@ +exchange_tests_LDADD = \ + $(top_builddir)/src/libcharon/libcharon.la \ + $(top_builddir)/src/libstrongswan/libstrongswan.la \ + $(top_builddir)/src/libstrongswan/tests/libtest.la diff --git a/src/libcharon/tests/Makefile.in b/src/libcharon/tests/Makefile.in index 87dea161a..7a0d34292 100644 --- a/src/libcharon/tests/Makefile.in +++ b/src/libcharon/tests/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. +# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2013 Free Software Foundation, Inc. +# Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -14,7 +14,17 @@ @SET_MAKE@ VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -77,11 +87,9 @@ PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ -TESTS = libcharon_tests$(EXEEXT) +TESTS = libcharon_tests$(EXEEXT) exchange_tests$(EXEEXT) check_PROGRAMS = $(am__EXEEXT_1) subdir = src/libcharon/tests -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(top_srcdir)/depcomp ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/config/libtool.m4 \ $(top_srcdir)/m4/config/ltoptions.m4 \ @@ -95,13 +103,41 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/config/libtool.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = -am__EXEEXT_1 = libcharon_tests$(EXEEXT) +am__EXEEXT_1 = libcharon_tests$(EXEEXT) exchange_tests$(EXEEXT) am__dirstamp = $(am__leading_dot)dirstamp +am_exchange_tests_OBJECTS = \ + suites/exchange_tests-test_child_create.$(OBJEXT) \ + suites/exchange_tests-test_child_delete.$(OBJEXT) \ + suites/exchange_tests-test_child_rekey.$(OBJEXT) \ + suites/exchange_tests-test_ike_delete.$(OBJEXT) \ + suites/exchange_tests-test_ike_rekey.$(OBJEXT) \ + utils/exchange_tests-exchange_test_asserts.$(OBJEXT) \ + utils/exchange_tests-exchange_test_helper.$(OBJEXT) \ + utils/exchange_tests-mock_dh.$(OBJEXT) \ + utils/exchange_tests-mock_ipsec.$(OBJEXT) \ + utils/exchange_tests-mock_nonce_gen.$(OBJEXT) \ + utils/exchange_tests-mock_sender.$(OBJEXT) \ + exchange_tests-exchange_tests.$(OBJEXT) +exchange_tests_OBJECTS = $(am_exchange_tests_OBJECTS) +exchange_tests_DEPENDENCIES = \ + $(top_builddir)/src/libcharon/libcharon.la \ + $(top_builddir)/src/libstrongswan/libstrongswan.la \ + $(top_builddir)/src/libstrongswan/tests/libtest.la +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +exchange_tests_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(exchange_tests_CFLAGS) $(CFLAGS) $(exchange_tests_LDFLAGS) \ + $(LDFLAGS) -o $@ am_libcharon_tests_OBJECTS = \ + suites/libcharon_tests-test_proposal.$(OBJEXT) \ suites/libcharon_tests-test_ike_cfg.$(OBJEXT) \ suites/libcharon_tests-test_mem_pool.$(OBJEXT) \ suites/libcharon_tests-test_message_chapoly.$(OBJEXT) \ @@ -111,10 +147,6 @@ libcharon_tests_DEPENDENCIES = \ $(top_builddir)/src/libcharon/libcharon.la \ $(top_builddir)/src/libstrongswan/libstrongswan.la \ $(top_builddir)/src/libstrongswan/tests/libtest.la -AM_V_lt = $(am__v_lt_@AM_V@) -am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) -am__v_lt_0 = --silent -am__v_lt_1 = libcharon_tests_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(libcharon_tests_CFLAGS) $(CFLAGS) $(libcharon_tests_LDFLAGS) \ @@ -153,8 +185,8 @@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = -SOURCES = $(libcharon_tests_SOURCES) -DIST_SOURCES = $(libcharon_tests_SOURCES) +SOURCES = $(exchange_tests_SOURCES) $(libcharon_tests_SOURCES) +DIST_SOURCES = $(exchange_tests_SOURCES) $(libcharon_tests_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ @@ -201,12 +233,14 @@ am__tty_colors = { \ std='[m'; \ fi; \ } +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ALLOCA = @ALLOCA@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ +ATOMICLIB = @ATOMICLIB@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -256,6 +290,7 @@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ @@ -290,6 +325,7 @@ PTHREADLIB = @PTHREADLIB@ PYTHON = @PYTHON@ PYTHONEGGINSTALLDIR = @PYTHONEGGINSTALLDIR@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PACKAGE_VERSION = @PYTHON_PACKAGE_VERSION@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ @@ -401,6 +437,7 @@ random_device = @random_device@ resolv_conf = @resolv_conf@ routing_table = @routing_table@ routing_table_prio = @routing_table_prio@ +runstatedir = @runstatedir@ s_plugins = @s_plugins@ sbindir = @sbindir@ scepclient_plugins = @scepclient_plugins@ @@ -430,6 +467,7 @@ urandom_device = @urandom_device@ xml_CFLAGS = @xml_CFLAGS@ xml_LIBS = @xml_LIBS@ libcharon_tests_SOURCES = \ + suites/test_proposal.c \ suites/test_ike_cfg.c \ suites/test_mem_pool.c \ suites/test_message_chapoly.c \ @@ -449,6 +487,36 @@ libcharon_tests_LDADD = \ $(top_builddir)/src/libstrongswan/libstrongswan.la \ $(top_builddir)/src/libstrongswan/tests/libtest.la +exchange_tests_SOURCES = \ + suites/test_child_create.c \ + suites/test_child_delete.c \ + suites/test_child_rekey.c \ + suites/test_ike_delete.c \ + suites/test_ike_rekey.c \ + utils/exchange_test_asserts.h utils/exchange_test_asserts.c \ + utils/exchange_test_helper.h utils/exchange_test_helper.c \ + utils/job_asserts.h \ + utils/mock_dh.h utils/mock_dh.c \ + utils/mock_ipsec.h utils/mock_ipsec.c \ + utils/mock_nonce_gen.h utils/mock_nonce_gen.c \ + utils/mock_sender.h utils/mock_sender.c \ + utils/sa_asserts.h \ + exchange_tests.h exchange_tests.c + +exchange_tests_CFLAGS = \ + -I$(top_srcdir)/src/libcharon \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libstrongswan/tests \ + -DPLUGINDIR=\""$(abs_top_builddir)/src/libstrongswan/plugins\"" \ + -DPLUGINS=\""${s_plugins}\"" \ + @COVERAGE_CFLAGS@ + +exchange_tests_LDFLAGS = @COVERAGE_LDFLAGS@ +exchange_tests_LDADD = \ + $(top_builddir)/src/libcharon/libcharon.la \ + $(top_builddir)/src/libstrongswan/libstrongswan.la \ + $(top_builddir)/src/libstrongswan/tests/libtest.la + all: all-am .SUFFIXES: @@ -465,7 +533,6 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/libcharon/tests/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu src/libcharon/tests/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -498,6 +565,40 @@ suites/$(am__dirstamp): suites/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) suites/$(DEPDIR) @: > suites/$(DEPDIR)/$(am__dirstamp) +suites/exchange_tests-test_child_create.$(OBJEXT): \ + suites/$(am__dirstamp) suites/$(DEPDIR)/$(am__dirstamp) +suites/exchange_tests-test_child_delete.$(OBJEXT): \ + suites/$(am__dirstamp) suites/$(DEPDIR)/$(am__dirstamp) +suites/exchange_tests-test_child_rekey.$(OBJEXT): \ + suites/$(am__dirstamp) suites/$(DEPDIR)/$(am__dirstamp) +suites/exchange_tests-test_ike_delete.$(OBJEXT): \ + suites/$(am__dirstamp) suites/$(DEPDIR)/$(am__dirstamp) +suites/exchange_tests-test_ike_rekey.$(OBJEXT): \ + suites/$(am__dirstamp) suites/$(DEPDIR)/$(am__dirstamp) +utils/$(am__dirstamp): + @$(MKDIR_P) utils + @: > utils/$(am__dirstamp) +utils/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) utils/$(DEPDIR) + @: > utils/$(DEPDIR)/$(am__dirstamp) +utils/exchange_tests-exchange_test_asserts.$(OBJEXT): \ + utils/$(am__dirstamp) utils/$(DEPDIR)/$(am__dirstamp) +utils/exchange_tests-exchange_test_helper.$(OBJEXT): \ + utils/$(am__dirstamp) utils/$(DEPDIR)/$(am__dirstamp) +utils/exchange_tests-mock_dh.$(OBJEXT): utils/$(am__dirstamp) \ + utils/$(DEPDIR)/$(am__dirstamp) +utils/exchange_tests-mock_ipsec.$(OBJEXT): utils/$(am__dirstamp) \ + utils/$(DEPDIR)/$(am__dirstamp) +utils/exchange_tests-mock_nonce_gen.$(OBJEXT): utils/$(am__dirstamp) \ + utils/$(DEPDIR)/$(am__dirstamp) +utils/exchange_tests-mock_sender.$(OBJEXT): utils/$(am__dirstamp) \ + utils/$(DEPDIR)/$(am__dirstamp) + +exchange_tests$(EXEEXT): $(exchange_tests_OBJECTS) $(exchange_tests_DEPENDENCIES) $(EXTRA_exchange_tests_DEPENDENCIES) + @rm -f exchange_tests$(EXEEXT) + $(AM_V_CCLD)$(exchange_tests_LINK) $(exchange_tests_OBJECTS) $(exchange_tests_LDADD) $(LIBS) +suites/libcharon_tests-test_proposal.$(OBJEXT): \ + suites/$(am__dirstamp) suites/$(DEPDIR)/$(am__dirstamp) suites/libcharon_tests-test_ike_cfg.$(OBJEXT): suites/$(am__dirstamp) \ suites/$(DEPDIR)/$(am__dirstamp) suites/libcharon_tests-test_mem_pool.$(OBJEXT): \ @@ -512,14 +613,28 @@ libcharon_tests$(EXEEXT): $(libcharon_tests_OBJECTS) $(libcharon_tests_DEPENDENC mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f suites/*.$(OBJEXT) + -rm -f utils/*.$(OBJEXT) distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/exchange_tests-exchange_tests.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcharon_tests-libcharon_tests.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@suites/$(DEPDIR)/exchange_tests-test_child_create.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@suites/$(DEPDIR)/exchange_tests-test_child_delete.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@suites/$(DEPDIR)/exchange_tests-test_child_rekey.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@suites/$(DEPDIR)/exchange_tests-test_ike_delete.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@suites/$(DEPDIR)/exchange_tests-test_ike_rekey.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@suites/$(DEPDIR)/libcharon_tests-test_ike_cfg.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@suites/$(DEPDIR)/libcharon_tests-test_mem_pool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@suites/$(DEPDIR)/libcharon_tests-test_message_chapoly.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@suites/$(DEPDIR)/libcharon_tests-test_proposal.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/exchange_tests-exchange_test_asserts.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/exchange_tests-exchange_test_helper.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/exchange_tests-mock_dh.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/exchange_tests-mock_ipsec.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/exchange_tests-mock_nonce_gen.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/exchange_tests-mock_sender.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @@ -545,6 +660,188 @@ distclean-compile: @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< +suites/exchange_tests-test_child_create.o: suites/test_child_create.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT suites/exchange_tests-test_child_create.o -MD -MP -MF suites/$(DEPDIR)/exchange_tests-test_child_create.Tpo -c -o suites/exchange_tests-test_child_create.o `test -f 'suites/test_child_create.c' || echo '$(srcdir)/'`suites/test_child_create.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/exchange_tests-test_child_create.Tpo suites/$(DEPDIR)/exchange_tests-test_child_create.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_child_create.c' object='suites/exchange_tests-test_child_create.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o suites/exchange_tests-test_child_create.o `test -f 'suites/test_child_create.c' || echo '$(srcdir)/'`suites/test_child_create.c + +suites/exchange_tests-test_child_create.obj: suites/test_child_create.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT suites/exchange_tests-test_child_create.obj -MD -MP -MF suites/$(DEPDIR)/exchange_tests-test_child_create.Tpo -c -o suites/exchange_tests-test_child_create.obj `if test -f 'suites/test_child_create.c'; then $(CYGPATH_W) 'suites/test_child_create.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_child_create.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/exchange_tests-test_child_create.Tpo suites/$(DEPDIR)/exchange_tests-test_child_create.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_child_create.c' object='suites/exchange_tests-test_child_create.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o suites/exchange_tests-test_child_create.obj `if test -f 'suites/test_child_create.c'; then $(CYGPATH_W) 'suites/test_child_create.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_child_create.c'; fi` + +suites/exchange_tests-test_child_delete.o: suites/test_child_delete.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT suites/exchange_tests-test_child_delete.o -MD -MP -MF suites/$(DEPDIR)/exchange_tests-test_child_delete.Tpo -c -o suites/exchange_tests-test_child_delete.o `test -f 'suites/test_child_delete.c' || echo '$(srcdir)/'`suites/test_child_delete.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/exchange_tests-test_child_delete.Tpo suites/$(DEPDIR)/exchange_tests-test_child_delete.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_child_delete.c' object='suites/exchange_tests-test_child_delete.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o suites/exchange_tests-test_child_delete.o `test -f 'suites/test_child_delete.c' || echo '$(srcdir)/'`suites/test_child_delete.c + +suites/exchange_tests-test_child_delete.obj: suites/test_child_delete.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT suites/exchange_tests-test_child_delete.obj -MD -MP -MF suites/$(DEPDIR)/exchange_tests-test_child_delete.Tpo -c -o suites/exchange_tests-test_child_delete.obj `if test -f 'suites/test_child_delete.c'; then $(CYGPATH_W) 'suites/test_child_delete.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_child_delete.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/exchange_tests-test_child_delete.Tpo suites/$(DEPDIR)/exchange_tests-test_child_delete.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_child_delete.c' object='suites/exchange_tests-test_child_delete.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o suites/exchange_tests-test_child_delete.obj `if test -f 'suites/test_child_delete.c'; then $(CYGPATH_W) 'suites/test_child_delete.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_child_delete.c'; fi` + +suites/exchange_tests-test_child_rekey.o: suites/test_child_rekey.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT suites/exchange_tests-test_child_rekey.o -MD -MP -MF suites/$(DEPDIR)/exchange_tests-test_child_rekey.Tpo -c -o suites/exchange_tests-test_child_rekey.o `test -f 'suites/test_child_rekey.c' || echo '$(srcdir)/'`suites/test_child_rekey.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/exchange_tests-test_child_rekey.Tpo suites/$(DEPDIR)/exchange_tests-test_child_rekey.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_child_rekey.c' object='suites/exchange_tests-test_child_rekey.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o suites/exchange_tests-test_child_rekey.o `test -f 'suites/test_child_rekey.c' || echo '$(srcdir)/'`suites/test_child_rekey.c + +suites/exchange_tests-test_child_rekey.obj: suites/test_child_rekey.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT suites/exchange_tests-test_child_rekey.obj -MD -MP -MF suites/$(DEPDIR)/exchange_tests-test_child_rekey.Tpo -c -o suites/exchange_tests-test_child_rekey.obj `if test -f 'suites/test_child_rekey.c'; then $(CYGPATH_W) 'suites/test_child_rekey.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_child_rekey.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/exchange_tests-test_child_rekey.Tpo suites/$(DEPDIR)/exchange_tests-test_child_rekey.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_child_rekey.c' object='suites/exchange_tests-test_child_rekey.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o suites/exchange_tests-test_child_rekey.obj `if test -f 'suites/test_child_rekey.c'; then $(CYGPATH_W) 'suites/test_child_rekey.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_child_rekey.c'; fi` + +suites/exchange_tests-test_ike_delete.o: suites/test_ike_delete.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT suites/exchange_tests-test_ike_delete.o -MD -MP -MF suites/$(DEPDIR)/exchange_tests-test_ike_delete.Tpo -c -o suites/exchange_tests-test_ike_delete.o `test -f 'suites/test_ike_delete.c' || echo '$(srcdir)/'`suites/test_ike_delete.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/exchange_tests-test_ike_delete.Tpo suites/$(DEPDIR)/exchange_tests-test_ike_delete.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_ike_delete.c' object='suites/exchange_tests-test_ike_delete.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o suites/exchange_tests-test_ike_delete.o `test -f 'suites/test_ike_delete.c' || echo '$(srcdir)/'`suites/test_ike_delete.c + +suites/exchange_tests-test_ike_delete.obj: suites/test_ike_delete.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT suites/exchange_tests-test_ike_delete.obj -MD -MP -MF suites/$(DEPDIR)/exchange_tests-test_ike_delete.Tpo -c -o suites/exchange_tests-test_ike_delete.obj `if test -f 'suites/test_ike_delete.c'; then $(CYGPATH_W) 'suites/test_ike_delete.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_ike_delete.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/exchange_tests-test_ike_delete.Tpo suites/$(DEPDIR)/exchange_tests-test_ike_delete.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_ike_delete.c' object='suites/exchange_tests-test_ike_delete.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o suites/exchange_tests-test_ike_delete.obj `if test -f 'suites/test_ike_delete.c'; then $(CYGPATH_W) 'suites/test_ike_delete.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_ike_delete.c'; fi` + +suites/exchange_tests-test_ike_rekey.o: suites/test_ike_rekey.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT suites/exchange_tests-test_ike_rekey.o -MD -MP -MF suites/$(DEPDIR)/exchange_tests-test_ike_rekey.Tpo -c -o suites/exchange_tests-test_ike_rekey.o `test -f 'suites/test_ike_rekey.c' || echo '$(srcdir)/'`suites/test_ike_rekey.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/exchange_tests-test_ike_rekey.Tpo suites/$(DEPDIR)/exchange_tests-test_ike_rekey.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_ike_rekey.c' object='suites/exchange_tests-test_ike_rekey.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o suites/exchange_tests-test_ike_rekey.o `test -f 'suites/test_ike_rekey.c' || echo '$(srcdir)/'`suites/test_ike_rekey.c + +suites/exchange_tests-test_ike_rekey.obj: suites/test_ike_rekey.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT suites/exchange_tests-test_ike_rekey.obj -MD -MP -MF suites/$(DEPDIR)/exchange_tests-test_ike_rekey.Tpo -c -o suites/exchange_tests-test_ike_rekey.obj `if test -f 'suites/test_ike_rekey.c'; then $(CYGPATH_W) 'suites/test_ike_rekey.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_ike_rekey.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/exchange_tests-test_ike_rekey.Tpo suites/$(DEPDIR)/exchange_tests-test_ike_rekey.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_ike_rekey.c' object='suites/exchange_tests-test_ike_rekey.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o suites/exchange_tests-test_ike_rekey.obj `if test -f 'suites/test_ike_rekey.c'; then $(CYGPATH_W) 'suites/test_ike_rekey.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_ike_rekey.c'; fi` + +utils/exchange_tests-exchange_test_asserts.o: utils/exchange_test_asserts.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT utils/exchange_tests-exchange_test_asserts.o -MD -MP -MF utils/$(DEPDIR)/exchange_tests-exchange_test_asserts.Tpo -c -o utils/exchange_tests-exchange_test_asserts.o `test -f 'utils/exchange_test_asserts.c' || echo '$(srcdir)/'`utils/exchange_test_asserts.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/$(DEPDIR)/exchange_tests-exchange_test_asserts.Tpo utils/$(DEPDIR)/exchange_tests-exchange_test_asserts.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/exchange_test_asserts.c' object='utils/exchange_tests-exchange_test_asserts.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o utils/exchange_tests-exchange_test_asserts.o `test -f 'utils/exchange_test_asserts.c' || echo '$(srcdir)/'`utils/exchange_test_asserts.c + +utils/exchange_tests-exchange_test_asserts.obj: utils/exchange_test_asserts.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT utils/exchange_tests-exchange_test_asserts.obj -MD -MP -MF utils/$(DEPDIR)/exchange_tests-exchange_test_asserts.Tpo -c -o utils/exchange_tests-exchange_test_asserts.obj `if test -f 'utils/exchange_test_asserts.c'; then $(CYGPATH_W) 'utils/exchange_test_asserts.c'; else $(CYGPATH_W) '$(srcdir)/utils/exchange_test_asserts.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/$(DEPDIR)/exchange_tests-exchange_test_asserts.Tpo utils/$(DEPDIR)/exchange_tests-exchange_test_asserts.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/exchange_test_asserts.c' object='utils/exchange_tests-exchange_test_asserts.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o utils/exchange_tests-exchange_test_asserts.obj `if test -f 'utils/exchange_test_asserts.c'; then $(CYGPATH_W) 'utils/exchange_test_asserts.c'; else $(CYGPATH_W) '$(srcdir)/utils/exchange_test_asserts.c'; fi` + +utils/exchange_tests-exchange_test_helper.o: utils/exchange_test_helper.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT utils/exchange_tests-exchange_test_helper.o -MD -MP -MF utils/$(DEPDIR)/exchange_tests-exchange_test_helper.Tpo -c -o utils/exchange_tests-exchange_test_helper.o `test -f 'utils/exchange_test_helper.c' || echo '$(srcdir)/'`utils/exchange_test_helper.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/$(DEPDIR)/exchange_tests-exchange_test_helper.Tpo utils/$(DEPDIR)/exchange_tests-exchange_test_helper.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/exchange_test_helper.c' object='utils/exchange_tests-exchange_test_helper.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o utils/exchange_tests-exchange_test_helper.o `test -f 'utils/exchange_test_helper.c' || echo '$(srcdir)/'`utils/exchange_test_helper.c + +utils/exchange_tests-exchange_test_helper.obj: utils/exchange_test_helper.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT utils/exchange_tests-exchange_test_helper.obj -MD -MP -MF utils/$(DEPDIR)/exchange_tests-exchange_test_helper.Tpo -c -o utils/exchange_tests-exchange_test_helper.obj `if test -f 'utils/exchange_test_helper.c'; then $(CYGPATH_W) 'utils/exchange_test_helper.c'; else $(CYGPATH_W) '$(srcdir)/utils/exchange_test_helper.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/$(DEPDIR)/exchange_tests-exchange_test_helper.Tpo utils/$(DEPDIR)/exchange_tests-exchange_test_helper.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/exchange_test_helper.c' object='utils/exchange_tests-exchange_test_helper.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o utils/exchange_tests-exchange_test_helper.obj `if test -f 'utils/exchange_test_helper.c'; then $(CYGPATH_W) 'utils/exchange_test_helper.c'; else $(CYGPATH_W) '$(srcdir)/utils/exchange_test_helper.c'; fi` + +utils/exchange_tests-mock_dh.o: utils/mock_dh.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT utils/exchange_tests-mock_dh.o -MD -MP -MF utils/$(DEPDIR)/exchange_tests-mock_dh.Tpo -c -o utils/exchange_tests-mock_dh.o `test -f 'utils/mock_dh.c' || echo '$(srcdir)/'`utils/mock_dh.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/$(DEPDIR)/exchange_tests-mock_dh.Tpo utils/$(DEPDIR)/exchange_tests-mock_dh.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/mock_dh.c' object='utils/exchange_tests-mock_dh.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o utils/exchange_tests-mock_dh.o `test -f 'utils/mock_dh.c' || echo '$(srcdir)/'`utils/mock_dh.c + +utils/exchange_tests-mock_dh.obj: utils/mock_dh.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT utils/exchange_tests-mock_dh.obj -MD -MP -MF utils/$(DEPDIR)/exchange_tests-mock_dh.Tpo -c -o utils/exchange_tests-mock_dh.obj `if test -f 'utils/mock_dh.c'; then $(CYGPATH_W) 'utils/mock_dh.c'; else $(CYGPATH_W) '$(srcdir)/utils/mock_dh.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/$(DEPDIR)/exchange_tests-mock_dh.Tpo utils/$(DEPDIR)/exchange_tests-mock_dh.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/mock_dh.c' object='utils/exchange_tests-mock_dh.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o utils/exchange_tests-mock_dh.obj `if test -f 'utils/mock_dh.c'; then $(CYGPATH_W) 'utils/mock_dh.c'; else $(CYGPATH_W) '$(srcdir)/utils/mock_dh.c'; fi` + +utils/exchange_tests-mock_ipsec.o: utils/mock_ipsec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT utils/exchange_tests-mock_ipsec.o -MD -MP -MF utils/$(DEPDIR)/exchange_tests-mock_ipsec.Tpo -c -o utils/exchange_tests-mock_ipsec.o `test -f 'utils/mock_ipsec.c' || echo '$(srcdir)/'`utils/mock_ipsec.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/$(DEPDIR)/exchange_tests-mock_ipsec.Tpo utils/$(DEPDIR)/exchange_tests-mock_ipsec.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/mock_ipsec.c' object='utils/exchange_tests-mock_ipsec.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o utils/exchange_tests-mock_ipsec.o `test -f 'utils/mock_ipsec.c' || echo '$(srcdir)/'`utils/mock_ipsec.c + +utils/exchange_tests-mock_ipsec.obj: utils/mock_ipsec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT utils/exchange_tests-mock_ipsec.obj -MD -MP -MF utils/$(DEPDIR)/exchange_tests-mock_ipsec.Tpo -c -o utils/exchange_tests-mock_ipsec.obj `if test -f 'utils/mock_ipsec.c'; then $(CYGPATH_W) 'utils/mock_ipsec.c'; else $(CYGPATH_W) '$(srcdir)/utils/mock_ipsec.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/$(DEPDIR)/exchange_tests-mock_ipsec.Tpo utils/$(DEPDIR)/exchange_tests-mock_ipsec.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/mock_ipsec.c' object='utils/exchange_tests-mock_ipsec.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o utils/exchange_tests-mock_ipsec.obj `if test -f 'utils/mock_ipsec.c'; then $(CYGPATH_W) 'utils/mock_ipsec.c'; else $(CYGPATH_W) '$(srcdir)/utils/mock_ipsec.c'; fi` + +utils/exchange_tests-mock_nonce_gen.o: utils/mock_nonce_gen.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT utils/exchange_tests-mock_nonce_gen.o -MD -MP -MF utils/$(DEPDIR)/exchange_tests-mock_nonce_gen.Tpo -c -o utils/exchange_tests-mock_nonce_gen.o `test -f 'utils/mock_nonce_gen.c' || echo '$(srcdir)/'`utils/mock_nonce_gen.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/$(DEPDIR)/exchange_tests-mock_nonce_gen.Tpo utils/$(DEPDIR)/exchange_tests-mock_nonce_gen.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/mock_nonce_gen.c' object='utils/exchange_tests-mock_nonce_gen.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o utils/exchange_tests-mock_nonce_gen.o `test -f 'utils/mock_nonce_gen.c' || echo '$(srcdir)/'`utils/mock_nonce_gen.c + +utils/exchange_tests-mock_nonce_gen.obj: utils/mock_nonce_gen.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT utils/exchange_tests-mock_nonce_gen.obj -MD -MP -MF utils/$(DEPDIR)/exchange_tests-mock_nonce_gen.Tpo -c -o utils/exchange_tests-mock_nonce_gen.obj `if test -f 'utils/mock_nonce_gen.c'; then $(CYGPATH_W) 'utils/mock_nonce_gen.c'; else $(CYGPATH_W) '$(srcdir)/utils/mock_nonce_gen.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/$(DEPDIR)/exchange_tests-mock_nonce_gen.Tpo utils/$(DEPDIR)/exchange_tests-mock_nonce_gen.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/mock_nonce_gen.c' object='utils/exchange_tests-mock_nonce_gen.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o utils/exchange_tests-mock_nonce_gen.obj `if test -f 'utils/mock_nonce_gen.c'; then $(CYGPATH_W) 'utils/mock_nonce_gen.c'; else $(CYGPATH_W) '$(srcdir)/utils/mock_nonce_gen.c'; fi` + +utils/exchange_tests-mock_sender.o: utils/mock_sender.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT utils/exchange_tests-mock_sender.o -MD -MP -MF utils/$(DEPDIR)/exchange_tests-mock_sender.Tpo -c -o utils/exchange_tests-mock_sender.o `test -f 'utils/mock_sender.c' || echo '$(srcdir)/'`utils/mock_sender.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/$(DEPDIR)/exchange_tests-mock_sender.Tpo utils/$(DEPDIR)/exchange_tests-mock_sender.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/mock_sender.c' object='utils/exchange_tests-mock_sender.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o utils/exchange_tests-mock_sender.o `test -f 'utils/mock_sender.c' || echo '$(srcdir)/'`utils/mock_sender.c + +utils/exchange_tests-mock_sender.obj: utils/mock_sender.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT utils/exchange_tests-mock_sender.obj -MD -MP -MF utils/$(DEPDIR)/exchange_tests-mock_sender.Tpo -c -o utils/exchange_tests-mock_sender.obj `if test -f 'utils/mock_sender.c'; then $(CYGPATH_W) 'utils/mock_sender.c'; else $(CYGPATH_W) '$(srcdir)/utils/mock_sender.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/$(DEPDIR)/exchange_tests-mock_sender.Tpo utils/$(DEPDIR)/exchange_tests-mock_sender.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/mock_sender.c' object='utils/exchange_tests-mock_sender.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o utils/exchange_tests-mock_sender.obj `if test -f 'utils/mock_sender.c'; then $(CYGPATH_W) 'utils/mock_sender.c'; else $(CYGPATH_W) '$(srcdir)/utils/mock_sender.c'; fi` + +exchange_tests-exchange_tests.o: exchange_tests.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT exchange_tests-exchange_tests.o -MD -MP -MF $(DEPDIR)/exchange_tests-exchange_tests.Tpo -c -o exchange_tests-exchange_tests.o `test -f 'exchange_tests.c' || echo '$(srcdir)/'`exchange_tests.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/exchange_tests-exchange_tests.Tpo $(DEPDIR)/exchange_tests-exchange_tests.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exchange_tests.c' object='exchange_tests-exchange_tests.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o exchange_tests-exchange_tests.o `test -f 'exchange_tests.c' || echo '$(srcdir)/'`exchange_tests.c + +exchange_tests-exchange_tests.obj: exchange_tests.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -MT exchange_tests-exchange_tests.obj -MD -MP -MF $(DEPDIR)/exchange_tests-exchange_tests.Tpo -c -o exchange_tests-exchange_tests.obj `if test -f 'exchange_tests.c'; then $(CYGPATH_W) 'exchange_tests.c'; else $(CYGPATH_W) '$(srcdir)/exchange_tests.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/exchange_tests-exchange_tests.Tpo $(DEPDIR)/exchange_tests-exchange_tests.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exchange_tests.c' object='exchange_tests-exchange_tests.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(exchange_tests_CFLAGS) $(CFLAGS) -c -o exchange_tests-exchange_tests.obj `if test -f 'exchange_tests.c'; then $(CYGPATH_W) 'exchange_tests.c'; else $(CYGPATH_W) '$(srcdir)/exchange_tests.c'; fi` + +suites/libcharon_tests-test_proposal.o: suites/test_proposal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcharon_tests_CFLAGS) $(CFLAGS) -MT suites/libcharon_tests-test_proposal.o -MD -MP -MF suites/$(DEPDIR)/libcharon_tests-test_proposal.Tpo -c -o suites/libcharon_tests-test_proposal.o `test -f 'suites/test_proposal.c' || echo '$(srcdir)/'`suites/test_proposal.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/libcharon_tests-test_proposal.Tpo suites/$(DEPDIR)/libcharon_tests-test_proposal.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_proposal.c' object='suites/libcharon_tests-test_proposal.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcharon_tests_CFLAGS) $(CFLAGS) -c -o suites/libcharon_tests-test_proposal.o `test -f 'suites/test_proposal.c' || echo '$(srcdir)/'`suites/test_proposal.c + +suites/libcharon_tests-test_proposal.obj: suites/test_proposal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcharon_tests_CFLAGS) $(CFLAGS) -MT suites/libcharon_tests-test_proposal.obj -MD -MP -MF suites/$(DEPDIR)/libcharon_tests-test_proposal.Tpo -c -o suites/libcharon_tests-test_proposal.obj `if test -f 'suites/test_proposal.c'; then $(CYGPATH_W) 'suites/test_proposal.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_proposal.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/libcharon_tests-test_proposal.Tpo suites/$(DEPDIR)/libcharon_tests-test_proposal.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_proposal.c' object='suites/libcharon_tests-test_proposal.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcharon_tests_CFLAGS) $(CFLAGS) -c -o suites/libcharon_tests-test_proposal.obj `if test -f 'suites/test_proposal.c'; then $(CYGPATH_W) 'suites/test_proposal.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_proposal.c'; fi` + suites/libcharon_tests-test_ike_cfg.o: suites/test_ike_cfg.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcharon_tests_CFLAGS) $(CFLAGS) -MT suites/libcharon_tests-test_ike_cfg.o -MD -MP -MF suites/$(DEPDIR)/libcharon_tests-test_ike_cfg.Tpo -c -o suites/libcharon_tests-test_ike_cfg.o `test -f 'suites/test_ike_cfg.c' || echo '$(srcdir)/'`suites/test_ike_cfg.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/libcharon_tests-test_ike_cfg.Tpo suites/$(DEPDIR)/libcharon_tests-test_ike_cfg.Po @@ -816,6 +1113,8 @@ distclean-generic: -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -rm -f suites/$(DEPDIR)/$(am__dirstamp) -rm -f suites/$(am__dirstamp) + -rm -f utils/$(DEPDIR)/$(am__dirstamp) + -rm -f utils/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @@ -826,7 +1125,7 @@ clean-am: clean-checkPROGRAMS clean-generic clean-libtool \ mostlyclean-am distclean: distclean-am - -rm -rf ./$(DEPDIR) suites/$(DEPDIR) + -rm -rf ./$(DEPDIR) suites/$(DEPDIR) utils/$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags @@ -872,7 +1171,7 @@ install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am - -rm -rf ./$(DEPDIR) suites/$(DEPDIR) + -rm -rf ./$(DEPDIR) suites/$(DEPDIR) utils/$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic @@ -907,6 +1206,8 @@ uninstall-am: mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am +.PRECIOUS: Makefile + # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/src/libcharon/tests/exchange_tests.c b/src/libcharon/tests/exchange_tests.c new file mode 100644 index 000000000..eab50a875 --- /dev/null +++ b/src/libcharon/tests/exchange_tests.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * 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 <test_runner.h> +#include <daemon.h> + +#include "utils/exchange_test_helper.h" + +/* declare test suite constructors */ +#define TEST_SUITE(x) test_suite_t* x(); +#define TEST_SUITE_DEPEND(x, ...) TEST_SUITE(x) +#include "exchange_tests.h" +#undef TEST_SUITE +#undef TEST_SUITE_DEPEND + +static test_configuration_t tests[] = { +#define TEST_SUITE(x) \ + { .suite = x, }, +#define TEST_SUITE_DEPEND(x, type, ...) \ + { .suite = x, .feature = PLUGIN_DEPENDS(type, __VA_ARGS__) }, +#include "exchange_tests.h" + { .suite = NULL, } +}; + +static bool test_runner_init(bool init) +{ + if (init) + { + char *plugins, *plugindir; + + libcharon_init(); + + plugins = getenv("TESTS_PLUGINS") ?: + lib->settings->get_str(lib->settings, + "tests.load", PLUGINS); + plugindir = lib->settings->get_str(lib->settings, + "tests.plugindir", PLUGINDIR); + plugin_loader_add_plugindirs(plugindir, plugins); + exchange_test_helper_init(plugins); + } + else + { + exchange_test_helper_deinit(); + libcharon_deinit(); + } + return TRUE; +} + +int main(int argc, char *argv[]) +{ + return test_runner_run("exchanges", tests, test_runner_init); +} diff --git a/src/libcharon/tests/exchange_tests.h b/src/libcharon/tests/exchange_tests.h new file mode 100644 index 000000000..30086721f --- /dev/null +++ b/src/libcharon/tests/exchange_tests.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * 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. + */ + +TEST_SUITE(ike_delete_suite_create) +TEST_SUITE(ike_rekey_suite_create) +TEST_SUITE(child_create_suite_create) +TEST_SUITE(child_delete_suite_create) +TEST_SUITE(child_rekey_suite_create) diff --git a/src/libcharon/tests/libcharon_tests.c b/src/libcharon/tests/libcharon_tests.c index 4692c3094..e25e5434f 100644 --- a/src/libcharon/tests/libcharon_tests.c +++ b/src/libcharon/tests/libcharon_tests.c @@ -53,9 +53,6 @@ static bool test_runner_init(bool init) } else { - lib->processor->set_threads(lib->processor, 0); - lib->processor->cancel(lib->processor); - lib->plugins->unload(lib->plugins); libcharon_deinit(); } return TRUE; diff --git a/src/libcharon/tests/libcharon_tests.h b/src/libcharon/tests/libcharon_tests.h index fb82baccb..f770f464d 100644 --- a/src/libcharon/tests/libcharon_tests.h +++ b/src/libcharon/tests/libcharon_tests.h @@ -1,4 +1,7 @@ /* + * Copyright (C) 2014-2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG * @@ -13,6 +16,15 @@ * for more details. */ +/** + * @defgroup libcharon-tests tests + * @ingroup libcharon + * + * @defgroup test_utils_c test_utils + * @ingroup libcharon-tests + */ + +TEST_SUITE(proposal_suite_create) TEST_SUITE(ike_cfg_suite_create) TEST_SUITE(mem_pool_suite_create) TEST_SUITE_DEPEND(message_chapoly_suite_create, AEAD, ENCR_CHACHA20_POLY1305, 32) diff --git a/src/libcharon/tests/suites/test_child_create.c b/src/libcharon/tests/suites/test_child_create.c new file mode 100644 index 000000000..20a47f6bf --- /dev/null +++ b/src/libcharon/tests/suites/test_child_create.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * 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 "test_suite.h" + +#include <daemon.h> +#include <tests/utils/exchange_test_helper.h> +#include <tests/utils/exchange_test_asserts.h> +#include <tests/utils/job_asserts.h> +#include <tests/utils/sa_asserts.h> + +/** + * One of the peers tries to create a new CHILD_SA while the other concurrently + * started to rekey the IKE_SA. TEMPORARY_FAILURE should be returned on both + * sides and the peers should prepare to retry. + */ +START_TEST(test_collision_ike_rekey) +{ + child_cfg_t *child_cfg; + child_cfg_create_t child = { + .mode = MODE_TUNNEL, + }; + ike_sa_t *a, *b; + + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + + assert_hook_not_called(child_updown); + child_cfg = child_cfg_create("child", &child); + child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP)); + child_cfg->add_traffic_selector(child_cfg, TRUE, + traffic_selector_create_dynamic(0, 0, 65535)); + child_cfg->add_traffic_selector(child_cfg, FALSE, + traffic_selector_create_dynamic(0, 0, 65535)); + call_ikesa(a, initiate, child_cfg, 0, NULL, NULL); + assert_child_sa_count(a, 1); + assert_hook(); + + call_ikesa(b, rekey); + + /* CREATE_CHILD_SA { SA, Ni, [KEi,] TSi, TSr } --> */ + assert_hook_not_called(child_updown); + assert_single_notify(OUT, TEMPORARY_FAILURE); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_count(b, 1); + assert_hook(); + + /* <-- CREATE_CHILD_SA { SA, Ni, KEi } */ + assert_single_notify(OUT, TEMPORARY_FAILURE); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + + /* <-- CREATE_CHILD_SA { N(TEMP_FAIL) } */ + assert_hook_not_called(child_updown); + assert_jobs_scheduled(1); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_count(a, 1); + assert_scheduler(); + assert_hook(); + + /* CREATE_CHILD_SA { N(TEMP_FAIL) } --> */ + assert_jobs_scheduled(1); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_ESTABLISHED); + assert_scheduler(); + + /* make sure no message was sent after handling the TEMPORARY_FAILURE and + * that the task to retry creating the CHILD_SA is queued and not active + * and it can't be initiated immediately */ + ck_assert(!exchange_test_helper->sender->dequeue(exchange_test_helper->sender)); + assert_num_tasks(a, 0, TASK_QUEUE_ACTIVE); + assert_num_tasks(a, 1, TASK_QUEUE_QUEUED); + call_ikesa(a, initiate, NULL, 0, NULL, NULL); + assert_num_tasks(a, 0, TASK_QUEUE_ACTIVE); + + assert_sa_idle(b); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); +} +END_TEST + +Suite *child_create_suite_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("child create"); + + tc = tcase_create("collisions ike rekey"); + tcase_add_test(tc, test_collision_ike_rekey); + suite_add_tcase(s, tc); + + return s; +} diff --git a/src/libcharon/tests/suites/test_child_delete.c b/src/libcharon/tests/suites/test_child_delete.c new file mode 100644 index 000000000..437e919c7 --- /dev/null +++ b/src/libcharon/tests/suites/test_child_delete.c @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * 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 "test_suite.h" + +#include <daemon.h> +#include <tests/utils/exchange_test_helper.h> +#include <tests/utils/exchange_test_asserts.h> +#include <tests/utils/job_asserts.h> +#include <tests/utils/sa_asserts.h> + +/** + * Regular CHILD_SA deletion either initiated by the original initiator or + * responder of the IKE_SA. + */ +START_TEST(test_regular) +{ + ike_sa_t *a, *b; + + if (_i) + { /* responder deletes the CHILD_SA (SPI 2) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &b, &a, NULL); + } + else + { /* initiator deletes the CHILD_SA (SPI 1) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + } + assert_hook_not_called(child_updown); + call_ikesa(a, delete_child_sa, PROTO_ESP, _i+1, FALSE); + assert_child_sa_state(a, _i+1, CHILD_DELETING); + assert_hook(); + + /* INFORMATIONAL { D } --> */ + assert_hook_updown(child_updown, FALSE); + assert_single_payload(IN, PLV2_DELETE); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_count(b, 0); + assert_hook(); + + /* <-- INFORMATIONAL { D } */ + assert_hook_updown(child_updown, FALSE); + assert_single_payload(IN, PLV2_DELETE); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_count(a, 0); + assert_hook(); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); +} +END_TEST + +/** + * Both peers initiate the CHILD_SA deletion concurrently and should handle + * the collision properly. + */ +START_TEST(test_collision) +{ + ike_sa_t *a, *b; + + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + /* both peers delete the CHILD_SA concurrently */ + assert_hook_not_called(child_updown); + call_ikesa(a, delete_child_sa, PROTO_ESP, 1, FALSE); + assert_child_sa_state(a, 1, CHILD_DELETING); + call_ikesa(b, delete_child_sa, PROTO_ESP, 2, FALSE); + assert_child_sa_state(b, 2, CHILD_DELETING); + assert_hook(); + + /* RFC 7296 says: + * + * Normally, the response in the INFORMATIONAL exchange will contain + * Delete payloads for the paired SAs going in the other direction. + * There is one exception. If, by chance, both ends of a set of SAs + * independently decide to close them, each may send a Delete payload + * and the two requests may cross in the network. If a node receives a + * delete request for SAs for which it has already issued a delete + * request, it MUST delete the outgoing SAs while processing the request + * and the incoming SAs while processing the response. In that case, + * the responses MUST NOT include Delete payloads for the deleted SAs, + * since that would result in duplicate deletion and could in theory + * delete the wrong SA. + * + * We don't handle SAs separately so we expect both are still installed, + * but the INFORMATIONAL response should not contain a DELETE payload. + */ + + /* INFORMATIONAL { D } --> */ + assert_hook_not_called(child_updown); + assert_single_payload(IN, PLV2_DELETE); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, 2, CHILD_DELETING); + /* <-- INFORMATIONAL { D } */ + assert_single_payload(IN, PLV2_DELETE); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 1, CHILD_DELETING); + assert_hook(); + + /* <-- INFORMATIONAL { } */ + assert_hook_updown(child_updown, FALSE); + assert_message_empty(IN); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_count(a, 0); + assert_hook(); + /* INFORMATIONAL { } --> */ + assert_hook_updown(child_updown, FALSE); + assert_message_empty(IN); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_count(b, 0); + assert_hook(); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); +} +END_TEST + +/** + * This is like the collision above but one of the DELETEs is dropped or delayed + * so the other peer is not aware that there is a collision. + */ +START_TEST(test_collision_drop) +{ + ike_sa_t *a, *b; + message_t *msg; + + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + /* both peers delete the CHILD_SA concurrently */ + assert_hook_not_called(child_updown); + call_ikesa(a, delete_child_sa, PROTO_ESP, 1, FALSE); + assert_child_sa_state(a, 1, CHILD_DELETING); + call_ikesa(b, delete_child_sa, PROTO_ESP, 2, FALSE); + assert_child_sa_state(b, 2, CHILD_DELETING); + assert_hook(); + + /* INFORMATIONAL { D } --> */ + assert_hook_not_called(child_updown); + assert_single_payload(IN, PLV2_DELETE); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, 2, CHILD_DELETING); + assert_hook(); + + /* drop/delay the responder's message */ + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + + /* <-- INFORMATIONAL { } */ + assert_hook_updown(child_updown, FALSE); + assert_message_empty(IN); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_count(a, 0); + assert_hook(); + + /* <-- INFORMATIONAL { D } (delayed/retransmitted) */ + assert_hook_not_called(child_updown); + assert_single_payload(IN, PLV2_DELETE); + exchange_test_helper->process_message(exchange_test_helper, a, msg); + assert_hook(); + + /* INFORMATIONAL { } --> */ + assert_hook_updown(child_updown, FALSE); + assert_message_empty(IN); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_count(b, 0); + assert_hook(); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); +} +END_TEST + +/** + * One of the hosts initiates a rekey of the IKE_SA of the CHILD_SA the other + * peer is concurrently trying to delete. + * + * delete ----\ /---- rekey IKE + * \-----/----> detect collision + * detect collision <---------/ /---- delete + * TEMP_FAIL ----\ / + * \----/-----> + * <--------/ + */ +START_TEST(test_collision_ike_rekey) +{ + ike_sa_t *a, *b; + uint32_t spi_a = _i+1; + + if (_i) + { /* responder deletes the CHILD_SA (SPI 2) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &b, &a, NULL); + } + else + { /* initiator deletes the CHILD_SA (SPI 1) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + } + call_ikesa(a, delete_child_sa, PROTO_ESP, spi_a, FALSE); + assert_child_sa_state(a, spi_a, CHILD_DELETING); + call_ikesa(b, rekey); + assert_ike_sa_state(b, IKE_REKEYING); + + /* this should never get called as there is no successful rekeying */ + assert_hook_not_called(ike_rekey); + + /* RFC 7296, 2.25.2: If a peer receives a request to delete a Child SA when + * it is currently rekeying the IKE SA, it SHOULD reply as usual, with a + * Delete payload. + */ + + /* INFORMATIONAL { D } --> */ + assert_hook_updown(child_updown, FALSE); + assert_single_payload(OUT, PLV2_DELETE); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_REKEYING); + assert_child_sa_count(b, 0); + assert_hook(); + + /* RFC 7296, 2.25.1: If a peer receives a request to rekey the IKE SA, and + * it is currently, rekeying, or closing a Child SA of that IKE SA, it + * SHOULD reply with TEMPORARY_FAILURE. + */ + + /* <-- CREATE_CHILD_SA { SA, Ni, KEi } */ + assert_single_notify(OUT, TEMPORARY_FAILURE); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, spi_a, CHILD_DELETING); + + /* <-- INFORMATIONAL { D } */ + assert_hook_updown(child_updown, FALSE); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_count(a, 0); + assert_hook(); + + /* CREATE_CHILD_SA { N(TEMP_FAIL) } --> */ + /* we expect a job to retry the rekeying is scheduled */ + assert_jobs_scheduled(1); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_ESTABLISHED); + assert_scheduler(); + + /* ike_rekey */ + assert_hook(); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); +} +END_TEST + +/** + * One of the hosts initiates a delete of the IKE_SA of the CHILD_SA the other + * peer is concurrently trying to delete. + * + * delete ----\ /---- delete IKE + * \-----/----> detect collision + * <---------/ /---- delete + * delete ----\ / + * \----/-----> + * sa already gone <--------/ + */ +START_TEST(test_collision_ike_delete) +{ + ike_sa_t *a, *b; + uint32_t spi_a = _i+1; + message_t *msg; + status_t s; + + if (_i) + { /* responder rekeys the CHILD_SA (SPI 2) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &b, &a, NULL); + } + else + { /* initiator rekeys the CHILD_SA (SPI 1) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + } + call_ikesa(a, delete_child_sa, PROTO_ESP, spi_a, FALSE); + assert_child_sa_state(a, spi_a, CHILD_DELETING); + call_ikesa(b, delete); + assert_ike_sa_state(b, IKE_DELETING); + + /* RFC 7296, 2.25.2 does not explicitly state what the behavior SHOULD be if + * a peer receives a request to delete a CHILD_SA when it is currently + * closing the IKE SA. We expect a regular response. + */ + + /* INFORMATIONAL { D } --> */ + assert_hook_updown(child_updown, FALSE); + assert_single_payload(OUT, PLV2_DELETE); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_DELETING); + assert_child_sa_count(b, 0); + assert_hook(); + + /* RFC 7296, 2.25.1 does not explicitly state what the behavior SHOULD be if + * a peer receives a request to close the IKE SA if it is currently deleting + * a Child SA of that IKE SA. Let's just close the IKE_SA and forget the + * delete. + */ + + /* <-- INFORMATIONAL { D } */ + assert_hook_updown(ike_updown, FALSE); + assert_hook_updown(child_updown, FALSE); + assert_message_empty(OUT); + s = exchange_test_helper->process_message(exchange_test_helper, a, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(a, destroy); + assert_hook(); + assert_hook(); + + /* <-- INFORMATIONAL { D } */ + /* the SA is already gone */ + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + msg->destroy(msg); + + /* INFORMATIONAL { } --> */ + assert_hook_updown(ike_updown, FALSE); + assert_hook_not_called(child_updown); + s = exchange_test_helper->process_message(exchange_test_helper, b, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(b, destroy); + assert_hook(); + assert_hook(); +} +END_TEST + +Suite *child_delete_suite_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("child delete"); + + tc = tcase_create("regular"); + tcase_add_loop_test(tc, test_regular, 0, 2); + suite_add_tcase(s, tc); + + tc = tcase_create("collisions"); + tcase_add_test(tc, test_collision); + tcase_add_test(tc, test_collision_drop); + suite_add_tcase(s, tc); + + tc = tcase_create("collisions ike rekey"); + tcase_add_loop_test(tc, test_collision_ike_rekey, 0, 2); + suite_add_tcase(s, tc); + + tc = tcase_create("collisions ike delete"); + tcase_add_loop_test(tc, test_collision_ike_delete, 0, 2); + suite_add_tcase(s, tc); + + return s; +} diff --git a/src/libcharon/tests/suites/test_child_rekey.c b/src/libcharon/tests/suites/test_child_rekey.c new file mode 100644 index 000000000..fcac49388 --- /dev/null +++ b/src/libcharon/tests/suites/test_child_rekey.c @@ -0,0 +1,1569 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * 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 "test_suite.h" + +#include <daemon.h> +#include <tests/utils/exchange_test_helper.h> +#include <tests/utils/exchange_test_asserts.h> +#include <tests/utils/job_asserts.h> +#include <tests/utils/sa_asserts.h> + +/** + * Initiate rekeying the CHILD_SA with the given SPI on the given IKE_SA. + */ +#define initiate_rekey(sa, spi) ({ \ + assert_hook_not_called(child_updown); \ + assert_hook_not_called(child_rekey); \ + call_ikesa(sa, rekey_child_sa, PROTO_ESP, spi); \ + assert_child_sa_state(sa, spi, CHILD_REKEYING); \ + assert_hook(); \ + assert_hook(); \ +}) + +/** + * Regular CHILD_SA rekey either initiated by the original initiator or + * responder of the IKE_SA. + */ +START_TEST(test_regular) +{ + ike_sa_t *a, *b; + uint32_t spi_a = _i+1, spi_b = 2-_i; + + if (_i) + { /* responder rekeys the CHILD_SA (SPI 2) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &b, &a, NULL); + } + else + { /* initiator rekeys the CHILD_SA (SPI 1) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + } + initiate_rekey(a, spi_a); + + /* this should never get called as this results in a successful rekeying */ + assert_hook_not_called(child_updown); + + /* CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } --> */ + assert_hook_called(child_rekey); + assert_notify(IN, REKEY_SA); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, spi_b, CHILD_REKEYED); + assert_child_sa_state(b, 4, CHILD_INSTALLED); + assert_hook(); + + /* <-- CREATE_CHILD_SA { SA, Nr, [KEr,] TSi, TSr } */ + assert_hook_called(child_rekey); + assert_no_notify(IN, REKEY_SA); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, spi_a, CHILD_DELETING); + assert_child_sa_state(a, 3, CHILD_INSTALLED); + assert_hook(); + + /* INFORMATIONAL { D } --> */ + assert_hook_not_called(child_rekey); + assert_single_payload(IN, PLV2_DELETE); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, 4, CHILD_INSTALLED); + assert_child_sa_count(b, 1); + assert_hook(); + /* <-- INFORMATIONAL { D } */ + assert_hook_not_called(child_rekey); + assert_single_payload(IN, PLV2_DELETE); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 3, CHILD_INSTALLED); + assert_child_sa_count(a, 1); + assert_hook(); + + /* child_updown */ + assert_hook(); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); +} +END_TEST + +/** + * CHILD_SA rekey where the responder does not agree with the DH group selected + * by the initiator, either initiated by the original initiator or responder of + * the IKE_SA. + */ +START_TEST(test_regular_ke_invalid) +{ + exchange_test_sa_conf_t conf = { + .initiator = { + .esp = "aes128-sha256-modp2048-modp3072", + }, + .responder = { + .esp = "aes128-sha256-modp3072-modp2048", + }, + }; + ike_sa_t *a, *b; + uint32_t spi_a = _i+1, spi_b = 2-_i; + + if (_i) + { /* responder rekeys the CHILD_SA (SPI 2) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &b, &a, &conf); + } + else + { /* initiator rekeys the CHILD_SA (SPI 1) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, &conf); + } + initiate_rekey(a, spi_a); + + /* this should never get called as this results in a successful rekeying */ + assert_hook_not_called(child_updown); + + /* CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } --> */ + assert_hook_not_called(child_rekey); + assert_notify(IN, REKEY_SA); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, spi_b, CHILD_INSTALLED); + assert_child_sa_count(b, 1); + assert_hook(); + + /* <-- CREATE_CHILD_SA { N(INVAL_KE) } */ + assert_hook_not_called(child_rekey); + assert_single_notify(IN, INVALID_KE_PAYLOAD); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, spi_a, CHILD_REKEYING); + assert_child_sa_count(a, 1); + assert_hook(); + + /* CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } --> */ + assert_hook_called(child_rekey); + assert_notify(IN, REKEY_SA); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, spi_b, CHILD_REKEYED); + assert_child_sa_state(b, 6, CHILD_INSTALLED); + assert_hook(); + + /* <-- CREATE_CHILD_SA { SA, Nr, [KEr,] TSi, TSr } */ + assert_hook_called(child_rekey); + assert_no_notify(IN, REKEY_SA); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, spi_a, CHILD_DELETING); + assert_child_sa_state(a, 5, CHILD_INSTALLED); + assert_hook(); + + /* INFORMATIONAL { D } --> */ + assert_hook_not_called(child_rekey); + assert_single_payload(IN, PLV2_DELETE); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, 6, CHILD_INSTALLED); + assert_child_sa_count(b, 1); + assert_hook(); + /* <-- INFORMATIONAL { D } */ + assert_hook_not_called(child_rekey); + assert_single_payload(IN, PLV2_DELETE); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 5, CHILD_INSTALLED); + assert_child_sa_count(a, 1); + assert_hook(); + + /* child_updown */ + assert_hook(); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); +} +END_TEST + +/** + * Check that the responder ignores soft expires while waiting for the delete + * after a rekeying. + */ +START_TEST(test_regular_responder_ignore_soft_expire) +{ + ike_sa_t *a, *b; + + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + initiate_rekey(a, 1); + + /* this should never get called as this results in a successful rekeying */ + assert_hook_not_called(child_updown); + + /* CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } --> */ + assert_hook_called(child_rekey); + assert_notify(IN, REKEY_SA); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, 2, CHILD_REKEYED); + assert_child_sa_state(b, 4, CHILD_INSTALLED); + assert_hook(); + + /* <-- CREATE_CHILD_SA { SA, Nr, [KEr,] TSi, TSr } */ + assert_hook_called(child_rekey); + assert_no_notify(IN, REKEY_SA); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 1, CHILD_DELETING); + assert_child_sa_state(a, 3, CHILD_INSTALLED); + assert_hook(); + + /* we don't expect this to get called anymore */ + assert_hook_not_called(child_rekey); + /* this should not produce a message, if it does there won't be a delete + * payload below */ + call_ikesa(b, rekey_child_sa, PROTO_ESP, 2); + assert_child_sa_state(b, 2, CHILD_REKEYED); + + /* INFORMATIONAL { D } --> */ + assert_single_payload(IN, PLV2_DELETE); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, 4, CHILD_INSTALLED); + assert_child_sa_count(b, 1); + /* <-- INFORMATIONAL { D } */ + assert_single_payload(IN, PLV2_DELETE); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 3, CHILD_INSTALLED); + assert_child_sa_count(a, 1); + + /* child_rekey/child_updown */ + assert_hook(); + assert_hook(); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); +} +END_TEST + +/** + * Check that the responder handles hard expires properly while waiting for the + * delete after a rekeying (e.g. if the initiator of the rekeying fails to + * delete the CHILD_SA for some reason). + */ +START_TEST(test_regular_responder_handle_hard_expire) +{ + ike_sa_t *a, *b; + + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + initiate_rekey(a, 1); + + /* this should never get called as this results in a successful rekeying */ + assert_hook_not_called(child_updown); + + /* CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } --> */ + assert_hook_called(child_rekey); + assert_notify(IN, REKEY_SA); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, 2, CHILD_REKEYED); + assert_child_sa_state(b, 4, CHILD_INSTALLED); + assert_hook(); + + /* <-- CREATE_CHILD_SA { SA, Nr, [KEr,] TSi, TSr } */ + assert_hook_called(child_rekey); + assert_no_notify(IN, REKEY_SA); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 1, CHILD_DELETING); + assert_child_sa_state(a, 3, CHILD_INSTALLED); + assert_hook(); + + /* we don't expect this to get called anymore */ + assert_hook_not_called(child_rekey); + /* this is similar to a regular delete collision */ + assert_single_payload(OUT, PLV2_DELETE); + call_ikesa(b, delete_child_sa, PROTO_ESP, 2, TRUE); + assert_child_sa_state(b, 2, CHILD_DELETING); + + /* INFORMATIONAL { D } --> */ + assert_single_payload(IN, PLV2_DELETE); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, 4, CHILD_INSTALLED); + assert_child_sa_state(a, 2, CHILD_DELETING); + /* <-- INFORMATIONAL { D } */ + assert_single_payload(IN, PLV2_DELETE); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 3, CHILD_INSTALLED); + assert_child_sa_state(a, 1, CHILD_DELETING); + /* <-- INFORMATIONAL { } */ + assert_message_empty(IN); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 3, CHILD_INSTALLED); + assert_child_sa_count(a, 1); + /* INFORMATIONAL { } --> */ + assert_message_empty(IN); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, 4, CHILD_INSTALLED); + assert_child_sa_count(b, 1); + + /* child_rekey/child_updown */ + assert_hook(); + assert_hook(); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); +} +END_TEST + +/** + * Both peers initiate the CHILD_SA reekying concurrently and should handle + * the collision properly depending on the nonces. + */ +START_TEST(test_collision) +{ + ike_sa_t *a, *b; + + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + + /* When rekeyings collide we get two CHILD_SAs with a total of four nonces. + * The CHILD_SA with the lowest nonce SHOULD be deleted by the peer that + * created that CHILD_SA. The replaced CHILD_SA is deleted by the peer that + * initiated the surviving SA. + * Four nonces and SPIs are needed (SPI 1 and 2 are used for the initial + * CHILD_SA): + * N1/3 -----\ /----- N2/4 + * \--/-----> N3/5 + * N4/6 <-------/ /----- ... + * ... -----\ + * We test this four times, each time a different nonce is the lowest. + */ + struct { + /* Nonces used at each point */ + u_char nonces[4]; + /* SPIs of the deleted CHILD_SA (either redundant or replaced) */ + uint32_t spi_del_a, spi_del_b; + /* SPIs of the kept CHILD_SA */ + uint32_t spi_a, spi_b; + } data[] = { + { { 0x00, 0xFF, 0xFF, 0xFF }, 3, 2, 6, 4 }, + { { 0xFF, 0x00, 0xFF, 0xFF }, 1, 4, 3, 5 }, + { { 0xFF, 0xFF, 0x00, 0xFF }, 3, 2, 6, 4 }, + { { 0xFF, 0xFF, 0xFF, 0x00 }, 1, 4, 3, 5 }, + }; + + exchange_test_helper->nonce_first_byte = data[_i].nonces[0]; + initiate_rekey(a, 1); + exchange_test_helper->nonce_first_byte = data[_i].nonces[1]; + initiate_rekey(b, 2); + + /* this should never get called as this results in a successful rekeying */ + assert_hook_not_called(child_updown); + + /* CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } --> */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[2]; + assert_hook_rekey(child_rekey, 2, 5); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, 2, CHILD_REKEYED); + assert_child_sa_state(b, 5, CHILD_INSTALLED); + assert_hook(); + /* <-- CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[3]; + assert_hook_rekey(child_rekey, 1, 6); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 1, CHILD_REKEYED); + assert_child_sa_state(a, 6, CHILD_INSTALLED); + assert_hook(); + + /* <-- CREATE_CHILD_SA { SA, Nr, [KEr,] TSi, TSr } */ + if (data[_i].spi_del_a == 1) + { /* currently we call this again if we keep our own replacement as we + * already called it above */ + assert_hook_rekey(child_rekey, 1, data[_i].spi_a); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_hook(); + } + else + { + assert_hook_not_called(child_rekey); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_hook(); + } + assert_child_sa_state(a, data[_i].spi_del_a, CHILD_DELETING); + assert_child_sa_state(a, data[_i].spi_del_b, CHILD_REKEYED); + assert_child_sa_state(a, data[_i].spi_a, CHILD_INSTALLED); + /* CREATE_CHILD_SA { SA, Nr, [KEr,] TSi, TSr } --> */ + if (data[_i].spi_del_b == 2) + { + assert_hook_rekey(child_rekey, 2, data[_i].spi_b); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_hook(); + } + else + { + assert_hook_not_called(child_rekey); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_hook(); + } + assert_child_sa_state(b, data[_i].spi_del_b, CHILD_DELETING); + assert_child_sa_state(b, data[_i].spi_del_a, CHILD_REKEYED); + assert_child_sa_state(b, data[_i].spi_b, CHILD_INSTALLED); + + /* we don't expect this hook to get called anymore */ + assert_hook_not_called(child_rekey); + /* INFORMATIONAL { D } --> */ + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, data[_i].spi_del_b, CHILD_DELETING); + assert_child_sa_state(b, data[_i].spi_b, CHILD_INSTALLED); + assert_child_sa_count(b, 2); + /* <-- INFORMATIONAL { D } */ + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, data[_i].spi_del_a, CHILD_DELETING); + assert_child_sa_state(a, data[_i].spi_a, CHILD_INSTALLED); + assert_child_sa_count(a, 2); + /* <-- INFORMATIONAL { D } */ + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, data[_i].spi_a, CHILD_INSTALLED); + assert_child_sa_count(a, 1); + /* INFORMATIONAL { D } --> */ + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, data[_i].spi_b, CHILD_INSTALLED); + assert_child_sa_count(b, 1); + + /* child_rekey/child_updown */ + assert_hook(); + assert_hook(); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); +} +END_TEST + +/** + * This is like the rekey collision above, but one peer deletes the + * redundant/old SA before the other peer receives the CREATE_CHILD_SA + * response: + * + * rekey ----\ /---- rekey + * \-----/----> detect collision + * detect collision <---------/ /---- + * ----\ / + * \----/-----> + * handle delete <--------/------- delete SA + * --------/-------> + * handle rekey <------/ + * delete SA ----------------> + * <---------------- + */ +START_TEST(test_collision_delayed_response) +{ + ike_sa_t *a, *b; + message_t *msg; + + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + + /* Four nonces and SPIs are needed (SPI 1 and 2 are used for the initial + * CHILD_SA): + * N1/3 -----\ /----- N2/4 + * \--/-----> N3/5 + * N4/6 <-------/ /----- ... + * ... -----\ + * We test this four times, each time a different nonce is the lowest. + */ + struct { + /* Nonces used at each point */ + u_char nonces[4]; + /* SPIs of the deleted CHILD_SA (either redundant or replaced) */ + uint32_t spi_del_a, spi_del_b; + /* SPIs of the kept CHILD_SA */ + uint32_t spi_a, spi_b; + } data[] = { + { { 0x00, 0xFF, 0xFF, 0xFF }, 3, 2, 6, 4 }, + { { 0xFF, 0x00, 0xFF, 0xFF }, 1, 4, 3, 5 }, + { { 0xFF, 0xFF, 0x00, 0xFF }, 3, 2, 6, 4 }, + { { 0xFF, 0xFF, 0xFF, 0x00 }, 1, 4, 3, 5 }, + }; + + exchange_test_helper->nonce_first_byte = data[_i].nonces[0]; + initiate_rekey(a, 1); + exchange_test_helper->nonce_first_byte = data[_i].nonces[1]; + initiate_rekey(b, 2); + + /* this should never get called as this results in a successful rekeying */ + assert_hook_not_called(child_updown); + + /* CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } --> */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[2]; + assert_hook_rekey(child_rekey, 2, 5); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, 2, CHILD_REKEYED); + assert_child_sa_state(b, 5, CHILD_INSTALLED); + assert_hook(); + /* <-- CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[3]; + assert_hook_rekey(child_rekey, 1, 6); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 1, CHILD_REKEYED); + assert_child_sa_state(a, 6, CHILD_INSTALLED); + assert_hook(); + + /* delay the CREATE_CHILD_SA response from b to a */ + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + + /* CREATE_CHILD_SA { SA, Nr, [KEr,] TSi, TSr } --> */ + if (data[_i].spi_del_b == 2) + { + assert_hook_rekey(child_rekey, 2, data[_i].spi_b); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_hook(); + } + else + { + assert_hook_not_called(child_rekey); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_hook(); + } + assert_child_sa_state(b, data[_i].spi_del_b, CHILD_DELETING); + assert_child_sa_state(b, data[_i].spi_del_a, CHILD_REKEYED); + assert_child_sa_state(b, data[_i].spi_b, CHILD_INSTALLED); + + /* <-- INFORMATIONAL { D } */ + assert_hook_not_called(child_rekey); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + if (data[_i].spi_del_b == 2) + { + assert_child_sa_state(a, data[_i].spi_a, CHILD_INSTALLED); + assert_child_sa_count(a, 1); + } + else + { + assert_child_sa_state(a, 1, CHILD_REKEYED); + assert_child_sa_count(a, 1); + } + /* INFORMATIONAL { D } --> */ + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, data[_i].spi_del_a, CHILD_REKEYED); + assert_child_sa_state(b, data[_i].spi_b, CHILD_INSTALLED); + assert_child_sa_count(b, 2); + assert_hook(); + + /* <-- CREATE_CHILD_SA { SA, Nr, [KEr,] TSi, TSr } (delayed) */ + if (data[_i].spi_del_a == 1) + { + assert_hook_rekey(child_rekey, 1, data[_i].spi_a); + exchange_test_helper->process_message(exchange_test_helper, a, msg); + assert_hook(); + } + else + { + assert_hook_not_called(child_rekey); + exchange_test_helper->process_message(exchange_test_helper, a, msg); + assert_hook(); + } + assert_child_sa_state(a, data[_i].spi_del_a, CHILD_DELETING); + assert_child_sa_state(a, data[_i].spi_a, CHILD_INSTALLED); + assert_child_sa_count(a, 2); + + /* we don't expect this hook to get called anymore */ + assert_hook_not_called(child_rekey); + /* INFORMATIONAL { D } --> */ + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, data[_i].spi_b, CHILD_INSTALLED); + assert_child_sa_count(b, 1); + /* <-- INFORMATIONAL { D } */ + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, data[_i].spi_a, CHILD_INSTALLED); + assert_child_sa_count(a, 1); + + /* child_rekey/child_updown */ + assert_hook(); + assert_hook(); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); +} +END_TEST + +/** + * In this scenario one of the peers does not notice that there is a + * rekey collision: + * + * rekey ----\ /---- rekey + * \ / + * detect collision <-----\---/ + * -------\--------> + * \ /---- delete old SA + * \-/----> detect collision + * detect collision <---------/ /---- TEMP_FAIL + * delete -----------/----> + * aborts rekeying <---------/ + */ +START_TEST(test_collision_delayed_request) +{ + ike_sa_t *a, *b; + message_t *msg; + + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + + /* Three nonces and SPIs are needed (SPI 1 and 2 are used for the initial + * CHILD_SA): + * N1/3 -----\ /----- N2/4 + * N3/5 <-----\--/ + * ... -----\ \-------> ... + * We test this three times, each time a different nonce is the lowest. + */ + struct { + /* Nonces used at each point */ + u_char nonces[3]; + } data[] = { + { { 0x00, 0xFF, 0xFF } }, + { { 0xFF, 0x00, 0xFF } }, + { { 0xFF, 0xFF, 0x00 } }, + }; + + exchange_test_helper->nonce_first_byte = data[_i].nonces[0]; + initiate_rekey(a, 1); + exchange_test_helper->nonce_first_byte = data[_i].nonces[1]; + initiate_rekey(b, 2); + + /* delay the CREATE_CHILD_SA request from a to b */ + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + + /* this should never get called as this results in a successful rekeying */ + assert_hook_not_called(child_updown); + + /* <-- CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[2]; + assert_hook_rekey(child_rekey, 1, 5); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 1, CHILD_REKEYED); + assert_child_sa_state(a, 5, CHILD_INSTALLED); + assert_hook(); + /* CREATE_CHILD_SA { SA, Nr, [KEr,] TSi, TSr } --> */ + assert_hook_rekey(child_rekey, 2, 4); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, 2, CHILD_DELETING); + assert_child_sa_state(b, 4, CHILD_INSTALLED); + assert_hook(); + + /* we don't expect this hook to get called anymore */ + assert_hook_not_called(child_rekey); + + /* CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } --> (delayed) */ + assert_single_notify(OUT, TEMPORARY_FAILURE); + exchange_test_helper->process_message(exchange_test_helper, b, msg); + assert_child_sa_state(b, 2, CHILD_DELETING); + assert_child_sa_state(b, 4, CHILD_INSTALLED); + + /* <-- INFORMATIONAL { D } */ + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 5, CHILD_INSTALLED); + assert_child_sa_count(a, 1); + + /* <-- CREATE_CHILD_SA { N(TEMP_FAIL) } */ + assert_no_jobs_scheduled(); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 5, CHILD_INSTALLED); + assert_child_sa_count(a, 1); + assert_scheduler(); + + /* INFORMATIONAL { D } --> */ + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, 4, CHILD_INSTALLED); + assert_child_sa_count(b, 1); + + /* child_rekey/child_updown */ + assert_hook(); + assert_hook(); + + assert_sa_idle(a); + assert_sa_idle(b); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); +} +END_TEST + +/** + * Similar to above one peer fails to notice the collision but the + * CREATE_CHILD_SA request is even more delayed: + * + * rekey ----\ /---- rekey + * \ / + * detect collision <-----\---/ + * -------\--------> + * detect collision <-------\-------- delete old SA + * delete ---------\------> + * \-----> + * /---- CHILD_SA_NOT_FOUND + * aborts rekeying <----------/ + */ +START_TEST(test_collision_delayed_request_more) +{ + ike_sa_t *a, *b; + message_t *msg; + + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + + /* Three nonces and SPIs are needed (SPI 1 and 2 are used for the initial + * CHILD_SA): + * N1/3 -----\ /----- N2/4 + * N3/5 <-----\--/ + * ... -----\ \-------> ... + * We test this three times, each time a different nonce is the lowest. + */ + struct { + /* Nonces used at each point */ + u_char nonces[3]; + } data[] = { + { { 0x00, 0xFF, 0xFF } }, + { { 0xFF, 0x00, 0xFF } }, + { { 0xFF, 0xFF, 0x00 } }, + }; + + exchange_test_helper->nonce_first_byte = data[_i].nonces[0]; + initiate_rekey(a, 1); + exchange_test_helper->nonce_first_byte = data[_i].nonces[1]; + initiate_rekey(b, 2); + + /* delay the CREATE_CHILD_SA request from a to b */ + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + + /* this should never get called as this results in a successful rekeying */ + assert_hook_not_called(child_updown); + + /* <-- CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[2]; + assert_hook_rekey(child_rekey, 1, 5); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 1, CHILD_REKEYED); + assert_child_sa_state(a, 5, CHILD_INSTALLED); + assert_hook(); + /* CREATE_CHILD_SA { SA, Nr, [KEr,] TSi, TSr } --> */ + assert_hook_rekey(child_rekey, 2, 4); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, 2, CHILD_DELETING); + assert_child_sa_state(b, 4, CHILD_INSTALLED); + assert_hook(); + + /* we don't expect this hook to get called anymore */ + assert_hook_not_called(child_rekey); + + /* <-- INFORMATIONAL { D } */ + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 5, CHILD_INSTALLED); + assert_child_sa_count(a, 1); + /* INFORMATIONAL { D } --> */ + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, 4, CHILD_INSTALLED); + assert_child_sa_count(b, 1); + + /* CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } --> */ + assert_single_notify(OUT, CHILD_SA_NOT_FOUND); + exchange_test_helper->process_message(exchange_test_helper, b, msg); + assert_child_sa_state(b, 4, CHILD_INSTALLED); + assert_child_sa_count(b, 1); + /* <-- CREATE_CHILD_SA { N(NO_CHILD_SA) } */ + assert_no_jobs_scheduled(); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 5, CHILD_INSTALLED); + assert_child_sa_count(a, 1); + assert_scheduler(); + + /* child_rekey/child_updown */ + assert_hook(); + assert_hook(); + + assert_sa_idle(a); + assert_sa_idle(b); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); +} +END_TEST + +/** + * Both peers initiate the CHILD_SA reekying concurrently but the proposed DH + * groups are not the same after handling the INVALID_KE_PAYLOAD they should + * still handle the collision properly depending on the nonces. + */ +START_TEST(test_collision_ke_invalid) +{ + exchange_test_sa_conf_t conf = { + .initiator = { + .esp = "aes128-sha256-modp2048-modp3072", + }, + .responder = { + .esp = "aes128-sha256-modp3072-modp2048", + }, + }; + ike_sa_t *a, *b; + + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, &conf); + + /* Eight nonces and SPIs are needed (SPI 1 and 2 are used for the initial + * CHILD_SA): + * N1/3 -----\ /----- N2/4 + * \--/-----> N3/5 + * N4/6 <-------/ /---- INVAL_KE + * INVAL_KE -----\ / + * <-----\--/ + * N5/7 -----\ \-------> + * \ /---- N6/8 + * \--/----> N7/9 + * N8/10 <--------/ /---- ... + * ... ------\ + * + * We test this four times, each time a different nonce is the lowest. + */ + struct { + /* Nonces used at each point */ + u_char nonces[4]; + /* SPIs of the deleted CHILD_SA (either redundant or replaced) */ + uint32_t spi_del_a, spi_del_b; + /* SPIs of the kept CHILD_SA */ + uint32_t spi_a, spi_b; + } data[] = { + { { 0x00, 0xFF, 0xFF, 0xFF }, 7, 2,10, 8 }, + { { 0xFF, 0x00, 0xFF, 0xFF }, 1, 8, 7, 9 }, + { { 0xFF, 0xFF, 0x00, 0xFF }, 7, 2,10, 8 }, + { { 0xFF, 0xFF, 0xFF, 0x00 }, 1, 8, 7, 9 }, + }; + + /* make sure the nonces of the first try don't affect the retries */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[1]; + initiate_rekey(a, 1); + exchange_test_helper->nonce_first_byte = data[_i].nonces[0]; + initiate_rekey(b, 2); + + /* this should never get called as this results in a successful rekeying */ + assert_hook_not_called(child_updown); + + /* CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } --> */ + assert_hook_not_called(child_rekey); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, 2, CHILD_REKEYING); + assert_child_sa_count(b, 1); + assert_hook(); + /* <-- CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } */ + assert_hook_not_called(child_rekey); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 1, CHILD_REKEYING); + assert_child_sa_count(a, 1); + assert_hook(); + + /* <-- CREATE_CHILD_SA { N(INVAL_KE) } */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[0]; + assert_hook_not_called(child_rekey); + assert_single_notify(IN, INVALID_KE_PAYLOAD); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 1, CHILD_REKEYING); + assert_child_sa_count(a, 1); + assert_hook(); + /* CREATE_CHILD_SA { N(INVAL_KE) } --> */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[1]; + assert_hook_not_called(child_rekey); + assert_single_notify(IN, INVALID_KE_PAYLOAD); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, 2, CHILD_REKEYING); + assert_child_sa_count(b, 1); + assert_hook(); + + /* CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } --> */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[2]; + assert_hook_rekey(child_rekey, 2, 9); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, 2, CHILD_REKEYED); + assert_child_sa_state(b, 9, CHILD_INSTALLED); + assert_hook(); + /* <-- CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[3]; + assert_hook_rekey(child_rekey, 1, 10); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 1, CHILD_REKEYED); + assert_child_sa_state(a,10, CHILD_INSTALLED); + assert_hook(); + + /* <-- CREATE_CHILD_SA { SA, Nr, [KEr,] TSi, TSr } */ + if (data[_i].spi_del_a == 1) + { /* currently we call this again if we keep our own replacement as we + * already called it above */ + assert_hook_rekey(child_rekey, 1, data[_i].spi_a); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_hook(); + } + else + { + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + } + assert_child_sa_state(a, data[_i].spi_del_a, CHILD_DELETING); + assert_child_sa_state(a, data[_i].spi_del_b, CHILD_REKEYED); + assert_child_sa_state(a, data[_i].spi_a, CHILD_INSTALLED); + /* CREATE_CHILD_SA { SA, Nr, [KEr,] TSi, TSr } --> */ + if (data[_i].spi_del_b == 2) + { + assert_hook_rekey(child_rekey, 2, data[_i].spi_b); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_hook(); + } + else + { + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + } + assert_child_sa_state(b, data[_i].spi_del_b, CHILD_DELETING); + assert_child_sa_state(b, data[_i].spi_del_a, CHILD_REKEYED); + assert_child_sa_state(b, data[_i].spi_b, CHILD_INSTALLED); + + /* we don't expect this hook to get called anymore */ + assert_hook_not_called(child_rekey); + /* INFORMATIONAL { D } --> */ + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, data[_i].spi_del_b, CHILD_DELETING); + assert_child_sa_state(b, data[_i].spi_b, CHILD_INSTALLED); + assert_child_sa_count(b, 2); + /* <-- INFORMATIONAL { D } */ + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, data[_i].spi_del_a, CHILD_DELETING); + assert_child_sa_state(a, data[_i].spi_a, CHILD_INSTALLED); + assert_child_sa_count(a, 2); + /* <-- INFORMATIONAL { D } */ + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, data[_i].spi_a, CHILD_INSTALLED); + assert_child_sa_count(a, 1); + /* INFORMATIONAL { D } --> */ + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, data[_i].spi_b, CHILD_INSTALLED); + assert_child_sa_count(b, 1); + + /* child_rekey/child_updown */ + assert_hook(); + assert_hook(); + + assert_sa_idle(a); + assert_sa_idle(b); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); +} +END_TEST + +/** + * This is a variation of the above but with the retry by one peer delayed so + * that to the other peer it looks like there is no collision. + */ +START_TEST(test_collision_ke_invalid_delayed_retry) +{ + exchange_test_sa_conf_t conf = { + .initiator = { + .esp = "aes128-sha256-modp2048-modp3072", + }, + .responder = { + .esp = "aes128-sha256-modp3072-modp2048", + }, + }; + ike_sa_t *a, *b; + message_t *msg; + + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, &conf); + + /* Seven nonces and SPIs are needed (SPI 1 and 2 are used for the initial + * CHILD_SA): + * N1/3 -----\ /----- N2/4 + * \--/-----> N3/5 + * N4/6 <-------/ /---- INVAL_KE + * INVAL_KE -----\ / + * <-----\--/ + * N5/7 -----\ \-------> + * <-----\--------- N6/8 + * N7/9 -------\-------> + * <-------\------- DELETE + * ... ------\ \-----> + * /---- TEMP_FAIL + * + * We test this three times, each time a different nonce is the lowest. + */ + struct { + /* Nonces used at each point */ + u_char nonces[3]; + } data[] = { + { { 0x00, 0xFF, 0xFF } }, + { { 0xFF, 0x00, 0xFF } }, + { { 0xFF, 0xFF, 0x00 } }, + }; + + /* make sure the nonces of the first try don't affect the retries */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[1]; + initiate_rekey(a, 1); + exchange_test_helper->nonce_first_byte = data[_i].nonces[0]; + initiate_rekey(b, 2); + + /* this should never get called as this results in a successful rekeying */ + assert_hook_not_called(child_updown); + + /* CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } --> */ + assert_hook_not_called(child_rekey); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, 2, CHILD_REKEYING); + assert_child_sa_count(b, 1); + assert_hook(); + /* <-- CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } */ + assert_hook_not_called(child_rekey); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 1, CHILD_REKEYING); + assert_child_sa_count(a, 1); + assert_hook(); + + /* <-- CREATE_CHILD_SA { N(INVAL_KE) } */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[0]; + assert_hook_not_called(child_rekey); + assert_single_notify(IN, INVALID_KE_PAYLOAD); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 1, CHILD_REKEYING); + assert_child_sa_count(a, 1); + assert_hook(); + /* CREATE_CHILD_SA { N(INVAL_KE) } --> */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[1]; + assert_hook_not_called(child_rekey); + assert_single_notify(IN, INVALID_KE_PAYLOAD); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, 2, CHILD_REKEYING); + assert_child_sa_count(b, 1); + assert_hook(); + + /* delay the CREATE_CHILD_SA request from a to b */ + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + + /* <-- CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[2]; + assert_hook_rekey(child_rekey, 1, 9); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 1, CHILD_REKEYED); + assert_child_sa_state(a, 9, CHILD_INSTALLED); + assert_hook(); + /* CREATE_CHILD_SA { SA, Nr, [KEr,] TSi, TSr } --> */ + assert_hook_rekey(child_rekey, 2, 8); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, 2, CHILD_DELETING); + assert_child_sa_state(b, 8, CHILD_INSTALLED); + assert_hook(); + + /* we don't expect this hook to get called anymore */ + assert_hook_not_called(child_rekey); + + /* CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } --> (delayed) */ + assert_single_notify(OUT, TEMPORARY_FAILURE); + exchange_test_helper->process_message(exchange_test_helper, b, msg); + assert_child_sa_state(b, 2, CHILD_DELETING); + assert_child_sa_state(b, 8, CHILD_INSTALLED); + + /* <-- INFORMATIONAL { D } */ + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 9, CHILD_INSTALLED); + assert_child_sa_count(a, 1); + + /* <-- CREATE_CHILD_SA { N(TEMP_FAIL) } */ + assert_no_jobs_scheduled(); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, 9, CHILD_INSTALLED); + assert_child_sa_count(a, 1); + assert_scheduler(); + + /* INFORMATIONAL { D } --> */ + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, 8, CHILD_INSTALLED); + assert_child_sa_count(b, 1); + + /* child_rekey/child_updown */ + assert_hook(); + assert_hook(); + + assert_sa_idle(a); + assert_sa_idle(b); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); +} +END_TEST + +/** + * One of the hosts initiates a DELETE of the CHILD_SA the other peer is + * concurrently trying to rekey. + * + * rekey ----\ /---- delete + * \-----/----> detect collision + * detect collision <---------/ /---- TEMP_FAIL + * delete ----\ / + * \----/-----> + * aborts rekeying <--------/ + */ +START_TEST(test_collision_delete) +{ + ike_sa_t *a, *b; + uint32_t spi_a = _i+1, spi_b = 2-_i; + + if (_i) + { /* responder rekeys the CHILD_SA (SPI 2) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &b, &a, NULL); + } + else + { /* initiator rekeys the CHILD_SA (SPI 1) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + } + initiate_rekey(a, spi_a); + call_ikesa(b, delete_child_sa, PROTO_ESP, spi_b, FALSE); + assert_child_sa_state(b, spi_b, CHILD_DELETING); + + /* this should never get called as there is no successful rekeying on + * either side */ + assert_hook_not_called(child_rekey); + + /* RFC 7296, 2.25.1: If a peer receives a request to rekey a CHILD_SA that + * it is currently trying to close, it SHOULD reply with TEMPORARY_FAILURE. + */ + + /* CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } --> */ + assert_hook_not_called(child_updown); + assert_notify(IN, REKEY_SA); + assert_single_notify(OUT, TEMPORARY_FAILURE); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, spi_b, CHILD_DELETING); + assert_hook(); + + /* RFC 7296, 2.25.1: If a peer receives a request to delete a CHILD_SA that + * it is currently trying to rekey, it SHOULD reply as usual, with a DELETE + * payload. + */ + + /* <-- INFORMATIONAL { D } */ + assert_hook_updown(child_updown, FALSE); + assert_single_payload(IN, PLV2_DELETE); + assert_single_payload(OUT, PLV2_DELETE); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_count(a, 0); + assert_hook(); + + /* <-- CREATE_CHILD_SA { N(TEMP_FAIL) } */ + assert_hook_not_called(child_updown); + /* we don't expect a job to retry the rekeying */ + assert_no_jobs_scheduled(); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_scheduler(); + assert_hook(); + + /* INFORMATIONAL { D } --> */ + assert_hook_updown(child_updown, FALSE); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_count(b, 0); + assert_hook(); + + /* child_rekey */ + assert_hook(); + + assert_sa_idle(a); + assert_sa_idle(b); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); +} +END_TEST + +/** + * One of the hosts initiates a DELETE of the CHILD_SA the other peer is + * concurrently trying to rekey. However, the delete request is delayed or + * dropped, so the peer doing the rekeying is unaware of the collision. + * + * rekey ----\ /---- delete + * \-----/----> detect collision + * reschedule <---------/------ TEMP_FAIL + * <--------/ + * delete ----------------> + * + * The job will not find the SA to retry rekeying. + */ +START_TEST(test_collision_delete_drop_delete) +{ + ike_sa_t *a, *b; + message_t *msg; + uint32_t spi_a = _i+1, spi_b = 2-_i; + + if (_i) + { /* responder rekeys the CHILD_SA (SPI 2) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &b, &a, NULL); + } + else + { /* initiator rekeys the CHILD_SA (SPI 1) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + } + initiate_rekey(a, spi_a); + call_ikesa(b, delete_child_sa, PROTO_ESP, spi_b, FALSE); + assert_child_sa_state(b, spi_b, CHILD_DELETING); + + /* this should never get called as there is no successful rekeying on + * either side */ + assert_hook_not_called(child_rekey); + + /* RFC 7296, 2.25.1: If a peer receives a request to rekey a CHILD_SA that + * it is currently trying to close, it SHOULD reply with TEMPORARY_FAILURE. + */ + + /* CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } --> */ + assert_hook_not_called(child_updown); + assert_notify(IN, REKEY_SA); + assert_single_notify(OUT, TEMPORARY_FAILURE); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_state(b, spi_b, CHILD_DELETING); + assert_hook(); + + /* delay the DELETE request */ + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + + /* <-- CREATE_CHILD_SA { N(TEMP_FAIL) } */ + assert_hook_not_called(child_updown); + /* we expect a job to retry the rekeying is scheduled */ + assert_jobs_scheduled(1); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, spi_a, CHILD_INSTALLED); + assert_scheduler(); + assert_hook(); + + /* <-- INFORMATIONAL { D } (delayed) */ + assert_hook_updown(child_updown, FALSE); + assert_single_payload(IN, PLV2_DELETE); + assert_single_payload(OUT, PLV2_DELETE); + exchange_test_helper->process_message(exchange_test_helper, a, msg); + assert_child_sa_count(a, 0); + assert_hook(); + + /* INFORMATIONAL { D } --> */ + assert_hook_updown(child_updown, FALSE); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_count(b, 0); + assert_hook(); + + /* child_rekey */ + assert_hook(); + + assert_sa_idle(a); + assert_sa_idle(b); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); +} +END_TEST + +/** + * One of the hosts initiates a DELETE of the CHILD_SA the other peer is + * concurrently trying to rekey. However, the rekey request is delayed or + * dropped, so the peer doing the deleting is unaware of the collision. + * + * rekey ----\ /---- delete + * detect collision <----\-----/ + * delete ------\---------> + * \--------> + * /---- CHILD_SA_NOT_FOUND + * aborts rekeying <----------/ + */ + START_TEST(test_collision_delete_drop_rekey) +{ + ike_sa_t *a, *b; + message_t *msg; + uint32_t spi_a = _i+1, spi_b = 2-_i; + + if (_i) + { /* responder rekeys the CHILD_SA (SPI 2) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &b, &a, NULL); + } + else + { /* initiator rekeys the CHILD_SA (SPI 1) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + } + initiate_rekey(a, spi_a); + call_ikesa(b, delete_child_sa, PROTO_ESP, spi_b, FALSE); + assert_child_sa_state(b, spi_b, CHILD_DELETING); + + /* this should never get called as there is no successful rekeying on + * either side */ + assert_hook_not_called(child_rekey); + + /* delay the CREATE_CHILD_SA request */ + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + + /* RFC 7296, 2.25.1: If a peer receives a request to delete a CHILD_SA that + * it is currently trying to rekey, it SHOULD reply as usual, with a DELETE + * payload. + */ + + /* <-- INFORMATIONAL { D } */ + assert_hook_updown(child_updown, FALSE); + assert_single_payload(IN, PLV2_DELETE); + assert_single_payload(OUT, PLV2_DELETE); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_count(a, 0); + assert_hook(); + + /* INFORMATIONAL { D } --> */ + assert_hook_updown(child_updown, FALSE); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_child_sa_count(b, 0); + assert_hook(); + + /* RFC 7296, 2.25.1: If a peer receives a to rekey a Child SA that does not + * exist, it SHOULD reply with CHILD_SA_NOT_FOUND. + */ + + /* CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } --> (delayed) */ + assert_hook_not_called(child_updown); + assert_notify(IN, REKEY_SA); + assert_single_notify(OUT, CHILD_SA_NOT_FOUND); + exchange_test_helper->process_message(exchange_test_helper, b, msg); + assert_hook(); + + /* <-- CREATE_CHILD_SA { N(NO_CHILD_SA) } */ + assert_hook_not_called(child_updown); + /* no jobs or tasks should get scheduled/queued */ + assert_no_jobs_scheduled(); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_scheduler(); + assert_hook(); + + /* child_rekey */ + assert_hook(); + + assert_sa_idle(a); + assert_sa_idle(b); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); +} +END_TEST + +/** + * FIXME: Not sure what we can do about the following: + * + * One of the hosts initiates a rekeying of a CHILD_SA and after responding to + * it the other peer deletes the new SA. However, the rekey response is + * delayed or dropped, so the peer doing the rekeying receives a delete for an + * unknown CHILD_SA and then has a rekeyed CHILD_SA that should not exist. + * + * rekey ----------------> + * /---- rekey + * unknown SA <----------/----- delete new SA + * ----------/-----> + * <--------/ + * + * The peers' states are now out of sync. + * + * Perhaps the rekey initiator could keep track of deletes for non-existing SAs + * while rekeying and then check against the SPIs when handling the + * CREATE_CHILD_SA response. + */ + + +/** + * One of the hosts initiates a rekey of the IKE_SA of the CHILD_SA the other + * peer is concurrently trying to rekey. + * + * rekey ----\ /---- rekey IKE + * \-----/----> detect collision + * detect collision <---------/ /---- TEMP_FAIL + * TEMP_FAIL ----\ / + * \----/-----> + * <--------/ + */ +START_TEST(test_collision_ike_rekey) +{ + ike_sa_t *a, *b; + uint32_t spi_a = _i+1; + + if (_i) + { /* responder rekeys the CHILD_SA (SPI 2) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &b, &a, NULL); + } + else + { /* initiator rekeys the CHILD_SA (SPI 1) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + } + initiate_rekey(a, spi_a); + call_ikesa(b, rekey); + assert_ike_sa_state(b, IKE_REKEYING); + + /* these should never get called as there is no successful rekeying on + * either side */ + assert_hook_not_called(ike_rekey); + assert_hook_not_called(child_rekey); + + /* RFC 7296, 2.25.2: If a peer receives a request to rekey a CHILD_SA when + * it is currently rekeying the IKE SA, it SHOULD reply with + * TEMPORARY_FAILURE. + */ + + /* CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } --> */ + assert_single_notify(OUT, TEMPORARY_FAILURE); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_REKEYING); + + /* RFC 7296, 2.25.1: If a peer receives a request to rekey the IKE SA, and + * it is currently, rekeying, or closing a Child SA of that IKE SA, it + * SHOULD reply with TEMPORARY_FAILURE. + */ + + /* <-- CREATE_CHILD_SA { SA, Ni, KEi } */ + assert_single_notify(OUT, TEMPORARY_FAILURE); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, spi_a, CHILD_REKEYING); + + /* <-- CREATE_CHILD_SA { N(TEMP_FAIL) } */ + /* we expect a job to retry the rekeying is scheduled */ + assert_jobs_scheduled(1); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, spi_a, CHILD_INSTALLED); + assert_scheduler(); + + /* CREATE_CHILD_SA { N(TEMP_FAIL) } --> */ + /* we expect a job to retry the rekeying is scheduled */ + assert_jobs_scheduled(1); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_ESTABLISHED); + assert_scheduler(); + + /* ike_rekey/child_rekey */ + assert_hook(); + assert_hook(); + + assert_sa_idle(a); + assert_sa_idle(b); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); +} +END_TEST + +/** + * One of the hosts initiates a delete of the IKE_SA of the CHILD_SA the other + * peer is concurrently trying to rekey. + * + * rekey ----\ /---- delete IKE + * \-----/----> detect collision + * <---------/ /---- TEMP_FAIL + * delete ----\ / + * \----/-----> + * sa already gone <--------/ + */ +START_TEST(test_collision_ike_delete) +{ + ike_sa_t *a, *b; + uint32_t spi_a = _i+1; + message_t *msg; + status_t s; + + if (_i) + { /* responder rekeys the CHILD_SA (SPI 2) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &b, &a, NULL); + } + else + { /* initiator rekeys the CHILD_SA (SPI 1) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + } + initiate_rekey(a, spi_a); + call_ikesa(b, delete); + assert_ike_sa_state(b, IKE_DELETING); + + /* this should never get called as there is no successful rekeying on + * either side */ + assert_hook_not_called(child_rekey); + + /* RFC 7296, 2.25.2 does not explicitly state what the behavior SHOULD be if + * a peer receives a request to rekey a CHILD_SA when it is currently + * closing the IKE SA. We expect a TEMPORARY_FAILURE notify. + */ + + /* CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } --> */ + assert_single_notify(OUT, TEMPORARY_FAILURE); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_DELETING); + + /* RFC 7296, 2.25.1 does not explicitly state what the behavior SHOULD be if + * a peer receives a request to close the IKE SA if it is currently rekeying + * a Child SA of that IKE SA. Let's just close the IKE_SA and forget the + * rekeying. + */ + + /* <-- INFORMATIONAL { D } */ + assert_hook_updown(ike_updown, FALSE); + assert_hook_updown(child_updown, FALSE); + assert_message_empty(OUT); + s = exchange_test_helper->process_message(exchange_test_helper, a, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(a, destroy); + assert_hook(); + assert_hook(); + + /* <-- CREATE_CHILD_SA { N(TEMP_FAIL) } */ + /* the SA is already gone */ + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + msg->destroy(msg); + + /* INFORMATIONAL { } --> */ + assert_hook_updown(ike_updown, FALSE); + assert_hook_updown(child_updown, FALSE); + s = exchange_test_helper->process_message(exchange_test_helper, b, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(b, destroy); + assert_hook(); + assert_hook(); + + /* child_rekey */ + assert_hook(); +} +END_TEST + +Suite *child_rekey_suite_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("child rekey"); + + tc = tcase_create("regular"); + tcase_add_loop_test(tc, test_regular, 0, 2); + tcase_add_loop_test(tc, test_regular_ke_invalid, 0, 2); + tcase_add_test(tc, test_regular_responder_ignore_soft_expire); + tcase_add_test(tc, test_regular_responder_handle_hard_expire); + suite_add_tcase(s, tc); + + tc = tcase_create("collisions rekey"); + tcase_add_loop_test(tc, test_collision, 0, 4); + tcase_add_loop_test(tc, test_collision_delayed_response, 0, 4); + tcase_add_loop_test(tc, test_collision_delayed_request, 0, 3); + tcase_add_loop_test(tc, test_collision_delayed_request_more, 0, 3); + tcase_add_loop_test(tc, test_collision_ke_invalid, 0, 4); + tcase_add_loop_test(tc, test_collision_ke_invalid_delayed_retry, 0, 3); + suite_add_tcase(s, tc); + + tc = tcase_create("collisions delete"); + tcase_add_loop_test(tc, test_collision_delete, 0, 2); + tcase_add_loop_test(tc, test_collision_delete_drop_delete, 0, 2); + tcase_add_loop_test(tc, test_collision_delete_drop_rekey, 0, 2); + suite_add_tcase(s, tc); + + tc = tcase_create("collisions ike rekey"); + tcase_add_loop_test(tc, test_collision_ike_rekey, 0, 2); + suite_add_tcase(s, tc); + + tc = tcase_create("collisions ike delete"); + tcase_add_loop_test(tc, test_collision_ike_delete, 0, 2); + suite_add_tcase(s, tc); + + return s; +} diff --git a/src/libcharon/tests/suites/test_ike_delete.c b/src/libcharon/tests/suites/test_ike_delete.c new file mode 100644 index 000000000..d79f9bc50 --- /dev/null +++ b/src/libcharon/tests/suites/test_ike_delete.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * 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 "test_suite.h" + +#include <tests/utils/exchange_test_helper.h> +#include <tests/utils/exchange_test_asserts.h> +#include <tests/utils/sa_asserts.h> + +/** + * Regular IKE_SA delete either initiated by the original initiator or + * responder of the IKE_SA. + */ +START_TEST(test_regular) +{ + ike_sa_t *a, *b; + status_t s; + + if (_i) + { /* responder deletes the IKE_SA */ + exchange_test_helper->establish_sa(exchange_test_helper, + &b, &a, NULL); + } + else + { /* initiator deletes the IKE_SA */ + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + } + assert_hook_not_called(ike_updown); + assert_hook_not_called(child_updown); + call_ikesa(a, delete); + assert_ike_sa_state(a, IKE_DELETING); + assert_hook(); + assert_hook(); + + /* INFORMATIONAL { D } --> */ + assert_hook_updown(ike_updown, FALSE); + assert_hook_updown(child_updown, FALSE); + assert_single_payload(IN, PLV2_DELETE); + s = exchange_test_helper->process_message(exchange_test_helper, b, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(b, destroy); + assert_hook(); + assert_hook(); + + /* <-- INFORMATIONAL { } */ + assert_hook_updown(ike_updown, FALSE); + assert_hook_updown(child_updown, FALSE); + assert_message_empty(IN); + s = exchange_test_helper->process_message(exchange_test_helper, a, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(a, destroy); + assert_hook(); + assert_hook(); +} +END_TEST + +/** + * Both peers initiate the IKE_SA deletion concurrently and should handle the + * collision properly. + */ +START_TEST(test_collision) +{ + ike_sa_t *a, *b; + status_t s; + + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + + assert_hook_not_called(ike_updown); + assert_hook_not_called(child_updown); + call_ikesa(a, delete); + assert_ike_sa_state(a, IKE_DELETING); + call_ikesa(b, delete); + assert_ike_sa_state(b, IKE_DELETING); + assert_hook(); + assert_hook(); + + /* RFC 7296 says: If a peer receives a request to close an IKE SA that it + * is currently trying to close, it SHOULD reply as usual, and forget about + * its own close request. + * So we expect the SA to just get closed with an empty response still sent. + */ + + /* INFORMATIONAL { D } --> */ + assert_hook_updown(ike_updown, FALSE); + assert_hook_updown(child_updown, FALSE); + assert_single_payload(IN, PLV2_DELETE); + assert_message_empty(OUT); + s = exchange_test_helper->process_message(exchange_test_helper, b, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(b, destroy); + assert_hook(); + assert_hook(); + + /* <-- INFORMATIONAL { D } */ + assert_hook_updown(ike_updown, FALSE); + assert_hook_updown(child_updown, FALSE); + assert_single_payload(IN, PLV2_DELETE); + assert_message_empty(OUT); + s = exchange_test_helper->process_message(exchange_test_helper, a, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(a, destroy); + assert_hook(); + assert_hook(); +} +END_TEST + +Suite *ike_delete_suite_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("ike delete"); + + tc = tcase_create("regular"); + tcase_add_loop_test(tc, test_regular, 0, 2); + suite_add_tcase(s, tc); + + tc = tcase_create("collisions"); + tcase_add_test(tc, test_collision); + suite_add_tcase(s, tc); + + return s; +} diff --git a/src/libcharon/tests/suites/test_ike_rekey.c b/src/libcharon/tests/suites/test_ike_rekey.c new file mode 100644 index 000000000..ba39657a4 --- /dev/null +++ b/src/libcharon/tests/suites/test_ike_rekey.c @@ -0,0 +1,1480 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * 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 "test_suite.h" + +#include <tests/utils/exchange_test_helper.h> +#include <tests/utils/exchange_test_asserts.h> +#include <tests/utils/job_asserts.h> +#include <tests/utils/sa_asserts.h> + +/** + * Initiate rekeying the given IKE_SA. + */ +#define initiate_rekey(sa) ({ \ + assert_hook_not_called(ike_rekey); \ + call_ikesa(sa, rekey); \ + assert_ike_sa_state(a, IKE_REKEYING); \ + assert_hook(); \ +}) + +/** + * Regular IKE_SA rekeying either initiated by the original initiator or + * responder of the IKE_SA. + */ +START_TEST(test_regular) +{ + ike_sa_t *a, *b, *new_sa; + status_t s; + + if (_i) + { /* responder rekeys the IKE_SA */ + exchange_test_helper->establish_sa(exchange_test_helper, + &b, &a, NULL); + } + else + { /* initiator rekeys the IKE_SA */ + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + } + /* these should never get called as this results in a successful rekeying */ + assert_hook_not_called(ike_updown); + assert_hook_not_called(child_updown); + + initiate_rekey(a); + + /* CREATE_CHILD_SA { SA, Ni, KEi } --> */ + assert_hook_rekey(ike_rekey, 1, 3); + assert_no_notify(IN, REKEY_SA); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_REKEYED); + assert_child_sa_count(b, 0); + new_sa = assert_ike_sa_checkout(3, 4, FALSE); + assert_ike_sa_state(new_sa, IKE_ESTABLISHED); + assert_child_sa_count(new_sa, 1); + assert_ike_sa_count(1); + assert_hook(); + + /* <-- CREATE_CHILD_SA { SA, Nr, KEr } */ + assert_hook_rekey(ike_rekey, 1, 3); + assert_no_notify(IN, REKEY_SA); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_ike_sa_state(a, IKE_DELETING); + assert_child_sa_count(a, 0); + new_sa = assert_ike_sa_checkout(3, 4, TRUE); + assert_ike_sa_state(new_sa, IKE_ESTABLISHED); + assert_child_sa_count(new_sa, 1); + assert_ike_sa_count(2); + assert_hook(); + + /* we don't expect this hook to get called anymore */ + assert_hook_not_called(ike_rekey); + + /* INFORMATIONAL { D } --> */ + assert_single_payload(IN, PLV2_DELETE); + s = exchange_test_helper->process_message(exchange_test_helper, b, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(b, destroy); + /* <-- INFORMATIONAL { } */ + assert_message_empty(IN); + s = exchange_test_helper->process_message(exchange_test_helper, a, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(a, destroy); + + /* ike_rekey/ike_updown/child_updown */ + assert_hook(); + assert_hook(); + assert_hook(); + + charon->ike_sa_manager->flush(charon->ike_sa_manager); +} +END_TEST + +/** + * IKE_SA rekeying where the responder does not agree with the DH group selected + * by the initiator, either initiated by the original initiator or responder of + * the IKE_SA. + */ +START_TEST(test_regular_ke_invalid) +{ + exchange_test_sa_conf_t conf = { + .initiator = { + .ike = "aes128-sha256-modp2048-modp3072", + }, + .responder = { + .ike = "aes128-sha256-modp3072-modp2048", + }, + }; + ike_sa_t *a, *b, *sa; + status_t s; + + lib->settings->set_bool(lib->settings, "%s.prefer_configured_proposals", + FALSE, lib->ns); + if (_i) + { /* responder rekeys the IKE_SA */ + exchange_test_helper->establish_sa(exchange_test_helper, + &b, &a, &conf); + } + else + { /* initiator rekeys the IKE_SA */ + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, &conf); + } + /* these should never get called as this results in a successful rekeying */ + assert_hook_not_called(ike_updown); + assert_hook_not_called(child_updown); + + lib->settings->set_bool(lib->settings, "%s.prefer_configured_proposals", + TRUE, lib->ns); + + initiate_rekey(a); + + /* CREATE_CHILD_SA { SA, Ni, KEi } --> */ + assert_hook_not_called(ike_rekey); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_ESTABLISHED); + assert_child_sa_count(b, 1); + assert_ike_sa_count(0); + + /* <-- CREATE_CHILD_SA { N(INVAL_KE) } */ + assert_single_notify(IN, INVALID_KE_PAYLOAD); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_ike_sa_state(a, IKE_REKEYING); + assert_child_sa_count(a, 1); + assert_ike_sa_count(0); + assert_hook(); + + /* CREATE_CHILD_SA { SA, Ni, KEi } --> */ + assert_hook_rekey(ike_rekey, 1, 3); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_REKEYED); + assert_child_sa_count(b, 0); + sa = assert_ike_sa_checkout(3, 5, FALSE); + assert_ike_sa_state(sa, IKE_ESTABLISHED); + assert_child_sa_count(sa, 1); + assert_ike_sa_count(1); + assert_hook(); + + /* <-- CREATE_CHILD_SA { SA, Nr, KEr } */ + assert_hook_rekey(ike_rekey, 1, 3); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_ike_sa_state(a, IKE_DELETING); + assert_child_sa_count(a, 0); + sa = assert_ike_sa_checkout(3, 5, TRUE); + assert_ike_sa_state(sa, IKE_ESTABLISHED); + assert_child_sa_count(sa, 1); + assert_ike_sa_count(2); + assert_hook(); + + /* we don't expect this hook to get called anymore */ + assert_hook_not_called(ike_rekey); + + /* INFORMATIONAL { D } --> */ + assert_single_payload(IN, PLV2_DELETE); + s = exchange_test_helper->process_message(exchange_test_helper, b, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(b, destroy); + /* <-- INFORMATIONAL { } */ + assert_message_empty(IN); + s = exchange_test_helper->process_message(exchange_test_helper, a, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(a, destroy); + + /* ike_rekey/ike_updown/child_updown */ + assert_hook(); + assert_hook(); + assert_hook(); + + charon->ike_sa_manager->flush(charon->ike_sa_manager); +} +END_TEST + +/** + * Both peers initiate the IKE_SA rekeying concurrently and should handle the + * collision properly depending on the nonces. + */ +START_TEST(test_collision) +{ + ike_sa_t *a, *b, *sa; + status_t status; + + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + + /* When rekeyings collide we get two IKE_SAs with a total of four nonces. + * The IKE_SA with the lowest nonce SHOULD be deleted by the peer that + * created that IKE_SA. The replaced IKE_SA is deleted by the peer that + * initiated the surviving SA. + * Four nonces and SPIs are needed (SPI 1 and 2 are used for the initial + * IKE_SA): + * N1/3 -----\ /----- N2/4 + * \--/-----> N3/5 + * N4/6 <-------/ /----- ... + * ... -----\ + * We test this four times, each time a different nonce is the lowest. + */ + struct { + /* Nonces used at each point */ + u_char nonces[4]; + /* SPIs of the deleted IKE_SAs (either redundant or replaced) */ + uint32_t del_a_i, del_a_r; + uint32_t del_b_i, del_b_r; + /* SPIs of the kept IKE_SA */ + uint32_t spi_i, spi_r; + } data[] = { + { { 0x00, 0xFF, 0xFF, 0xFF }, 3, 5, 1, 2, 4, 6 }, + { { 0xFF, 0x00, 0xFF, 0xFF }, 1, 2, 4, 6, 3, 5 }, + { { 0xFF, 0xFF, 0x00, 0xFF }, 3, 5, 1, 2, 4, 6 }, + { { 0xFF, 0xFF, 0xFF, 0x00 }, 1, 2, 4, 6, 3, 5 }, + }; + /* these should never get called as this results in a successful rekeying */ + assert_hook_not_called(ike_updown); + assert_hook_not_called(child_updown); + + exchange_test_helper->nonce_first_byte = data[_i].nonces[0]; + initiate_rekey(a); + exchange_test_helper->nonce_first_byte = data[_i].nonces[1]; + initiate_rekey(b); + + /* CREATE_CHILD_SA { SA, Ni, KEi } --> */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[2]; + assert_hook_not_called(ike_rekey); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_REKEYING); + assert_child_sa_count(b, 1); + assert_ike_sa_count(0); + assert_hook(); + + /* <-- CREATE_CHILD_SA { SA, Ni, KEi } */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[3]; + assert_hook_not_called(ike_rekey); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_ike_sa_state(a, IKE_REKEYING); + assert_child_sa_count(a, 1); + assert_ike_sa_count(0); + assert_hook(); + + /* simplify next steps by checking in original IKE_SAs */ + charon->ike_sa_manager->checkin(charon->ike_sa_manager, a); + charon->ike_sa_manager->checkin(charon->ike_sa_manager, b); + assert_ike_sa_count(2); + + /* <-- CREATE_CHILD_SA { SA, Nr, KEr } */ + assert_hook_rekey(ike_rekey, 1, data[_i].spi_i); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + /* as original initiator a is initiator of both SAs it could delete */ + sa = assert_ike_sa_checkout(data[_i].del_a_i, data[_i].del_a_r, TRUE); + assert_ike_sa_state(sa, IKE_DELETING); + assert_child_sa_count(sa, 0); + /* if b won it will delete the original SA a initiated */ + sa = assert_ike_sa_checkout(data[_i].del_b_i, data[_i].del_b_r, + data[_i].del_b_i == 1); + assert_ike_sa_state(sa, IKE_REKEYED); + assert_child_sa_count(sa, 0); + sa = assert_ike_sa_checkout(data[_i].spi_i, data[_i].spi_r, + data[_i].del_a_i == 1); + assert_ike_sa_state(sa, IKE_ESTABLISHED); + assert_child_sa_count(sa, 1); + assert_ike_sa_count(4); + assert_hook(); + + /* CREATE_CHILD_SA { SA, Nr, KEr } --> */ + assert_hook_rekey(ike_rekey, 1, data[_i].spi_i); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + /* if b wins it deletes the SA originally initiated by a */ + sa = assert_ike_sa_checkout(data[_i].del_b_i, data[_i].del_b_r, + data[_i].del_b_i != 1); + assert_ike_sa_state(sa, IKE_DELETING); + assert_child_sa_count(sa, 0); + /* a only deletes SAs for which b is responder */ + sa = assert_ike_sa_checkout(data[_i].del_a_i, data[_i].del_a_r, FALSE); + assert_ike_sa_state(sa, IKE_REKEYED); + assert_child_sa_count(sa, 0); + sa = assert_ike_sa_checkout(data[_i].spi_i, data[_i].spi_r, + data[_i].del_b_i == 1); + assert_ike_sa_state(sa, IKE_ESTABLISHED); + assert_child_sa_count(sa, 1); + assert_ike_sa_count(6); + assert_hook(); + + /* we don't expect this hook to get called anymore */ + assert_hook_not_called(ike_rekey); + + /* INFORMATIONAL { D } --> */ + assert_single_payload(IN, PLV2_DELETE); + sa = assert_ike_sa_checkout(data[_i].del_a_i, data[_i].del_a_r, FALSE); + status = exchange_test_helper->process_message(exchange_test_helper, sa, + NULL); + ck_assert_int_eq(DESTROY_ME, status); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, sa); + assert_ike_sa_count(5); + /* <-- INFORMATIONAL { D } */ + assert_single_payload(IN, PLV2_DELETE); + sa = assert_ike_sa_checkout(data[_i].del_b_i, data[_i].del_b_r, + data[_i].del_b_i == 1); + status = exchange_test_helper->process_message(exchange_test_helper, sa, + NULL); + ck_assert_int_eq(DESTROY_ME, status); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, sa); + assert_ike_sa_count(4); + /* <-- INFORMATIONAL { } */ + assert_message_empty(IN); + sa = assert_ike_sa_checkout(data[_i].del_a_i, data[_i].del_a_r, TRUE); + status = exchange_test_helper->process_message(exchange_test_helper, sa, + NULL); + ck_assert_int_eq(DESTROY_ME, status); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, sa); + assert_ike_sa_count(3); + /* INFORMATIONAL { } --> */ + assert_message_empty(IN); + sa = assert_ike_sa_checkout(data[_i].del_b_i, data[_i].del_b_r, + data[_i].del_b_i != 1); + status = exchange_test_helper->process_message(exchange_test_helper, sa, + NULL); + ck_assert_int_eq(DESTROY_ME, status); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, sa); + assert_ike_sa_count(2); + + /* ike_rekey/ike_updown/child_updown */ + assert_hook(); + assert_hook(); + assert_hook(); + + charon->ike_sa_manager->flush(charon->ike_sa_manager); +} +END_TEST + +/** + * Both peers initiate the IKE_SA rekeying concurrently but the proposed DH + * gropus are not the same. After handling the INVALID_KE_PAYLOAD they should + * still handle the collision properly depending on the nonces. + */ +START_TEST(test_collision_ke_invalid) +{ + exchange_test_sa_conf_t conf = { + .initiator = { + .ike = "aes128-sha256-modp2048-modp3072", + }, + .responder = { + .ike = "aes128-sha256-modp3072-modp2048", + }, + }; + ike_sa_t *a, *b, *sa; + status_t status; + + lib->settings->set_bool(lib->settings, "%s.prefer_configured_proposals", + FALSE, lib->ns); + + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, &conf); + + lib->settings->set_bool(lib->settings, "%s.prefer_configured_proposals", + TRUE, lib->ns); + + /* Six nonces and SPIs are needed (SPI 1 and 2 are used for the initial + * IKE_SA): + * N1/3 -----\ /----- N2/4 + * \--/-----> N3/5 + * N4/6 <-------/ /---- INVAL_KE + * INVAL_KE -----\ / + * <-----\--/ + * N1/3 -----\ \-------> + * \ /---- N2/4 + * \--/----> N5/7 + * N6/8 <--------/ /---- ... + * ... ------\ + * We test this four times, each time a different nonce is the lowest. + */ + struct { + /* Nonces used at each point */ + u_char nonces[4]; + /* SPIs of the deleted IKE_SAs (either redundant or replaced) */ + uint32_t del_a_i, del_a_r; + uint32_t del_b_i, del_b_r; + /* SPIs of the kept IKE_SA */ + uint32_t spi_i, spi_r; + } data[] = { + { { 0x00, 0xFF, 0xFF, 0xFF }, 3, 7, 1, 2, 4, 8 }, + { { 0xFF, 0x00, 0xFF, 0xFF }, 1, 2, 4, 8, 3, 7 }, + { { 0xFF, 0xFF, 0x00, 0xFF }, 3, 7, 1, 2, 4, 8 }, + { { 0xFF, 0xFF, 0xFF, 0x00 }, 1, 2, 4, 8, 3, 7 }, + }; + /* these should never get called as this results in a successful rekeying */ + assert_hook_not_called(ike_updown); + assert_hook_not_called(child_updown); + + exchange_test_helper->nonce_first_byte = data[_i].nonces[0]; + initiate_rekey(a); + exchange_test_helper->nonce_first_byte = data[_i].nonces[1]; + initiate_rekey(b); + + /* CREATE_CHILD_SA { SA, Ni, KEi } --> */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[2]; + assert_hook_not_called(ike_rekey); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_REKEYING); + assert_child_sa_count(b, 1); + assert_ike_sa_count(0); + assert_hook(); + + /* <-- CREATE_CHILD_SA { SA, Ni, KEi } */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[3]; + assert_hook_not_called(ike_rekey); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_ike_sa_state(a, IKE_REKEYING); + assert_child_sa_count(a, 1); + assert_ike_sa_count(0); + assert_hook(); + + /* <-- CREATE_CHILD_SA { N(INVAL_KE) } */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[0]; + assert_hook_not_called(ike_rekey); + assert_single_notify(IN, INVALID_KE_PAYLOAD); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_ike_sa_state(a, IKE_REKEYING); + assert_child_sa_count(a, 1); + assert_ike_sa_count(0); + assert_hook(); + + /* CREATE_CHILD_SA { N(INVAL_KE) } --> */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[1]; + assert_hook_not_called(child_rekey); + assert_single_notify(IN, INVALID_KE_PAYLOAD); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_REKEYING); + assert_child_sa_count(b, 1); + assert_ike_sa_count(0); + assert_hook(); + + /* CREATE_CHILD_SA { SA, Ni, KEi } --> */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[2]; + assert_hook_not_called(ike_rekey); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_REKEYING); + assert_child_sa_count(b, 1); + assert_ike_sa_count(0); + assert_hook(); + + /* <-- CREATE_CHILD_SA { SA, Ni, KEi } */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[3]; + assert_hook_not_called(ike_rekey); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_ike_sa_state(a, IKE_REKEYING); + assert_child_sa_count(a, 1); + assert_ike_sa_count(0); + assert_hook(); + + /* simplify next steps by checking in original IKE_SAs */ + charon->ike_sa_manager->checkin(charon->ike_sa_manager, a); + charon->ike_sa_manager->checkin(charon->ike_sa_manager, b); + assert_ike_sa_count(2); + + /* <-- CREATE_CHILD_SA { SA, Nr, KEr } */ + assert_hook_rekey(ike_rekey, 1, data[_i].spi_i); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + /* as original initiator a is initiator of both SAs it could delete */ + sa = assert_ike_sa_checkout(data[_i].del_a_i, data[_i].del_a_r, TRUE); + assert_ike_sa_state(sa, IKE_DELETING); + assert_child_sa_count(sa, 0); + /* if b won it will delete the original SA a initiated */ + sa = assert_ike_sa_checkout(data[_i].del_b_i, data[_i].del_b_r, + data[_i].del_b_i == 1); + assert_ike_sa_state(sa, IKE_REKEYED); + assert_child_sa_count(sa, 0); + sa = assert_ike_sa_checkout(data[_i].spi_i, data[_i].spi_r, + data[_i].del_a_i == 1); + assert_ike_sa_state(sa, IKE_ESTABLISHED); + assert_child_sa_count(sa, 1); + assert_ike_sa_count(4); + assert_hook(); + + /* CREATE_CHILD_SA { SA, Nr, KEr } --> */ + assert_hook_rekey(ike_rekey, 1, data[_i].spi_i); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + /* if b wins it deletes the SA originally initiated by a */ + sa = assert_ike_sa_checkout(data[_i].del_b_i, data[_i].del_b_r, + data[_i].del_b_i != 1); + assert_ike_sa_state(sa, IKE_DELETING); + assert_child_sa_count(sa, 0); + /* a only deletes SAs for which b is responder */ + sa = assert_ike_sa_checkout(data[_i].del_a_i, data[_i].del_a_r, FALSE); + assert_ike_sa_state(sa, IKE_REKEYED); + assert_child_sa_count(sa, 0); + sa = assert_ike_sa_checkout(data[_i].spi_i, data[_i].spi_r, + data[_i].del_b_i == 1); + assert_ike_sa_state(sa, IKE_ESTABLISHED); + assert_child_sa_count(sa, 1); + assert_ike_sa_count(6); + assert_hook(); + + /* we don't expect this hook to get called anymore */ + assert_hook_not_called(ike_rekey); + + /* INFORMATIONAL { D } --> */ + assert_single_payload(IN, PLV2_DELETE); + sa = assert_ike_sa_checkout(data[_i].del_a_i, data[_i].del_a_r, FALSE); + status = exchange_test_helper->process_message(exchange_test_helper, sa, + NULL); + ck_assert_int_eq(DESTROY_ME, status); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, sa); + assert_ike_sa_count(5); + /* <-- INFORMATIONAL { D } */ + assert_single_payload(IN, PLV2_DELETE); + sa = assert_ike_sa_checkout(data[_i].del_b_i, data[_i].del_b_r, + data[_i].del_b_i == 1); + status = exchange_test_helper->process_message(exchange_test_helper, sa, + NULL); + ck_assert_int_eq(DESTROY_ME, status); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, sa); + assert_ike_sa_count(4); + /* <-- INFORMATIONAL { } */ + assert_message_empty(IN); + sa = assert_ike_sa_checkout(data[_i].del_a_i, data[_i].del_a_r, TRUE); + status = exchange_test_helper->process_message(exchange_test_helper, sa, + NULL); + ck_assert_int_eq(DESTROY_ME, status); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, sa); + assert_ike_sa_count(3); + /* INFORMATIONAL { } --> */ + assert_message_empty(IN); + sa = assert_ike_sa_checkout(data[_i].del_b_i, data[_i].del_b_r, + data[_i].del_b_i != 1); + status = exchange_test_helper->process_message(exchange_test_helper, sa, + NULL); + ck_assert_int_eq(DESTROY_ME, status); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, sa); + assert_ike_sa_count(2); + + /* ike_rekey/ike_updown/child_updown */ + assert_hook(); + assert_hook(); + assert_hook(); + + charon->ike_sa_manager->flush(charon->ike_sa_manager); +} +END_TEST + +/** + * This is like the collision above but one of the retries is delayed. + */ +START_TEST(test_collision_ke_invalid_delayed_retry) +{ + exchange_test_sa_conf_t conf = { + .initiator = { + .ike = "aes128-sha256-modp2048-modp3072", + }, + .responder = { + .ike = "aes128-sha256-modp3072-modp2048", + }, + }; + ike_sa_t *a, *b, *sa; + message_t *msg; + status_t s; + + lib->settings->set_bool(lib->settings, "%s.prefer_configured_proposals", + FALSE, lib->ns); + + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, &conf); + + lib->settings->set_bool(lib->settings, "%s.prefer_configured_proposals", + TRUE, lib->ns); + + /* Five nonces and SPIs are needed (SPI 1 and 2 are used for the initial + * IKE_SA): + * N1/3 -----\ /----- N2/4 + * \--/-----> N3/5 + * N4/6 <-------/ /---- INVAL_KE + * INVAL_KE -----\ / + * <-----\--/ + * N1/3 -----\ \-------> + * <-----\--------- N2/4 + * N5/7 -------\-------> + * <-------\------- DELETE + * ... ------\ \-----> + * /---- TEMP_FAIL + * + * We test this three times, each time a different nonce is the lowest. + */ + struct { + /* Nonces used at each point */ + u_char nonces[3]; + } data[] = { + { { 0x00, 0xFF, 0xFF } }, + { { 0xFF, 0x00, 0xFF } }, + { { 0xFF, 0xFF, 0x00 } }, + }; + /* these should never get called as this results in a successful rekeying */ + assert_hook_not_called(ike_updown); + assert_hook_not_called(child_updown); + + exchange_test_helper->nonce_first_byte = data[_i].nonces[0]; + initiate_rekey(a); + exchange_test_helper->nonce_first_byte = data[_i].nonces[1]; + initiate_rekey(b); + + /* CREATE_CHILD_SA { SA, Ni, KEi } --> */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[2]; + assert_hook_not_called(ike_rekey); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_REKEYING); + assert_child_sa_count(b, 1); + assert_ike_sa_count(0); + assert_hook(); + + /* <-- CREATE_CHILD_SA { SA, Ni, KEi } */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[0]; + assert_hook_not_called(ike_rekey); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_ike_sa_state(a, IKE_REKEYING); + assert_child_sa_count(a, 1); + assert_ike_sa_count(0); + assert_hook(); + + /* <-- CREATE_CHILD_SA { N(INVAL_KE) } */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[0]; + assert_hook_not_called(ike_rekey); + assert_single_notify(IN, INVALID_KE_PAYLOAD); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_ike_sa_state(a, IKE_REKEYING); + assert_child_sa_count(a, 1); + assert_ike_sa_count(0); + assert_hook(); + + /* CREATE_CHILD_SA { N(INVAL_KE) } --> */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[1]; + assert_hook_not_called(child_rekey); + assert_single_notify(IN, INVALID_KE_PAYLOAD); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_REKEYING); + assert_child_sa_count(b, 1); + assert_ike_sa_count(0); + assert_hook(); + + /* delay the CREATE_CHILD_SA request from a to b */ + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + + /* <-- CREATE_CHILD_SA { SA, Ni, KEi } */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[2]; + assert_hook_not_called(ike_rekey); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_ike_sa_state(a, IKE_REKEYING); + assert_child_sa_count(a, 1); + assert_ike_sa_count(0); + assert_hook(); + + /* CREATE_CHILD_SA { SA, Nr, KEr } --> */ + assert_hook_rekey(ike_rekey, 1, 4); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_DELETING); + assert_child_sa_count(b, 0); + sa = assert_ike_sa_checkout(4, 7, TRUE); + assert_ike_sa_state(sa, IKE_ESTABLISHED); + assert_child_sa_count(sa, 1); + assert_ike_sa_count(1); + assert_hook(); + + /* CREATE_CHILD_SA { SA, Ni, KEi } --> (delayed) */ + assert_single_notify(OUT, TEMPORARY_FAILURE); + exchange_test_helper->process_message(exchange_test_helper, b, msg); + assert_ike_sa_state(b, IKE_DELETING); + + /* <-- INFORMATIONAL { D } */ + assert_hook_rekey(ike_rekey, 1, 4); + assert_single_payload(IN, PLV2_DELETE); + s = exchange_test_helper->process_message(exchange_test_helper, a, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(a, destroy); + sa = assert_ike_sa_checkout(4, 7, FALSE); + assert_ike_sa_state(sa, IKE_ESTABLISHED); + assert_child_sa_count(sa, 1); + assert_ike_sa_count(2); + assert_hook(); + + /* <-- CREATE_CHILD_SA { N(TEMP_FAIL) } */ + /* the SA is already gone */ + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + msg->destroy(msg); + + /* INFORMATIONAL { } --> */ + assert_hook_not_called(ike_rekey); + assert_message_empty(IN); + s = exchange_test_helper->process_message(exchange_test_helper, b, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(b, destroy); + assert_hook(); + + /* ike_updown/child_updown */ + assert_hook(); + assert_hook(); + + charon->ike_sa_manager->flush(charon->ike_sa_manager); +} +END_TEST + +/** + * This is like the rekey collision above, but one peer deletes the + * redundant/old SA before the other peer receives the CREATE_CHILD_SA + * response: + * Peer A Peer B + * rekey ----\ /---- rekey + * \-----/----> detect collision + * detect collision <---------/ /---- + * -----------/----> + * handle delete <---------/------ delete redundant/old SA + * ---------/------> + * handle rekey <-------/ + * delete SA ----------------> + * <---------------- + * + * If peer B won the collision it deletes the old IKE_SA, in which case + * this situation is handled as if peer B was not aware of the collision (see + * below). That is, peer A finalizes the rekeying initiated by the peer and + * deletes the IKE_SA (it has no way of knowing whether the peer was aware of + * the collision or not). Peer B will expect the redundant IKE_SA to get + * deleted, but that will never happen if the response arrives after the SA is + * already gone. So a job should be queued that deletes it after a while. + * + * If peer B lost it will switch to the new IKE_SA and delete the redundant + * IKE_SA and expect a delete for the old IKE_SA. In this case peer A will + * simply retransmit until it receives a response to the rekey request, all the + * while ignoring the delete requests for the unknown IKE_SA. Afterwards, + * everything works as in a regular collision (however, until peer A receives + * the response it will not be able to receive any messages on the new IKE_SA). + */ +START_TEST(test_collision_delayed_response) +{ + ike_sa_t *a, *b, *sa; + message_t *msg, *d; + status_t s; + + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + + /* Four nonces and SPIs are needed (SPI 1 and 2 are used for the initial + * IKE_SA): + * N1/3 -----\ /----- N2/4 + * \--/-----> N3/5 + * N4/6 <-------/ /----- ... + * ... -----\ + * We test this four times, each time a different nonce is the lowest. + */ + struct { + /* Nonces used at each point */ + u_char nonces[4]; + /* SPIs of the deleted IKE_SAs (either redundant or replaced) */ + uint32_t del_a_i, del_a_r; + uint32_t del_b_i, del_b_r; + /* SPIs of the kept IKE_SA */ + uint32_t spi_i, spi_r; + } data[] = { + { { 0x00, 0xFF, 0xFF, 0xFF }, 3, 5, 1, 2, 4, 6 }, + { { 0xFF, 0x00, 0xFF, 0xFF }, 1, 2, 4, 6, 3, 5 }, + { { 0xFF, 0xFF, 0x00, 0xFF }, 3, 5, 1, 2, 4, 6 }, + { { 0xFF, 0xFF, 0xFF, 0x00 }, 1, 2, 4, 6, 3, 5 }, + }; + /* these should never get called as this results in a successful rekeying */ + assert_hook_not_called(ike_updown); + assert_hook_not_called(child_updown); + + exchange_test_helper->nonce_first_byte = data[_i].nonces[0]; + initiate_rekey(a); + exchange_test_helper->nonce_first_byte = data[_i].nonces[1]; + initiate_rekey(b); + + /* CREATE_CHILD_SA { SA, Ni, KEi } --> */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[2]; + assert_hook_not_called(ike_rekey); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_REKEYING); + assert_child_sa_count(b, 1); + assert_ike_sa_count(0); + assert_hook(); + + /* <-- CREATE_CHILD_SA { SA, Ni, KEi } */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[3]; + assert_hook_not_called(ike_rekey); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_ike_sa_state(a, IKE_REKEYING); + assert_child_sa_count(a, 1); + assert_ike_sa_count(0); + assert_hook(); + + /* delay the CREATE_CHILD_SA response from b to a */ + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + + /* simplify next steps by checking in original IKE_SAs */ + charon->ike_sa_manager->checkin(charon->ike_sa_manager, a); + charon->ike_sa_manager->checkin(charon->ike_sa_manager, b); + assert_ike_sa_count(2); + + /* CREATE_CHILD_SA { SA, Nr, KEr } --> */ + assert_hook_rekey(ike_rekey, 1, data[_i].spi_i); + /* besides the job that retransmits the delete, we expect a job that + * deletes the redundant IKE_SA if we expect the other to delete it */ + assert_jobs_scheduled(data[_i].del_b_i == 1 ? 2 : 1); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + /* if b wins it deletes the SA originally initiated by a */ + sa = assert_ike_sa_checkout(data[_i].del_b_i, data[_i].del_b_r, + data[_i].del_b_i != 1); + assert_ike_sa_state(sa, IKE_DELETING); + assert_child_sa_count(sa, 0); + /* a only deletes SAs for which b is responder */ + sa = assert_ike_sa_checkout(data[_i].del_a_i, data[_i].del_a_r, FALSE); + assert_ike_sa_state(sa, IKE_REKEYED); + assert_child_sa_count(sa, 0); + sa = assert_ike_sa_checkout(data[_i].spi_i, data[_i].spi_r, + data[_i].del_b_i == 1); + assert_ike_sa_state(sa, IKE_ESTABLISHED); + assert_child_sa_count(sa, 1); + assert_ike_sa_count(4); + assert_scheduler(); + assert_hook(); + + /* <-- INFORMATIONAL { D } */ + if (data[_i].del_b_i == 1) + { /* b won, it deletes the replaced IKE_SA */ + assert_hook_rekey(ike_rekey, 1, data[_i].spi_i); + assert_single_payload(IN, PLV2_DELETE); + s = exchange_test_helper->process_message(exchange_test_helper, a, + NULL); + ck_assert_int_eq(DESTROY_ME, s); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, a); + sa = assert_ike_sa_checkout(data[_i].spi_i, data[_i].spi_r, FALSE); + assert_ike_sa_state(sa, IKE_ESTABLISHED); + assert_child_sa_count(sa, 1); + assert_ike_sa_count(4); + assert_hook(); + + /* INFORMATIONAL { } --> */ + assert_hook_not_called(ike_rekey); + assert_message_empty(IN); + s = exchange_test_helper->process_message(exchange_test_helper, b, + NULL); + ck_assert_int_eq(DESTROY_ME, s); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, b); + assert_ike_sa_count(3); + assert_hook(); + /* the job will later remove this redundant IKE_SA on b */ + sa = assert_ike_sa_checkout(data[_i].del_a_i, data[_i].del_a_r, FALSE); + assert_ike_sa_state(sa, IKE_REKEYED); + assert_sa_idle(sa); + /* <-- CREATE_CHILD_SA { SA, Nr, KEr } (delayed) */ + /* the IKE_SA (a) does not exist anymore */ + msg->destroy(msg); + } + else + { /* b lost, the delete is for the non-existing redundant IKE_SA */ + d = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + + /* <-- CREATE_CHILD_SA { SA, Nr, KEr } (delayed) */ + assert_hook_rekey(ike_rekey, 1, data[_i].spi_i); + exchange_test_helper->process_message(exchange_test_helper, a, msg); + /* as original initiator a is initiator of both SAs it could delete */ + sa = assert_ike_sa_checkout(data[_i].del_a_i, data[_i].del_a_r, TRUE); + assert_ike_sa_state(sa, IKE_DELETING); + assert_child_sa_count(sa, 0); + /* this is the redundant SA b is trying to delete */ + sa = assert_ike_sa_checkout(data[_i].del_b_i, data[_i].del_b_r, FALSE); + assert_ike_sa_state(sa, IKE_REKEYED); + assert_child_sa_count(sa, 0); + sa = assert_ike_sa_checkout(data[_i].spi_i, data[_i].spi_r, + data[_i].del_a_i == 1); + assert_ike_sa_state(sa, IKE_ESTABLISHED); + assert_child_sa_count(sa, 1); + assert_ike_sa_count(6); + assert_hook(); + + /* we don't expect this hook to get called anymore */ + assert_hook_not_called(ike_rekey); + + /* INFORMATIONAL { D } --> */ + assert_single_payload(IN, PLV2_DELETE); + sa = assert_ike_sa_checkout(data[_i].del_a_i, data[_i].del_a_r, FALSE); + s = exchange_test_helper->process_message(exchange_test_helper, sa, + NULL); + ck_assert_int_eq(DESTROY_ME, s); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, sa); + assert_ike_sa_count(5); + /* <-- INFORMATIONAL { } */ + assert_message_empty(IN); + sa = assert_ike_sa_checkout(data[_i].del_a_i, data[_i].del_a_r, TRUE); + s = exchange_test_helper->process_message(exchange_test_helper, sa, + NULL); + ck_assert_int_eq(DESTROY_ME, s); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, sa); + assert_ike_sa_count(4); + + /* <-- INFORMATIONAL { D } (retransmit/delayed) */ + assert_single_payload(IN, PLV2_DELETE); + sa = assert_ike_sa_checkout(data[_i].del_b_i, data[_i].del_b_r, FALSE); + s = exchange_test_helper->process_message(exchange_test_helper, sa, d); + ck_assert_int_eq(DESTROY_ME, s); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, sa); + assert_ike_sa_count(3); + /* INFORMATIONAL { } --> */ + assert_message_empty(IN); + sa = assert_ike_sa_checkout(data[_i].del_b_i, data[_i].del_b_r, TRUE); + s = exchange_test_helper->process_message(exchange_test_helper, sa, + NULL); + ck_assert_int_eq(DESTROY_ME, s); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, sa); + assert_ike_sa_count(2); + /* ike_rekey */ + assert_hook(); + } + + /* ike_updown/child_updown */ + assert_hook(); + assert_hook(); + + charon->ike_sa_manager->flush(charon->ike_sa_manager); +} +END_TEST + +/** + * In this scenario one of the peers does not notice that there is a rekey + * collision because the other request is dropped: + * + * rekey ----\ /---- rekey + * \ / + * detect collision <-----\---/ + * -------\--------> + * detect collision <-------\-------- delete old SA + * delete ---------\------> + * rekey done \-----> SA not found (or it never arrives) + */ +START_TEST(test_collision_dropped_request) +{ + ike_sa_t *a, *b, *sa; + message_t *msg; + status_t s; + + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + + /* Three nonces and SPIs are needed (SPI 1 and 2 are used for the initial + * CHILD_SA): + * N1/3 -----\ /----- N2/4 + * N3/5 <-----\--/ + * ... -----\ \-------> ... + * We test this three times, each time a different nonce is the lowest. + */ + struct { + /* Nonces used at each point */ + u_char nonces[3]; + /* SPIs of the deleted IKE_SAs (either redundant or replaced) */ + uint32_t del_a_i, del_a_r; + uint32_t del_b_i, del_b_r; + /* SPIs of the kept IKE_SA */ + uint32_t spi_i, spi_r; + } data[] = { + { { 0x00, 0xFF, 0xFF }, 3, 5, 1, 2, 4, 6 }, + { { 0xFF, 0x00, 0xFF }, 1, 2, 4, 6, 3, 5 }, + { { 0xFF, 0xFF, 0x00 }, 3, 5, 1, 2, 4, 6 }, + { { 0xFF, 0xFF, 0xFF }, 1, 2, 4, 6, 3, 5 }, + }; + /* these should never get called as this results in a successful rekeying */ + assert_hook_not_called(ike_updown); + assert_hook_not_called(child_updown); + + exchange_test_helper->nonce_first_byte = data[_i].nonces[0]; + initiate_rekey(a); + /* drop the CREATE_CHILD_SA request from a to b */ + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + msg->destroy(msg); + exchange_test_helper->nonce_first_byte = data[_i].nonces[1]; + initiate_rekey(b); + + /* <-- CREATE_CHILD_SA { SA, Ni, KEi } */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[2]; + assert_hook_not_called(ike_rekey); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_ike_sa_state(a, IKE_REKEYING); + assert_child_sa_count(a, 1); + assert_ike_sa_count(0); + assert_hook(); + + /* CREATE_CHILD_SA { SA, Ni, KEi } --> */ + assert_hook_rekey(ike_rekey, 1, 4); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_DELETING); + assert_child_sa_count(b, 0); + sa = assert_ike_sa_checkout(4, 5, TRUE); + assert_ike_sa_state(sa, IKE_ESTABLISHED); + assert_child_sa_count(sa, 1); + assert_ike_sa_count(1); + assert_hook(); + + /* <-- INFORMATIONAL { D } */ + assert_hook_rekey(ike_rekey, 1, 4); + assert_single_payload(IN, PLV2_DELETE); + s = exchange_test_helper->process_message(exchange_test_helper, a, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(a, destroy); + sa = assert_ike_sa_checkout(4, 5, FALSE); + assert_ike_sa_state(sa, IKE_ESTABLISHED); + assert_child_sa_count(sa, 1); + assert_ike_sa_count(2); + assert_hook(); + + /* INFORMATIONAL { } --> */ + assert_hook_not_called(ike_rekey); + assert_message_empty(IN); + s = exchange_test_helper->process_message(exchange_test_helper, b, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(b, destroy); + assert_hook(); + + /* ike_updown/child_updown */ + assert_hook(); + assert_hook(); + + charon->ike_sa_manager->flush(charon->ike_sa_manager); +} +END_TEST + +/** + * In this scenario one of the peers does not notice that there is a rekey + * collision because the other request is delayed: + * + * rekey ----\ /---- rekey + * \ / + * detect collision <-----\---/ + * -------\--------> + * \ /---- delete old SA + * \-/----> detect collision + * detect collision <---------/ /---- TEMP_FAIL + * delete -----------/----> + * rekey done / + * sa already gone <--------/ + */ +START_TEST(test_collision_delayed_request) +{ + ike_sa_t *a, *b, *sa; + message_t *msg; + status_t s; + + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + + /* Three nonces and SPIs are needed (SPI 1 and 2 are used for the initial + * CHILD_SA): + * N1/3 -----\ /----- N2/4 + * N3/5 <-----\--/ + * ... -----\ \-------> ... + * We test this three times, each time a different nonce is the lowest. + */ + struct { + /* Nonces used at each point */ + u_char nonces[3]; + /* SPIs of the deleted IKE_SAs (either redundant or replaced) */ + uint32_t del_a_i, del_a_r; + uint32_t del_b_i, del_b_r; + /* SPIs of the kept IKE_SA */ + uint32_t spi_i, spi_r; + } data[] = { + { { 0x00, 0xFF, 0xFF }, 3, 5, 1, 2, 4, 6 }, + { { 0xFF, 0x00, 0xFF }, 1, 2, 4, 6, 3, 5 }, + { { 0xFF, 0xFF, 0x00 }, 3, 5, 1, 2, 4, 6 }, + { { 0xFF, 0xFF, 0xFF }, 1, 2, 4, 6, 3, 5 }, + }; + /* these should never get called as this results in a successful rekeying */ + assert_hook_not_called(ike_updown); + assert_hook_not_called(child_updown); + + exchange_test_helper->nonce_first_byte = data[_i].nonces[0]; + initiate_rekey(a); + exchange_test_helper->nonce_first_byte = data[_i].nonces[1]; + initiate_rekey(b); + + /* delay the CREATE_CHILD_SA request from a to b */ + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + + /* <-- CREATE_CHILD_SA { SA, Ni, KEi } */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[2]; + assert_hook_not_called(ike_rekey); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_ike_sa_state(a, IKE_REKEYING); + assert_child_sa_count(a, 1); + assert_ike_sa_count(0); + assert_hook(); + + /* CREATE_CHILD_SA { SA, Ni, KEi } --> */ + assert_hook_rekey(ike_rekey, 1, 4); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_DELETING); + assert_child_sa_count(b, 0); + sa = assert_ike_sa_checkout(4, 5, TRUE); + assert_ike_sa_state(sa, IKE_ESTABLISHED); + assert_child_sa_count(sa, 1); + assert_ike_sa_count(1); + assert_hook(); + + /* CREATE_CHILD_SA { SA, Ni, KEi } --> (delayed) */ + assert_single_notify(OUT, TEMPORARY_FAILURE); + exchange_test_helper->process_message(exchange_test_helper, b, msg); + assert_ike_sa_state(b, IKE_DELETING); + + /* <-- INFORMATIONAL { D } */ + assert_hook_rekey(ike_rekey, 1, 4); + assert_single_payload(IN, PLV2_DELETE); + s = exchange_test_helper->process_message(exchange_test_helper, a, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(a, destroy); + sa = assert_ike_sa_checkout(4, 5, FALSE); + assert_ike_sa_state(sa, IKE_ESTABLISHED); + assert_child_sa_count(sa, 1); + assert_ike_sa_count(2); + assert_hook(); + + /* <-- CREATE_CHILD_SA { N(TEMP_FAIL) } */ + /* the SA is already gone */ + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + msg->destroy(msg); + + /* INFORMATIONAL { } --> */ + assert_hook_not_called(ike_rekey); + assert_message_empty(IN); + s = exchange_test_helper->process_message(exchange_test_helper, b, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(b, destroy); + assert_hook(); + + /* ike_updown/child_updown */ + assert_hook(); + assert_hook(); + + charon->ike_sa_manager->flush(charon->ike_sa_manager); +} +END_TEST + +/** + * In this scenario one of the peers does not notice that there is a rekey + * collision and the delete arrives after the TEMPORARY_FAILURE notify: + * + * rekey ----\ /---- rekey + * \ / + * detect collision <-----\---/ + * -------\--------> + * \ /---- delete old SA + * \-/----> detect collision + * no reschedule <---------/------ TEMP_FAIL + * detect collision <--------/ + * delete ----------------> + * rekey done + */ +START_TEST(test_collision_delayed_request_and_delete) +{ + ike_sa_t *a, *b, *sa; + message_t *msg; + status_t s; + + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + + /* Three nonces and SPIs are needed (SPI 1 and 2 are used for the initial + * CHILD_SA): + * N1/3 -----\ /----- N2/4 + * N3/5 <-----\--/ + * ... -----\ \-------> ... + * We test this three times, each time a different nonce is the lowest. + */ + struct { + /* Nonces used at each point */ + u_char nonces[3]; + /* SPIs of the deleted IKE_SAs (either redundant or replaced) */ + uint32_t del_a_i, del_a_r; + uint32_t del_b_i, del_b_r; + /* SPIs of the kept IKE_SA */ + uint32_t spi_i, spi_r; + } data[] = { + { { 0x00, 0xFF, 0xFF }, 3, 5, 1, 2, 4, 6 }, + { { 0xFF, 0x00, 0xFF }, 1, 2, 4, 6, 3, 5 }, + { { 0xFF, 0xFF, 0x00 }, 3, 5, 1, 2, 4, 6 }, + { { 0xFF, 0xFF, 0xFF }, 1, 2, 4, 6, 3, 5 }, + }; + /* these should never get called as this results in a successful rekeying */ + assert_hook_not_called(ike_updown); + assert_hook_not_called(child_updown); + + exchange_test_helper->nonce_first_byte = data[_i].nonces[0]; + initiate_rekey(a); + exchange_test_helper->nonce_first_byte = data[_i].nonces[1]; + initiate_rekey(b); + + /* delay the CREATE_CHILD_SA request from a to b */ + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + + /* <-- CREATE_CHILD_SA { SA, Ni, KEi } */ + exchange_test_helper->nonce_first_byte = data[_i].nonces[2]; + assert_hook_not_called(ike_rekey); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_ike_sa_state(a, IKE_REKEYING); + assert_child_sa_count(a, 1); + assert_ike_sa_count(0); + assert_hook(); + + /* CREATE_CHILD_SA { SA, Ni, KEi } --> */ + assert_hook_rekey(ike_rekey, 1, 4); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_DELETING); + assert_child_sa_count(b, 0); + sa = assert_ike_sa_checkout(4, 5, TRUE); + assert_ike_sa_state(sa, IKE_ESTABLISHED); + assert_child_sa_count(sa, 1); + assert_ike_sa_count(1); + assert_hook(); + + /* CREATE_CHILD_SA { SA, Ni, KEi } --> (delayed) */ + assert_single_notify(OUT, TEMPORARY_FAILURE); + exchange_test_helper->process_message(exchange_test_helper, b, msg); + assert_ike_sa_state(b, IKE_DELETING); + + /* delay the INFORMATIONAL request from b to a */ + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + + /* <-- CREATE_CHILD_SA { N(TEMP_FAIL) } */ + assert_hook_rekey(ike_rekey, 1, 4); + assert_no_jobs_scheduled(); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_ike_sa_state(a, IKE_REKEYED); + assert_child_sa_count(a, 0); + sa = assert_ike_sa_checkout(4, 5, FALSE); + assert_ike_sa_state(sa, IKE_ESTABLISHED); + assert_child_sa_count(sa, 1); + assert_ike_sa_count(2); + assert_scheduler(); + assert_hook(); + + /* <-- INFORMATIONAL { D } (delayed) */ + assert_single_payload(IN, PLV2_DELETE); + s = exchange_test_helper->process_message(exchange_test_helper, a, msg); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(a, destroy); + + /* INFORMATIONAL { } --> */ + assert_hook_not_called(ike_rekey); + assert_message_empty(IN); + s = exchange_test_helper->process_message(exchange_test_helper, b, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(b, destroy); + assert_hook(); + + /* ike_updown/child_updown */ + assert_hook(); + assert_hook(); + + charon->ike_sa_manager->flush(charon->ike_sa_manager); +} +END_TEST + +/** + * One of the hosts initiates a DELETE of the IKE_SA the other peer is + * concurrently trying to rekey. + * + * rekey ----\ /---- delete + * \-----/----> detect collision + * detect collision <---------/ /---- TEMP_FAIL + * delete ----\ / + * \----/-----> + * sa already gone <--------/ + */ +START_TEST(test_collision_delete) +{ + ike_sa_t *a, *b; + message_t *msg; + status_t s; + + if (_i) + { /* responder rekeys the IKE_SA */ + exchange_test_helper->establish_sa(exchange_test_helper, + &b, &a, NULL); + } + else + { /* initiator rekeys the IKE_SA */ + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + } + /* this should never get called as this does not result in a successful + * rekeying on either side */ + assert_hook_not_called(ike_rekey); + + initiate_rekey(a); + call_ikesa(b, delete); + assert_ike_sa_state(b, IKE_DELETING); + + /* RFC 7296, 2.25.2: If a peer receives a request to rekey an IKE SA that + * it is currently trying to close, it SHOULD reply with TEMPORARY_FAILURE. + */ + + /* CREATE_CHILD_SA { SA, Ni, KEi } --> */ + assert_hook_not_called(ike_updown); + assert_single_notify(OUT, TEMPORARY_FAILURE); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_DELETING); + assert_ike_sa_count(0); + assert_hook(); + + /* RFC 7296, 2.25.2: If a peer receives a request to close an IKE SA that + * it is currently rekeying, it SHOULD reply as usual, and forget its own + * rekeying request. + */ + + /* <-- INFORMATIONAL { D } */ + assert_hook_updown(ike_updown, FALSE); + assert_hook_updown(child_updown, FALSE); + assert_single_payload(IN, PLV2_DELETE); + assert_message_empty(OUT); + s = exchange_test_helper->process_message(exchange_test_helper, a, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(a, destroy); + assert_hook(); + assert_hook(); + + /* <-- CREATE_CHILD_SA { N(TEMP_FAIL) } */ + /* the SA is already gone */ + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + msg->destroy(msg); + + /* INFORMATIONAL { } --> */ + assert_hook_updown(ike_updown, FALSE); + assert_hook_updown(child_updown, FALSE); + s = exchange_test_helper->process_message(exchange_test_helper, b, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(b, destroy); + assert_hook(); + assert_hook(); + + /* ike_rekey */ + assert_hook(); +} +END_TEST + +/** + * One of the hosts initiates a DELETE of the IKE_SA the other peer is + * concurrently trying to rekey. However, the delete request is delayed or + * dropped, so the peer doing the rekeying is unaware of the collision. + * + * rekey ----\ /---- delete + * \-----/----> detect collision + * reschedule <---------/------ TEMP_FAIL + * <--------/ + * delete ----------------> + */ +START_TEST(test_collision_delete_drop_delete) +{ + ike_sa_t *a, *b; + message_t *msg; + status_t s; + + if (_i) + { /* responder rekeys the IKE_SA */ + exchange_test_helper->establish_sa(exchange_test_helper, + &b, &a, NULL); + } + else + { /* initiator rekeys the IKE_SA */ + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + } + /* this should never get called as this does not result in a successful + * rekeying on either side */ + assert_hook_not_called(ike_rekey); + + initiate_rekey(a); + call_ikesa(b, delete); + assert_ike_sa_state(b, IKE_DELETING); + + /* RFC 7296, 2.25.2: If a peer receives a request to rekey an IKE SA that + * it is currently trying to close, it SHOULD reply with TEMPORARY_FAILURE. + */ + + /* CREATE_CHILD_SA { SA, Ni, KEi } --> */ + assert_hook_not_called(ike_updown); + assert_single_notify(OUT, TEMPORARY_FAILURE); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_DELETING); + assert_ike_sa_count(0); + assert_hook(); + + /* delay the DELETE request */ + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + + /* <-- CREATE_CHILD_SA { N(TEMP_FAIL) } */ + assert_hook_not_called(ike_updown); + assert_hook_not_called(child_updown); + /* we expect a job to retry the rekeying is scheduled */ + assert_jobs_scheduled(1); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_ike_sa_state(a, IKE_ESTABLISHED); + assert_scheduler(); + assert_hook(); + assert_hook(); + + /* <-- INFORMATIONAL { D } (delayed) */ + assert_hook_updown(ike_updown, FALSE); + assert_hook_updown(child_updown, FALSE); + assert_single_payload(IN, PLV2_DELETE); + assert_message_empty(OUT); + s = exchange_test_helper->process_message(exchange_test_helper, a, msg); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(a, destroy); + assert_hook(); + assert_hook(); + + /* INFORMATIONAL { } --> */ + assert_hook_updown(ike_updown, FALSE); + assert_hook_updown(child_updown, FALSE); + s = exchange_test_helper->process_message(exchange_test_helper, b, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(b, destroy); + assert_hook(); + assert_hook(); + + /* ike_rekey */ + assert_hook(); +} +END_TEST + +Suite *ike_rekey_suite_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("ike rekey"); + + tc = tcase_create("regular"); + tcase_add_loop_test(tc, test_regular, 0, 2); + tcase_add_loop_test(tc, test_regular_ke_invalid, 0, 2); + suite_add_tcase(s, tc); + + tc = tcase_create("collisions rekey"); + tcase_add_loop_test(tc, test_collision, 0, 4); + tcase_add_loop_test(tc, test_collision_ke_invalid, 0, 4); + tcase_add_loop_test(tc, test_collision_ke_invalid_delayed_retry, 0, 3); + tcase_add_loop_test(tc, test_collision_delayed_response, 0, 4); + tcase_add_loop_test(tc, test_collision_dropped_request, 0, 3); + tcase_add_loop_test(tc, test_collision_delayed_request, 0, 3); + tcase_add_loop_test(tc, test_collision_delayed_request_and_delete, 0, 3); + suite_add_tcase(s, tc); + + tc = tcase_create("collisions delete"); + tcase_add_loop_test(tc, test_collision_delete, 0, 2); + tcase_add_loop_test(tc, test_collision_delete_drop_delete, 0, 2); + suite_add_tcase(s, tc); + + return s; +} diff --git a/src/libcharon/tests/suites/test_message_chapoly.c b/src/libcharon/tests/suites/test_message_chapoly.c index e871cf6c2..f4a74abb4 100644 --- a/src/libcharon/tests/suites/test_message_chapoly.c +++ b/src/libcharon/tests/suites/test_message_chapoly.c @@ -40,7 +40,7 @@ METHOD(aead_t, get_iv_gen, iv_gen_t*, } METHOD(iv_gen_t, get_iv, bool, - iv_gen_t *this, u_int64_t seq, size_t size, u_int8_t *buffer) + iv_gen_t *this, uint64_t seq, size_t size, uint8_t *buffer) { if (size != 8) { @@ -51,7 +51,7 @@ METHOD(iv_gen_t, get_iv, bool, } METHOD(iv_gen_t, allocate_iv, bool, - iv_gen_t *this, u_int64_t seq, size_t size, chunk_t *chunk) + iv_gen_t *this, uint64_t seq, size_t size, chunk_t *chunk) { if (size != 8) { @@ -66,10 +66,10 @@ METHOD(iv_gen_t, allocate_iv, bool, */ START_TEST(test_chacha20poly1305) { - u_int64_t spii, spir; + uint64_t spii, spir; ike_sa_id_t *id; message_t *m; - u_int32_t window = htonl(10); + uint32_t window = htonl(10); chunk_t chunk, exp; keymat_t keymat = { .get_version = _get_version, diff --git a/src/libcharon/tests/suites/test_proposal.c b/src/libcharon/tests/suites/test_proposal.c new file mode 100644 index 000000000..a6226f68f --- /dev/null +++ b/src/libcharon/tests/suites/test_proposal.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * 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 "test_suite.h" + +#include <config/proposal.h> + +static struct { + char *self; + char *other; + char *expected; +} select_data[] = { + { "aes128", "aes128", "aes128" }, + { "aes128", "aes256", NULL }, + { "aes128-aes256", "aes256-aes128", "aes128" }, + { "aes256-aes128", "aes128-aes256", "aes256" }, + { "aes128-aes256-sha1-sha256", "aes256-aes128-sha256-sha1", "aes128-sha1" }, + { "aes256-aes128-sha256-sha1", "aes128-aes256-sha1-sha256", "aes256-sha256" }, + { "aes128-sha256-modp3072", "aes128-sha256", NULL }, + { "aes128-sha256", "aes128-sha256-modp3072", NULL }, + { "aes128-sha256-modp3072", "aes128-sha256-modpnone", NULL }, + { "aes128-sha256-modpnone", "aes128-sha256-modp3072", NULL }, + { "aes128-sha256-modp3072-modpnone", "aes128-sha256", "aes128-sha256" }, + { "aes128-sha256", "aes128-sha256-modp3072-modpnone", "aes128-sha256" }, + { "aes128-sha256-modp3072-modpnone", "aes128-sha256-modpnone-modp3072", "aes128-sha256-modp3072" }, + { "aes128-sha256-modpnone-modp3072", "aes128-sha256-modp3072-modpnone", "aes128-sha256-modpnone" }, +}; + +START_TEST(test_select) +{ + proposal_t *self, *other, *selected, *expected; + + self = proposal_create_from_string(PROTO_ESP, + select_data[_i].self); + other = proposal_create_from_string(PROTO_ESP, + select_data[_i].other); + selected = self->select(self, other, FALSE); + if (select_data[_i].expected) + { + expected = proposal_create_from_string(PROTO_ESP, + select_data[_i].expected); + ck_assert(selected); + ck_assert_msg(expected->equals(expected, selected), "proposal %P does " + "not match expected %P", selected, expected); + expected->destroy(expected); + } + else + { + ck_assert(!selected); + } + DESTROY_IF(selected); + other->destroy(other); + self->destroy(self); +} +END_TEST + +Suite *proposal_suite_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("proposal"); + + tc = tcase_create("select"); + tcase_add_loop_test(tc, test_select, 0, countof(select_data)); + suite_add_tcase(s, tc); + + return s; +} diff --git a/src/libcharon/tests/utils/exchange_test_asserts.c b/src/libcharon/tests/utils/exchange_test_asserts.c new file mode 100644 index 000000000..2602b97b7 --- /dev/null +++ b/src/libcharon/tests/utils/exchange_test_asserts.c @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * 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 <inttypes.h> + +#include <test_suite.h> + +#include "exchange_test_asserts.h" + +/* + * Described in header + */ +bool exchange_test_asserts_hook(listener_t *listener) +{ + listener_hook_assert_t *this = (listener_hook_assert_t*)listener; + + this->count++; + return TRUE; +} + +/* + * Described in header + */ +bool exchange_test_asserts_ike_updown(listener_t *listener, ike_sa_t *ike_sa, + bool up) +{ + listener_hook_assert_t *this = (listener_hook_assert_t*)listener; + + this->count++; + assert_listener_msg(this->up == up, this, "IKE_SA not '%s'", + this->up ? "up" : "down"); + return TRUE; +} + +/* + * Described in header + */ +bool exchange_test_asserts_child_updown(listener_t *listener, ike_sa_t *ike_sa, + child_sa_t *child_sa, bool up) +{ + listener_hook_assert_t *this = (listener_hook_assert_t*)listener; + + this->count++; + assert_listener_msg(this->up == up, this, "CHILD_SA not '%s'", + this->up ? "up" : "down"); + return TRUE; +} + +/* + * Described in header + */ +bool exchange_test_asserts_ike_rekey(listener_t *listener, ike_sa_t *old, + ike_sa_t *new) +{ + listener_hook_assert_t *this = (listener_hook_assert_t*)listener; + ike_sa_id_t *id; + uint64_t spi; + + this->count++; + id = old->get_id(old); + spi = id->get_initiator_spi(id); + assert_listener_msg(this->spi_old == spi, this, "unexpected old IKE_SA " + "%.16"PRIx64"_i instead of %.16"PRIx64"_i", + be64toh(spi), be64toh(this->spi_old)); + id = new->get_id(new); + spi = id->get_initiator_spi(id); + assert_listener_msg(this->spi_new == spi, this, "unexpected new IKE_SA " + "%.16"PRIx64"_i instead of %.16"PRIx64"_i", + be64toh(spi), be64toh(this->spi_new)); + return TRUE; +} + +/* + * Described in header + */ +bool exchange_test_asserts_child_rekey(listener_t *listener, ike_sa_t *ike_sa, + child_sa_t *old, child_sa_t *new) +{ + listener_hook_assert_t *this = (listener_hook_assert_t*)listener; + uint32_t spi, expected; + + this->count++; + spi = old->get_spi(old, TRUE); + expected = this->spi_old; + assert_listener_msg(expected == spi, this, "unexpected old CHILD_SA %.8x " + "instead of %.8x", spi, expected); + spi = new->get_spi(new, TRUE); + expected = this->spi_new; + assert_listener_msg(expected == spi, this, "unexpected new CHILD_SA %.8x " + "instead of %.8x", spi, expected); + return TRUE; +} + +/** + * Assert a given message rule + */ +static void assert_message_rule(listener_message_assert_t *this, message_t *msg, + listener_message_rule_t *rule) +{ + if (rule->expected) + { + if (rule->payload) + { + assert_listener_msg(msg->get_payload(msg, rule->payload), + this, "expected payload (%N) not found", + payload_type_names, rule->payload); + + } + if (rule->notify) + { + assert_listener_msg(msg->get_notify(msg, rule->notify), + this, "expected notify payload (%N) not found", + notify_type_names, rule->notify); + } + } + else + { + if (rule->payload) + { + assert_listener_msg(!msg->get_payload(msg, rule->payload), + this, "unexpected payload (%N) found", + payload_type_names, rule->payload); + + } + if (rule->notify) + { + assert_listener_msg(!msg->get_notify(msg, rule->notify), + this, "unexpected notify payload (%N) found", + notify_type_names, rule->notify); + } + } +} + +/* + * Described in header + */ +bool exchange_test_asserts_message(listener_t *listener, ike_sa_t *ike_sa, + message_t *message, bool incoming, bool plain) +{ + listener_message_assert_t *this = (listener_message_assert_t*)listener; + + if (plain && this->incoming == incoming) + { + if (this->count >= 0) + { + enumerator_t *enumerator; + int count = 0; + enumerator = message->create_payload_enumerator(message); + while (enumerator->enumerate(enumerator, NULL)) + { + count++; + } + enumerator->destroy(enumerator); + assert_listener_msg(this->count == count, this, "unexpected payload " + "count in message (%d != %d)", this->count, + count); + } + if (this->num_rules) + { + int i; + + for (i = 0; i < this->num_rules; i++) + { + assert_message_rule(this, message, &this->rules[i]); + } + } + return FALSE; + } + return TRUE; +} diff --git a/src/libcharon/tests/utils/exchange_test_asserts.h b/src/libcharon/tests/utils/exchange_test_asserts.h new file mode 100644 index 000000000..32afcc2e4 --- /dev/null +++ b/src/libcharon/tests/utils/exchange_test_asserts.h @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * 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. + */ + +/** + * Special assertions using listener_t. + * + * @defgroup exchange_test_asserts exchange_test_asserts + * @{ @ingroup test_utils_c + */ + +#ifndef EXCHANGE_TEST_ASSERTS_H_ +#define EXCHANGE_TEST_ASSERTS_H_ + +#include <bus/listeners/listener.h> + +typedef struct listener_hook_assert_t listener_hook_assert_t; +typedef struct listener_message_assert_t listener_message_assert_t; +typedef struct listener_message_rule_t listener_message_rule_t; + +struct listener_hook_assert_t { + + /** + * Implemented interface + */ + listener_t listener; + + /** + * Original source file + */ + const char *file; + + /** + * Source line + */ + int line; + + /** + * Name of the hook + */ + const char *name; + + /** + * Expected number of calls (-1 to ignore) + */ + int expected; + + /** + * Number of times the hook was called + */ + int count; + + /** + * Expected updown result + */ + bool up; + + /** + * Initiator/Inbound SPIs to expect in rekey event + */ + uint64_t spi_old, spi_new; +}; + +/** + * Basic callback for methods on listener_t, counting the number of calls. + */ +bool exchange_test_asserts_hook(listener_t *this); + +/** + * Implementation of listener_t::ike_updown. + */ +bool exchange_test_asserts_ike_updown(listener_t *this, ike_sa_t *ike_sa, + bool up); + +/** + * Implementation of listener_t::child_updown. + */ +bool exchange_test_asserts_child_updown(listener_t *this, ike_sa_t *ike_sa, + child_sa_t *child_sa, bool up); + +/** + * Implementation of listener_t::ike_rekey. + */ +bool exchange_test_asserts_ike_rekey(listener_t *this, ike_sa_t *old, + ike_sa_t *new); + +/** + * Implementation of listener_t::child_rekey. + */ +bool exchange_test_asserts_child_rekey(listener_t *this, ike_sa_t *ike_sa, + child_sa_t *old, child_sa_t *new); + +/** + * Check if a statement evaluates to TRUE, use original source file and line + * in the error message if not. + * + * @param x statement to evaluate + * @param l listener providing original source file and line + * @param fmt printf format string + * @param ... arguments for fmt + */ +#define assert_listener_msg(x, l, fmt, ...) ({ \ + test_fail_if_worker_failed(); \ + if (!(x)) \ + { \ + test_fail_msg((l)->file, (l)->line, "%s: " fmt, #x, ##__VA_ARGS__); \ + } \ +}) + +/** + * Initialize an assertion that enforces that the given hook was called. + * Must be matched by a call to assert_hook(). + * + * @param name name of the hook + */ +#define assert_hook_called(name) \ + _assert_hook_init(name, exchange_test_asserts_hook, .expected = 1) + +/** + * Initialize an assertion that enforces that the given hook was not called. + * Must be matched by a call to assert_hook(). + * + * @param name name of the hook + */ +#define assert_hook_not_called(name) \ + _assert_hook_init(name, exchange_test_asserts_hook, .expected = 0) + +/** + * Initialize an assertion that enforces that the given updown hook was called + * with the expected result. + * Must be matched by a call to assert_hook(). + * + * @param name name of the hook + * @param e whether to expect up in the hook to be TRUE or not + */ +#define assert_hook_updown(name, e) \ + _assert_hook_init(name, \ + streq(#name, "ike_updown") ? (void*)exchange_test_asserts_ike_updown \ + : (void*)exchange_test_asserts_child_updown, \ + .expected = 1, \ + .up = e, \ + ) + +/** + * Initialize an assertion that enforces that the given rekey hook was called + * with the SAs with the matching initiator/inbound SPIs. + * Must be matched by a call to assert_hook(). + * + * @param name name of the hook + * @param old SPI of the old SA + * @param new SPI of the new SA + */ +#define assert_hook_rekey(name, old, new) \ + _assert_hook_init(name, \ + streq(#name, "ike_rekey") ? (void*)exchange_test_asserts_ike_rekey \ + : (void*)exchange_test_asserts_child_rekey, \ + .expected = 1, \ + .spi_old = old, \ + .spi_new = new, \ + ) + +/** + * Initialize assertions against invocations of listener_t hooks. Each call + * must be matched by a call to assert_hook(). + */ +#define _assert_hook_init(n, callback, ...) \ +do { \ + listener_hook_assert_t _hook_listener = { \ + .listener = { .n = (void*)callback, }, \ + .file = __FILE__, \ + .line = __LINE__, \ + .name = #n, \ + ##__VA_ARGS__ \ + }; \ + exchange_test_helper->add_listener(exchange_test_helper, &_hook_listener.listener) + +/** + * Enforce the most recently initialized hook assertion. + */ +#define assert_hook() \ + charon->bus->remove_listener(charon->bus, &_hook_listener.listener); \ + if (_hook_listener.expected > 0) { \ + if (_hook_listener.count > 0) { \ + assert_listener_msg(_hook_listener.expected == _hook_listener.count, \ + &_hook_listener, "hook '%s' was called %d times " \ + "instead of %d", _hook_listener.name, \ + _hook_listener.count, _hook_listener.expected); \ + } else { \ + assert_listener_msg(_hook_listener.count, &_hook_listener, \ + "hook '%s' was not called (expected %d)", _hook_listener.name, \ + _hook_listener.expected); \ + } \ + } else if (_hook_listener.expected == 0) { \ + assert_listener_msg(_hook_listener.count == 0, &_hook_listener, \ + "hook '%s' was called unexpectedly", _hook_listener.name); \ + } \ +} while(FALSE) + +/** + * Rules regarding payloads/notifies to expect/not expect in a message + */ +struct listener_message_rule_t { + + /** + * Whether the payload/notify is expected in the message, FALSE to fail if + * it is found + */ + bool expected; + + /** + * Payload type to expect/not expect + */ + payload_type_t payload; + + /** + * Notify type to expect/not expect (paylod type does not have to be + * specified) + */ + notify_type_t notify; +}; + +/** + * Data used to check plaintext messages via listener_t + */ +struct listener_message_assert_t { + + /** + * Implemented interface + */ + listener_t listener; + + /** + * Original source file + */ + const char *file; + + /** + * Source line + */ + int line; + + /** + * Whether to check the next inbound or outbound message + */ + bool incoming; + + /** + * Payload count to expect (-1 to ignore the count) + */ + int count; + + /** + * Payloads to expect or not expect in a message + */ + listener_message_rule_t *rules; + + /** + * Number of rules + */ + int num_rules; +}; + +/** + * Implementation of listener_t::message collecting data and asserting + * certain things. + */ +bool exchange_test_asserts_message(listener_t *this, ike_sa_t *ike_sa, + message_t *message, bool incoming, bool plain); + +/** + * Assert that the next in- or outbound plaintext message is empty. + * + * @param dir IN or OUT to check the next in- or outbound message + */ +#define assert_message_empty(dir) \ + _assert_payload(dir, 0) + +/** + * Assert that the next in- or outbound plaintext message contains exactly + * one payload of the given type. + * + * @param dir IN or OUT to check the next in- or outbound message + * @param expected expected payload type + */ +#define assert_single_payload(dir, expected) \ + _assert_payload(dir, 1, { TRUE, expected, 0 }) + +/** + * Assert that the next in- or outbound plaintext message contains exactly + * one notify of the given type. + * + * @param dir IN or OUT to check the next in- or outbound message + * @param expected expected notify type + */ +#define assert_single_notify(dir, expected) \ + _assert_payload(dir, 1, { TRUE, 0, expected }) + +/** + * Assert that the next in- or outbound plaintext message contains a notify + * of the given type. + * + * @param dir IN or OUT to check the next in- or outbound message + * @param expected expected notify type + */ +#define assert_notify(dir, expected) \ + _assert_payload(dir, -1, { TRUE, 0, expected }) + +/** + * Assert that the next in- or outbound plaintext message does not contain a + * notify of the given type. + * + * @param dir IN or OUT to check the next in- or outbound message + * @param unexpected not expected notify type + */ +#define assert_no_notify(dir, unexpected) \ + _assert_payload(dir, -1, { FALSE, 0, unexpected }) + +#define _assert_payload(dir, c, ...) ({ \ + listener_message_rule_t _rules[] = { __VA_ARGS__ }; \ + listener_message_assert_t _listener = { \ + .listener = { .message = exchange_test_asserts_message, }, \ + .file = __FILE__, \ + .line = __LINE__, \ + .incoming = streq(#dir, "IN") ? TRUE : FALSE, \ + .count = c, \ + .rules = _rules, \ + .num_rules = countof(_rules), \ + }; \ + exchange_test_helper->add_listener(exchange_test_helper, &_listener.listener); \ +}) + +#endif /** EXCHANGE_TEST_ASSERTS_H_ @}*/ diff --git a/src/libcharon/tests/utils/exchange_test_helper.c b/src/libcharon/tests/utils/exchange_test_helper.c new file mode 100644 index 000000000..f32906d5d --- /dev/null +++ b/src/libcharon/tests/utils/exchange_test_helper.c @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * 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 "exchange_test_helper.h" +#include "mock_dh.h" +#include "mock_ipsec.h" +#include "mock_nonce_gen.h" + +#include <collections/array.h> +#include <credentials/sets/mem_cred.h> + +typedef struct private_exchange_test_helper_t private_exchange_test_helper_t; +typedef struct private_backend_t private_backend_t; + +/** + * Private data + */ +struct private_exchange_test_helper_t { + + /** + * Public interface + */ + exchange_test_helper_t public; + + /** + * Credentials + */ + mem_cred_t *creds; + + /** + * IKE_SA SPI counter + */ + refcount_t ike_spi; + + /** + * List of registered listeners + */ + array_t *listeners; +}; + +/** + * Custom backend_t implementation + */ +struct private_backend_t { + + /** + * Public interface + */ + backend_t public; + + /** + * Responder ike_cfg + */ + ike_cfg_t *ike_cfg; + + /** + * Responder peer_cfg/child_cfg + */ + peer_cfg_t *peer_cfg; +}; + +CALLBACK(get_ike_spi, uint64_t, + private_exchange_test_helper_t *this) +{ + return (uint64_t)ref_get(&this->ike_spi); +} + +/* + * Described in header + */ +exchange_test_helper_t *exchange_test_helper; + +static ike_cfg_t *create_ike_cfg(bool initiator, exchange_test_sa_conf_t *conf) +{ + ike_cfg_t *ike_cfg; + char *proposal = NULL; + + ike_cfg = ike_cfg_create(IKEV2, TRUE, FALSE, "127.0.0.1", IKEV2_UDP_PORT, + "127.0.0.1", IKEV2_UDP_PORT, FRAGMENTATION_NO, 0); + if (conf) + { + proposal = initiator ? conf->initiator.ike : conf->responder.ike; + } + if (proposal) + { + ike_cfg->add_proposal(ike_cfg, + proposal_create_from_string(PROTO_IKE, proposal)); + } + else + { + ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); + } + return ike_cfg; +} + +static child_cfg_t *create_child_cfg(bool initiator, + exchange_test_sa_conf_t *conf) +{ + child_cfg_t *child_cfg; + child_cfg_create_t child = { + .mode = MODE_TUNNEL, + }; + char *proposal = NULL; + + child_cfg = child_cfg_create(initiator ? "init" : "resp", &child); + if (conf) + { + proposal = initiator ? conf->initiator.esp : conf->responder.esp; + } + if (proposal) + { + child_cfg->add_proposal(child_cfg, + proposal_create_from_string(PROTO_ESP, proposal)); + } + else + { + child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP)); + } + child_cfg->add_traffic_selector(child_cfg, TRUE, + traffic_selector_create_dynamic(0, 0, 65535)); + child_cfg->add_traffic_selector(child_cfg, FALSE, + traffic_selector_create_dynamic(0, 0, 65535)); + return child_cfg; +} + +static void add_auth_cfg(peer_cfg_t *peer_cfg, bool initiator, bool local) +{ + auth_cfg_t *auth; + char *id = "init"; + + auth = auth_cfg_create(); + auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PSK); + if (initiator ^ local) + { + id = "resp"; + } + auth->add(auth, AUTH_RULE_IDENTITY, identification_create_from_string(id)); + peer_cfg->add_auth_cfg(peer_cfg, auth, local); +} + +static peer_cfg_t *create_peer_cfg(bool initiator, + exchange_test_sa_conf_t *conf) +{ + peer_cfg_t *peer_cfg; + peer_cfg_create_t peer = { + .cert_policy = CERT_SEND_IF_ASKED, + .unique = UNIQUE_REPLACE, + .keyingtries = 1, + }; + + peer_cfg = peer_cfg_create(initiator ? "init" : "resp", + create_ike_cfg(initiator, conf), &peer); + add_auth_cfg(peer_cfg, initiator, TRUE); + add_auth_cfg(peer_cfg, initiator, FALSE); + return peer_cfg; +} + +METHOD(backend_t, create_ike_cfg_enumerator, enumerator_t*, + private_backend_t *this, host_t *me, host_t *other) +{ + return enumerator_create_single(this->ike_cfg, NULL); +} + +METHOD(backend_t, create_peer_cfg_enumerator, enumerator_t*, + private_backend_t *this, identification_t *me, identification_t *other) +{ + return enumerator_create_single(this->peer_cfg, NULL); +} + +METHOD(exchange_test_helper_t, process_message, status_t, + private_exchange_test_helper_t *this, ike_sa_t *ike_sa, message_t *message) +{ + status_t status = FAILED; + ike_sa_id_t *id; + + if (!message) + { + message = this->public.sender->dequeue(this->public.sender); + } + id = message->get_ike_sa_id(message); + id = id->clone(id); + id->switch_initiator(id); + if (!id->get_responder_spi(id) || id->equals(id, ike_sa->get_id(ike_sa))) + { + charon->bus->set_sa(charon->bus, ike_sa); + status = ike_sa->process_message(ike_sa, message); + charon->bus->set_sa(charon->bus, NULL); + } + message->destroy(message); + id->destroy(id); + return status; +} + +METHOD(exchange_test_helper_t, establish_sa, void, + private_exchange_test_helper_t *this, ike_sa_t **init, ike_sa_t **resp, + exchange_test_sa_conf_t *conf) +{ + private_backend_t backend = { + .public = { + .create_ike_cfg_enumerator = _create_ike_cfg_enumerator, + .create_peer_cfg_enumerator = _create_peer_cfg_enumerator, + .get_peer_cfg_by_name = (void*)return_null, + }, + }; + ike_sa_id_t *id_i, *id_r; + ike_sa_t *sa_i, *sa_r; + peer_cfg_t *peer_cfg; + child_cfg_t *child_cfg; + + sa_i = *init = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, + IKEV2, TRUE); + id_i = sa_i->get_id(sa_i); + + sa_r = *resp = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, + IKEV2, FALSE); + id_r = sa_r->get_id(sa_r); + + peer_cfg = create_peer_cfg(TRUE, conf); + child_cfg = create_child_cfg(TRUE, conf); + peer_cfg->add_child_cfg(peer_cfg, child_cfg->get_ref(child_cfg)); + sa_i->set_peer_cfg(sa_i, peer_cfg); + peer_cfg->destroy(peer_cfg); + call_ikesa(sa_i, initiate, child_cfg, 0, NULL, NULL); + + backend.ike_cfg = create_ike_cfg(FALSE, conf); + peer_cfg = backend.peer_cfg = create_peer_cfg(FALSE, conf); + child_cfg = create_child_cfg(FALSE, conf); + peer_cfg->add_child_cfg(peer_cfg, child_cfg->get_ref(child_cfg)); + child_cfg->destroy(child_cfg); + charon->backends->add_backend(charon->backends, &backend.public); + + /* IKE_SA_INIT --> */ + id_r->set_initiator_spi(id_r, id_i->get_initiator_spi(id_i)); + process_message(this, sa_r, NULL); + /* <-- IKE_SA_INIT */ + id_i->set_responder_spi(id_i, id_r->get_responder_spi(id_r)); + process_message(this, sa_i, NULL); + /* IKE_AUTH --> */ + process_message(this, sa_r, NULL); + /* <-- IKE_AUTH */ + process_message(this, sa_i, NULL); + + charon->backends->remove_backend(charon->backends, &backend.public); + DESTROY_IF(backend.peer_cfg); + DESTROY_IF(backend.ike_cfg); +} + +METHOD(exchange_test_helper_t, add_listener, void, + private_exchange_test_helper_t *this, listener_t *listener) +{ + array_insert_create(&this->listeners, ARRAY_TAIL, listener); + charon->bus->add_listener(charon->bus, listener); +} + +/** + * Enable logging in charon as requested + */ +static void initialize_logging() +{ + int level = LEVEL_SILENT; + char *verbosity; + + verbosity = getenv("TESTS_VERBOSITY"); + if (verbosity) + { + level = atoi(verbosity); + } + lib->settings->set_int(lib->settings, "%s.filelog.stderr.default", + lib->settings->get_int(lib->settings, "%s.filelog.stderr.default", + level, lib->ns), lib->ns); + lib->settings->set_bool(lib->settings, "%s.filelog.stderr.ike_name", TRUE, + lib->ns); + charon->load_loggers(charon, NULL, TRUE); +} + +/** + * Create a nonce generator with the first byte + */ +static nonce_gen_t *create_nonce_gen() +{ + return mock_nonce_gen_create(exchange_test_helper->nonce_first_byte); +} + +/* + * Described in header + */ +void exchange_test_helper_init(char *plugins) +{ + private_exchange_test_helper_t *this; + plugin_feature_t features[] = { + PLUGIN_REGISTER(DH, mock_dh_create), + /* we only need to support a limited number of DH groups */ + PLUGIN_PROVIDE(DH, MODP_2048_BIT), + PLUGIN_PROVIDE(DH, MODP_3072_BIT), + PLUGIN_PROVIDE(DH, ECP_256_BIT), + PLUGIN_REGISTER(NONCE_GEN, create_nonce_gen), + PLUGIN_PROVIDE(NONCE_GEN), + PLUGIN_DEPENDS(RNG, RNG_WEAK), + }; + + INIT(this, + .public = { + .sender = mock_sender_create(), + .establish_sa = _establish_sa, + .process_message = _process_message, + .add_listener = _add_listener, + }, + .creds = mem_cred_create(), + ); + + initialize_logging(); + lib->plugins->add_static_features(lib->plugins, "exchange-test-helper", + features, countof(features), TRUE, NULL, NULL); + /* the libcharon unit tests only load the libstrongswan plugins, unless + * TESTS_PLUGINS is defined */ + charon->initialize(charon, plugins); + lib->plugins->status(lib->plugins, LEVEL_CTRL); + + /* the original sender is not initialized because there is no socket */ + charon->sender = (sender_t*)this->public.sender; + /* and there is no kernel plugin loaded + * TODO: we'd have more control if we'd implement kernel_interface_t */ + charon->kernel->add_ipsec_interface(charon->kernel, mock_ipsec_create); + /* like SPIs for IPsec SAs, make IKE SPIs predictable */ + charon->ike_sa_manager->set_spi_cb(charon->ike_sa_manager, get_ike_spi, + this); + + lib->credmgr->add_set(lib->credmgr, &this->creds->set); + + this->creds->add_shared(this->creds, + shared_key_create(SHARED_IKE, chunk_clone(chunk_from_str("test"))), + identification_create_from_string("%any"), NULL); + + exchange_test_helper = &this->public; +} + +/* + * Described in header + */ +void exchange_test_helper_deinit() +{ + private_exchange_test_helper_t *this; + listener_t *listener; + + this = (private_exchange_test_helper_t*)exchange_test_helper; + + while (array_remove(this->listeners, ARRAY_HEAD, &listener)) + { + charon->bus->remove_listener(charon->bus, listener); + } + lib->credmgr->remove_set(lib->credmgr, &this->creds->set); + this->creds->destroy(this->creds); + /* flush SAs before destroying the sender (in case of test failures) */ + charon->ike_sa_manager->flush(charon->ike_sa_manager); + /* charon won't destroy this as it didn't initialize the original sender */ + charon->sender->destroy(charon->sender); + charon->sender = NULL; + array_destroy(this->listeners); + free(this); +} diff --git a/src/libcharon/tests/utils/exchange_test_helper.h b/src/libcharon/tests/utils/exchange_test_helper.h new file mode 100644 index 000000000..e1fdb012a --- /dev/null +++ b/src/libcharon/tests/utils/exchange_test_helper.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * 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. + */ + +/** + * This class and singleton object initializes charon and provides helper + * methods to create unit tests for IKEv2 exchanges. + * + * It also registers special implementations for the kernel_ipsec_t interface, + * the sender and provides dummy configs and credentials. + * + * @defgroup exchange_test_helper exchange_test_helper + * @{ @ingroup test_utils_c + */ + +#ifndef EXCHANGE_TEST_HELPER_H_ +#define EXCHANGE_TEST_HELPER_H_ + +#include <daemon.h> + +#include "mock_sender.h" + +typedef struct exchange_test_helper_t exchange_test_helper_t; +typedef struct exchange_test_sa_conf_t exchange_test_sa_conf_t; + +struct exchange_test_helper_t { + + /** + * Sender instance used during tests + */ + mock_sender_t *sender; + + /** + * Set the initial byte of all nonces generated by future nonce + * generators (already instatiated nonce generators are not affected). + */ + u_char nonce_first_byte; + + /** + * Creates an established IKE_SA/CHILD_SA + * + * @param[out] init IKE_SA of the initiator + * @param[out] resp IKE_SA of the responder + * @param conf configuration for SAs + */ + void (*establish_sa)(exchange_test_helper_t *this, ike_sa_t **init, + ike_sa_t **resp, exchange_test_sa_conf_t *conf); + + /** + * Pass a message to the given IKE_SA for processing, setting the IKE_SA on + * the bus while processing the message. + * + * @param ike_sa the IKE_SA receiving the message + * @param message the message, or NULL to pass the next message in the + * send queue (adopted) + * @return return value from ike_sa_t::process_message() + */ + status_t (*process_message)(exchange_test_helper_t *this, ike_sa_t *sa, + message_t *message); + + /** + * Register a listener with the bus. + * + * Don't use bus_t::add_listener() directly for listeners on the stack + * as that could lead to invalid listeners registered when hooks are + * triggered during cleanup if a test case fails. All of the listeners + * added this way are unregistered with the bus before cleaning up. + * + * @param listener listener to add to the bus + */ + void (*add_listener)(exchange_test_helper_t *this, listener_t *listener); +}; + +struct exchange_test_sa_conf_t { + + /** + * Configuration for initiator and responder + */ + struct { + /** IKE proposal */ + char *ike; + /** ESP proposal */ + char *esp; + } initiator, responder; +}; + +/** + * Since we don't use the IKE_SA manager to checkout SAs use this to call a + * method on the given IKE_SA in its context. + */ +#define call_ikesa(sa, method, ...) ({ \ + charon->bus->set_sa(charon->bus, sa); \ + sa->method(sa, ##__VA_ARGS__); \ + charon->bus->set_sa(charon->bus, NULL); \ +}) + +/** + * The one and only instance of the helper object. + * + * Set between exchange_test_helper_setup() and exchange_test_helper_teardown() + * calls. + */ +extern exchange_test_helper_t *exchange_test_helper; + +/** + * Initialize charon and the helper object. + * + * @param plugins plugins to load + */ +void exchange_test_helper_init(char *plugins); + +/** + * Deinitialize the helper object. + */ +void exchange_test_helper_deinit(); + +#endif /** EXCHANGE_TEST_HELPER_H_ @} */ diff --git a/src/libcharon/tests/utils/job_asserts.h b/src/libcharon/tests/utils/job_asserts.h new file mode 100644 index 000000000..3491f08c3 --- /dev/null +++ b/src/libcharon/tests/utils/job_asserts.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * 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. + */ + +/** + * Special assertions against job handling. + * + * @defgroup job_asserts job_asserts + * @{ @ingroup test_utils_c + */ + +#ifndef JOB_ASSERTS_H_ +#define JOB_ASSERTS_H_ + +/** + * Initialize an assertion that enforces that no jobs were scheduled. + * Must be matched by a call to assert_scheduler(). + */ +#define assert_no_jobs_scheduled() _assert_jobs_scheduled(0) + +/** + * Initialize an assertion that enforces that a specific number of jobs was + * scheduled. + * Must be matched by a call to assert_scheduler(). + * + * @param count expected number of jobs getting scheduled + */ +#define assert_jobs_scheduled(count) _assert_jobs_scheduled(count) + +/** + * Initialize assertions against job scheduling. + * Must be matched by a call to assert_scheduler(). + */ +#define _assert_jobs_scheduled(count) \ +do { \ + u_int _initial = lib->scheduler->get_job_load(lib->scheduler); \ + u_int _expected = count + +/** + * Enforce scheduler asserts. + */ +#define assert_scheduler() \ + u_int _actual = lib->scheduler->get_job_load(lib->scheduler) - _initial; \ + test_assert_msg(_expected == _actual, "unexpected number of jobs " \ + "scheduled (%u != %u)", _expected, _actual); \ +} while(FALSE) + +#endif /** JOB_ASSERTS_H_ @}*/ diff --git a/src/libcharon/tests/utils/mock_dh.c b/src/libcharon/tests/utils/mock_dh.c new file mode 100644 index 000000000..153bf1166 --- /dev/null +++ b/src/libcharon/tests/utils/mock_dh.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * Copyright (C) 2008 Martin Willi + * HSR Hochschule fuer Technik Rapperswil + * + * 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 "mock_dh.h" + +typedef struct private_diffie_hellman_t private_diffie_hellman_t; + +/** + * Private data + */ +struct private_diffie_hellman_t { + + /** + * Public interface + */ + diffie_hellman_t public; + + /** + * Instantiated DH group + */ + diffie_hellman_group_t group; +}; + +METHOD(diffie_hellman_t, get_my_public_value, bool, + private_diffie_hellman_t *this, chunk_t *value) +{ + *value = chunk_empty; + return TRUE; +} + +METHOD(diffie_hellman_t, set_other_public_value, bool, + private_diffie_hellman_t *this, chunk_t value) +{ + return TRUE; +} + +METHOD(diffie_hellman_t, get_shared_secret, bool, + private_diffie_hellman_t *this, chunk_t *secret) +{ + *secret = chunk_empty; + return TRUE; +} + +METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t, + private_diffie_hellman_t *this) +{ + return this->group; +} + +METHOD(diffie_hellman_t, destroy, void, + private_diffie_hellman_t *this) +{ + free(this); +} + +/** + * See header + */ +diffie_hellman_t *mock_dh_create(diffie_hellman_group_t group) +{ + private_diffie_hellman_t *this; + + INIT(this, + .public = { + .get_shared_secret = _get_shared_secret, + .set_other_public_value = _set_other_public_value, + .get_my_public_value = _get_my_public_value, + .get_dh_group = _get_dh_group, + .destroy = _destroy, + }, + .group = group, + ); + return &this->public; +} diff --git a/src/libcharon/tests/utils/mock_dh.h b/src/libcharon/tests/utils/mock_dh.h new file mode 100644 index 000000000..332c65537 --- /dev/null +++ b/src/libcharon/tests/utils/mock_dh.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * 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. + */ + +/** + * Provides a DH implementation that does no real work to make the tests run + * faster. + * + * @defgroup mock_dh mock_dh + * @{ @ingroup test_utils_c + */ + +#ifndef MOCK_DH_H_ +#define MOCK_DH_H_ + +#include <crypto/diffie_hellman.h> + +/** + * Creates a diffie_hellman_t object. + * + * @param group Diffie Hellman group, supports MODP_NULL only + * @return created object + */ +diffie_hellman_t *mock_dh_create(diffie_hellman_group_t group); + +#endif /** MOCK_DH_H_ @}*/ diff --git a/src/libcharon/tests/utils/mock_ipsec.c b/src/libcharon/tests/utils/mock_ipsec.c new file mode 100644 index 000000000..d57a26a87 --- /dev/null +++ b/src/libcharon/tests/utils/mock_ipsec.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * Copyright (C) 2008 Martin Willi + * HSR Hochschule fuer Technik Rapperswil + * + * 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 "mock_ipsec.h" + +typedef struct private_kernel_ipsec_t private_kernel_ipsec_t; + +/** + * Private data + */ +struct private_kernel_ipsec_t { + + /** + * Public interface + */ + kernel_ipsec_t public; + + /** + * Allocated SPI + */ + refcount_t spi; +}; + +METHOD(kernel_ipsec_t, get_spi, status_t, + private_kernel_ipsec_t *this, host_t *src, host_t *dst, uint8_t protocol, + uint32_t *spi) +{ + *spi = (uint32_t)ref_get(&this->spi); + return SUCCESS; +} + +METHOD(kernel_ipsec_t, get_cpi, status_t, + private_kernel_ipsec_t *this, host_t *src, host_t *dst, uint16_t *cpi) +{ + return FAILED; +} + +METHOD(kernel_ipsec_t, add_sa, status_t, + private_kernel_ipsec_t *this, kernel_ipsec_sa_id_t *id, + kernel_ipsec_add_sa_t *data) +{ + return SUCCESS; +} + +METHOD(kernel_ipsec_t, update_sa, status_t, + private_kernel_ipsec_t *this, kernel_ipsec_sa_id_t *id, + kernel_ipsec_update_sa_t *data) +{ + return SUCCESS; +} + +METHOD(kernel_ipsec_t, query_sa, status_t, + private_kernel_ipsec_t *this, kernel_ipsec_sa_id_t *id, + kernel_ipsec_query_sa_t *data, uint64_t *bytes, uint64_t *packets, + time_t *time) +{ + return NOT_SUPPORTED; +} + +METHOD(kernel_ipsec_t, del_sa, status_t, + private_kernel_ipsec_t *this, kernel_ipsec_sa_id_t *id, + kernel_ipsec_del_sa_t *data) +{ + return SUCCESS; +} + +METHOD(kernel_ipsec_t, add_policy, status_t, + private_kernel_ipsec_t *this, kernel_ipsec_policy_id_t *id, + kernel_ipsec_manage_policy_t *data) +{ + return SUCCESS; +} + +METHOD(kernel_ipsec_t, query_policy, status_t, + private_kernel_ipsec_t *this, kernel_ipsec_policy_id_t *id, + kernel_ipsec_query_policy_t *data, time_t *use_time) +{ + *use_time = 1; + return SUCCESS; +} + +METHOD(kernel_ipsec_t, del_policy, status_t, + private_kernel_ipsec_t *this, kernel_ipsec_policy_id_t *id, + kernel_ipsec_manage_policy_t *data) +{ + return SUCCESS; +} + +/* + * Described in header + */ +kernel_ipsec_t *mock_ipsec_create() +{ + private_kernel_ipsec_t *this; + + INIT(this, + .public = { + .get_spi = _get_spi, + .get_cpi = _get_cpi, + .add_sa = _add_sa, + .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 = (void*)return_true, + .enable_udp_decap = (void*)return_true, + .destroy = (void*)free, + }, + ); + return &this->public; +} diff --git a/src/libcharon/tests/utils/mock_ipsec.h b/src/libcharon/tests/utils/mock_ipsec.h new file mode 100644 index 000000000..cbf21524a --- /dev/null +++ b/src/libcharon/tests/utils/mock_ipsec.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * 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. + */ + +/** + * kernel_ipsec_t implementation used for exchange unit tests. Currently + * returns sequential SPIs, all other methods are noops. + * + * @defgroup mock_ipsec mock_ipsec + * @{ @ingroup test_utils_c + */ + +#ifndef MOCK_IPSEC_H_ +#define MOCK_IPSEC_H_ + +#include <kernel/kernel_ipsec.h> + +/** + * Create an instance of kernel_ipsec_t + * + * @return created object + */ +kernel_ipsec_t *mock_ipsec_create(); + +#endif /** MOCK_IPSEC_H_ @}*/ diff --git a/src/libcharon/tests/utils/mock_nonce_gen.c b/src/libcharon/tests/utils/mock_nonce_gen.c new file mode 100644 index 000000000..30910f991 --- /dev/null +++ b/src/libcharon/tests/utils/mock_nonce_gen.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * 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 "mock_nonce_gen.h" + +typedef struct private_nonce_gen_t private_nonce_gen_t; + +struct private_nonce_gen_t { + + /** + * Public interface + */ + nonce_gen_t public; + + /** + * Random number generator + */ + rng_t* rng; + + /** + * First byte to set to the nonces + */ + u_char first; +}; + +METHOD(nonce_gen_t, get_nonce, bool, + private_nonce_gen_t *this, size_t size, uint8_t *buffer) +{ + if (size > 0) + { + buffer[0] = this->first; + buffer++; + size--; + } + return this->rng->get_bytes(this->rng, size, buffer); +} + +METHOD(nonce_gen_t, allocate_nonce, bool, + private_nonce_gen_t *this, size_t size, chunk_t *chunk) +{ + *chunk = chunk_alloc(size); + if (!get_nonce(this, chunk->len, chunk->ptr)) + { + chunk_free(chunk); + return FALSE; + } + return TRUE; +} + +METHOD(nonce_gen_t, destroy, void, + private_nonce_gen_t *this) +{ + DESTROY_IF(this->rng); + free(this); +} + +/* + * Described in header + */ +nonce_gen_t *mock_nonce_gen_create(u_char first) +{ + private_nonce_gen_t *this; + + INIT(this, + .public = { + .get_nonce = _get_nonce, + .allocate_nonce = _allocate_nonce, + .destroy = _destroy, + }, + .rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK), + .first = first, + ); + if (!this->rng) + { + destroy(this); + return NULL; + } + return &this->public; +} diff --git a/src/libcharon/tests/utils/mock_nonce_gen.h b/src/libcharon/tests/utils/mock_nonce_gen.h new file mode 100644 index 000000000..feeab8bc0 --- /dev/null +++ b/src/libcharon/tests/utils/mock_nonce_gen.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * 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. + */ + +/** + * Special nonce generator that sets the first byte of the generated nonces to + * a fixed specified value. + * + * @defgroup mock_nonce_gen mock_nonce_gen + * @{ @ingroup test_utils_c + */ + +#ifndef MOCK_NONCE_GEN_H_ +#define MOCK_NONCE_GEN_H_ + +#include <crypto/nonce_gen.h> + +/** + * Creates a nonce_gen_t instance. + * + * @param first first byte to set in generated nonces + * @return created object + */ +nonce_gen_t *mock_nonce_gen_create(u_char first); + +#endif /** MOCK_NONCE_GEN_H_ @} */ diff --git a/src/libcharon/tests/utils/mock_sender.c b/src/libcharon/tests/utils/mock_sender.c new file mode 100644 index 000000000..c090ff439 --- /dev/null +++ b/src/libcharon/tests/utils/mock_sender.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * 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 "mock_sender.h" + +#include <collections/linked_list.h> + +typedef struct private_mock_sender_t private_mock_sender_t; + +/** + * Private data + */ +struct private_mock_sender_t { + + /** + * Public interface + */ + mock_sender_t public; + + /** + * Packet queue, as message_t* + */ + linked_list_t *queue; +}; + + +METHOD(sender_t, send_, void, + private_mock_sender_t *this, packet_t *packet) +{ + message_t *message; + + message = message_create_from_packet(packet); + message->parse_header(message); + this->queue->insert_last(this->queue, message); +} + +METHOD(mock_sender_t, dequeue, message_t*, + private_mock_sender_t *this) +{ + message_t *message = NULL; + + this->queue->remove_first(this->queue, (void**)&message); + return message; +} + +METHOD(sender_t, destroy, void, + private_mock_sender_t *this) +{ + this->queue->destroy_offset(this->queue, offsetof(message_t, destroy)); + free(this); +} + +/* + * Described in header + */ +mock_sender_t *mock_sender_create() +{ + private_mock_sender_t *this; + + INIT(this, + .public = { + .interface = { + .send = _send_, + .send_no_marker = (void*)nop, + .flush = (void*)nop, + .destroy = _destroy, + }, + .dequeue = _dequeue, + }, + .queue = linked_list_create(), + ); + return &this->public; +} diff --git a/src/libcharon/tests/utils/mock_sender.h b/src/libcharon/tests/utils/mock_sender.h new file mode 100644 index 000000000..5eabddadc --- /dev/null +++ b/src/libcharon/tests/utils/mock_sender.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * 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. + */ + +/** + * sender_t implementation that does not pass the sent packet to a socket but + * instead provides it for immediate delivery to an ike_sa_t object. + * + * @defgroup mock_sender mock_sender + * @{ @ingroup test_utils_c + */ + +#ifndef MOCK_SENDER_H_ +#define MOCK_SENDER_H_ + +#include <encoding/message.h> +#include <network/sender.h> + +typedef struct mock_sender_t mock_sender_t; + +struct mock_sender_t { + + /** + * Implemented interface + */ + sender_t interface; + + /** + * Remove the next packet in the send queue as message_t object. The IKE + * header is already parsed (which is assumed does not fail) so it can + * directly be passed to ike_sa_t::process_message(). + * + * @return message or NULL if none is queued + */ + message_t *(*dequeue)(mock_sender_t *this); +}; + +/** + * Creates a mock_sender_t instance. + * + * @return created object + */ +mock_sender_t *mock_sender_create(); + +#endif /** MOCK_SENDER_H_ @} */ diff --git a/src/libcharon/tests/utils/sa_asserts.h b/src/libcharon/tests/utils/sa_asserts.h new file mode 100644 index 000000000..7afa3b55b --- /dev/null +++ b/src/libcharon/tests/utils/sa_asserts.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * 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. + */ + +/** + * Special assertions against IKE_SAs and CHILD_SAs (e.g. regarding their + * state). + * + * @defgroup sa_asserts sa_asserts + * @{ @ingroup test_utils_c + */ + +#ifndef SA_ASSERTS_H_ +#define SA_ASSERTS_H_ + +#include <inttypes.h> + +/** + * Check that there exists a specific number of IKE_SAs in the manager. + */ +#define assert_ike_sa_count(count) \ +({ \ + typeof(count) _count = count; \ + u_int _actual = charon->ike_sa_manager->get_count(charon->ike_sa_manager); \ + test_assert_msg(_count == _actual, "unexpected number of IKE_SAs in " \ + "manager (%d != %d)", _count, _actual); \ +}) + +/** + * Check that the IKE_SA with the given SPIs and initiator flag is in the + * manager and return it. Does not actually keep the SA checked out as + * that would block cleaning up if asserts against it fail (since we control + * access to SAs it's also not really necessary). + */ +#define assert_ike_sa_checkout(spi_i, spi_r, initiator) \ +({ \ + typeof(spi_i) _spi_i = spi_i; \ + typeof(spi_r) _spi_r = spi_r; \ + typeof(initiator) _init = initiator; \ + ike_sa_id_t *_id = ike_sa_id_create(IKEV2, _spi_i, _spi_r, _init); \ + ike_sa_t *_ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, _id); \ + test_assert_msg(_ike_sa, "IKE_SA with SPIs %.16"PRIx64"_i %.16"PRIx64"_r " \ + "(%d) does not exist", be64toh(_spi_i), be64toh(_spi_r), _init); \ + _id->destroy(_id); \ + charon->ike_sa_manager->checkin(charon->ike_sa_manager, _ike_sa); \ + _ike_sa; \ +}) + +/** + * Check if the given IKE_SA is in the expected state. + */ +#define assert_ike_sa_state(ike_sa, state) \ +({ \ + typeof(ike_sa) _sa = ike_sa; \ + typeof(state) _state = state; \ + test_assert_msg(_state == _sa->get_state(_sa), "%N != %N", \ + ike_sa_state_names, _state, \ + ike_sa_state_names, _sa->get_state(_sa)); \ +}) + +/** + * Check that there exists a specific number of CHILD_SAs. + */ +#define assert_child_sa_count(ike_sa, count) \ +({ \ + typeof(ike_sa) _sa = ike_sa; \ + typeof(count) _count = count; \ + test_assert_msg(_count == _sa->get_child_count(_sa), "unexpected number " \ + "of CHILD_SAs in IKE_SA %s (%d != %d)", #ike_sa, _count, \ + _sa->get_child_count(_sa)); \ +}) + +/** + * Check if the CHILD_SA with the given SPI is in the expected state. + */ +#define assert_child_sa_state(ike_sa, spi, state) \ +({ \ + typeof(ike_sa) _sa = ike_sa; \ + typeof(spi) _spi = spi; \ + typeof(state) _state = state; \ + child_sa_t *_child = _sa->get_child_sa(_sa, PROTO_ESP, _spi, TRUE) ?: \ + _sa->get_child_sa(_sa, PROTO_ESP, _spi, FALSE); \ + test_assert_msg(_child, "CHILD_SA with SPI %.8x does not exist", \ + ntohl(_spi)); \ + test_assert_msg(_state == _child->get_state(_child), "%N != %N", \ + child_sa_state_names, _state, \ + child_sa_state_names, _child->get_state(_child)); \ +}) + +/** + * Assert that the CHILD_SA with the given inbound SPI does not exist. + */ +#define assert_child_sa_not_exists(ike_sa, spi) \ +({ \ + typeof(ike_sa) _sa = ike_sa; \ + typeof(spi) _spi = spi; \ + child_sa_t *_child = _sa->get_child_sa(_sa, PROTO_ESP, _spi, TRUE) ?: \ + _sa->get_child_sa(_sa, PROTO_ESP, _spi, FALSE); \ + test_assert_msg(!_child, "CHILD_SA with SPI %.8x exists", ntohl(_spi)); \ +}) + +/** + * Assert that there is a specific number of tasks in a given queue + * + * @param ike_sa IKE_SA to check + * @param count number of expected tasks + * @param queue queue to check (task_queue_t) + */ +#define assert_num_tasks(ike_sa, count, queue) \ +({ \ + typeof(ike_sa) _sa = ike_sa; \ + typeof(count) _count = count; \ + int _c = 0; task_t *_task; \ + enumerator_t *_enumerator = _sa->create_task_enumerator(_sa, queue); \ + while (_enumerator->enumerate(_enumerator, &_task)) { _c++; } \ + _enumerator->destroy(_enumerator); \ + test_assert_msg(_count == _c, "unexpected number of tasks in " #queue " " \ + "of IKE_SA %s (%d != %d)", #ike_sa, _count, _c); \ +}) + +/** + * Assert that all task queues of the given IKE_SA are empty + * + * @param ike_sa IKE_SA to check + */ +#define assert_sa_idle(ike_sa) \ +({ \ + typeof(ike_sa) _ike_sa = ike_sa; \ + assert_num_tasks(_ike_sa, 0, TASK_QUEUE_QUEUED); \ + assert_num_tasks(_ike_sa, 0, TASK_QUEUE_ACTIVE); \ + assert_num_tasks(_ike_sa, 0, TASK_QUEUE_PASSIVE); \ +}) + +#endif /** SA_ASSERTS_H_ @}*/ |