summaryrefslogtreecommitdiff
path: root/src/libcharon/tests
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/tests')
-rw-r--r--src/libcharon/tests/Makefile.am34
-rw-r--r--src/libcharon/tests/Makefile.in333
-rw-r--r--src/libcharon/tests/exchange_tests.c64
-rw-r--r--src/libcharon/tests/exchange_tests.h20
-rw-r--r--src/libcharon/tests/libcharon_tests.c3
-rw-r--r--src/libcharon/tests/libcharon_tests.h12
-rw-r--r--src/libcharon/tests/suites/test_child_create.c106
-rw-r--r--src/libcharon/tests/suites/test_child_delete.c366
-rw-r--r--src/libcharon/tests/suites/test_child_rekey.c1569
-rw-r--r--src/libcharon/tests/suites/test_ike_delete.c137
-rw-r--r--src/libcharon/tests/suites/test_ike_rekey.c1480
-rw-r--r--src/libcharon/tests/suites/test_message_chapoly.c8
-rw-r--r--src/libcharon/tests/suites/test_proposal.c81
-rw-r--r--src/libcharon/tests/utils/exchange_test_asserts.c182
-rw-r--r--src/libcharon/tests/utils/exchange_test_asserts.h343
-rw-r--r--src/libcharon/tests/utils/exchange_test_helper.c372
-rw-r--r--src/libcharon/tests/utils/exchange_test_helper.h128
-rw-r--r--src/libcharon/tests/utils/job_asserts.h59
-rw-r--r--src/libcharon/tests/utils/mock_dh.c87
-rw-r--r--src/libcharon/tests/utils/mock_dh.h37
-rw-r--r--src/libcharon/tests/utils/mock_ipsec.c128
-rw-r--r--src/libcharon/tests/utils/mock_ipsec.h36
-rw-r--r--src/libcharon/tests/utils/mock_nonce_gen.c91
-rw-r--r--src/libcharon/tests/utils/mock_nonce_gen.h37
-rw-r--r--src/libcharon/tests/utils/mock_sender.c85
-rw-r--r--src/libcharon/tests/utils/mock_sender.h56
-rw-r--r--src/libcharon/tests/utils/sa_asserts.h145
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=''; \
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_ @}*/