summaryrefslogtreecommitdiffstats
path: root/src/bin/perfdhcp/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:15:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:15:43 +0000
commitf5f56e1a1c4d9e9496fcb9d81131066a964ccd23 (patch)
tree49e44c6f87febed37efb953ab5485aa49f6481a7 /src/bin/perfdhcp/tests
parentInitial commit. (diff)
downloadisc-kea-upstream.tar.xz
isc-kea-upstream.zip
Adding upstream version 2.4.1.upstream/2.4.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/bin/perfdhcp/tests/Makefile.am58
-rw-r--r--src/bin/perfdhcp/tests/Makefile.in1284
-rw-r--r--src/bin/perfdhcp/tests/avalanche_scen_unittest.cc323
-rw-r--r--src/bin/perfdhcp/tests/basic_scen_unittest.cc364
-rw-r--r--src/bin/perfdhcp/tests/command_options_helper.h134
-rw-r--r--src/bin/perfdhcp/tests/command_options_unittest.cc1017
-rw-r--r--src/bin/perfdhcp/tests/localized_option_unittest.cc42
-rw-r--r--src/bin/perfdhcp/tests/packet_storage_unittest.cc199
-rw-r--r--src/bin/perfdhcp/tests/perf_pkt4_unittest.cc425
-rw-r--r--src/bin/perfdhcp/tests/perf_pkt6_unittest.cc324
-rw-r--r--src/bin/perfdhcp/tests/perf_socket_unittest.cc58
-rw-r--r--src/bin/perfdhcp/tests/random_number_generator_unittest.cc296
-rw-r--r--src/bin/perfdhcp/tests/rate_control_unittest.cc105
-rw-r--r--src/bin/perfdhcp/tests/receiver_unittest.cc116
-rw-r--r--src/bin/perfdhcp/tests/run_unittests.cc17
-rw-r--r--src/bin/perfdhcp/tests/stats_mgr_unittest.cc598
-rw-r--r--src/bin/perfdhcp/tests/test_control_unittest.cc1971
-rw-r--r--src/bin/perfdhcp/tests/testdata/Makefile.am5
-rw-r--r--src/bin/perfdhcp/tests/testdata/Makefile.in731
-rw-r--r--src/bin/perfdhcp/tests/testdata/discover-example.hex1
-rw-r--r--src/bin/perfdhcp/tests/testdata/mac-list.txt4
-rw-r--r--src/bin/perfdhcp/tests/testdata/relay4-list.txt5
-rw-r--r--src/bin/perfdhcp/tests/testdata/relay6-list.txt2
-rw-r--r--src/bin/perfdhcp/tests/testdata/request4-example.hex1
-rw-r--r--src/bin/perfdhcp/tests/testdata/request6-example.hex1
-rw-r--r--src/bin/perfdhcp/tests/testdata/solicit-example.hex1
26 files changed, 8082 insertions, 0 deletions
diff --git a/src/bin/perfdhcp/tests/Makefile.am b/src/bin/perfdhcp/tests/Makefile.am
new file mode 100644
index 0000000..4de5e30
--- /dev/null
+++ b/src/bin/perfdhcp/tests/Makefile.am
@@ -0,0 +1,58 @@
+SUBDIRS = . testdata
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += -I$(top_builddir)/src/bin -I$(top_srcdir)/src/bin
+AM_CPPFLAGS += -I$(srcdir)/.. -I$(builddir)/..
+AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_srcdir)/testdata\"
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+# The test[1-5].hex are created by the TestControl.PacketTemplates
+# unit tests and have to be removed.
+CLEANFILES += test1.hex test2.hex test3.hex test4.hex test5.hex
+
+TESTS_ENVIRONMENT = $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += command_options_unittest.cc
+run_unittests_SOURCES += perf_pkt6_unittest.cc
+run_unittests_SOURCES += perf_pkt4_unittest.cc
+run_unittests_SOURCES += localized_option_unittest.cc
+run_unittests_SOURCES += packet_storage_unittest.cc
+run_unittests_SOURCES += rate_control_unittest.cc
+run_unittests_SOURCES += stats_mgr_unittest.cc
+run_unittests_SOURCES += test_control_unittest.cc
+run_unittests_SOURCES += receiver_unittest.cc
+run_unittests_SOURCES += perf_socket_unittest.cc
+run_unittests_SOURCES += basic_scen_unittest.cc
+run_unittests_SOURCES += avalanche_scen_unittest.cc
+run_unittests_SOURCES += command_options_helper.h
+run_unittests_SOURCES += random_number_generator_unittest.cc
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
+
+run_unittests_LDADD = $(top_builddir)/src/bin/perfdhcp/libperfdhcp.la
+run_unittests_LDADD += $(top_builddir)/src/lib/process/libkea-process.la
+run_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+run_unittests_LDADD += $(CRYPTO_LIBS) $(BOOST_LIBS) $(GTEST_LDADD)
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/perfdhcp/tests/Makefile.in b/src/bin/perfdhcp/tests/Makefile.in
new file mode 100644
index 0000000..43865a3
--- /dev/null
+++ b/src/bin/perfdhcp/tests/Makefile.in
@@ -0,0 +1,1284 @@
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+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 \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+TESTS = $(am__EXEEXT_1)
+@HAVE_GTEST_TRUE@am__append_1 = run_unittests
+noinst_PROGRAMS = $(am__EXEEXT_2)
+subdir = src/bin/perfdhcp/tests
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \
+ $(top_srcdir)/m4macros/ax_cpp11.m4 \
+ $(top_srcdir)/m4macros/ax_cpp20.m4 \
+ $(top_srcdir)/m4macros/ax_crypto.m4 \
+ $(top_srcdir)/m4macros/ax_find_library.m4 \
+ $(top_srcdir)/m4macros/ax_gssapi.m4 \
+ $(top_srcdir)/m4macros/ax_gtest.m4 \
+ $(top_srcdir)/m4macros/ax_isc_rpath.m4 \
+ $(top_srcdir)/m4macros/ax_netconf.m4 \
+ $(top_srcdir)/m4macros/libtool.m4 \
+ $(top_srcdir)/m4macros/ltoptions.m4 \
+ $(top_srcdir)/m4macros/ltsugar.m4 \
+ $(top_srcdir)/m4macros/ltversion.m4 \
+ $(top_srcdir)/m4macros/lt~obsolete.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 =
+@HAVE_GTEST_TRUE@am__EXEEXT_1 = run_unittests$(EXEEXT)
+am__EXEEXT_2 = $(am__EXEEXT_1)
+PROGRAMS = $(noinst_PROGRAMS)
+am__run_unittests_SOURCES_DIST = run_unittests.cc \
+ command_options_unittest.cc perf_pkt6_unittest.cc \
+ perf_pkt4_unittest.cc localized_option_unittest.cc \
+ packet_storage_unittest.cc rate_control_unittest.cc \
+ stats_mgr_unittest.cc test_control_unittest.cc \
+ receiver_unittest.cc perf_socket_unittest.cc \
+ basic_scen_unittest.cc avalanche_scen_unittest.cc \
+ command_options_helper.h random_number_generator_unittest.cc
+@HAVE_GTEST_TRUE@am_run_unittests_OBJECTS = \
+@HAVE_GTEST_TRUE@ run_unittests-run_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-command_options_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-perf_pkt6_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-perf_pkt4_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-localized_option_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-packet_storage_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-rate_control_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-stats_mgr_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-test_control_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-receiver_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-perf_socket_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-basic_scen_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-avalanche_scen_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-random_number_generator_unittest.$(OBJEXT)
+run_unittests_OBJECTS = $(am_run_unittests_OBJECTS)
+am__DEPENDENCIES_1 =
+@HAVE_GTEST_TRUE@run_unittests_DEPENDENCIES = $(top_builddir)/src/bin/perfdhcp/libperfdhcp.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/libkea-process.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/hooks/libkea-hooks.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dns/libkea-dns++.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/unittests/libutil_unittests.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/libkea-util.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \
+@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1)
+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 =
+run_unittests_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \
+ $(AM_CXXFLAGS) $(CXXFLAGS) $(run_unittests_LDFLAGS) $(LDFLAGS) \
+ -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = \
+ ./$(DEPDIR)/run_unittests-avalanche_scen_unittest.Po \
+ ./$(DEPDIR)/run_unittests-basic_scen_unittest.Po \
+ ./$(DEPDIR)/run_unittests-command_options_unittest.Po \
+ ./$(DEPDIR)/run_unittests-localized_option_unittest.Po \
+ ./$(DEPDIR)/run_unittests-packet_storage_unittest.Po \
+ ./$(DEPDIR)/run_unittests-perf_pkt4_unittest.Po \
+ ./$(DEPDIR)/run_unittests-perf_pkt6_unittest.Po \
+ ./$(DEPDIR)/run_unittests-perf_socket_unittest.Po \
+ ./$(DEPDIR)/run_unittests-random_number_generator_unittest.Po \
+ ./$(DEPDIR)/run_unittests-rate_control_unittest.Po \
+ ./$(DEPDIR)/run_unittests-receiver_unittest.Po \
+ ./$(DEPDIR)/run_unittests-run_unittests.Po \
+ ./$(DEPDIR)/run_unittests-stats_mgr_unittest.Po \
+ ./$(DEPDIR)/run_unittests-test_control_unittest.Po
+am__mv = mv -f
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+AM_V_CXX = $(am__v_CXX_@AM_V@)
+am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
+am__v_CXX_0 = @echo " CXX " $@;
+am__v_CXX_1 =
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
+am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
+am__v_CXXLD_0 = @echo " CXXLD " $@;
+am__v_CXXLD_1 =
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(run_unittests_SOURCES)
+DIST_SOURCES = $(am__run_unittests_SOURCES_DIST)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+ASCIIDOC = @ASCIIDOC@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BOOST_INCLUDES = @BOOST_INCLUDES@
+BOOST_LIBS = @BOOST_LIBS@
+BOTAN_TOOL = @BOTAN_TOOL@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CONTRIB_DIR = @CONTRIB_DIR@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRYPTO_CFLAGS = @CRYPTO_CFLAGS@
+CRYPTO_INCLUDES = @CRYPTO_INCLUDES@
+CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@
+CRYPTO_LIBS = @CRYPTO_LIBS@
+CRYPTO_PACKAGE = @CRYPTO_PACKAGE@
+CRYPTO_RPATH = @CRYPTO_RPATH@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@
+DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@
+DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@
+DISTCHECK_GSSAPI_CONFIGURE_FLAG = @DISTCHECK_GSSAPI_CONFIGURE_FLAG@
+DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@
+DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@
+DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG = @DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG@
+DISTCHECK_LIBYANG_CONFIGURE_FLAG = @DISTCHECK_LIBYANG_CONFIGURE_FLAG@
+DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@
+DISTCHECK_MYSQL_CONFIGURE_FLAG = @DISTCHECK_MYSQL_CONFIGURE_FLAG@
+DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@
+DISTCHECK_PGSQL_CONFIGURE_FLAG = @DISTCHECK_PGSQL_CONFIGURE_FLAG@
+DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@
+DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG = @DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG@
+DISTCHECK_SYSREPO_CONFIGURE_FLAG = @DISTCHECK_SYSREPO_CONFIGURE_FLAG@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GREP = @GREP@
+GSSAPI_CFLAGS = @GSSAPI_CFLAGS@
+GSSAPI_LIBS = @GSSAPI_LIBS@
+GTEST_CONFIG = @GTEST_CONFIG@
+GTEST_INCLUDES = @GTEST_INCLUDES@
+GTEST_LDADD = @GTEST_LDADD@
+GTEST_LDFLAGS = @GTEST_LDFLAGS@
+GTEST_SOURCE = @GTEST_SOURCE@
+HAVE_NETCONF = @HAVE_NETCONF@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KEA_CXXFLAGS = @KEA_CXXFLAGS@
+KEA_SRCID = @KEA_SRCID@
+KRB5_CONFIG = @KRB5_CONFIG@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBYANGCPP_CPPFLAGS = @LIBYANGCPP_CPPFLAGS@
+LIBYANGCPP_INCLUDEDIR = @LIBYANGCPP_INCLUDEDIR@
+LIBYANGCPP_LIBS = @LIBYANGCPP_LIBS@
+LIBYANGCPP_PREFIX = @LIBYANGCPP_PREFIX@
+LIBYANGCPP_VERSION = @LIBYANGCPP_VERSION@
+LIBYANG_CPPFLAGS = @LIBYANG_CPPFLAGS@
+LIBYANG_INCLUDEDIR = @LIBYANG_INCLUDEDIR@
+LIBYANG_LIBS = @LIBYANG_LIBS@
+LIBYANG_PREFIX = @LIBYANG_PREFIX@
+LIBYANG_VERSION = @LIBYANG_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@
+LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@
+MYSQL_LIBS = @MYSQL_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PDFLATEX = @PDFLATEX@
+PERL = @PERL@
+PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@
+PGSQL_LIBS = @PGSQL_LIBS@
+PKGPYTHONDIR = @PKGPYTHONDIR@
+PKG_CONFIG = @PKG_CONFIG@
+PLANTUML = @PLANTUML@
+PREMIUM_DIR = @PREMIUM_DIR@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+SED = @SED@
+SEP = @SEP@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINXBUILD = @SPHINXBUILD@
+SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@
+SR_PLUGINS_PATH = @SR_PLUGINS_PATH@
+SR_REPO_PATH = @SR_REPO_PATH@
+STRIP = @STRIP@
+SYSREPOCPP_CPPFLAGS = @SYSREPOCPP_CPPFLAGS@
+SYSREPOCPP_INCLUDEDIR = @SYSREPOCPP_INCLUDEDIR@
+SYSREPOCPP_LIBS = @SYSREPOCPP_LIBS@
+SYSREPOCPP_PREFIX = @SYSREPOCPP_PREFIX@
+SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@
+SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@
+SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@
+SYSREPO_LIBS = @SYSREPO_LIBS@
+SYSREPO_PREFIX = @SYSREPO_PREFIX@
+SYSREPO_VERSION = @SYSREPO_VERSION@
+USE_LCOV = @USE_LCOV@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@
+YACC = @YACC@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = . testdata
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib \
+ -I$(top_builddir)/src/bin -I$(top_srcdir)/src/bin \
+ -I$(srcdir)/.. -I$(builddir)/.. \
+ -DTEST_DATA_DIR=\"$(abs_srcdir)/testdata\" $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+@USE_STATIC_LINK_TRUE@AM_LDFLAGS = -static
+# The test[1-5].hex are created by the TestControl.PacketTemplates
+# unit tests and have to be removed.
+CLEANFILES = *.gcno *.gcda test1.hex test2.hex test3.hex test4.hex \
+ test5.hex
+TESTS_ENVIRONMENT = $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+@HAVE_GTEST_TRUE@run_unittests_SOURCES = run_unittests.cc \
+@HAVE_GTEST_TRUE@ command_options_unittest.cc \
+@HAVE_GTEST_TRUE@ perf_pkt6_unittest.cc perf_pkt4_unittest.cc \
+@HAVE_GTEST_TRUE@ localized_option_unittest.cc \
+@HAVE_GTEST_TRUE@ packet_storage_unittest.cc \
+@HAVE_GTEST_TRUE@ rate_control_unittest.cc \
+@HAVE_GTEST_TRUE@ stats_mgr_unittest.cc \
+@HAVE_GTEST_TRUE@ test_control_unittest.cc receiver_unittest.cc \
+@HAVE_GTEST_TRUE@ perf_socket_unittest.cc \
+@HAVE_GTEST_TRUE@ basic_scen_unittest.cc \
+@HAVE_GTEST_TRUE@ avalanche_scen_unittest.cc \
+@HAVE_GTEST_TRUE@ command_options_helper.h \
+@HAVE_GTEST_TRUE@ random_number_generator_unittest.cc
+@HAVE_GTEST_TRUE@run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+@HAVE_GTEST_TRUE@run_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
+@HAVE_GTEST_TRUE@run_unittests_LDADD = $(top_builddir)/src/bin/perfdhcp/libperfdhcp.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/libkea-process.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/hooks/libkea-hooks.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dns/libkea-dns++.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/unittests/libutil_unittests.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/libkea-util.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \
+@HAVE_GTEST_TRUE@ $(CRYPTO_LIBS) $(BOOST_LIBS) $(GTEST_LDADD)
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .cc .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/bin/perfdhcp/tests/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/bin/perfdhcp/tests/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+run_unittests$(EXEEXT): $(run_unittests_OBJECTS) $(run_unittests_DEPENDENCIES) $(EXTRA_run_unittests_DEPENDENCIES)
+ @rm -f run_unittests$(EXEEXT)
+ $(AM_V_CXXLD)$(run_unittests_LINK) $(run_unittests_OBJECTS) $(run_unittests_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-avalanche_scen_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-basic_scen_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-command_options_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-localized_option_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-packet_storage_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-perf_pkt4_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-perf_pkt6_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-perf_socket_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-random_number_generator_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rate_control_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-receiver_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-run_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-stats_mgr_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-test_control_unittest.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.cc.o:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
+
+run_unittests-run_unittests.o: run_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-run_unittests.o -MD -MP -MF $(DEPDIR)/run_unittests-run_unittests.Tpo -c -o run_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-run_unittests.Tpo $(DEPDIR)/run_unittests-run_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='run_unittests-run_unittests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc
+
+run_unittests-run_unittests.obj: run_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-run_unittests.obj -MD -MP -MF $(DEPDIR)/run_unittests-run_unittests.Tpo -c -o run_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-run_unittests.Tpo $(DEPDIR)/run_unittests-run_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='run_unittests-run_unittests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi`
+
+run_unittests-command_options_unittest.o: command_options_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-command_options_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-command_options_unittest.Tpo -c -o run_unittests-command_options_unittest.o `test -f 'command_options_unittest.cc' || echo '$(srcdir)/'`command_options_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-command_options_unittest.Tpo $(DEPDIR)/run_unittests-command_options_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='command_options_unittest.cc' object='run_unittests-command_options_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-command_options_unittest.o `test -f 'command_options_unittest.cc' || echo '$(srcdir)/'`command_options_unittest.cc
+
+run_unittests-command_options_unittest.obj: command_options_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-command_options_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-command_options_unittest.Tpo -c -o run_unittests-command_options_unittest.obj `if test -f 'command_options_unittest.cc'; then $(CYGPATH_W) 'command_options_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/command_options_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-command_options_unittest.Tpo $(DEPDIR)/run_unittests-command_options_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='command_options_unittest.cc' object='run_unittests-command_options_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-command_options_unittest.obj `if test -f 'command_options_unittest.cc'; then $(CYGPATH_W) 'command_options_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/command_options_unittest.cc'; fi`
+
+run_unittests-perf_pkt6_unittest.o: perf_pkt6_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-perf_pkt6_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-perf_pkt6_unittest.Tpo -c -o run_unittests-perf_pkt6_unittest.o `test -f 'perf_pkt6_unittest.cc' || echo '$(srcdir)/'`perf_pkt6_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-perf_pkt6_unittest.Tpo $(DEPDIR)/run_unittests-perf_pkt6_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='perf_pkt6_unittest.cc' object='run_unittests-perf_pkt6_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-perf_pkt6_unittest.o `test -f 'perf_pkt6_unittest.cc' || echo '$(srcdir)/'`perf_pkt6_unittest.cc
+
+run_unittests-perf_pkt6_unittest.obj: perf_pkt6_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-perf_pkt6_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-perf_pkt6_unittest.Tpo -c -o run_unittests-perf_pkt6_unittest.obj `if test -f 'perf_pkt6_unittest.cc'; then $(CYGPATH_W) 'perf_pkt6_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/perf_pkt6_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-perf_pkt6_unittest.Tpo $(DEPDIR)/run_unittests-perf_pkt6_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='perf_pkt6_unittest.cc' object='run_unittests-perf_pkt6_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-perf_pkt6_unittest.obj `if test -f 'perf_pkt6_unittest.cc'; then $(CYGPATH_W) 'perf_pkt6_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/perf_pkt6_unittest.cc'; fi`
+
+run_unittests-perf_pkt4_unittest.o: perf_pkt4_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-perf_pkt4_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-perf_pkt4_unittest.Tpo -c -o run_unittests-perf_pkt4_unittest.o `test -f 'perf_pkt4_unittest.cc' || echo '$(srcdir)/'`perf_pkt4_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-perf_pkt4_unittest.Tpo $(DEPDIR)/run_unittests-perf_pkt4_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='perf_pkt4_unittest.cc' object='run_unittests-perf_pkt4_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-perf_pkt4_unittest.o `test -f 'perf_pkt4_unittest.cc' || echo '$(srcdir)/'`perf_pkt4_unittest.cc
+
+run_unittests-perf_pkt4_unittest.obj: perf_pkt4_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-perf_pkt4_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-perf_pkt4_unittest.Tpo -c -o run_unittests-perf_pkt4_unittest.obj `if test -f 'perf_pkt4_unittest.cc'; then $(CYGPATH_W) 'perf_pkt4_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/perf_pkt4_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-perf_pkt4_unittest.Tpo $(DEPDIR)/run_unittests-perf_pkt4_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='perf_pkt4_unittest.cc' object='run_unittests-perf_pkt4_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-perf_pkt4_unittest.obj `if test -f 'perf_pkt4_unittest.cc'; then $(CYGPATH_W) 'perf_pkt4_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/perf_pkt4_unittest.cc'; fi`
+
+run_unittests-localized_option_unittest.o: localized_option_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-localized_option_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-localized_option_unittest.Tpo -c -o run_unittests-localized_option_unittest.o `test -f 'localized_option_unittest.cc' || echo '$(srcdir)/'`localized_option_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-localized_option_unittest.Tpo $(DEPDIR)/run_unittests-localized_option_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='localized_option_unittest.cc' object='run_unittests-localized_option_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-localized_option_unittest.o `test -f 'localized_option_unittest.cc' || echo '$(srcdir)/'`localized_option_unittest.cc
+
+run_unittests-localized_option_unittest.obj: localized_option_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-localized_option_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-localized_option_unittest.Tpo -c -o run_unittests-localized_option_unittest.obj `if test -f 'localized_option_unittest.cc'; then $(CYGPATH_W) 'localized_option_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/localized_option_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-localized_option_unittest.Tpo $(DEPDIR)/run_unittests-localized_option_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='localized_option_unittest.cc' object='run_unittests-localized_option_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-localized_option_unittest.obj `if test -f 'localized_option_unittest.cc'; then $(CYGPATH_W) 'localized_option_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/localized_option_unittest.cc'; fi`
+
+run_unittests-packet_storage_unittest.o: packet_storage_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-packet_storage_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-packet_storage_unittest.Tpo -c -o run_unittests-packet_storage_unittest.o `test -f 'packet_storage_unittest.cc' || echo '$(srcdir)/'`packet_storage_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-packet_storage_unittest.Tpo $(DEPDIR)/run_unittests-packet_storage_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='packet_storage_unittest.cc' object='run_unittests-packet_storage_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-packet_storage_unittest.o `test -f 'packet_storage_unittest.cc' || echo '$(srcdir)/'`packet_storage_unittest.cc
+
+run_unittests-packet_storage_unittest.obj: packet_storage_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-packet_storage_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-packet_storage_unittest.Tpo -c -o run_unittests-packet_storage_unittest.obj `if test -f 'packet_storage_unittest.cc'; then $(CYGPATH_W) 'packet_storage_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/packet_storage_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-packet_storage_unittest.Tpo $(DEPDIR)/run_unittests-packet_storage_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='packet_storage_unittest.cc' object='run_unittests-packet_storage_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-packet_storage_unittest.obj `if test -f 'packet_storage_unittest.cc'; then $(CYGPATH_W) 'packet_storage_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/packet_storage_unittest.cc'; fi`
+
+run_unittests-rate_control_unittest.o: rate_control_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rate_control_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rate_control_unittest.Tpo -c -o run_unittests-rate_control_unittest.o `test -f 'rate_control_unittest.cc' || echo '$(srcdir)/'`rate_control_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rate_control_unittest.Tpo $(DEPDIR)/run_unittests-rate_control_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rate_control_unittest.cc' object='run_unittests-rate_control_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rate_control_unittest.o `test -f 'rate_control_unittest.cc' || echo '$(srcdir)/'`rate_control_unittest.cc
+
+run_unittests-rate_control_unittest.obj: rate_control_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rate_control_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rate_control_unittest.Tpo -c -o run_unittests-rate_control_unittest.obj `if test -f 'rate_control_unittest.cc'; then $(CYGPATH_W) 'rate_control_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rate_control_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rate_control_unittest.Tpo $(DEPDIR)/run_unittests-rate_control_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rate_control_unittest.cc' object='run_unittests-rate_control_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rate_control_unittest.obj `if test -f 'rate_control_unittest.cc'; then $(CYGPATH_W) 'rate_control_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rate_control_unittest.cc'; fi`
+
+run_unittests-stats_mgr_unittest.o: stats_mgr_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-stats_mgr_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-stats_mgr_unittest.Tpo -c -o run_unittests-stats_mgr_unittest.o `test -f 'stats_mgr_unittest.cc' || echo '$(srcdir)/'`stats_mgr_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-stats_mgr_unittest.Tpo $(DEPDIR)/run_unittests-stats_mgr_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='stats_mgr_unittest.cc' object='run_unittests-stats_mgr_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-stats_mgr_unittest.o `test -f 'stats_mgr_unittest.cc' || echo '$(srcdir)/'`stats_mgr_unittest.cc
+
+run_unittests-stats_mgr_unittest.obj: stats_mgr_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-stats_mgr_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-stats_mgr_unittest.Tpo -c -o run_unittests-stats_mgr_unittest.obj `if test -f 'stats_mgr_unittest.cc'; then $(CYGPATH_W) 'stats_mgr_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/stats_mgr_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-stats_mgr_unittest.Tpo $(DEPDIR)/run_unittests-stats_mgr_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='stats_mgr_unittest.cc' object='run_unittests-stats_mgr_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-stats_mgr_unittest.obj `if test -f 'stats_mgr_unittest.cc'; then $(CYGPATH_W) 'stats_mgr_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/stats_mgr_unittest.cc'; fi`
+
+run_unittests-test_control_unittest.o: test_control_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-test_control_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-test_control_unittest.Tpo -c -o run_unittests-test_control_unittest.o `test -f 'test_control_unittest.cc' || echo '$(srcdir)/'`test_control_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-test_control_unittest.Tpo $(DEPDIR)/run_unittests-test_control_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='test_control_unittest.cc' object='run_unittests-test_control_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-test_control_unittest.o `test -f 'test_control_unittest.cc' || echo '$(srcdir)/'`test_control_unittest.cc
+
+run_unittests-test_control_unittest.obj: test_control_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-test_control_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-test_control_unittest.Tpo -c -o run_unittests-test_control_unittest.obj `if test -f 'test_control_unittest.cc'; then $(CYGPATH_W) 'test_control_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/test_control_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-test_control_unittest.Tpo $(DEPDIR)/run_unittests-test_control_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='test_control_unittest.cc' object='run_unittests-test_control_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-test_control_unittest.obj `if test -f 'test_control_unittest.cc'; then $(CYGPATH_W) 'test_control_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/test_control_unittest.cc'; fi`
+
+run_unittests-receiver_unittest.o: receiver_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-receiver_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-receiver_unittest.Tpo -c -o run_unittests-receiver_unittest.o `test -f 'receiver_unittest.cc' || echo '$(srcdir)/'`receiver_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-receiver_unittest.Tpo $(DEPDIR)/run_unittests-receiver_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='receiver_unittest.cc' object='run_unittests-receiver_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-receiver_unittest.o `test -f 'receiver_unittest.cc' || echo '$(srcdir)/'`receiver_unittest.cc
+
+run_unittests-receiver_unittest.obj: receiver_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-receiver_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-receiver_unittest.Tpo -c -o run_unittests-receiver_unittest.obj `if test -f 'receiver_unittest.cc'; then $(CYGPATH_W) 'receiver_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/receiver_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-receiver_unittest.Tpo $(DEPDIR)/run_unittests-receiver_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='receiver_unittest.cc' object='run_unittests-receiver_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-receiver_unittest.obj `if test -f 'receiver_unittest.cc'; then $(CYGPATH_W) 'receiver_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/receiver_unittest.cc'; fi`
+
+run_unittests-perf_socket_unittest.o: perf_socket_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-perf_socket_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-perf_socket_unittest.Tpo -c -o run_unittests-perf_socket_unittest.o `test -f 'perf_socket_unittest.cc' || echo '$(srcdir)/'`perf_socket_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-perf_socket_unittest.Tpo $(DEPDIR)/run_unittests-perf_socket_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='perf_socket_unittest.cc' object='run_unittests-perf_socket_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-perf_socket_unittest.o `test -f 'perf_socket_unittest.cc' || echo '$(srcdir)/'`perf_socket_unittest.cc
+
+run_unittests-perf_socket_unittest.obj: perf_socket_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-perf_socket_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-perf_socket_unittest.Tpo -c -o run_unittests-perf_socket_unittest.obj `if test -f 'perf_socket_unittest.cc'; then $(CYGPATH_W) 'perf_socket_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/perf_socket_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-perf_socket_unittest.Tpo $(DEPDIR)/run_unittests-perf_socket_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='perf_socket_unittest.cc' object='run_unittests-perf_socket_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-perf_socket_unittest.obj `if test -f 'perf_socket_unittest.cc'; then $(CYGPATH_W) 'perf_socket_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/perf_socket_unittest.cc'; fi`
+
+run_unittests-basic_scen_unittest.o: basic_scen_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-basic_scen_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-basic_scen_unittest.Tpo -c -o run_unittests-basic_scen_unittest.o `test -f 'basic_scen_unittest.cc' || echo '$(srcdir)/'`basic_scen_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-basic_scen_unittest.Tpo $(DEPDIR)/run_unittests-basic_scen_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='basic_scen_unittest.cc' object='run_unittests-basic_scen_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-basic_scen_unittest.o `test -f 'basic_scen_unittest.cc' || echo '$(srcdir)/'`basic_scen_unittest.cc
+
+run_unittests-basic_scen_unittest.obj: basic_scen_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-basic_scen_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-basic_scen_unittest.Tpo -c -o run_unittests-basic_scen_unittest.obj `if test -f 'basic_scen_unittest.cc'; then $(CYGPATH_W) 'basic_scen_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/basic_scen_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-basic_scen_unittest.Tpo $(DEPDIR)/run_unittests-basic_scen_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='basic_scen_unittest.cc' object='run_unittests-basic_scen_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-basic_scen_unittest.obj `if test -f 'basic_scen_unittest.cc'; then $(CYGPATH_W) 'basic_scen_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/basic_scen_unittest.cc'; fi`
+
+run_unittests-avalanche_scen_unittest.o: avalanche_scen_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-avalanche_scen_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-avalanche_scen_unittest.Tpo -c -o run_unittests-avalanche_scen_unittest.o `test -f 'avalanche_scen_unittest.cc' || echo '$(srcdir)/'`avalanche_scen_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-avalanche_scen_unittest.Tpo $(DEPDIR)/run_unittests-avalanche_scen_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='avalanche_scen_unittest.cc' object='run_unittests-avalanche_scen_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-avalanche_scen_unittest.o `test -f 'avalanche_scen_unittest.cc' || echo '$(srcdir)/'`avalanche_scen_unittest.cc
+
+run_unittests-avalanche_scen_unittest.obj: avalanche_scen_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-avalanche_scen_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-avalanche_scen_unittest.Tpo -c -o run_unittests-avalanche_scen_unittest.obj `if test -f 'avalanche_scen_unittest.cc'; then $(CYGPATH_W) 'avalanche_scen_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/avalanche_scen_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-avalanche_scen_unittest.Tpo $(DEPDIR)/run_unittests-avalanche_scen_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='avalanche_scen_unittest.cc' object='run_unittests-avalanche_scen_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-avalanche_scen_unittest.obj `if test -f 'avalanche_scen_unittest.cc'; then $(CYGPATH_W) 'avalanche_scen_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/avalanche_scen_unittest.cc'; fi`
+
+run_unittests-random_number_generator_unittest.o: random_number_generator_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-random_number_generator_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-random_number_generator_unittest.Tpo -c -o run_unittests-random_number_generator_unittest.o `test -f 'random_number_generator_unittest.cc' || echo '$(srcdir)/'`random_number_generator_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-random_number_generator_unittest.Tpo $(DEPDIR)/run_unittests-random_number_generator_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='random_number_generator_unittest.cc' object='run_unittests-random_number_generator_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-random_number_generator_unittest.o `test -f 'random_number_generator_unittest.cc' || echo '$(srcdir)/'`random_number_generator_unittest.cc
+
+run_unittests-random_number_generator_unittest.obj: random_number_generator_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-random_number_generator_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-random_number_generator_unittest.Tpo -c -o run_unittests-random_number_generator_unittest.obj `if test -f 'random_number_generator_unittest.cc'; then $(CYGPATH_W) 'random_number_generator_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/random_number_generator_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-random_number_generator_unittest.Tpo $(DEPDIR)/run_unittests-random_number_generator_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='random_number_generator_unittest.cc' object='run_unittests-random_number_generator_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-random_number_generator_unittest.obj `if test -f 'random_number_generator_unittest.cc'; then $(CYGPATH_W) 'random_number_generator_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/random_number_generator_unittest.cc'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+check-TESTS: $(TESTS)
+ @failed=0; all=0; xfail=0; xpass=0; skip=0; \
+ srcdir=$(srcdir); export srcdir; \
+ list=' $(TESTS) '; \
+ $(am__tty_colors); \
+ if test -n "$$list"; then \
+ for tst in $$list; do \
+ if test -f ./$$tst; then dir=./; \
+ elif test -f $$tst; then dir=; \
+ else dir="$(srcdir)/"; fi; \
+ if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xpass=`expr $$xpass + 1`; \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=XPASS; \
+ ;; \
+ *) \
+ col=$$grn; res=PASS; \
+ ;; \
+ esac; \
+ elif test $$? -ne 77; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xfail=`expr $$xfail + 1`; \
+ col=$$lgn; res=XFAIL; \
+ ;; \
+ *) \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=FAIL; \
+ ;; \
+ esac; \
+ else \
+ skip=`expr $$skip + 1`; \
+ col=$$blu; res=SKIP; \
+ fi; \
+ echo "$${col}$$res$${std}: $$tst"; \
+ done; \
+ if test "$$all" -eq 1; then \
+ tests="test"; \
+ All=""; \
+ else \
+ tests="tests"; \
+ All="All "; \
+ fi; \
+ if test "$$failed" -eq 0; then \
+ if test "$$xfail" -eq 0; then \
+ banner="$$All$$all $$tests passed"; \
+ else \
+ if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
+ banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
+ fi; \
+ else \
+ if test "$$xpass" -eq 0; then \
+ banner="$$failed of $$all $$tests failed"; \
+ else \
+ if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
+ banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ fi; \
+ fi; \
+ dashes="$$banner"; \
+ skipped=""; \
+ if test "$$skip" -ne 0; then \
+ if test "$$skip" -eq 1; then \
+ skipped="($$skip test was not run)"; \
+ else \
+ skipped="($$skip tests were not run)"; \
+ fi; \
+ test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$skipped"; \
+ fi; \
+ report=""; \
+ if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+ report="Please report to $(PACKAGE_BUGREPORT)"; \
+ test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$report"; \
+ fi; \
+ dashes=`echo "$$dashes" | sed s/./=/g`; \
+ if test "$$failed" -eq 0; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ fi; \
+ echo "$${col}$$dashes$${std}"; \
+ echo "$${col}$$banner$${std}"; \
+ test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
+ test -z "$$report" || echo "$${col}$$report$${std}"; \
+ echo "$${col}$$dashes$${std}"; \
+ test "$$failed" -eq 0; \
+ else :; fi
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-recursive
+all-am: Makefile $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/run_unittests-avalanche_scen_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-basic_scen_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-command_options_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-localized_option_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-packet_storage_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-perf_pkt4_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-perf_pkt6_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-perf_socket_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-random_number_generator_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-rate_control_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-receiver_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-run_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-stats_mgr_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-test_control_unittest.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f ./$(DEPDIR)/run_unittests-avalanche_scen_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-basic_scen_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-command_options_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-localized_option_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-packet_storage_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-perf_pkt4_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-perf_pkt6_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-perf_socket_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-random_number_generator_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-rate_control_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-receiver_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-run_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-stats_mgr_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-test_control_unittest.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) check-am install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-TESTS check-am clean clean-generic \
+ clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs installdirs-am maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ 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.
+.NOEXPORT:
diff --git a/src/bin/perfdhcp/tests/avalanche_scen_unittest.cc b/src/bin/perfdhcp/tests/avalanche_scen_unittest.cc
new file mode 100644
index 0000000..d2e119a
--- /dev/null
+++ b/src/bin/perfdhcp/tests/avalanche_scen_unittest.cc
@@ -0,0 +1,323 @@
+// Copyright (C) 2012-2019,2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include "command_options_helper.h"
+#include "../avalanche_scen.h"
+
+#include <asiolink/io_address.h>
+#include <exceptions/exceptions.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_iaprefix.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/foreach.hpp>
+
+#include <algorithm>
+#include <cstddef>
+#include <stdint.h>
+#include <string>
+#include <fstream>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace boost::posix_time;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::perfdhcp;
+
+/// \brief FakeAvalancheScenPerfSocket class that mocks PerfSocket.
+///
+/// It stubs send and receive operations and collects statistics.
+/// Beside that it simulates DHCP server responses for received
+/// packets.
+class FakeAvalancheScenPerfSocket: public BasePerfSocket {
+public:
+ /// \brief Default constructor for FakeAvalancheScenPerfSocket.
+ FakeAvalancheScenPerfSocket(CommandOptions &opt) :
+ opt_(opt),
+ iface_(boost::make_shared<Iface>("fake", 0)),
+ sent_cnt_(0),
+ recv_cnt_(0),
+ initial_drops_cnt_(0) {};
+
+ CommandOptions &opt_;
+
+ IfacePtr iface_; ///< Local fake interface.
+
+ int sent_cnt_; ///< Counter of sent packets.
+ int recv_cnt_; ///< Counter of received packets.
+ int initial_drops_cnt_;
+
+ /// List of pairs <msg_type, trans_id> containing responses
+ /// planned to send to perfdhcp.
+ std::list<std::tuple<uint8_t, uint32_t>> planned_responses_;
+
+ /// \brief Simulate receiving DHCPv4 packet.
+ virtual dhcp::Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec) override {
+ (void)timeout_sec; // silence compile 'unused parameter' warning;
+ (void)timeout_usec; // silence compile 'unused parameter' warning;
+ recv_cnt_++;
+
+ if (planned_responses_.empty()) {
+ return Pkt4Ptr();
+ }
+
+ // simulate initial drops
+ if (initial_drops_cnt_ > 0) {
+ planned_responses_.pop_front();
+ initial_drops_cnt_--;
+ return(Pkt4Ptr());
+ }
+
+ // prepare received packet
+ auto msg = planned_responses_.front();
+ planned_responses_.pop_front();
+ auto msg_type = std::get<0>(msg);
+ Pkt4Ptr pkt(new Pkt4(msg_type, std::get<1>(msg)));
+ OptionPtr opt_serverid = Option::factory(Option::V4,
+ DHO_DHCP_SERVER_IDENTIFIER,
+ OptionBuffer(4, 1));
+ pkt->setYiaddr(asiolink::IOAddress("127.0.0.1"));
+ pkt->addOption(opt_serverid);
+ pkt->updateTimestamp();
+ return (pkt);
+ };
+
+ /// \brief Simulate receiving DHCPv6 packet.
+ virtual dhcp::Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec) override {
+ (void)timeout_sec; // silence compile 'unused parameter' warning;
+ (void)timeout_usec; // silence compile 'unused parameter' warning;
+ recv_cnt_++;
+
+ if (planned_responses_.empty()) {
+ return Pkt6Ptr();
+ }
+
+ // simulate initial drops
+ if (initial_drops_cnt_ > 0) {
+ planned_responses_.pop_front();
+ initial_drops_cnt_--;
+ return(Pkt6Ptr());
+ }
+
+ // prepare received packet
+ auto msg = planned_responses_.front();
+ planned_responses_.pop_front();
+ auto msg_type = std::get<0>(msg);
+ Pkt6Ptr pkt(new Pkt6(msg_type, std::get<1>(msg)));
+ // Add IA_NA if requested by the client.
+ if (opt_.getLeaseType().includes(CommandOptions::LeaseType::ADDRESS)) {
+ OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA);
+ OptionPtr iaaddr(new Option6IAAddr(D6O_IAADDR, isc::asiolink::IOAddress("fe80::abcd"), 300, 500));
+ opt_ia_na->addOption(iaaddr);
+ pkt->addOption(opt_ia_na);
+ }
+ // Add IA_PD if requested by the client.
+ if (opt_.getLeaseType().includes(CommandOptions::LeaseType::PREFIX)) {
+ OptionPtr opt_ia_pd = Option::factory(Option::V6, D6O_IA_PD);
+ OptionPtr iapref(new Option6IAPrefix(D6O_IAPREFIX, isc::asiolink::IOAddress("fe80::"), 64, 300, 500));
+ opt_ia_pd->addOption(iapref);
+ pkt->addOption(opt_ia_pd);
+ }
+ OptionPtr opt_serverid(new Option(Option::V6, D6O_SERVERID));
+ std::vector<uint8_t> duid({0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
+ OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid));
+ pkt->addOption(opt_serverid);
+ pkt->addOption(opt_clientid);
+ pkt->updateTimestamp();
+ return (pkt);
+ };
+
+ /// \brief Simulate sending DHCPv4 packet.
+ virtual bool send(const dhcp::Pkt4Ptr& pkt) override {
+ sent_cnt_++;
+ pkt->updateTimestamp();
+ if (pkt->getType() == DHCPDISCOVER) {
+ planned_responses_.push_back(std::make_tuple(DHCPOFFER, pkt->getTransid()));
+ } else if (pkt->getType() == DHCPREQUEST) {
+ planned_responses_.push_back(std::make_tuple(DHCPACK, pkt->getTransid()));
+ } else {
+ assert(0);
+ }
+ return true;
+ };
+
+ /// \brief Simulate sending DHCPv6 packet.
+ virtual bool send(const dhcp::Pkt6Ptr& pkt) override {
+ sent_cnt_++;
+ pkt->updateTimestamp();
+ if (pkt->getType() == DHCPV6_SOLICIT) {
+ planned_responses_.push_back(std::make_tuple(DHCPV6_ADVERTISE, pkt->getTransid()));
+ } else if (pkt->getType() == DHCPV6_REQUEST) {
+ planned_responses_.push_back(std::make_tuple(DHCPV6_REPLY, pkt->getTransid()));
+ } else {
+ assert(0);
+ }
+ return true;
+ };
+
+ /// \brief Override getting interface.
+ virtual IfacePtr getIface() override { return iface_; }
+
+ void reset() {
+ sent_cnt_ = 0;
+ recv_cnt_ = 0;
+ }
+};
+
+
+/// \brief NakedAvalancheScen class.
+///
+/// It exposes AvalancheScen internals for UT.
+class NakedAvalancheScen: public AvalancheScen {
+public:
+ using AvalancheScen::tc_;
+ using AvalancheScen::total_resent_;
+
+ FakeAvalancheScenPerfSocket fake_sock_;
+
+ NakedAvalancheScen(CommandOptions &opt) : AvalancheScen(opt, fake_sock_), fake_sock_(opt) {};
+
+};
+
+
+/// \brief Test Fixture Class
+///
+/// This test fixture class is used to perform
+/// unit tests on perfdhcp AvalancheScenTest class.
+class AvalancheScenTest : public virtual ::testing::Test
+{
+public:
+ AvalancheScenTest() { }
+
+ /// \brief Parse command line string with CommandOptions.
+ ///
+ /// \param cmdline command line string to be parsed.
+ /// \throw isc::Unexpected if unexpected error occurred.
+ /// \throw isc::InvalidParameter if command line is invalid.
+ void processCmdLine(CommandOptions &opt, const std::string& cmdline) const {
+ CommandOptionsHelper::process(opt, cmdline);
+ }
+
+ /// \brief Get full path to a file in testdata directory.
+ ///
+ /// \param filename filename being appended to absolute
+ /// path to testdata directory
+ ///
+ /// \return full path to a file in testdata directory.
+ std::string getFullPath(const std::string& filename) const {
+ std::ostringstream stream;
+ stream << TEST_DATA_DIR << "/" << filename;
+ return (stream.str());
+ }
+};
+
+
+TEST_F(AvalancheScenTest, Packet4Exchange) {
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -l fake -4 -R 10 --scenario avalanche -g single 127.0.0.1");
+ NakedAvalancheScen as(opt);
+
+ as.run();
+
+ // Check if basic exchange of packets happened. No retransmissions expected.
+ EXPECT_EQ(as.total_resent_, 0);
+ EXPECT_EQ(as.fake_sock_.sent_cnt_, 20); // Discovery + Request
+ EXPECT_EQ(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO), 10);
+ EXPECT_EQ(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 10);
+ EXPECT_EQ(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA), 10);
+ EXPECT_EQ(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA), 10);
+}
+
+
+TEST_F(AvalancheScenTest, Packet4ExchangeOnlyDO) {
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -l fake -4 -R 10 -i --scenario avalanche -g single 127.0.0.1");
+ NakedAvalancheScen as(opt);
+
+ as.run();
+
+ // Check if DO exchange of packets happened only. No retransmissions expected.
+ EXPECT_EQ(as.total_resent_, 0);
+ EXPECT_EQ(as.fake_sock_.sent_cnt_, 10); // Discovery + Request
+ EXPECT_EQ(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO), 10);
+ EXPECT_EQ(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 10);
+ EXPECT_THROW(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA), isc::BadValue);
+}
+
+
+TEST_F(AvalancheScenTest, Packet4ExchangeWithRetransmissions) {
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -l fake -4 -R 10 --scenario avalanche -g single 127.0.0.1");
+ NakedAvalancheScen as(opt);
+
+ as.fake_sock_.initial_drops_cnt_ = 2;
+ as.run();
+
+ // Check if basic exchange of packets happened. No retransmissions expected.
+ EXPECT_EQ(as.total_resent_, 2);
+ EXPECT_EQ(as.fake_sock_.sent_cnt_, 22); // Discovery + Request
+ EXPECT_EQ(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO), 10);
+ EXPECT_EQ(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 10);
+ EXPECT_EQ(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA), 10);
+ EXPECT_EQ(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA), 10);
+}
+
+
+TEST_F(AvalancheScenTest, Packet6Exchange) {
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -l fake -6 -R 10 --scenario avalanche -g single ::1");
+ NakedAvalancheScen as(opt);
+
+ as.run();
+
+ // Check if basic exchange of packets happened. No retransmissions expected.
+ EXPECT_EQ(as.total_resent_, 0);
+ EXPECT_GE(as.fake_sock_.sent_cnt_, 20); // Solicit + Request
+ EXPECT_GE(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA), 10);
+ EXPECT_GE(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 10);
+ EXPECT_GE(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR), 10);
+ EXPECT_GE(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR), 10);
+}
+
+
+TEST_F(AvalancheScenTest, Packet6ExchangeOnlySA) {
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -l fake -6 -R 10 -i --scenario avalanche -g single ::1");
+ NakedAvalancheScen as(opt);
+
+ as.run();
+
+ // Check if SA exchange of packets happened only. No retransmissions expected.
+ EXPECT_EQ(as.total_resent_, 0);
+ EXPECT_GE(as.fake_sock_.sent_cnt_, 10); // Solicit + Request
+ EXPECT_GE(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA), 10);
+ EXPECT_GE(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 10);
+ EXPECT_THROW(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR), isc::BadValue);
+}
+
+
+TEST_F(AvalancheScenTest, Packet6ExchangeWithRetransmissions) {
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -l fake -6 -R 10 --scenario avalanche -g single ::1");
+ NakedAvalancheScen as(opt);
+
+ as.fake_sock_.initial_drops_cnt_ = 2;
+ as.run();
+
+ // Check if basic exchange of packets happened. No retransmissions expected.
+ EXPECT_EQ(as.total_resent_, 2);
+ EXPECT_EQ(as.fake_sock_.sent_cnt_, 22); // Discovery + Request
+ EXPECT_EQ(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA), 10);
+ EXPECT_EQ(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 10);
+ EXPECT_EQ(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR), 10);
+ EXPECT_EQ(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR), 10);
+}
diff --git a/src/bin/perfdhcp/tests/basic_scen_unittest.cc b/src/bin/perfdhcp/tests/basic_scen_unittest.cc
new file mode 100644
index 0000000..5240293
--- /dev/null
+++ b/src/bin/perfdhcp/tests/basic_scen_unittest.cc
@@ -0,0 +1,364 @@
+// Copyright (C) 2012-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include "command_options_helper.h"
+#include "../basic_scen.h"
+
+#include <asiolink/io_address.h>
+#include <exceptions/exceptions.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_iaprefix.h>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/foreach.hpp>
+
+#include <algorithm>
+#include <cstddef>
+#include <stdint.h>
+#include <string>
+#include <fstream>
+#include <mutex>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace boost::posix_time;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::perfdhcp;
+
+/// \brief FakeScenPerfSocket class that mocks PerfSocket.
+///
+/// It stubs send and receive operations and collects statistics.
+/// Beside that it simulates DHCP server responses for received
+/// packets.
+class FakeScenPerfSocket: public BasePerfSocket {
+public:
+ /// \brief Default constructor for FakeScenPerfSocket.
+ FakeScenPerfSocket(CommandOptions &opt) :
+ opt_(opt),
+ iface_(boost::make_shared<Iface>("fake", 0)),
+ sent_cnt_(0),
+ recv_cnt_(0),
+ start_dropping_after_cnt_(100000) {};
+
+ CommandOptions &opt_;
+
+ IfacePtr iface_; ///< Local fake interface.
+
+ int sent_cnt_; ///< Counter of sent packets.
+ int recv_cnt_; ///< Counter of received packets.
+
+ /// List of pairs <msg_type, trans_id> containing responses
+ /// planned to send to perfdhcp.
+ std::list<std::tuple<uint8_t, uint32_t>> planned_responses_;
+
+ /// Mutex to protect internal state.
+ std::mutex mutex_;
+
+ /// Limit for sent packets. After this limit not more packets
+ /// are sent. This simulate dropping responses.
+ int start_dropping_after_cnt_;
+
+ /// \brief Simulate receiving DHCPv4 packet.
+ virtual dhcp::Pkt4Ptr receive4(uint32_t /*timeout_sec*/, uint32_t /*timeout_usec*/) override {
+ std::lock_guard<std::mutex> lock(mutex_);
+ recv_cnt_++;
+
+ if (planned_responses_.empty() || sent_cnt_ >= start_dropping_after_cnt_) {
+ return Pkt4Ptr();
+ }
+ auto msg = planned_responses_.front();
+ planned_responses_.pop_front();
+ auto msg_type = std::get<0>(msg);
+ Pkt4Ptr pkt(new Pkt4(msg_type, std::get<1>(msg)));
+ OptionPtr opt_serverid = Option::factory(Option::V4,
+ DHO_DHCP_SERVER_IDENTIFIER,
+ OptionBuffer(4, 1));
+ pkt->setYiaddr(asiolink::IOAddress("127.0.0.1"));
+ pkt->addOption(opt_serverid);
+ pkt->updateTimestamp();
+ return (pkt);
+ };
+
+ /// \brief Simulate receiving DHCPv6 packet.
+ virtual dhcp::Pkt6Ptr receive6(uint32_t /*timeout_sec*/, uint32_t /*timeout_usec*/) override {
+ std::lock_guard<std::mutex> lock(mutex_);
+ recv_cnt_++;
+
+ if (planned_responses_.empty() || sent_cnt_ >= start_dropping_after_cnt_) {
+ return Pkt6Ptr();
+ }
+ auto msg = planned_responses_.front();
+ planned_responses_.pop_front();
+ auto msg_type = std::get<0>(msg);
+ Pkt6Ptr pkt(new Pkt6(msg_type, std::get<1>(msg)));
+ // Add IA_NA if requested by the client.
+ if (opt_.getLeaseType().includes(CommandOptions::LeaseType::ADDRESS)) {
+ OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA);
+ OptionPtr iaaddr(new Option6IAAddr(D6O_IAADDR,
+ isc::asiolink::IOAddress("fe80::abcd"), 300, 500));
+ opt_ia_na->addOption(iaaddr);
+ pkt->addOption(opt_ia_na);
+ }
+ // Add IA_PD if requested by the client.
+ if (opt_.getLeaseType().includes(CommandOptions::LeaseType::PREFIX)) {
+ OptionPtr opt_ia_pd = Option::factory(Option::V6, D6O_IA_PD);
+ OptionPtr iapref(new Option6IAPrefix(D6O_IAPREFIX,
+ isc::asiolink::IOAddress("fe80::"), 64, 300, 500));
+ opt_ia_pd->addOption(iapref);
+ pkt->addOption(opt_ia_pd);
+ }
+ OptionPtr opt_serverid(new Option(Option::V6, D6O_SERVERID));
+ std::vector<uint8_t> duid({0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
+ OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid));
+ pkt->addOption(opt_serverid);
+ pkt->addOption(opt_clientid);
+ pkt->updateTimestamp();
+ return (pkt);
+ };
+
+ /// \brief Simulate sending DHCPv4 packet.
+ virtual bool send(const dhcp::Pkt4Ptr& pkt) override {
+ std::lock_guard<std::mutex> lock(mutex_);
+ sent_cnt_++;
+ pkt->updateTimestamp();
+ if (sent_cnt_ >= start_dropping_after_cnt_) {
+ return true;
+ }
+ if (pkt->getType() == DHCPDISCOVER) {
+ planned_responses_.push_back(std::make_tuple(DHCPOFFER, pkt->getTransid()));
+ } else if (pkt->getType() == DHCPREQUEST) {
+ planned_responses_.push_back(std::make_tuple(DHCPACK, pkt->getTransid()));
+ } else {
+ assert(0);
+ }
+ return true;
+ };
+
+ /// \brief Simulate sending DHCPv6 packet.
+ virtual bool send(const dhcp::Pkt6Ptr& pkt) override {
+ std::lock_guard<std::mutex> lock(mutex_);
+ sent_cnt_++;
+ pkt->updateTimestamp();
+ if (sent_cnt_ >= start_dropping_after_cnt_) {
+ return true;
+ }
+ if (pkt->getType() == DHCPV6_SOLICIT) {
+ planned_responses_.push_back(std::make_tuple(DHCPV6_ADVERTISE, pkt->getTransid()));
+ } else if (pkt->getType() == DHCPV6_REQUEST) {
+ planned_responses_.push_back(std::make_tuple(DHCPV6_REPLY, pkt->getTransid()));
+ } else {
+ assert(0);
+ }
+ return true;
+ };
+
+ /// \brief Override getting interface.
+ virtual IfacePtr getIface() override { return iface_; }
+
+ void reset() {
+ std::lock_guard<std::mutex> lock(mutex_);
+ sent_cnt_ = 0;
+ recv_cnt_ = 0;
+ }
+};
+
+
+/// \brief NakedBasicScen class.
+///
+/// It exposes BasicScen internals for UT.
+class NakedBasicScen: public BasicScen {
+public:
+ using BasicScen::basic_rate_control_;
+ using BasicScen::renew_rate_control_;
+ using BasicScen::release_rate_control_;
+ using BasicScen::tc_;
+
+ FakeScenPerfSocket fake_sock_;
+
+ NakedBasicScen(CommandOptions &opt) : BasicScen(opt, fake_sock_), fake_sock_(opt) {};
+
+};
+
+
+/// \brief Test Fixture Class
+///
+/// This test fixture class is used to perform
+/// unit tests on perfdhcp BasicScenTest class.
+class BasicScenTest : public virtual ::testing::Test
+{
+public:
+ BasicScenTest() { }
+
+ /// \brief Parse command line string with CommandOptions.
+ ///
+ /// \param cmdline command line string to be parsed.
+ /// \throw isc::Unexpected if unexpected error occurred.
+ /// \throw isc::InvalidParameter if command line is invalid.
+ void processCmdLine(CommandOptions &opt, const std::string& cmdline) const {
+ CommandOptionsHelper::process(opt, cmdline);
+ }
+
+ /// \brief Get full path to a file in testdata directory.
+ ///
+ /// \param filename filename being appended to absolute
+ /// path to testdata directory
+ ///
+ /// \return full path to a file in testdata directory.
+ std::string getFullPath(const std::string& filename) const {
+ std::ostringstream stream;
+ stream << TEST_DATA_DIR << "/" << filename;
+ return (stream.str());
+ }
+};
+
+
+// This test verifies that the class members are reset to expected values.
+TEST_F(BasicScenTest, initial_settings) {
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -6 -l ethx -r 50 -f 30 -F 10 all");
+ NakedBasicScen bs(opt);
+
+ EXPECT_EQ(50, bs.basic_rate_control_.getRate());
+ EXPECT_EQ(30, bs.renew_rate_control_.getRate());
+ EXPECT_EQ(10, bs.release_rate_control_.getRate());
+}
+
+
+TEST_F(BasicScenTest, Packet4Exchange) {
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -l fake -r 100 -n 10 -g single 127.0.0.1");
+ NakedBasicScen bs(opt);
+ bs.run();
+ // The command line restricts the number of iterations to 10
+ // with -n 10 parameter.
+ EXPECT_GE(bs.fake_sock_.sent_cnt_, 20); // Discovery + Request
+ EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO), 10);
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 10);
+ EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA), 10);
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA), 10);
+}
+
+TEST_F(BasicScenTest, Address4Unique) {
+ // send more than 1 discover+request but with the same address
+ // counter of a unique addresses should be 1
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -u -l fake -r 10 -n 10 -g single 127.0.0.1");
+ NakedBasicScen bs(opt);
+ bs.run();
+ EXPECT_GE(bs.fake_sock_.sent_cnt_, 5); // Discovery + Request
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 1);
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA), 1);
+ EXPECT_GE(bs.tc_.getAllUniqueAddrReply().size(), 1);
+ EXPECT_GE(bs.tc_.getAllUniqueAddrAdvert().size(), 1);
+ EXPECT_EQ(bs.tc_.getStatsMgr().getNonUniqueAddrNum(ExchangeType::DO), 9);
+ EXPECT_EQ(bs.tc_.getStatsMgr().getNonUniqueAddrNum(ExchangeType::RA), 9);
+}
+
+TEST_F(BasicScenTest, Address6Unique) {
+ // send more than 1 solicit+request but with the same address
+ // counter of a unique addresses should be 1
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -6 -u -l fake -r 10 -n 10 -g single ::1");
+ NakedBasicScen bs(opt);
+ bs.run();
+ EXPECT_GE(bs.fake_sock_.sent_cnt_, 5); // Solicit + Request
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 1);
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR), 1);
+ EXPECT_GE(bs.tc_.getAllUniqueAddrReply().size(), 1);
+ EXPECT_GE(bs.tc_.getAllUniqueAddrAdvert().size(), 1);
+ EXPECT_EQ(bs.tc_.getStatsMgr().getNonUniqueAddrNum(ExchangeType::SA), 9);
+ EXPECT_EQ(bs.tc_.getStatsMgr().getNonUniqueAddrNum(ExchangeType::RR), 9);
+}
+
+TEST_F(BasicScenTest, Packet4ExchangeMaxDrop10Proc) {
+ CommandOptions opt;
+
+ // With the following command line we restrict the maximum
+ // number of dropped packets to 10% of all.
+ // Use templates for this test.
+ processCmdLine(opt, "perfdhcp -l fake -r 100 -R 20 -n 100"
+ " -D 10% -L 10547 -g single"
+ // \todo seems to be broken as it crashes building pkt
+ // " -T " + getFullPath("discover-example.hex")
+ // + " -T " + getFullPath("request4-example.hex")
+ " 127.0.0.1");
+ // The number iterations is restricted by the percentage of
+ // dropped packets (-D 10%).
+ NakedBasicScen bs(opt);
+ bs.fake_sock_.start_dropping_after_cnt_ = 10;
+ bs.run();
+ EXPECT_GE(bs.fake_sock_.sent_cnt_, 15); // Discovery + Request
+ EXPECT_LE(bs.fake_sock_.sent_cnt_, 20); // Discovery + Request
+
+ EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO), 1);
+ EXPECT_LE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO), 15);
+
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 1);
+ EXPECT_LE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 15);
+
+ EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA), 1);
+ EXPECT_LE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA), 15);
+
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA), 1);
+ EXPECT_LE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA), 15);
+}
+
+
+TEST_F(BasicScenTest, Packet6Exchange) {
+ // Set number of iterations to 10.
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -l fake -6 -r 100 -n 10 -g single -R 20 -L 10547 ::1");
+ // Set number of received packets equal to number of iterations.
+ // This simulates no packet drops.
+ NakedBasicScen bs(opt);
+ bs.run();
+ EXPECT_GE(bs.fake_sock_.sent_cnt_, 20); // Solicit + Request
+ EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA), 10);
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 10);
+ EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR), 10);
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR), 10);
+}
+
+TEST_F(BasicScenTest, Packet6ExchangeMaxDrop3Pkt) {
+ CommandOptions opt;
+ // The maximum number of dropped packets is 3 (because of -D 3).
+ processCmdLine(opt, "perfdhcp -l fake"
+ " -6 -r 100 -n 100 -R 20 -D 3 -L 10547"
+ // \todo seems to be broken as it crashes building pkt
+ // " -T " + getFullPath("solicit-example.hex")
+ // + " -T " + getFullPath("request6-example.hex")
+ " ::1");
+
+ // Simulate the number of Solicit-Advertise-Request-Reply (SARR) exchanges.
+ // The test function generates server's responses and passes it to the
+ // TestControl class methods for processing. The number of exchanges
+ // actually performed is controller by 'start_dropping_after_cnt_'.
+ // All exchanged packets carry the IA_NA option
+ // to simulate the IPv6 address acquisition and to verify that the
+ // IA_NA options returned by the server are processed correctly.
+ NakedBasicScen bs(opt);
+ bs.fake_sock_.start_dropping_after_cnt_ = 10;
+ bs.run();
+ EXPECT_GE(bs.fake_sock_.sent_cnt_, 10);
+ EXPECT_LE(bs.fake_sock_.sent_cnt_, 20);
+
+ EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA), 1);
+ EXPECT_LE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA), 15);
+
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 1);
+ EXPECT_LE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 15);
+
+ EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR), 1);
+ EXPECT_LE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR), 15);
+
+ EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR), 1);
+ EXPECT_LE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR), 15);
+}
diff --git a/src/bin/perfdhcp/tests/command_options_helper.h b/src/bin/perfdhcp/tests/command_options_helper.h
new file mode 100644
index 0000000..dd8e512
--- /dev/null
+++ b/src/bin/perfdhcp/tests/command_options_helper.h
@@ -0,0 +1,134 @@
+// Copyright (C) 2012-2019 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef COMMAND_OPTIONS_HELPER_H
+#define COMMAND_OPTIONS_HELPER_H
+
+#include "../command_options.h"
+#include <exceptions/exceptions.h>
+
+#include <assert.h>
+#include <iterator>
+#include <cstring>
+#include <string>
+#include <vector>
+
+
+namespace isc {
+namespace perfdhcp {
+
+/// \brief Command Options Helper class.
+///
+/// This helper class can be shared between unit tests that
+/// need to initialize CommandOptions objects and feed it with
+/// specific command line. The command line can be given as a
+/// string representing program name, options and arguments.
+/// The static method exposed by this class can be used to
+/// tokenize this string into array of C-strings that are later
+/// consumed by \ref CommandOptions::parse. The state of the
+/// CommandOptions object is reset every time the process
+/// function is invoked. Also, when command line parsing is
+/// ended the array of C-string is freed from the memory.
+class CommandOptionsHelper {
+public:
+
+ /// \brief Wrapper class for allocated argv[] array.
+ ///
+ /// This class wraps allocated char** array and ensures that memory
+ /// allocated for this array is freed at the end o the scope.
+ class ArgvPtr {
+ public:
+ /// \brief Constructor.
+ ///
+ /// \param argv array of C-strings.
+ /// \param number of C-strings in the array.
+ ArgvPtr(char** argv, int argc) : argv_(argv), argc_(argc) { }
+
+ /// \brief Destructor.
+ ///
+ /// Deallocates wrapped array of C-strings.
+ ~ArgvPtr() {
+ if (argv_ != NULL) {
+ for(int i = 0; i < argc_; ++i) {
+ delete[] (argv_[i]);
+ argv_[i] = NULL;
+ }
+ delete[] (argv_);
+ }
+ }
+
+ /// \brief Return the array of C-strings.
+ ///
+ /// \return array of C-strings.
+ char** getArgv() const { return (argv_); }
+
+ /// \brief Return C-strings counter.
+ ///
+ /// \return C-strings counter.
+ int getArgc() const { return(argc_); }
+
+ public:
+ char** argv_; ///< array of C-strings being wrapped.
+ int argc_; ///< number of C-strings.
+ };
+
+ /// \brief Parse command line provided as string.
+ ///
+ /// Method transforms the string representing command line
+ /// to the array of C-strings consumed by the
+ /// \ref CommandOptions::parse function and performs
+ /// parsing.
+ ///
+ /// \param cmdline command line provided as single string.
+ /// \return true if program has been run in help or version mode ('h' or 'v' flag).
+ static bool process(CommandOptions& opt, const std::string& cmdline) {
+ int argc = 0;
+ char** argv = tokenizeString(cmdline, argc);
+ ArgvPtr args(argv, argc);
+ opt.reset();
+ return (opt.parse(args.getArgc(), args.getArgv()));
+ }
+
+private:
+
+ /// \brief Split string to the array of C-strings.
+ ///
+ /// \param text_to_split string to be split.
+ /// \param [out] num number of substrings returned.
+ /// \param std::bad_alloc if allocation of C-strings failed.
+ /// \return array of C-strings created from split.
+ static char** tokenizeString(const std::string& text_to_split, int& num) {
+ char** results = NULL;
+ // Tokenization with std streams
+ std::stringstream text_stream(text_to_split);
+ // Iterators to be used for tokenization
+ std::istream_iterator<std::string> text_iterator(text_stream);
+ std::istream_iterator<std::string> text_end;
+ // Tokenize string (space is a separator) using begin and end iterators
+ std::vector<std::string> tokens(text_iterator, text_end);
+
+ if (!tokens.empty()) {
+ // Allocate array of C-strings where we will store tokens
+ results = new char*[tokens.size()];
+ // Store tokens in C-strings array
+ for (size_t i = 0; i < tokens.size(); ++i) {
+ size_t cs_size = tokens[i].length() + 1;
+ char* cs = new char[cs_size];
+ strncpy(cs, tokens[i].c_str(), cs_size);
+ assert(cs[cs_size - 1] == '\0');
+ results[i] = cs;
+ }
+ // Return number of tokens to calling function
+ num = tokens.size();
+ }
+ return results;
+ }
+};
+
+} // namespace isc::perfdhcp
+} // namespace isc
+
+#endif // COMMAND_OPTIONS_HELPER_H
diff --git a/src/bin/perfdhcp/tests/command_options_unittest.cc b/src/bin/perfdhcp/tests/command_options_unittest.cc
new file mode 100644
index 0000000..08720d5
--- /dev/null
+++ b/src/bin/perfdhcp/tests/command_options_unittest.cc
@@ -0,0 +1,1017 @@
+// Copyright (C) 2012-2023 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <cstddef>
+#include <fstream>
+#include <gtest/gtest.h>
+#include <stdint.h>
+#include <string>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include <dhcp/iface_mgr.h>
+#include <exceptions/exceptions.h>
+
+#include "command_options_helper.h"
+
+using namespace std;
+using namespace isc;
+using namespace isc::perfdhcp;
+using namespace boost::posix_time;
+
+// Verify that default constructor sets lease type to the expected value.
+TEST(LeaseTypeTest, defaultConstructor) {
+ CommandOptions::LeaseType lease_type;
+ EXPECT_TRUE(lease_type.is(CommandOptions::LeaseType::ADDRESS));
+}
+
+// Verify that the constructor sets the lease type to the specified value.
+TEST(LeaseTypeTest, constructor) {
+ CommandOptions::LeaseType
+ lease_type1(CommandOptions::LeaseType::ADDRESS);
+ EXPECT_TRUE(lease_type1.is(CommandOptions::LeaseType::ADDRESS));
+
+ CommandOptions::LeaseType
+ lease_type2(CommandOptions::LeaseType::PREFIX);
+ EXPECT_TRUE(lease_type2.is(CommandOptions::LeaseType::PREFIX));
+}
+
+// Verify that the lease type can be modified using set() function.
+TEST(LeaseTypeTest, set) {
+ CommandOptions::LeaseType
+ lease_type(CommandOptions::LeaseType::ADDRESS);
+ EXPECT_TRUE(lease_type.is(CommandOptions::LeaseType::ADDRESS));
+
+ lease_type.set(CommandOptions::LeaseType::PREFIX);
+ EXPECT_TRUE(lease_type.is(CommandOptions::LeaseType::PREFIX));
+}
+
+// Verify that the includes() function returns true when the lease type
+// specified with the function argument is the same as the lease type
+// encapsulated by the LeaseType object on which include function is called
+// or when the lease type value encapsulated by this object is
+// ADDRESS_AND_PREFIX.
+TEST(LeaseTypeTest, includes) {
+ // Lease type: ADDRESS
+ CommandOptions::LeaseType lease_type(CommandOptions::LeaseType::ADDRESS);
+ // Lease type IS ADDRESS.
+ ASSERT_TRUE(lease_type.is(CommandOptions::LeaseType::ADDRESS));
+ // Lease type includes the ADDRESS.
+ EXPECT_TRUE(lease_type.includes(CommandOptions::LeaseType::ADDRESS));
+ // Lease type does not include PREFIX.
+ EXPECT_FALSE(lease_type.includes(CommandOptions::LeaseType::PREFIX));
+ // Lease type does not include ADDRESS_AND_PREFIX.
+ EXPECT_FALSE(
+ lease_type.includes(CommandOptions::LeaseType::ADDRESS_AND_PREFIX)
+ );
+
+ // Do the same check for PREFIX.
+ lease_type.set(CommandOptions::LeaseType::PREFIX);
+ EXPECT_FALSE(lease_type.includes(CommandOptions::LeaseType::ADDRESS));
+ EXPECT_TRUE(lease_type.includes(CommandOptions::LeaseType::PREFIX));
+ EXPECT_FALSE(
+ lease_type.includes(CommandOptions::LeaseType::ADDRESS_AND_PREFIX)
+ );
+
+ // When lease type is set to 'address-and-prefix' it means that client
+ // requests both address and prefix (IA_NA and IA_PD). Therefore, the
+ // LeaseType::includes() function should return true for both ADDRESS
+ // and PREFIX.
+ lease_type.set(CommandOptions::LeaseType::ADDRESS_AND_PREFIX);
+ EXPECT_TRUE(lease_type.includes(CommandOptions::LeaseType::ADDRESS));
+ EXPECT_TRUE(lease_type.includes(CommandOptions::LeaseType::PREFIX));
+ EXPECT_TRUE(
+ lease_type.includes(CommandOptions::LeaseType::ADDRESS_AND_PREFIX)
+ );
+
+}
+
+// Verify that the LeaseType::fromCommandLine() function parses the lease-type
+// argument specified as -e<lease-type>.
+TEST(LeaseTypeTest, fromCommandLine) {
+ CommandOptions::LeaseType
+ lease_type(CommandOptions::LeaseType::ADDRESS);
+ ASSERT_TRUE(lease_type.is(CommandOptions::LeaseType::ADDRESS));
+
+ lease_type.fromCommandLine("prefix-only");
+ ASSERT_TRUE(lease_type.is(CommandOptions::LeaseType::PREFIX));
+
+ lease_type.fromCommandLine("address-only");
+ EXPECT_TRUE(lease_type.is(CommandOptions::LeaseType::ADDRESS));
+
+ lease_type.fromCommandLine("address-and-prefix");
+ EXPECT_TRUE(lease_type.is(CommandOptions::LeaseType::ADDRESS_AND_PREFIX));
+
+ EXPECT_THROW(lease_type.fromCommandLine("bogus-parameter"),
+ isc::InvalidParameter);
+
+}
+
+// Verify that the LeaseType::toText() function returns the textual
+// representation of the lease type specified.
+TEST(LeaseTypeTest, toText) {
+ CommandOptions::LeaseType lease_type;
+ ASSERT_TRUE(lease_type.is(CommandOptions::LeaseType::ADDRESS));
+ EXPECT_EQ("address-only (IA_NA option added to the client's request)",
+ lease_type.toText());
+
+ lease_type.set(CommandOptions::LeaseType::PREFIX);
+ EXPECT_EQ("prefix-only (IA_PD option added to the client's request)",
+ lease_type.toText());
+
+ lease_type.set(CommandOptions::LeaseType::ADDRESS_AND_PREFIX);
+ EXPECT_EQ("address-and-prefix (Both IA_NA and IA_PD options added to the"
+ " client's request)", lease_type.toText());
+
+}
+
+/// \brief Test Fixture Class
+///
+/// This test fixture class is used to perform
+/// unit tests on perfdhcp CommandOptions class.
+class CommandOptionsTest : public virtual ::testing::Test
+{
+public:
+ /// \brief Default Constructor
+ CommandOptionsTest() { }
+
+protected:
+ /// \brief Parse command line and cleanup
+ ///
+ /// The method tokenizes command line to array of C-strings,
+ /// parses arguments using CommandOptions class to set
+ /// its data members and de-allocates array of C-strings.
+ ///
+ /// \param cmdline Command line to parse.
+ /// \throws std::bad allocation if tokenization failed.
+ /// \return true if program has been run in help or version mode ('h' or 'v' flag).
+ bool process(CommandOptions& opt, const std::string& cmdline) {
+ return (CommandOptionsHelper::process(opt, cmdline));
+ }
+
+ /// \brief Get full path to a file in testdata directory.
+ ///
+ /// \param filename filename being appended to absolute
+ /// path to testdata directory
+ ///
+ /// \return full path to a file in testdata directory.
+ std::string getFullPath(const std::string& filename) const {
+ std::ostringstream stream;
+ stream << TEST_DATA_DIR << "/" << filename;
+ return (stream.str());
+ }
+};
+
+TEST_F(CommandOptionsTest, Defaults) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp 192.168.0.1"));
+ EXPECT_EQ(4, opt.getIpVersion());
+ EXPECT_EQ(CommandOptions::DORA_SARR, opt.getExchangeMode());
+ EXPECT_TRUE(opt.getLeaseType().is(CommandOptions::LeaseType::ADDRESS));
+ EXPECT_EQ(0, opt.getRate());
+ EXPECT_EQ(0, opt.getRenewRate());
+ EXPECT_EQ(0, opt.getReleaseRate());
+ EXPECT_EQ(0, opt.getReportDelay());
+ EXPECT_EQ(0, opt.getClientsNum());
+
+ // default mac
+ const uint8_t mac[6] = { 0x00, 0x0C, 0x01, 0x02, 0x03, 0x04 };
+ std::vector<uint8_t> v1 = opt.getMacTemplate();
+ ASSERT_EQ(6, v1.size());
+ EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac));
+
+ // Check if DUID is initialized. The DUID-LLT is expected
+ // to start with DUID_LLT value of 1 and hardware ethernet
+ // type equal to 1 (HWETHER_TYPE).
+ const uint8_t duid_llt_and_hw[4] = { 0x0, 0x1, 0x0, 0x1 };
+ // We assume DUID-LLT length 14. This includes 4 octets of
+ // DUID_LLT value, two octets of hardware type, 4 octets
+ // of time value and 6 octets of variable link layer (MAC)
+ // address.
+ const int duid_llt_size = 14;
+ // DUID is not given from the command line but it is supposed
+ // to be initialized by the CommandOptions private method
+ // generateDuidTemplate().
+ std::vector<uint8_t> v2 = opt.getDuidTemplate();
+ ASSERT_EQ(duid_llt_size, opt.getDuidTemplate().size());
+ EXPECT_TRUE(std::equal(v2.begin(), v2.begin() + 4,
+ duid_llt_and_hw));
+ // Check time field contents.
+ ptime now = microsec_clock::universal_time();
+ ptime duid_epoch(from_iso_string("20000101T000000"));
+ time_period period(duid_epoch, now);
+ uint32_t duration_sec = period.length().total_seconds();
+ // Read time from the template generated.
+ uint32_t duration_from_template = 0;
+ memcpy(&duration_from_template, &v2[4], 4);
+ duration_from_template = htonl(duration_from_template);
+ // In special cases, we may have overflow in time field
+ // so we give ourselves the margin of 10 seconds here.
+ // If time value has been set more then 10 seconds back
+ // it is safe to compare it with the time value generated
+ // from now.
+ if (duration_from_template > 10) {
+ EXPECT_GE(duration_sec, duration_from_template);
+ }
+
+ EXPECT_EQ(0, opt.getBase().size());
+ EXPECT_EQ(0, opt.getNumRequests().size());
+ EXPECT_EQ(0, opt.getPeriod());
+ for (size_t i = 0; i < opt.getDropTime().size(); ++i) {
+ EXPECT_DOUBLE_EQ(1, opt.getDropTime()[i]);
+ }
+ ASSERT_EQ(opt.getMaxDrop().size(), opt.getMaxDropPercentage().size());
+ for (size_t i = 0; i < opt.getMaxDrop().size(); ++i) {
+ EXPECT_EQ(0, opt.getMaxDrop()[i]);
+ EXPECT_EQ(0, opt.getMaxDropPercentage()[i]);
+ }
+ EXPECT_EQ("", opt.getLocalName());
+ EXPECT_FALSE(opt.isInterface());
+ EXPECT_EQ(0, opt.getPreload());
+ EXPECT_EQ(0, opt.getLocalPort());
+ EXPECT_FALSE(opt.isSeeded());
+ EXPECT_EQ(0, opt.getSeed());
+ EXPECT_FALSE(opt.isBroadcast());
+ EXPECT_FALSE(opt.isRapidCommit());
+ EXPECT_FALSE(opt.isUseFirst());
+ EXPECT_FALSE(opt.getAddrUnique());
+ EXPECT_EQ(-1, opt.getIncreaseElapsedTime());
+ EXPECT_EQ(-1, opt.getWaitForElapsedTime());
+ EXPECT_EQ(0, opt.getTemplateFiles().size());
+ EXPECT_EQ(0, opt.getTransactionIdOffset().size());
+ EXPECT_EQ(0, opt.getRandomOffset().size());
+ EXPECT_GT(0, opt.getElapsedTimeOffset());
+ EXPECT_GT(0, opt.getServerIdOffset());
+ EXPECT_GT(0, opt.getRequestedIpOffset());
+ EXPECT_EQ("", opt.getDiags());
+ EXPECT_EQ("", opt.getWrapped());
+ EXPECT_EQ("192.168.0.1", opt.getServerName());
+}
+
+TEST_F(CommandOptionsTest, HelpVersion) {
+ // The parser is supposed to return true if 'h' or 'v' options
+ // are specified.
+ CommandOptions opt;
+ EXPECT_TRUE(process(opt, "perfdhcp -h"));
+ EXPECT_TRUE(process(opt, "perfdhcp -v"));
+ EXPECT_TRUE(process(opt, "perfdhcp -h -v"));
+ EXPECT_TRUE(process(opt, "perfdhcp -6 -l ethx -h all"));
+ EXPECT_TRUE(process(opt, "perfdhcp -l ethx -v all"));
+ // No 'h' or 'v' option specified. The false value
+ // should be returned.
+ EXPECT_FALSE(process(opt, "perfdhcp -l ethx all"));
+}
+
+TEST_F(CommandOptionsTest, CheckAddressUniqueness) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -6 -u -l ethx all"));
+ EXPECT_TRUE(opt.getAddrUnique());
+}
+
+TEST_F(CommandOptionsTest, UseFirst) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -1 -B -l ethx all"));
+ EXPECT_TRUE(opt.isUseFirst());
+}
+
+TEST_F(CommandOptionsTest, UseCleanOutput) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -6 -C, -l ethx all"));
+ EXPECT_TRUE(opt.getCleanReport());
+ EXPECT_EQ(",", opt.getCleanReportSeparator());
+}
+
+TEST_F(CommandOptionsTest, UseRelayV6) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -6 -A1 -l ethx all"));
+ EXPECT_TRUE(opt.isUseRelayedV6());
+ // -4 and -A must not coexist
+ EXPECT_THROW(process(opt, "perfdhcp -4 -A1 -l ethx all"), isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, IpVersion) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -6 -l ethx -c -i all"));
+ EXPECT_EQ(6, opt.getIpVersion());
+ EXPECT_EQ("ethx", opt.getLocalName());
+ EXPECT_TRUE(opt.isRapidCommit());
+ EXPECT_FALSE(opt.isBroadcast());
+ process(opt, "perfdhcp -4 -B -l ethx all");
+ EXPECT_EQ(4, opt.getIpVersion());
+ EXPECT_TRUE(opt.isBroadcast());
+ EXPECT_FALSE(opt.isRapidCommit());
+
+ // Negative test cases
+ // -4 and -6 must not coexist
+ EXPECT_THROW(process(opt, "perfdhcp -4 -6 -l ethx all"), isc::InvalidParameter);
+ // -6 and -B must not coexist
+ EXPECT_THROW(process(opt, "perfdhcp -6 -B -l ethx all"), isc::InvalidParameter);
+ // -c and -4 (default) must not coexist
+ EXPECT_THROW(process(opt, "perfdhcp -c -l ethx all"), isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, LeaseType) {
+ CommandOptions opt;
+ // Check that the -e address-only works for IPv6.
+ ASSERT_NO_THROW(process(opt, "perfdhcp -6 -l etx -e address-only all"));
+ EXPECT_EQ(6, opt.getIpVersion());
+ EXPECT_EQ("etx", opt.getLocalName());
+ EXPECT_TRUE(opt.getLeaseType().is(CommandOptions::LeaseType::ADDRESS));
+ // Check that the -e address-only works for IPv4.
+ ASSERT_NO_THROW(process(opt, "perfdhcp -4 -l etx -e address-only all"));
+ EXPECT_EQ(4, opt.getIpVersion());
+ EXPECT_EQ("etx", opt.getLocalName());
+ EXPECT_TRUE(opt.getLeaseType().is(CommandOptions::LeaseType::ADDRESS));
+ // Check that the -e prefix-only works.
+ ASSERT_NO_THROW(process(opt, "perfdhcp -6 -l etx -e prefix-only all"));
+ EXPECT_EQ(6, opt.getIpVersion());
+ EXPECT_EQ("etx", opt.getLocalName());
+ EXPECT_TRUE(opt.getLeaseType().is(CommandOptions::LeaseType::PREFIX));
+ // Check that -e prefix-only must not coexist with -4 option.
+ EXPECT_THROW(process(opt, "perfdhcp -4 -l ethx -e prefix-only all"),
+ InvalidParameter);
+ // Check that -e prefix-only must not coexist with -T options.
+ EXPECT_THROW(process(opt, "perfdhcp -6 -l ethx -e prefix-only -T file1.hex"
+ " -T file2.hex -E 4 all"), InvalidParameter);
+
+}
+
+TEST_F(CommandOptionsTest, Rate) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -4 -r 10 -l ethx all"));
+ EXPECT_EQ(10, opt.getRate());
+
+ // Negative test cases
+ // Rate must not be 0
+ EXPECT_THROW(process(opt, "perfdhcp -4 -r 0 -l ethx all"),
+ isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, RenewRate) {
+ CommandOptions opt;
+ // If -f is specified together with -r the command line should
+ // be accepted and the renew rate should be set.
+ EXPECT_NO_THROW(process(opt, "perfdhcp -6 -r 10 -f 10 -l ethx all"));
+ EXPECT_EQ(10, opt.getRenewRate());
+ // Check that the release rate can be set to different value than
+ // rate specified as -r<rate>. Also, swap -f and -r to make sure
+ // that order doesn't matter.
+ EXPECT_NO_THROW(process(opt, "perfdhcp -6 -f 5 -r 10 -l ethx all"));
+ EXPECT_EQ(5, opt.getRenewRate());
+ // Renew rate should also be accepted for DHCPv4 case.
+ EXPECT_NO_THROW(process(opt, "perfdhcp -4 -f 5 -r 10 -l ethx all"));
+ EXPECT_EQ(5, opt.getRenewRate());
+ // The renew rate should not be greater than the rate.
+ EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -f 11 -l ethx all"),
+ isc::InvalidParameter);
+ // The renew-rate of 0 is invalid.
+ EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -f 0 -l ethx all"),
+ isc::InvalidParameter);
+ // The negative renew-rate is invalid.
+ EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -f -5 -l ethx all"),
+ isc::InvalidParameter);
+ // If -r<rate> is not specified the -f<renew-rate> should not
+ // be accepted.
+ EXPECT_THROW(process(opt, "perfdhcp -6 -f 10 -l ethx all"),
+ isc::InvalidParameter);
+ // Renew rate should be specified.
+ EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -f -l ethx all"),
+ isc::InvalidParameter);
+
+ // -f and -i are mutually exclusive
+ EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -f 10 -l ethx -i all"),
+ isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, ReleaseRate) {
+ CommandOptions opt;
+ // If -F is specified together with -r the command line should
+ // be accepted and the release rate should be set.
+ EXPECT_NO_THROW(process(opt, "perfdhcp -6 -r 10 -F 10 -l ethx all"));
+ EXPECT_EQ(10, opt.getReleaseRate());
+ // Check that the release rate can be set to different value than
+ // rate specified as -r<rate>. Also, swap -F and -r to make sure
+ // that order doesn't matter.
+ EXPECT_NO_THROW(process(opt, "perfdhcp -6 -F 5 -r 10 -l ethx all"));
+ EXPECT_EQ(5, opt.getReleaseRate());
+ // The release rate should not be greater than the rate.
+ EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -F 11 -l ethx all"),
+ isc::InvalidParameter);
+ // The release-rate of 0 is invalid.
+ EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -F 0 -l ethx all"),
+ isc::InvalidParameter);
+ // The negative release-rate is invalid.
+ EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -F -5 -l ethx all"),
+ isc::InvalidParameter);
+ // If -r<rate> is not specified the -F<release-rate> should not
+ // be accepted.
+ EXPECT_THROW(process(opt, "perfdhcp -6 -F 10 -l ethx all"),
+ isc::InvalidParameter);
+ // -F<release-rate> should be usable in IPv6 mode.
+ EXPECT_NO_THROW(process(opt, "perfdhcp -4 -r 10 -F 10 -l ethx all"));
+ // Release rate should be specified.
+ EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -F -l ethx all"),
+ isc::InvalidParameter);
+ // -F and -i are mutually exclusive
+ EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -F 10 -l ethx -i all"),
+ isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, ReleaseRenew) {
+ CommandOptions opt;
+ // It should be possible to specify the -F, -f and -r options.
+ EXPECT_NO_THROW(process(opt, "perfdhcp -6 -r 10 -F 3 -f 5 -l ethx all"));
+ EXPECT_EQ(10, opt.getRate());
+ EXPECT_EQ(3, opt.getReleaseRate());
+ EXPECT_EQ(5, opt.getRenewRate());
+ // It should be possible to specify the -F and -f with the values which
+ // sum is equal to the rate specified as -r<rate>.
+ EXPECT_NO_THROW(process(opt, "perfdhcp -6 -r 8 -F 3 -f 5 -l ethx all"));
+ EXPECT_EQ(8, opt.getRate());
+ EXPECT_EQ(3, opt.getReleaseRate());
+ EXPECT_EQ(5, opt.getRenewRate());
+ // Check that the sum of the release and renew rate is not greater
+ // than the rate specified as -r<rate>.
+ EXPECT_THROW(process(opt, "perfdhcp -6 -F 6 -f 5 -r 10 -l ethx all"),
+ isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, ReportDelay) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -t 17 -l ethx all"));
+ EXPECT_EQ(17, opt.getReportDelay());
+
+ // Negative test cases
+ // -t must be positive integer
+ EXPECT_THROW(process(opt, "perfdhcp -t -8 -l ethx all"),
+ isc::InvalidParameter);
+ EXPECT_THROW(process(opt, "perfdhcp -t 0 -l ethx all"),
+ isc::InvalidParameter);
+ EXPECT_THROW(process(opt, "perfdhcp -t s -l ethx all"),
+ isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, ClientsNum) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -R 200 -l ethx all"));
+ EXPECT_EQ(200, opt.getClientsNum());
+ process(opt, "perfdhcp -R 0 -l ethx all");
+ EXPECT_EQ(0, opt.getClientsNum());
+
+ // Negative test cases
+ // Number of clients must be non-negative integer
+ EXPECT_THROW(process(opt, "perfdhcp -R -5 -l ethx all"),
+ isc::InvalidParameter);
+ EXPECT_THROW(process(opt, "perfdhcp -R gs -l ethx all"),
+ isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, Base) {
+ CommandOptions opt;
+ uint8_t mac[6] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60 };
+ uint8_t duid[14] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x10, 0x11, 0x1F, 0x14 };
+ // Test DUID and MAC together.
+ EXPECT_NO_THROW(process(opt, "perfdhcp -b DUID=0101010101010101010110111F14"
+ " -b MAC=10::20::30::40::50::60"
+ " -l 127.0.0.1 all"));
+ std::vector<uint8_t> v1 = opt.getMacTemplate();
+ std::vector<uint8_t> v2 = opt.getDuidTemplate();
+ EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac));
+ EXPECT_TRUE(std::equal(v2.begin(), v2.end(), duid));
+ // Test valid DUID.
+ EXPECT_NO_THROW(
+ process(opt, "perfdhcp -b duid=0101010101010101010110111F14 -l 127.0.0.1 all")
+ );
+
+ ASSERT_EQ(sizeof(duid) / sizeof(uint8_t), v2.size());
+ EXPECT_TRUE(std::equal(v2.begin(), v2.end(), duid));
+ // Test mix of upper/lower case letters.
+ EXPECT_NO_THROW(process(opt, "perfdhcp -b DuiD=0101010101010101010110111F14"
+ " -b Mac=10::20::30::40::50::60"
+ " -l 127.0.0.1 all"));
+ v1 = opt.getMacTemplate();
+ v2 = opt.getDuidTemplate();
+ EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac));
+ EXPECT_TRUE(std::equal(v2.begin(), v2.end(), duid));
+ // Use "ether" instead of "mac".
+ EXPECT_NO_THROW(process(opt, "perfdhcp -b ether=10::20::30::40::50::60"
+ " -l 127.0.0.1 all"));
+ v1 = opt.getMacTemplate();
+ EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac));
+ // Use "ETHER" in upper case.
+ EXPECT_NO_THROW(process(opt, "perfdhcp -b ETHER=10::20::30::40::50::60"
+ " -l 127.0.0.1 all"));
+ v1 = opt.getMacTemplate();
+ EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac));
+ // "t" is invalid character in DUID
+ EXPECT_THROW(process(opt, "perfdhcp -6 -l ethx -b "
+ "duid=010101010101010101t110111F14 all"),
+ isc::InvalidParameter);
+ // "3x" is invalid value in MAC address
+ EXPECT_THROW(process(opt, "perfdhcp -b mac=10::2::3x::4::5::6 -l ethx all"),
+ isc::InvalidParameter);
+ // Base is not specified
+ EXPECT_THROW(process(opt, "perfdhcp -b -l ethx all"),
+ isc::InvalidParameter);
+ // Typo: should be mac= instead of mc=
+ EXPECT_THROW(process(opt, "perfdhcp -l ethx -b mc=00:01:02:03::04:05 all"),
+ isc::InvalidParameter);
+ // Too short DUID (< 6).
+ EXPECT_THROW(process(opt, "perfdhcp -l ethx -b duid=00010203 all"),
+ isc::InvalidParameter);
+ // Odd number of digits.
+ EXPECT_THROW(process(opt, "perfdhcp -l ethx -b duid=000102030405060 all"),
+ isc::InvalidParameter);
+ // Too short MAC (!= 6).
+ EXPECT_THROW(process(opt, "perfdhcp -l ethx -b mac=00:01:02:04 all"),
+ isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, DropTime) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -l ethx -d 12 all"));
+ ASSERT_EQ(2, opt.getDropTime().size());
+ EXPECT_DOUBLE_EQ(12, opt.getDropTime()[0]);
+ EXPECT_DOUBLE_EQ(1, opt.getDropTime()[1]);
+
+ EXPECT_NO_THROW(process(opt, "perfdhcp -l ethx -d 2 -d 4.7 all"));
+ ASSERT_EQ(2, opt.getDropTime().size());
+ EXPECT_DOUBLE_EQ(2, opt.getDropTime()[0]);
+ EXPECT_DOUBLE_EQ(4.7, opt.getDropTime()[1]);
+
+ // Negative test cases
+ // Drop time must not be negative
+ EXPECT_THROW(process(opt, "perfdhcp -l ethx -d -2 -d 4.7 all"),
+ isc::InvalidParameter);
+ EXPECT_THROW(process(opt, "perfdhcp -l ethx -d -9.1 -d 0 all"),
+ isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, TimeOffset) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -l ethx -T file1.x -T file2.x -E 4 all"));
+ EXPECT_EQ(4, opt.getElapsedTimeOffset());
+
+ // Negative test cases
+ // Argument -E must be used with -T
+ EXPECT_THROW(process(opt, "perfdhcp -l ethx -E 3 -i all"),
+ isc::InvalidParameter);
+ // Value in -E not specified
+ EXPECT_THROW(process(opt, "perfdhcp -l ethx -T file.x -E -i all"),
+ isc::InvalidParameter);
+ // Value for -E must not be negative
+ EXPECT_THROW(process(opt, "perfdhcp -l ethx -E -3 -T file.x all"),
+ isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, ExchangeMode) {
+ CommandOptions opt;
+ process(opt, "perfdhcp -l ethx -i all");
+ EXPECT_EQ(CommandOptions::DO_SA, opt.getExchangeMode());
+
+ // Negative test cases
+ // No template file specified
+ EXPECT_THROW(process(opt, "perfdhcp -i -l ethx -X 3 all"),
+ isc::InvalidParameter);
+ // Offsets can't be used in simple exchanges (-i)
+ EXPECT_THROW(process(opt, "perfdhcp -i -l ethx -O 2 -T file.x all"),
+ isc::InvalidParameter);
+ EXPECT_THROW(process(opt, "perfdhcp -i -l ethx -E 3 -T file.x all"),
+ isc::InvalidParameter);
+ EXPECT_THROW(process(opt, "perfdhcp -i -l ethx -S 1 -T file.x all"),
+ isc::InvalidParameter);
+ EXPECT_THROW(process(opt, "perfdhcp -i -l ethx -I 2 -T file.x all"),
+ isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, Offsets) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -E5 -4 -I 2 -S3 -O 30 -X7 -l ethx "
+ "-X3 -T file1.x -T file2.x all"));
+ EXPECT_EQ(2, opt.getRequestedIpOffset());
+ EXPECT_EQ(5, opt.getElapsedTimeOffset());
+ EXPECT_EQ(3, opt.getServerIdOffset());
+ ASSERT_EQ(2, opt.getRandomOffset().size());
+ EXPECT_EQ(30, opt.getRandomOffset()[0]);
+ EXPECT_EQ(30, opt.getRandomOffset()[1]);
+ ASSERT_EQ(2, opt.getTransactionIdOffset().size());
+ EXPECT_EQ(7, opt.getTransactionIdOffset()[0]);
+ EXPECT_EQ(3, opt.getTransactionIdOffset()[1]);
+
+ // Negative test cases
+ // IP offset/IA_NA offset must be positive
+ EXPECT_THROW(process(opt, "perfdhcp -6 -I 0 -l ethx all"),
+ isc::InvalidParameter);
+ EXPECT_THROW(process(opt, "perfdhcp -6 -I -4 -l ethx all"),
+ isc::InvalidParameter);
+
+ // \todo other negative cases
+}
+
+TEST_F(CommandOptionsTest, LocalPort) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -l ethx -L 2000 all"));
+ EXPECT_EQ(2000, opt.getLocalPort());
+
+ // Negative test cases
+ // Local port must be between 0..65535
+ EXPECT_THROW(process(opt, "perfdhcp -l ethx -L -2 all"),
+ isc::InvalidParameter);
+ EXPECT_THROW(process(opt, "perfdhcp -l ethx -L all"),
+ isc::InvalidParameter);
+ EXPECT_THROW(process(opt, "perfdhcp -l ethx -L 65540 all"),
+ isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, Preload) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -1 -P 3 -l ethx all"));
+ EXPECT_EQ(3, opt.getPreload());
+
+ // Negative test cases
+ // Number of preload packages must not be negative integer
+ EXPECT_THROW(process(opt, "perfdhcp -P -1 -l ethx all"),
+ isc::InvalidParameter);
+ EXPECT_THROW(process(opt, "perfdhcp -P -3 -l ethx all"),
+ isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, Seed) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -6 -P 2 -s 23 -l ethx all"));
+ EXPECT_EQ(23, opt.getSeed());
+ EXPECT_TRUE(opt.isSeeded());
+
+ EXPECT_NO_THROW(process(opt, "perfdhcp -6 -P 2 -s 0 -l ethx all"));
+ EXPECT_EQ(0, opt.getSeed());
+ EXPECT_FALSE(opt.isSeeded());
+
+ // Negative test cases
+ // Seed must be non-negative integer
+ EXPECT_THROW(process(opt, "perfdhcp -6 -P 2 -s -5 -l ethx all"),
+ isc::InvalidParameter);
+ EXPECT_THROW(process(opt, "perfdhcp -6 -P 2 -s -l ethx all"),
+ isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, TemplateFiles) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -T file1.x -l ethx all"));
+ ASSERT_EQ(1, opt.getTemplateFiles().size());
+ EXPECT_EQ("file1.x", opt.getTemplateFiles()[0]);
+
+ EXPECT_NO_THROW(process(opt, "perfdhcp -T file1.x -s 12 -w start -T file2.x -4 -l ethx all"));
+ ASSERT_EQ(2, opt.getTemplateFiles().size());
+ EXPECT_EQ("file1.x", opt.getTemplateFiles()[0]);
+ EXPECT_EQ("file2.x", opt.getTemplateFiles()[1]);
+
+ // Negative test cases
+ // No template file specified
+ EXPECT_THROW(process(opt, "perfdhcp -s 12 -T -l ethx all"),
+ isc::InvalidParameter);
+ // Too many template files specified
+ EXPECT_THROW(process(opt, "perfdhcp -s 12 -l ethx -T file.x "
+ "-T file.x -T file.x all"),
+ isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, Wrapped) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -B -w start -i -l ethx all"));
+ EXPECT_EQ("start", opt.getWrapped());
+
+ // Negative test cases
+ // Missing command after -w, expected start/stop
+ EXPECT_THROW(process(opt, "perfdhcp -B -i -l ethx -w all"),
+ isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, Diagnostics) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -l ethx -i -x asTe all"));
+ EXPECT_EQ("asTe", opt.getDiags());
+
+ // Negative test cases
+ // No diagnostics string specified
+ EXPECT_THROW(process(opt, "perfdhcp -l ethx -i -x all"),
+ isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, MaxDrop) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -D 25 -l ethx all"));
+ EXPECT_EQ(25, opt.getMaxDrop()[0]);
+ EXPECT_NO_THROW(process(opt, "perfdhcp -D 25 -l ethx -D 15 all"));
+ EXPECT_EQ(25, opt.getMaxDrop()[0]);
+ EXPECT_EQ(15, opt.getMaxDrop()[1]);
+
+ EXPECT_NO_THROW(process(opt, "perfdhcp -D 15% -l ethx all"));
+ EXPECT_EQ(15, opt.getMaxDropPercentage()[0]);
+ EXPECT_NO_THROW(process(opt, "perfdhcp -D 15% -D25% -l ethx all"));
+ EXPECT_EQ(15, opt.getMaxDropPercentage()[0]);
+ EXPECT_EQ(25, opt.getMaxDropPercentage()[1]);
+ EXPECT_NO_THROW(process(opt, "perfdhcp -D 1% -D 99% -l ethx all"));
+ EXPECT_EQ(1, opt.getMaxDropPercentage()[0]);
+ EXPECT_EQ(99, opt.getMaxDropPercentage()[1]);
+
+ // Negative test cases
+ // Too many -D<value> options
+ EXPECT_THROW(process(opt, "perfdhcp -D 0% -D 1 -l ethx -D 3 all"),
+ isc::InvalidParameter);
+ // Too many -D<value%> options
+ EXPECT_THROW(process(opt, "perfdhcp -D 99% -D 13% -l ethx -D 10% all"),
+ isc::InvalidParameter);
+ // Percentage is out of bounds
+ EXPECT_THROW(process(opt, "perfdhcp -D101% -D 13% -l ethx all"),
+ isc::InvalidParameter);
+ EXPECT_THROW(process(opt, "perfdhcp -D0% -D 13% -l ethx all"),
+ isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, NumRequest) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -n 1000 -l ethx all"));
+ EXPECT_EQ(1000, opt.getNumRequests()[0]);
+ EXPECT_NO_THROW(process(opt, "perfdhcp -n 5 -n 500 -l ethx all"));
+ EXPECT_EQ(5, opt.getNumRequests()[0]);
+ EXPECT_EQ(500, opt.getNumRequests()[1]);
+
+ // Negative test cases
+ // Too many -n<value> parameters, expected maximum 2
+ EXPECT_THROW(process(opt, "perfdhcp -n 1 -n 2 -l ethx -n3 all"),
+ isc::InvalidParameter);
+ // Num request must be positive integer
+ EXPECT_THROW(process(opt, "perfdhcp -n 1 -n -22 -l ethx all"),
+ isc::InvalidParameter);
+ EXPECT_THROW(process(opt, "perfdhcp -n 0 -l ethx all"),
+ isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, Period) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -p 120 -l ethx all"));
+ EXPECT_EQ(120, opt.getPeriod());
+
+ // Negative test cases
+ // Test period must be positive integer
+ EXPECT_THROW(process(opt, "perfdhcp -p 0 -l ethx all"),
+ isc::InvalidParameter);
+ EXPECT_THROW(process(opt, "perfdhcp -p -3 -l ethx all"),
+ isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, Interface) {
+ // In order to make this test portable we need to know
+ // at least one interface name on OS where test is run.
+ // Interface Manager has ability to detect interfaces.
+ // Although we don't call initIsInterface explicitly
+ // here it is called by CommandOptions object internally
+ // so this function is covered by the test.
+ dhcp::IfaceMgr& iface_mgr = dhcp::IfaceMgr::instance();
+ const dhcp::IfaceCollection& ifaces = iface_mgr.getIfaces();
+ std::string iface_name;
+ CommandOptions opt;
+ // The local loopback interface should be available.
+ // If no interface have been found for any reason we should
+ // not fail this test.
+ if (!ifaces.empty()) {
+ // Get the name of the interface we detected.
+ iface_name = (*ifaces.begin())->getName();
+ // Use the name in the command parser.
+ ASSERT_NO_THROW(process(opt, "perfdhcp -4 -l " + iface_name + " abc"));
+ // We expect that command parser will detect that argument
+ // specified along with '-l' is the interface name.
+ EXPECT_TRUE(opt.isInterface());
+
+ // If neither interface nor server is specified then
+ // exception is expected to be thrown.
+ EXPECT_THROW(process(opt, "perfdhcp -4"), isc::InvalidParameter);
+ }
+}
+
+TEST_F(CommandOptionsTest, Server) {
+ CommandOptions opt;
+ // There is at least server parameter needed. If server is not
+ // specified the local interface must be specified.
+ // The server value equal to 'all' means use broadcast.
+ ASSERT_NO_THROW(process(opt, "perfdhcp all"));
+ // Once command line is parsed we expect that server name is
+ // set to broadcast address because 'all' was specified.
+ EXPECT_TRUE(opt.isBroadcast());
+ // The broadcast address is 255.255.255.255.
+ EXPECT_EQ(DHCP_IPV4_BROADCAST_ADDRESS, opt.getServerName());
+
+ // When all is specified for DHCPv6 mode we expect
+ // FF02::1:2 as a server name which means All DHCP
+ // servers and relay agents in local network segment
+ ASSERT_NO_THROW(process(opt, "perfdhcp -6 all"));
+ EXPECT_EQ(ALL_DHCP_RELAY_AGENTS_AND_SERVERS, opt.getServerName());
+
+ // When server='servers' in DHCPv6 mode we expect
+ // FF05::1:3 as server name which means All DHCP
+ // servers in local network.
+ ASSERT_NO_THROW(process(opt, "perfdhcp -6 servers"));
+ EXPECT_EQ(ALL_DHCP_SERVERS, opt.getServerName());
+
+ // If server name is neither 'all' nor 'servers'
+ // the given argument value is expected to be
+ // returned.
+ ASSERT_NO_THROW(process(opt, "perfdhcp -6 abc"));
+ EXPECT_EQ("abc", opt.getServerName());
+}
+
+TEST_F(CommandOptionsTest, LoadMacsFromFile) {
+ CommandOptions opt;
+
+ std::string mac_list_full_path = getFullPath("mac-list.txt");
+ std::ostringstream cmd;
+ cmd << "perfdhcp -M " << mac_list_full_path << " abc";
+ EXPECT_NO_THROW(process(opt, cmd.str()));
+ EXPECT_EQ(mac_list_full_path, opt.getMacListFile());
+
+ const CommandOptions::MacAddrsVector& m = opt.getMacsFromFile();
+ EXPECT_EQ(4, m.size());
+}
+
+TEST_F(CommandOptionsTest, LoadRelay4AddrFromFile) {
+ CommandOptions opt;
+ std::string relay_addr_list_full_path = getFullPath("relay4-list.txt");
+ std::ostringstream cmd;
+ cmd << "perfdhcp -4 -J " << relay_addr_list_full_path << " abc";
+ EXPECT_NO_THROW(process(opt, cmd.str()));
+ EXPECT_EQ(relay_addr_list_full_path, opt.getRelayAddrListFile());
+ EXPECT_TRUE(opt.checkMultiSubnet());
+ EXPECT_EQ(5, opt.getRelayAddrList().size());
+}
+
+TEST_F(CommandOptionsTest, LoadRelay6AddrFromFile) {
+ CommandOptions opt;
+ std::string relay_addr_list_full_path = getFullPath("relay6-list.txt");
+ std::ostringstream cmd;
+ cmd << "perfdhcp -6 -J " << relay_addr_list_full_path << " abc";
+ EXPECT_NO_THROW(process(opt, cmd.str()));
+ EXPECT_EQ(relay_addr_list_full_path, opt.getRelayAddrListFile());
+ EXPECT_TRUE(opt.checkMultiSubnet());
+ EXPECT_EQ(2, opt.getRelayAddrList().size());
+}
+
+TEST_F(CommandOptionsTest, RelayAddr6ForVersion4) {
+ CommandOptions opt;
+ std::string relay_addr_list_full_path = getFullPath("relay6-list.txt");
+ std::ostringstream cmd;
+ cmd << "perfdhcp -4 -J " << relay_addr_list_full_path << " abc";
+ EXPECT_THROW(process(opt, cmd.str()), isc::InvalidParameter);
+ EXPECT_FALSE(opt.checkMultiSubnet());
+ EXPECT_EQ(0, opt.getRelayAddrList().size());
+}
+
+TEST_F(CommandOptionsTest, RelayAddr4ForVersion6) {
+ CommandOptions opt;
+ std::string relay_addr_list_full_path = getFullPath("relay4-list.txt");
+ std::ostringstream cmd;
+ cmd << "perfdhcp -6 -J " << relay_addr_list_full_path << " abc";
+ EXPECT_THROW(process(opt, cmd.str()), isc::InvalidParameter);
+ EXPECT_FALSE(opt.checkMultiSubnet());
+ EXPECT_EQ(0, opt.getRelayAddrList().size());
+}
+
+
+TEST_F(CommandOptionsTest, LoadMacsFromFileNegativeCases) {
+ CommandOptions opt;
+ // Negative test cases
+ // Too many -M parameters, expected only 1
+ EXPECT_THROW(process(opt, "perfdhcp -M foo -M foo1 all"), isc::InvalidParameter);
+ // -M option can't use with -b option
+ EXPECT_THROW(process(opt, "perfdhcp -M foo -b mac=1234 all"),
+ isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, ElapsedTime) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -y 3 -Y 10 192.168.0.1"));
+
+ EXPECT_EQ(3, opt.getIncreaseElapsedTime());
+ EXPECT_EQ(10, opt.getWaitForElapsedTime());
+}
+
+TEST_F(CommandOptionsTest, UseRelayV6OptionsWithoutV6) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -6 -A1 --or 32,00000E10 -l ethx all"));
+ EXPECT_TRUE(opt.isUseRelayedV6());
+ EXPECT_EQ(1, opt.getRelayOpts().size());
+
+ // --or must be used together with -6
+ EXPECT_THROW(process(opt, "perfdhcp -A1 --or 32,00000E10 -l ethx all"), isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, UseRelayV6OptionsWithoutRelayEncapsulation) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -6 -A1 --or 32,00000E10 -l ethx all"));
+ EXPECT_TRUE(opt.isUseRelayedV6());
+ EXPECT_EQ(1, opt.getRelayOpts().size());
+
+ // --or must be used together with -A
+ EXPECT_THROW(process(opt, "perfdhcp -6 --or 32,00000E10 -l ethx all"), isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, UseMultipleRelayV6Options) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -6 -A1 --or 32,00000E10 --or "
+ "23,20010DB800010000000000000000CAFE -l ethx all"));
+ EXPECT_TRUE(opt.isUseRelayedV6());
+ // 2 options expected at 1st level of encapsulation
+ EXPECT_EQ(2, opt.getRelayOpts().size());
+ // no options expected at 2nd level of encapsulation
+ EXPECT_THROW(opt.getRelayOpts(2), isc::OutOfRange);
+}
+
+TEST_F(CommandOptionsTest, UseRelayV6OptionsWithMultiSubnets) {
+ CommandOptions opt;
+ std::string relay_addr_list_full_path = getFullPath("relay6-list.txt");
+ std::ostringstream cmd;
+ cmd << "perfdhcp -6 -J " << relay_addr_list_full_path
+ << " -A1 --or 32,00000E10 --or 23,20010DB800010000000000000000CAFE -l ethx all";
+ EXPECT_NO_THROW(process(opt, cmd.str()));
+ EXPECT_EQ(relay_addr_list_full_path, opt.getRelayAddrListFile());
+ EXPECT_TRUE(opt.checkMultiSubnet());
+ EXPECT_EQ(2, opt.getRelayAddrList().size());
+ EXPECT_TRUE(opt.isUseRelayedV6());
+ // 2 options expected at 1st level of encapsulation
+ EXPECT_EQ(2, opt.getRelayOpts().size());
+ // no options expected at 2nd level of encapsulation
+ EXPECT_THROW(opt.getRelayOpts(2), isc::OutOfRange);
+}
+
+TEST_F(CommandOptionsTest, UseRelayV6OptionsNoComma) {
+ CommandOptions opt;
+
+ // --or must be followed by encapsulation-level, colon, option code, a comma and hexstring
+ // in case encapsulation-level and colon are skipped, encapsulation-level is by default 1
+ EXPECT_THROW(process(opt, "perfdhcp -6 --or 3200000E10 -l ethx all"), isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, UseRelayV6OptionsNegativeOptionCode) {
+ CommandOptions opt;
+
+ // --or must be followed by encapsulation-level, colon, positive option code, a comma and hexstring
+ // in case encapsulation-level and colon are skipped, encapsulation-level is by default 1
+ EXPECT_THROW(process(opt, "perfdhcp -6 --or -32,00000E10 -l ethx all"), isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, UseRelayV6OptionsWrongHexstring) {
+ CommandOptions opt;
+
+ // --or hexstring containing char Z which is not correct
+ EXPECT_THROW(process(opt, "perfdhcp -6 --or 32,Z0000E10 -l ethx all"), isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, UseRelayV6OptionsWithEncapsulationLevel) {
+ CommandOptions opt;
+ EXPECT_NO_THROW(process(opt, "perfdhcp -6 -A1 --or 1:32,00000E10 -l ethx all"));
+ EXPECT_TRUE(opt.isUseRelayedV6());
+ EXPECT_EQ(1, opt.getRelayOpts().size());
+}
+
+TEST_F(CommandOptionsTest, UseRelayV6OptionsWithNegativeEncapsulationLevel) {
+ CommandOptions opt;
+
+ // provided Relayed option encapsulation level must be a positive integer.
+ EXPECT_THROW(process(opt, "perfdhcp -6 -A1 --or -1:32,00000E10 -l ethx all"), isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, UseRelayV6OptionsWithZeroEncapsulationLevel) {
+ CommandOptions opt;
+
+ // provided Relayed option encapsulation level must be a positive integer.
+ EXPECT_THROW(process(opt, "perfdhcp -6 -A1 --or 0:32,00000E10 -l ethx all"), isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, UseRelayV6OptionsWithWrongEncapsulationLevel) {
+ CommandOptions opt;
+
+ // provided Relayed option encapsulation level must be a positive integer.
+ EXPECT_THROW(process(opt, "perfdhcp -6 -A1 --or x:32,00000E10 -l ethx all"), isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, UseRelayV6OptionsWithEncapsulationLevelValueTwo) {
+ CommandOptions opt;
+
+ // Relayed option encapsulation level supports only value 1 at the moment.
+ EXPECT_THROW(process(opt, "perfdhcp -6 -A1 --or 2:32,00000E10 -l ethx all"), isc::InvalidParameter);
+}
+
+TEST_F(CommandOptionsTest, UseRelayV6OptionsDuplicated) {
+ CommandOptions opt;
+
+ // multiple relayed options with the same option code are supported.
+ EXPECT_NO_THROW(process(opt, "perfdhcp -6 -A1 --or 1:32,00000E10 --or 32,00000E11 -l ethx all"));
+ EXPECT_TRUE(opt.isUseRelayedV6());
+ EXPECT_EQ(2, opt.getRelayOpts().size());
+}
+
+
diff --git a/src/bin/perfdhcp/tests/localized_option_unittest.cc b/src/bin/perfdhcp/tests/localized_option_unittest.cc
new file mode 100644
index 0000000..6b849bd
--- /dev/null
+++ b/src/bin/perfdhcp/tests/localized_option_unittest.cc
@@ -0,0 +1,42 @@
+// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <gtest/gtest.h>
+
+#include <dhcp/option.h>
+#include <dhcp/dhcp6.h>
+
+#include <boost/scoped_ptr.hpp>
+
+#include "../localized_option.h"
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::perfdhcp;
+
+namespace {
+
+TEST(LocalizedOptionTest, Constructor) {
+ OptionBuffer opt_buf;
+ // Create option with default offset.
+ boost::scoped_ptr<LocalizedOption> opt1(new LocalizedOption(Option::V6,
+ D6O_CLIENTID,
+ opt_buf));
+ EXPECT_EQ(Option::V6, opt1->getUniverse());
+ EXPECT_EQ(D6O_CLIENTID, opt1->getType());
+ EXPECT_EQ(0, opt1->getOffset());
+
+ // Create option with non-default offset.
+ boost::scoped_ptr<LocalizedOption> opt2(new LocalizedOption(Option::V6,
+ D6O_CLIENTID,
+ opt_buf,
+ 40));
+ EXPECT_EQ(40, opt2->getOffset());
+}
+
+}
diff --git a/src/bin/perfdhcp/tests/packet_storage_unittest.cc b/src/bin/perfdhcp/tests/packet_storage_unittest.cc
new file mode 100644
index 0000000..157da2b
--- /dev/null
+++ b/src/bin/perfdhcp/tests/packet_storage_unittest.cc
@@ -0,0 +1,199 @@
+// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include "../packet_storage.h"
+#include <dhcp/dhcp6.h>
+#include <dhcp/pkt6.h>
+
+#include <gtest/gtest.h>
+
+namespace {
+
+using namespace isc;
+using namespace isc::dhcp;
+using namespace perfdhcp;
+
+/// @todo Implement the tests which use Pkt4 objects once the support for
+/// DHCPv4 renewals / releases is added.
+
+/// The number of packets in the test storage.
+const unsigned int STORAGE_SIZE = 20;
+
+/// Test fixture class for PacketStorage container testing.
+class PacketStorageTest : public ::testing::Test {
+public:
+ /// \brief Constructor, initializes the storage for each test.
+ PacketStorageTest() {
+ for (uint32_t i = 0; i < STORAGE_SIZE; ++i) {
+ storage_.append(createPacket6(DHCPV6_REPLY, i));
+ }
+ }
+
+ /// \brief Creates an instance of the Pkt6.
+ ///
+ /// \param packet_type A type of the packet.
+ /// \param transid Transaction id.
+ /// \return An instance of the Pkt6.
+ Pkt6Ptr createPacket6(const uint16_t packet_type,
+ const uint32_t transid) {
+ return (Pkt6Ptr(new Pkt6(packet_type, transid)));
+ }
+
+ /// Packet storage under test.
+ PacketStorage<Pkt6> storage_;
+
+};
+
+// This test verifies that the packets in the storage can be accessed
+// sequentially and when a packet is returned, it is removed from the
+// storage. It also verifies the correctness of the behaviour of the
+// empty() and size() functions.
+TEST_F(PacketStorageTest, getNext) {
+ ASSERT_EQ(STORAGE_SIZE, storage_.size());
+ for (int i = 0; i < STORAGE_SIZE; ++i) {
+ Pkt6Ptr packet = storage_.getNext();
+ ASSERT_TRUE(packet) << "NULL packet returned by storage_.getNext() for"
+ << " iteration number " << i;
+ EXPECT_EQ(i, packet->getTransid());
+ EXPECT_EQ(STORAGE_SIZE - i - 1, storage_.size());
+ }
+ EXPECT_TRUE(storage_.empty());
+ // When storage is empty, the attempt to get the next packet should
+ // result in returning NULL pointer.
+ EXPECT_FALSE(storage_.getNext());
+ // Let's try it again to see if the previous call to getNext didn't
+ // put the storage into the state in which the subsequent calls to
+ // getNext would result in incorrect behaviour.
+ EXPECT_FALSE(storage_.getNext());
+
+ // Let's add a new packet to the empty storage to check that storage
+ // "recovers" from being empty, i.e. that the internal indicator
+ // which points to current packet reinitializes correctly.
+ storage_.append(createPacket6(DHCPV6_REPLY, 100));
+ ASSERT_EQ(1, storage_.size());
+ Pkt6Ptr packet = storage_.getNext();
+ EXPECT_EQ(100, packet->getTransid());
+ EXPECT_FALSE(storage_.getNext());
+}
+
+// This test verifies that the packets in the storage can be accessed
+// randomly and when a packet is returned, it is removed from the
+// storage. It also verifies the correctness of the behaviour of the
+// empty() and size() functions.
+TEST_F(PacketStorageTest, getRandom) {
+ ASSERT_EQ(STORAGE_SIZE, storage_.size());
+ int cnt_equals = 0;
+ for (int i = 0; i < STORAGE_SIZE; ++i) {
+ Pkt6Ptr packet = storage_.getRandom();
+ ASSERT_TRUE(packet) << "NULL packet returned by storage_.getRandom()"
+ " for iteration number " << i;
+ EXPECT_EQ(STORAGE_SIZE - i - 1, storage_.size());
+ cnt_equals += (i == packet->getTransid() ? 1 : 0);
+ }
+ // If the number of times id is equal to i, is the same as the number
+ // of elements then they were NOT accessed randomly.
+ // The odds of 20 elements being randomly accessed sequential order
+ // is nil isn't it?
+ EXPECT_NE(cnt_equals, STORAGE_SIZE);
+
+ EXPECT_TRUE(storage_.empty());
+ // When storage is empty, the attempt to get the random packet should
+ // result in returning NULL pointer.
+ EXPECT_FALSE(storage_.getRandom());
+ // Let's try it again to see if the previous call to getRandom didn't
+ // put the storage into the state in which the subsequent calls to
+ // getRandom would result in incorrect behaviour.
+ EXPECT_FALSE(storage_.getRandom());
+
+ // Let's add a new packet to the empty storage to check that storage
+ // "recovers" from being empty, i.e. that the internal indicator
+ // which points to the current packet reinitializes correctly.
+ storage_.append(createPacket6(DHCPV6_REPLY, 100));
+ ASSERT_EQ(1, storage_.size());
+ Pkt6Ptr packet = storage_.getRandom();
+ ASSERT_TRUE(packet);
+ EXPECT_EQ(100, packet->getTransid());
+ EXPECT_FALSE(storage_.getRandom());
+}
+
+// This test verifies that the packets in the storage can be accessed
+// either randomly or sequentially in the same time. It verifies that
+// each returned packet is removed from the storage.
+TEST_F(PacketStorageTest, getNextAndRandom) {
+ ASSERT_EQ(STORAGE_SIZE, storage_.size());
+ for (int i = 0; i < STORAGE_SIZE / 2; ++i) {
+ Pkt6Ptr packet_random = storage_.getRandom();
+ ASSERT_TRUE(packet_random) << "NULL packet returned by"
+ " storage_.getRandom() for iteration number " << i;
+ EXPECT_EQ(STORAGE_SIZE - 2 *i - 1, storage_.size());
+ Pkt6Ptr packet_seq = storage_.getNext();
+ ASSERT_TRUE(packet_seq) << "NULL packet returned by"
+ " storage_.getNext() for iteration number " << i;
+ EXPECT_EQ(STORAGE_SIZE - 2 * i - 2, storage_.size());
+ }
+ EXPECT_TRUE(storage_.empty());
+ EXPECT_FALSE(storage_.getRandom());
+ EXPECT_FALSE(storage_.getNext());
+
+ // Append two packets to the storage to check if it can "recover"
+ // from being empty and that new elements can be accessed.
+ storage_.append(createPacket6(DHCPV6_REPLY, 100));
+ storage_.append(createPacket6(DHCPV6_REPLY, 101));
+ ASSERT_EQ(2, storage_.size());
+ // The newly added elements haven't been accessed yet. So, if we
+ // call getNext the first one should be returned.
+ Pkt6Ptr packet_next = storage_.getNext();
+ ASSERT_TRUE(packet_next);
+ // The first packet has transaction id equal to 100.
+ EXPECT_EQ(100, packet_next->getTransid());
+ // There should be just one packet left in the storage.
+ ASSERT_EQ(1, storage_.size());
+ // The call to getRandom should return the sole packet from the
+ // storage.
+ Pkt6Ptr packet_random = storage_.getRandom();
+ ASSERT_TRUE(packet_random);
+ EXPECT_EQ(101, packet_random->getTransid());
+ // Any further calls to getRandom and getNext should return NULL.
+ EXPECT_FALSE(storage_.getRandom());
+ EXPECT_FALSE(storage_.getNext());
+}
+
+// This test verifies that all packets are removed from the storage when
+// clear() function is invoked.
+TEST_F(PacketStorageTest, clearAll) {
+ ASSERT_EQ(STORAGE_SIZE, storage_.size());
+ ASSERT_NO_THROW(storage_.clear());
+ EXPECT_TRUE(storage_.empty());
+}
+
+// This test verifies that a set of packets can be removed from the
+// storage when a number of packets to be removed is specified. If
+// number of packets to be removed exceeds the storage size, all
+// packets should be removed.
+TEST_F(PacketStorageTest, clear) {
+ // Initially storage should have 20 elements.
+ ASSERT_EQ(STORAGE_SIZE, storage_.size());
+ // Remove 10 of them.
+ ASSERT_NO_THROW(storage_.clear(10));
+ // We should have 10 remaining.
+ ASSERT_EQ(10, storage_.size());
+
+ // Check that the retrieval still works after partial clear.
+ EXPECT_TRUE(storage_.getNext());
+ EXPECT_TRUE(storage_.getRandom());
+ // We should have 10 - 2 = 8 packets in the storage after retrieval.
+ ASSERT_EQ(8, storage_.size());
+
+ // Try to remove more elements that actually is. It
+ // should result in removal of all elements.
+ ASSERT_NO_THROW(storage_.clear(15));
+ EXPECT_TRUE(storage_.empty());
+}
+
+
+} // anonymous namespace
diff --git a/src/bin/perfdhcp/tests/perf_pkt4_unittest.cc b/src/bin/perfdhcp/tests/perf_pkt4_unittest.cc
new file mode 100644
index 0000000..b162051
--- /dev/null
+++ b/src/bin/perfdhcp/tests/perf_pkt4_unittest.cc
@@ -0,0 +1,425 @@
+// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <iostream>
+#include <sstream>
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+
+#include <asiolink/io_address.h>
+#include <dhcp/option.h>
+#include <dhcp/dhcp4.h>
+
+#include <boost/scoped_ptr.hpp>
+
+#include "../localized_option.h"
+#include "../perf_pkt4.h"
+
+using namespace std;
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace isc::perfdhcp;
+
+typedef PerfPkt4::LocalizedOptionPtr LocalizedOptionPtr;
+
+namespace {
+
+// A dummy MAC address, padded with 0s
+const uint8_t dummyChaddr[16] = {0, 1, 2, 3, 4, 5, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 };
+
+// Let's use some creative test content here (128 chars + \0)
+const uint8_t dummyFile[] = "Lorem ipsum dolor sit amet, consectetur "
+ "adipiscing elit. Proin mollis placerat metus, at "
+ "lacinia orci ornare vitae. Mauris amet.";
+
+// Yet another type of test content (64 chars + \0)
+const uint8_t dummySname[] = "Lorem ipsum dolor sit amet, consectetur "
+ "adipiscing elit posuere.";
+
+class PerfPkt4Test : public ::testing::Test {
+public:
+ PerfPkt4Test() {
+ }
+
+ /// \brief Returns buffer with sample DHCPDISCOVER message.
+ ///
+ /// This method creates buffer containing on-wire data of
+ /// DHCPDICOSVER message. This buffer is used by tests below
+ /// to create DHCPv4 test packets.
+ ///
+ /// \return vector containing on-wire data
+ std::vector<uint8_t>& capture() {
+
+ // That is only part of the header. It contains all "short" fields,
+ // larger fields are constructed separately.
+ uint8_t hdr[] = {
+ 1, 6, 6, 13, // op, htype, hlen, hops,
+ 0x12, 0x34, 0x56, 0x78, // transaction-id
+ 0, 42, 0x80, 0x00, // 42 secs, BROADCAST flags
+ 192, 0, 2, 1, // ciaddr
+ 1, 2, 3, 4, // yiaddr
+ 192, 0, 2, 255, // siaddr
+ 255, 255, 255, 255, // giaddr
+ };
+
+ // cppcheck-suppress variableScope
+ uint8_t v4Opts[] = {
+ DHO_HOST_NAME, 3, 0, 1, 2, // Host name option.
+ DHO_BOOT_SIZE, 3, 10, 11, 12, // Boot file size option
+ DHO_MERIT_DUMP, 3, 20, 21, 22, // Merit dump file
+ DHO_DHCP_MESSAGE_TYPE, 1, 1, // DHCP message type.
+ 128, 3, 30, 31, 32,
+ 254, 3, 40, 41, 42,
+ };
+
+ // Initialize the vector with the header fields defined above.
+ static std::vector<uint8_t> buf(hdr, hdr + sizeof(hdr));
+
+ // If this is a first call to this function. Initialize
+ // remaining data.
+ if (buf.size() == sizeof(hdr)) {
+
+ // Append the large header fields.
+ std::copy(dummyChaddr, dummyChaddr + Pkt4::MAX_CHADDR_LEN,
+ back_inserter(buf));
+ std::copy(dummySname, dummySname + Pkt4::MAX_SNAME_LEN,
+ back_inserter(buf));
+ std::copy(dummyFile, dummyFile + Pkt4::MAX_FILE_LEN,
+ back_inserter(buf));
+
+ // Append magic cookie.
+ buf.push_back(0x63);
+ buf.push_back(0x82);
+ buf.push_back(0x53);
+ buf.push_back(0x63);
+
+ // Append options.
+ std::copy(v4Opts, v4Opts + sizeof(v4Opts), back_inserter(buf));
+ }
+ return buf;
+ }
+};
+
+TEST_F(PerfPkt4Test, Constructor) {
+ // Initialize some dummy payload.
+ uint8_t data[250];
+ for (uint8_t i = 0; i < 250; ++i) {
+ data[i] = i;
+ }
+
+ // Test constructor to be used for incoming messages.
+ // Use default (1) offset value and don't specify transaction id.
+ const size_t offset_transid[] = { 1, 10 };
+ boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(data,
+ sizeof(data),
+ offset_transid[0]));
+ EXPECT_EQ(1, pkt1->getTransidOffset());
+
+ // Test constructor to be used for outgoing messages.
+ // Use non-zero offset and specify transaction id.
+ const uint32_t transid = 0x010203;
+ boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(data, sizeof(data),
+ offset_transid[1],
+ transid));
+ EXPECT_EQ(transid, pkt2->getTransid());
+ EXPECT_EQ(offset_transid[1], pkt2->getTransidOffset());
+
+ // Test default constructor. Transaction id offset is expected to be 1.
+ boost::scoped_ptr<PerfPkt4> pkt3(new PerfPkt4(data, sizeof(data)));
+ EXPECT_EQ(1, pkt3->getTransidOffset());
+}
+
+TEST_F(PerfPkt4Test, RawPack) {
+ // Create new packet.
+ std::vector<uint8_t> buf = capture();
+ boost::scoped_ptr<PerfPkt4> pkt(new PerfPkt4(&buf[0], buf.size()));
+
+ // Initialize options data.
+ uint8_t buf_hostname[] = { DHO_HOST_NAME, 3, 4, 5, 6 };
+ uint8_t buf_boot_filesize[] = { DHO_BOOT_SIZE, 3, 1, 2, 3 };
+ OptionBuffer vec_hostname(buf_hostname + 2,
+ buf_hostname + sizeof(buf_hostname));
+ OptionBuffer vec_boot_filesize(buf_boot_filesize + 2,
+ buf_boot_filesize + sizeof(buf_hostname));
+
+ // Create options objects.
+ const size_t offset_hostname = 240;
+ LocalizedOptionPtr pkt_hostname(new LocalizedOption(Option::V4,
+ DHO_HOST_NAME,
+ vec_hostname,
+ offset_hostname));
+ const size_t offset_boot_filesize = 245;
+ LocalizedOptionPtr pkt_boot_filesize(new LocalizedOption(Option::V4,
+ DHO_BOOT_SIZE,
+ vec_boot_filesize,
+ offset_boot_filesize));
+
+ // Try to add options to packet.
+ ASSERT_NO_THROW(pkt->addOption(pkt_boot_filesize));
+ ASSERT_NO_THROW(pkt->addOption(pkt_hostname));
+
+ // We have valid options addedwith valid offsets so
+ // pack operation should succeed.
+ ASSERT_TRUE(pkt->rawPack());
+
+ // Buffer should now contain new values of DHO_HOST_NAME and
+ // DHO_BOOT_SIZE options.
+ util::OutputBuffer pkt_output = pkt->getBuffer();
+ ASSERT_EQ(buf.size(), pkt_output.getLength());
+ const uint8_t* out_buf_data =
+ static_cast<const uint8_t*>(pkt_output.getData());
+
+ // Check if options we read from buffer is valid.
+ EXPECT_EQ(0, memcmp(buf_hostname,
+ out_buf_data + offset_hostname,
+ sizeof(buf_hostname)));
+ EXPECT_EQ(0, memcmp(buf_boot_filesize,
+ out_buf_data + offset_boot_filesize,
+ sizeof(buf_boot_filesize)));
+}
+
+TEST_F(PerfPkt4Test, RawUnpack) {
+ // Create new packet.
+ std::vector<uint8_t> buf = capture();
+ boost::scoped_ptr<PerfPkt4> pkt(new PerfPkt4(&buf[0], buf.size()));
+
+ // Create options (existing in the packet) and specify their offsets.
+ const size_t offset_merit = 250;
+ LocalizedOptionPtr opt_merit(new LocalizedOption(Option::V4,
+ DHO_MERIT_DUMP,
+ OptionBuffer(),
+ offset_merit));
+
+ const size_t offset_msg_type = 255;
+ LocalizedOptionPtr opt_msg_type(new LocalizedOption(Option::V4,
+ DHO_DHCP_MESSAGE_TYPE,
+ OptionBuffer(),
+ offset_msg_type));
+ // Addition should be successful
+ ASSERT_NO_THROW(pkt->addOption(opt_merit));
+ ASSERT_NO_THROW(pkt->addOption(opt_msg_type));
+
+ // Option fit to packet boundaries and offsets are valid,
+ // so this should unpack successfully.
+ ASSERT_TRUE(pkt->rawUnpack());
+
+ // At this point we should have updated options data (read from buffer).
+ // Let's try to retrieve them.
+ opt_merit = boost::dynamic_pointer_cast<LocalizedOption>
+ (pkt->getOption(DHO_MERIT_DUMP));
+ opt_msg_type = boost::dynamic_pointer_cast<LocalizedOption>
+ (pkt->getOption(DHO_DHCP_MESSAGE_TYPE));
+ ASSERT_TRUE(opt_merit);
+ ASSERT_TRUE(opt_msg_type);
+
+ // Get first option payload.
+ OptionBuffer opt_merit_data = opt_merit->getData();
+
+ // Define reference data.
+ uint8_t buf_merit[] = { 20, 21, 22 };
+
+ // Validate first option data.
+ ASSERT_EQ(sizeof(buf_merit), opt_merit_data.size());
+ EXPECT_TRUE(std::equal(opt_merit_data.begin(),
+ opt_merit_data.end(),
+ buf_merit));
+
+ // Get second option payload.
+ OptionBuffer opt_msg_type_data = opt_msg_type->getData();
+
+ // Expect one byte of message type payload.
+ ASSERT_EQ(1, opt_msg_type_data.size());
+ EXPECT_EQ(1, opt_msg_type_data[0]);
+}
+
+TEST_F(PerfPkt4Test, InvalidOptions) {
+ // Create new packet.
+ std::vector<uint8_t> buf = capture();
+ boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(&buf[0], buf.size()));
+
+ // Create option with invalid offset.
+ // This option is at offset 250 (not 251).
+ const size_t offset_merit = 251;
+ LocalizedOptionPtr opt_merit(new LocalizedOption(Option::V4,
+ DHO_MERIT_DUMP,
+ OptionBuffer(),
+ offset_merit));
+ ASSERT_NO_THROW(pkt1->addOption(opt_merit));
+
+ cout << "Testing unpack of invalid options. "
+ << "This may produce spurious errors." << endl;
+
+ // Unpack is expected to fail because it is supposed to read
+ // option type from buffer and match it with DHO_MERIT_DUMP.
+ // It will not match because option is shifted by on byte.
+ ASSERT_FALSE(pkt1->rawUnpack());
+
+ // Create another packet.
+ boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(&buf[0], buf.size()));
+
+ // Create DHO_DHCP_MESSAGE_TYPE option that has the wrong offset.
+ // With this offset, option goes beyond packet size (268).
+ const size_t offset_msg_type = 266;
+ LocalizedOptionPtr opt_msg_type(new LocalizedOption(Option::V4,
+ DHO_DHCP_MESSAGE_TYPE,
+ OptionBuffer(1, 2),
+ offset_msg_type));
+ // Adding option is expected to be successful because no
+ // offset validation takes place at this point.
+ ASSERT_NO_THROW(pkt2->addOption(opt_msg_type));
+
+ // This is expected to fail because option is out of bounds.
+ ASSERT_FALSE(pkt2->rawPack());
+}
+
+TEST_F(PerfPkt4Test, TruncatedPacket) {
+ // Get the whole packet and truncate it to 249 bytes.
+ std::vector<uint8_t> buf = capture();
+ buf.resize(249);
+ boost::scoped_ptr<PerfPkt4> pkt(new PerfPkt4(&buf[0], buf.size()));
+
+ // Option DHO_BOOT_SIZE is now truncated because whole packet
+ // is truncated. This option ends at 249 while last index of
+ // truncated packet is now 248.
+ const size_t offset_boot_filesize = 245;
+ LocalizedOptionPtr opt_boot_filesize(new LocalizedOption(Option::V4,
+ DHO_BOOT_SIZE,
+ OptionBuffer(3, 1),
+ offset_boot_filesize));
+ ASSERT_NO_THROW(pkt->addOption(opt_boot_filesize));
+
+ cout << "Testing pack and unpack of options in truncated "
+ << "packet. This may produce spurious errors." << endl;
+
+ // Both pack and unpack are expected to fail because
+ // added option is out of bounds.
+ EXPECT_FALSE(pkt->rawUnpack());
+ EXPECT_FALSE(pkt->rawPack());
+}
+
+TEST_F(PerfPkt4Test, PackTransactionId) {
+ // Create dummy packet that consists of zeros.
+ std::vector<uint8_t> buf(268, 0);
+
+ const size_t offset_transid[] = { 10, 265 };
+ const uint32_t transid = 0x0102;
+ // Initialize transaction id 0x00000102 at offset 10.
+ boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(&buf[0], buf.size(),
+ offset_transid[0],
+ transid));
+
+ // Pack will inject transaction id at offset 10 into the
+ // packet buffer.
+ ASSERT_TRUE(pkt1->rawPack());
+
+ // Get packet's output buffer and make sure it has valid size.
+ util::OutputBuffer out_buf = pkt1->getBuffer();
+ ASSERT_EQ(buf.size(), out_buf.getLength());
+ const uint8_t *out_buf_data =
+ static_cast<const uint8_t*>(out_buf.getData());
+
+ // Initialize reference data for transaction id.
+ const uint8_t ref_data[] = { 0, 0, 1, 2 };
+
+ // Expect that reference transaction id matches what we have
+ // read from buffer.
+ EXPECT_EQ(0, memcmp(ref_data, out_buf_data + offset_transid[0], 4));
+
+ cout << "Testing pack with invalid transaction id offset. "
+ << "This may produce spurious errors" << endl;
+
+ // Create packet with invalid transaction id offset.
+ // Packet length is 268, transaction id is 4 bytes long so last byte of
+ // transaction id is out of bounds.
+ boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(&buf[0], buf.size(),
+ offset_transid[1],
+ transid));
+ EXPECT_FALSE(pkt2->rawPack());
+}
+
+TEST_F(PerfPkt4Test, UnpackTransactionId) {
+ // Initialize packet data, length 268, zeros only.
+ std::vector<uint8_t> in_data(268, 0);
+
+ // Assume that transaction id is at offset 100.
+ // Fill 4 bytes at offset 100 with dummy transaction id.
+ for (uint8_t i = 100; i < 104; ++i) {
+ in_data[i] = i - 99;
+ }
+
+ // Create packet from initialized buffer.
+ const size_t offset_transid[] = { 100, 270 };
+ boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(&in_data[0],
+ in_data.size(),
+ offset_transid[0]));
+ ASSERT_TRUE(pkt1->rawUnpack());
+
+ // Get unpacked transaction id and compare with reference.
+ EXPECT_EQ(0x01020304, pkt1->getTransid());
+
+ // Create packet with transaction id at invalid offset.
+ boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(&in_data[0],
+ in_data.size(),
+ offset_transid[1]));
+
+ cout << "Testing unpack of transaction id at invalid offset. "
+ << "This may produce spurious errors." << endl;
+
+ // Unpack is supposed to fail because transaction id is at
+ // out of bounds offset.
+ EXPECT_FALSE(pkt2->rawUnpack());
+}
+
+TEST_F(PerfPkt4Test, Writes) {
+ // Initialize input buffer with 260 elements set to value 1.
+ dhcp::OptionBuffer in_data(260, 1);
+ // Initialize buffer to be used for write: 1,2,3,4,...,9
+ dhcp::OptionBuffer write_buf(10);
+ for (size_t i = 0; i < write_buf.size(); ++i) {
+ write_buf[i] = i;
+ }
+ // Create packet from the input buffer.
+ const size_t transid_offset = 4;
+ boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(&in_data[0],
+ in_data.size(),
+ transid_offset));
+ // Write numbers 4,5,6,7 to the packet's input buffer at position 10.
+ pkt1->writeAt(10, write_buf.begin() + 3, write_buf.begin() + 7);
+ // We have to pack data to output buffer here because Pkt4 provides no
+ // way to retrieve input buffer. If we pack data it will go to
+ // output buffer that has getter available.
+ ASSERT_TRUE(pkt1->rawPack());
+ const util::OutputBuffer& out_buf = pkt1->getBuffer();
+ ASSERT_EQ(in_data.size(), out_buf.getLength());
+ // Verify that 4,5,6,7 has been written to the packet's buffer.
+ const char* out_data = static_cast<const char*>(out_buf.getData());
+ EXPECT_TRUE(std::equal(write_buf.begin() + 3, write_buf.begin() + 7,
+ out_data + 10));
+ // Write 1 octet (0x51) at position 10.
+ pkt1->writeValueAt<uint8_t>(10, 0x51);
+ ASSERT_TRUE(pkt1->rawPack());
+ ASSERT_EQ(in_data.size(), pkt1->getBuffer().getLength());
+ EXPECT_EQ(0x51, pkt1->getBuffer()[10]);
+ // Write 2 octets (0x5251) at position 20.
+ pkt1->writeValueAt<uint16_t>(20, 0x5251);
+ ASSERT_TRUE(pkt1->rawPack());
+ ASSERT_EQ(in_data.size(), pkt1->getBuffer().getLength());
+ EXPECT_EQ(0x52, pkt1->getBuffer()[20]);
+ EXPECT_EQ(0x51, pkt1->getBuffer()[21]);
+ // Write 4 octets (0x54535251) at position 30.
+ pkt1->writeValueAt<uint32_t>(30, 0x54535251);
+ ASSERT_TRUE(pkt1->rawPack());
+ ASSERT_EQ(in_data.size(), pkt1->getBuffer().getLength());
+ EXPECT_EQ(0x54, pkt1->getBuffer()[30]);
+ EXPECT_EQ(0x53, pkt1->getBuffer()[31]);
+ EXPECT_EQ(0x52, pkt1->getBuffer()[32]);
+ EXPECT_EQ(0x51, pkt1->getBuffer()[33]);
+}
+
+}
diff --git a/src/bin/perfdhcp/tests/perf_pkt6_unittest.cc b/src/bin/perfdhcp/tests/perf_pkt6_unittest.cc
new file mode 100644
index 0000000..f4e5b41
--- /dev/null
+++ b/src/bin/perfdhcp/tests/perf_pkt6_unittest.cc
@@ -0,0 +1,324 @@
+// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <iostream>
+#include <sstream>
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+
+#include <asiolink/io_address.h>
+#include <dhcp/option.h>
+#include <dhcp/dhcp6.h>
+
+#include <boost/scoped_ptr.hpp>
+
+#include "../localized_option.h"
+#include "../perf_pkt6.h"
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::perfdhcp;
+
+typedef PerfPkt6::LocalizedOptionPtr LocalizedOptionPtr;
+
+namespace {
+
+class PerfPkt6Test : public ::testing::Test {
+public:
+ PerfPkt6Test() {
+ }
+
+ /// \brief Returns captured SOLICIT packet.
+ ///
+ /// Captured SOLICIT packet with transid=0x3d79fb and options: client-id,
+ /// in_na, dns-server, elapsed-time, option-request
+ /// This code was autogenerated
+ /// (see src/bin/dhcp6/tests/iface_mgr_unittest.c),
+ /// but we spent some time to make is less ugly than it used to be.
+ ///
+ /// \return pointer to Pkt6 that represents received SOLICIT
+ PerfPkt6* capture() {
+ uint8_t data[98];
+ data[0] = 1;
+ data[1] = 1; data[2] = 2; data[3] = 3; data[4] = 0;
+ data[5] = 1; data[6] = 0; data[7] = 14; data[8] = 0;
+ data[9] = 1; data[10] = 0; data[11] = 1; data[12] = 21;
+ data[13] = 158; data[14] = 60; data[15] = 22; data[16] = 0;
+ data[17] = 30; data[18] = 140; data[19] = 155; data[20] = 115;
+ data[21] = 73; data[22] = 0; data[23] = 3; data[24] = 0;
+ data[25] = 40; data[26] = 0; data[27] = 0; data[28] = 0;
+ data[29] = 1; data[30] = 255; data[31] = 255; data[32] = 255;
+ data[33] = 255; data[34] = 255; data[35] = 255; data[36] = 255;
+ data[37] = 255; data[38] = 0; data[39] = 5; data[40] = 0;
+ data[41] = 24; data[42] = 32; data[43] = 1; data[44] = 13;
+ data[45] = 184; data[46] = 0; data[47] = 1; data[48] = 0;
+ data[49] = 0; data[50] = 0; data[51] = 0; data[52] = 0;
+ data[53] = 0; data[54] = 0; data[55] = 0; data[56] = 18;
+ data[57] = 52; data[58] = 255; data[59] = 255; data[60] = 255;
+ data[61] = 255; data[62] = 255; data[63] = 255; data[64] = 255;
+ data[65] = 255; data[66] = 0; data[67] = 23; data[68] = 0;
+ data[69] = 16; data[70] = 32; data[71] = 1; data[72] = 13;
+ data[73] = 184; data[74] = 0; data[75] = 1; data[76] = 0;
+ data[77] = 0; data[78] = 0; data[79] = 0; data[80] = 0;
+ data[81] = 0; data[82] = 0; data[83] = 0; data[84] = 221;
+ data[85] = 221; data[86] = 0; data[87] = 8; data[88] = 0;
+ data[89] = 2; data[90] = 0; data[91] = 100; data[92] = 0;
+ data[93] = 6; data[94] = 0; data[95] = 2; data[96] = 0;
+ data[97] = 23;
+
+ PerfPkt6* pkt = new PerfPkt6(data, sizeof(data));
+
+ return (pkt);
+ }
+
+ /// \brief Returns truncated SOLICIT packet.
+ ///
+ /// Returns truncated SOLICIT packet which will be used for
+ /// negative tests: e.g. pack options out of packet.
+ ///
+ /// \return pointer to Pkt6 that represents truncated SOLICIT
+ PerfPkt6* captureTruncated() {
+ uint8_t data[17];
+ data[0] = 1;
+ data[1] = 1; data[2] = 2; data[3] = 3; data[4] = 0;
+ data[5] = 1; data[6] = 0; data[7] = 14; data[8] = 0;
+ data[9] = 1; data[10] = 0; data[11] = 1; data[12] = 21;
+ data[13] = 158; data[14] = 60; data[15] = 22; data[16] = 0;
+
+ PerfPkt6* pkt = new PerfPkt6(data, sizeof(data));
+
+ return (pkt);
+ }
+
+
+};
+
+TEST_F(PerfPkt6Test, Constructor) {
+ // Data to be used to create packet.
+ uint8_t data[] = { 0, 1, 2, 3, 4, 5 };
+
+ // Test constructor to be used for incoming messages.
+ // Use default (1) offset value and don't specify transaction id.
+ boost::scoped_ptr<PerfPkt6> pkt1(new PerfPkt6(data, sizeof(data)));
+ EXPECT_EQ(sizeof(data), pkt1->data_.size());
+ EXPECT_EQ(0, memcmp(&pkt1->data_[0], data, sizeof(data)));
+ EXPECT_EQ(1, pkt1->getTransidOffset());
+
+ // Test constructor to be used for outgoing messages.
+ // Use non-zero offset and specify transaction id.
+ const size_t offset_transid = 10;
+ const uint32_t transid = 0x010203;
+ boost::scoped_ptr<PerfPkt6> pkt2(new PerfPkt6(data, sizeof(data),
+ offset_transid, transid));
+ EXPECT_EQ(sizeof(data), pkt2->data_.size());
+ EXPECT_EQ(0, memcmp(&pkt2->data_[0], data, sizeof(data)));
+ EXPECT_EQ(0x010203, pkt2->getTransid());
+ EXPECT_EQ(10, pkt2->getTransidOffset());
+}
+
+TEST_F(PerfPkt6Test, RawPackUnpack) {
+ // Create first packet.
+ boost::scoped_ptr<PerfPkt6> pkt1(capture());
+
+ // Create some input buffers to initialize options.
+ uint8_t buf_elapsed_time[] = { 1, 1 };
+ uint8_t buf_duid[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
+
+ // Create options.
+ const size_t offset_elapsed_time = 86;
+ OptionBuffer vec_elapsed_time(buf_elapsed_time,
+ buf_elapsed_time + sizeof(buf_elapsed_time));
+ LocalizedOptionPtr pkt1_elapsed_time(new LocalizedOption(Option::V6,
+ D6O_ELAPSED_TIME,
+ vec_elapsed_time,
+ offset_elapsed_time));
+ const size_t offset_duid = 4;
+ OptionBuffer vec_duid(buf_duid, buf_duid + sizeof(buf_duid));
+ LocalizedOptionPtr pkt1_duid(new LocalizedOption(Option::V6,
+ D6O_CLIENTID,
+ vec_duid,
+ offset_duid));
+
+ // Add option to packet and create on-wire format from added options.
+ // Contents of options will override contents of packet buffer.
+ ASSERT_NO_THROW(pkt1->addOption(pkt1_elapsed_time));
+ ASSERT_NO_THROW(pkt1->addOption(pkt1_duid));
+ ASSERT_TRUE(pkt1->rawPack());
+
+ // Reset so as we can reuse them for another packet.
+ vec_elapsed_time.clear();
+ vec_duid.clear();
+
+ // Get output buffer from packet 1 to create new packet
+ // that will be later validated.
+ util::OutputBuffer pkt1_output = pkt1->getBuffer();
+ ASSERT_EQ(pkt1_output.getLength(), pkt1->data_.size());
+ const uint8_t* pkt1_output_data = static_cast<const uint8_t*>
+ (pkt1_output.getData());
+ boost::scoped_ptr<PerfPkt6> pkt2(new PerfPkt6(pkt1_output_data,
+ pkt1_output.getLength()));
+
+ // Create objects specifying options offset in a packet.
+ // Offsets will inform pkt2 object where to read data from.
+ LocalizedOptionPtr pkt2_elapsed_time(new LocalizedOption(Option::V6,
+ D6O_ELAPSED_TIME,
+ vec_elapsed_time,
+ offset_elapsed_time));
+ LocalizedOptionPtr pkt2_duid(new LocalizedOption(Option::V6,
+ D6O_CLIENTID,
+ vec_duid,
+ offset_duid));
+ // Add options to packet to pass their offsets.
+ pkt2->addOption(pkt2_elapsed_time);
+ pkt2->addOption(pkt2_duid);
+
+ // Unpack: get relevant parts of buffer data into option objects.
+ ASSERT_TRUE(pkt2->rawUnpack());
+
+ // Once option data is stored in options objects we pull it out.
+ pkt2_elapsed_time = boost::dynamic_pointer_cast<LocalizedOption>
+ (pkt2->getOption(D6O_ELAPSED_TIME));
+ pkt2_duid = boost::dynamic_pointer_cast<LocalizedOption>
+ (pkt2->getOption(D6O_CLIENTID));
+
+ // Check if options are present. They have to be there since
+ // we have added them ourselfs.
+ ASSERT_TRUE(pkt2_elapsed_time);
+ ASSERT_TRUE(pkt2_duid);
+
+ // Expecting option contents be the same as original.
+ OptionBuffer pkt2_elapsed_time_data = pkt2_elapsed_time->getData();
+ OptionBuffer pkt2_duid_data = pkt2_duid->getData();
+ EXPECT_EQ(0x0101, pkt2_elapsed_time->getUint16());
+ EXPECT_TRUE(std::equal(pkt2_duid_data.begin(),
+ pkt2_duid_data.end(),
+ buf_duid));
+}
+
+TEST_F(PerfPkt6Test, InvalidOptions) {
+ // Create packet.
+ boost::scoped_ptr<PerfPkt6> pkt1(capture());
+ OptionBuffer vec_server_id;
+ vec_server_id.resize(10);
+ // Testing invalid offset of the option (greater than packet size)
+ const size_t offset_serverid[] = { 150, 85 };
+ LocalizedOptionPtr pkt1_serverid(new LocalizedOption(Option::V6,
+ D6O_SERVERID,
+ vec_server_id,
+ offset_serverid[0]));
+ pkt1->addOption(pkt1_serverid);
+ // Pack has to fail due to invalid offset.
+ EXPECT_FALSE(pkt1->rawPack());
+
+ // Create packet.
+ boost::scoped_ptr<PerfPkt6> pkt2(capture());
+ // Testing offset of the option (lower than packet size but
+ // tail of the option out of bounds).
+ LocalizedOptionPtr pkt2_serverid(new LocalizedOption(Option::V6,
+ D6O_SERVERID,
+ vec_server_id,
+ offset_serverid[1]));
+ pkt2->addOption(pkt2_serverid);
+ // Pack must fail due to invalid offset.
+ EXPECT_FALSE(pkt2->rawPack());
+}
+
+
+TEST_F(PerfPkt6Test, TruncatedPacket) {
+ cout << "Testing parsing options from truncated packet."
+ << "This may produce spurious errors" << endl;
+
+ // Create truncated (in the middle of duid options)
+ boost::scoped_ptr<PerfPkt6> pkt1(captureTruncated());
+ OptionBuffer vec_duid;
+ vec_duid.resize(30);
+ const size_t offset_duid = 4;
+ LocalizedOptionPtr pkt1_duid(new LocalizedOption(Option::V6,
+ D6O_CLIENTID,
+ vec_duid,
+ offset_duid));
+ pkt1->addOption(pkt1_duid);
+ // Pack/unpack must fail because length of the option read from buffer
+ // will extend over the actual packet length.
+ EXPECT_FALSE(pkt1->rawUnpack());
+ EXPECT_FALSE(pkt1->rawPack());
+}
+
+TEST_F(PerfPkt6Test, PackTransactionId) {
+ uint8_t data[100];
+ memset(&data, 0, sizeof(data));
+
+ const size_t offset_transid[] = { 50, 100 };
+ const uint32_t transid = 0x010203;
+
+ // Create dummy packet that is simply filled with zeros.
+ boost::scoped_ptr<PerfPkt6> pkt1(new PerfPkt6(data,
+ sizeof(data),
+ offset_transid[0],
+ transid));
+
+ // Reference data are non zero so we can detect them in dummy packet.
+ uint8_t ref_data[3] = { 1, 2, 3 };
+
+ // This will store given transaction id in the packet data at
+ // offset of 50.
+ ASSERT_TRUE(pkt1->rawPack());
+
+ // Get the output buffer so we can validate it.
+ util::OutputBuffer out_buf = pkt1->getBuffer();
+ ASSERT_EQ(sizeof(data), out_buf.getLength());
+ const uint8_t *out_buf_data = static_cast<const uint8_t*>
+ (out_buf.getData());
+
+ // Try to make clang static analyzer happy.
+ ASSERT_LE(offset_transid[0], out_buf.getLength());
+
+ // Validate transaction id.
+ EXPECT_EQ(0, memcmp(ref_data, out_buf_data + offset_transid[0], 3));
+
+
+ // Out of bounds transaction id offset.
+ boost::scoped_ptr<PerfPkt6> pkt2(new PerfPkt6(data,
+ sizeof(data),
+ offset_transid[1],
+ transid));
+ cout << "Testing out of bounds offset. "
+ "This may produce spurious errors ..." << endl;
+ EXPECT_FALSE(pkt2->rawPack());
+}
+
+TEST_F(PerfPkt6Test, UnpackTransactionId) {
+ // Initialize data for dummy packet (zeros only).
+ uint8_t data[100] = { 0 };
+
+ // Generate transaction id = 0x010203 and inject at offset = 50.
+ for (uint8_t i = 50; i < 53; ++i) {
+ data[i] = i - 49;
+ }
+ // Create packet and point out that transaction id is at offset 50.
+ const size_t offset_transid[] = { 50, 300 };
+ boost::scoped_ptr<PerfPkt6> pkt1(new PerfPkt6(data,
+ sizeof(data),
+ offset_transid[0]));
+
+ // Get transaction id out of buffer and store in class member.
+ ASSERT_TRUE(pkt1->rawUnpack());
+ // Test value of transaction id.
+ EXPECT_EQ(0x010203, pkt1->getTransid());
+
+ // Out of bounds transaction id offset.
+ boost::scoped_ptr<PerfPkt6> pkt2(new PerfPkt6(data,
+ sizeof(data),
+ offset_transid[1]));
+ cout << "Testing out of bounds offset. "
+ "This may produce spurious errors ..." << endl;
+ EXPECT_FALSE(pkt2->rawUnpack());
+
+}
+
+}
diff --git a/src/bin/perfdhcp/tests/perf_socket_unittest.cc b/src/bin/perfdhcp/tests/perf_socket_unittest.cc
new file mode 100644
index 0000000..39a2db9
--- /dev/null
+++ b/src/bin/perfdhcp/tests/perf_socket_unittest.cc
@@ -0,0 +1,58 @@
+// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include "command_options_helper.h"
+#include "../perf_socket.h"
+
+#include <asiolink/io_address.h>
+#include <exceptions/exceptions.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/iface_mgr.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/foreach.hpp>
+
+#include <algorithm>
+#include <cstddef>
+#include <stdint.h>
+#include <string>
+#include <fstream>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace boost::posix_time;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::perfdhcp;
+
+
+/// \brief Test Fixture Class
+///
+/// This test fixture class is used to perform
+/// unit tests on perfdhcp PerfSocketTest class.
+class PerfSocketTest : public virtual ::testing::Test
+{
+public:
+ PerfSocketTest() { }
+};
+
+
+TEST_F(PerfSocketTest, WrongCommandOptions) {
+ // Check if incorrect command options are casing failure during
+ // socket setup.
+ CommandOptions opt;
+
+ // make sure we catch -6 paired with v4 address
+ CommandOptionsHelper::process(opt, "perfdhcp -l 127.0.0.1 -6 192.168.1.1");
+ EXPECT_THROW(PerfSocket sock(opt), isc::InvalidParameter);
+
+ // make sure we catch -4 paired with v6 address
+ CommandOptionsHelper::process(opt, "perfdhcp -l 127.0.0.1 -4 ff02::1:2");
+ EXPECT_THROW(PerfSocket sock(opt), isc::InvalidParameter);
+}
diff --git a/src/bin/perfdhcp/tests/random_number_generator_unittest.cc b/src/bin/perfdhcp/tests/random_number_generator_unittest.cc
new file mode 100644
index 0000000..58da955
--- /dev/null
+++ b/src/bin/perfdhcp/tests/random_number_generator_unittest.cc
@@ -0,0 +1,296 @@
+// Copyright (C) 2010-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <perfdhcp/random_number_generator.h>
+
+#include <gtest/gtest.h>
+#include <boost/shared_ptr.hpp>
+
+#include <iostream>
+
+using namespace isc;
+using namespace isc::perfdhcp;
+using namespace std;
+
+/// \brief Test Fixture Class for uniform random number generator
+///
+/// The hard part for this test is how to test that the number is random?
+/// and how to test that the number is uniformly distributed?
+/// Or maybe we can trust the boost implementation
+class UniformRandomIntegerGeneratorTest : public ::testing::Test {
+public:
+ UniformRandomIntegerGeneratorTest():
+ gen_(min_, max_)
+ {
+ }
+ virtual ~UniformRandomIntegerGeneratorTest(){}
+
+ int gen() { return (gen_()); }
+ int max() const { return (max_); }
+ int min() const { return (min_); }
+
+private:
+ UniformRandomIntegerGenerator gen_;
+
+ const static int min_ = 1;
+ const static int max_ = 10;
+};
+
+// Some validation tests will incur performance penalty, so the tests are
+// made only in "debug" version with assert(). But if NDEBUG is defined
+// the tests will be failed since assert() is non-op in non-debug version.
+// The "#ifndef NDEBUG" is added to make the tests be performed only in
+// non-debug environment.
+// Note: the death test is not supported by all platforms. We need to
+// compile tests using it selectively.
+#if !defined(NDEBUG)
+// Test of the constructor
+TEST_F(UniformRandomIntegerGeneratorTest, Constructor) {
+ // The range must be min<=max
+ ASSERT_THROW(UniformRandomIntegerGenerator(3, 2), InvalidLimits);
+}
+#endif
+
+// Test of the generated integers are in the range [min, max]
+TEST_F(UniformRandomIntegerGeneratorTest, IntegerRange) {
+ vector<int> numbers;
+
+ // Generate a lot of random integers
+ for (int i = 0; i < max()*10; ++i) {
+ numbers.push_back(gen());
+ }
+
+ // Remove the duplicated values
+ sort(numbers.begin(), numbers.end());
+ vector<int>::iterator it = unique(numbers.begin(), numbers.end());
+
+ // make sure the numbers are in range [min, max]
+ ASSERT_EQ(it - numbers.begin(), max() - min() + 1);
+}
+
+/// \brief Test Fixture Class for weighted random number generator
+class WeightedRandomIntegerGeneratorTest : public ::testing::Test {
+public:
+ WeightedRandomIntegerGeneratorTest()
+ { }
+
+ virtual ~WeightedRandomIntegerGeneratorTest()
+ { }
+};
+
+// Test of the weighted random number generator constructor
+TEST_F(WeightedRandomIntegerGeneratorTest, Constructor) {
+ vector<double> probabilities;
+
+ // If no probabilities is provided, the smallest integer will always
+ // be generated
+ WeightedRandomIntegerGenerator gen(probabilities, 123);
+ for (int i = 0; i < 100; ++i) {
+ ASSERT_EQ(gen(), 123);
+ }
+
+/// Some validation tests will incur performance penalty, so the tests are
+/// made only in "debug" version with assert(). But if NDEBUG is defined
+/// the tests will be failed since assert() is non-op in non-debug version.
+/// The "#ifndef NDEBUG" is added to make the tests be performed only in
+/// non-debug environment.
+#if !defined(NDEBUG)
+ //The probability must be >= 0
+ probabilities.push_back(-0.1);
+ probabilities.push_back(1.1);
+ ASSERT_THROW(WeightedRandomIntegerGenerator gen2(probabilities),
+ InvalidProbValue);
+
+ //The probability must be <= 1.0
+ probabilities.clear();
+ probabilities.push_back(0.1);
+ probabilities.push_back(1.1);
+ ASSERT_THROW(WeightedRandomIntegerGenerator gen3(probabilities),
+ InvalidProbValue);
+
+ //The sum must be equal to 1.0
+ probabilities.clear();
+ probabilities.push_back(0.2);
+ probabilities.push_back(0.9);
+ ASSERT_THROW(WeightedRandomIntegerGenerator gen4(probabilities), SumNotOne);
+
+ //The sum must be equal to 1.0
+ probabilities.clear();
+ probabilities.push_back(0.3);
+ probabilities.push_back(0.2);
+ probabilities.push_back(0.1);
+ ASSERT_THROW(WeightedRandomIntegerGenerator gen5(probabilities), SumNotOne);
+#endif
+}
+
+// Test the randomization of the generator
+TEST_F(WeightedRandomIntegerGeneratorTest, WeightedRandomization) {
+ const int repeats = 100000;
+ // We repeat the simulation for N=repeats times
+ // for each probability p, its average is mu = N*p
+ // variance sigma^2 = N * p * (1-p)
+ // sigma = sqrt(N*2/9)
+ // we should make sure that mu - 4sigma < count < mu + 4sigma
+ // which means for 99.99366% of the time this should be true
+ {
+ double p = 0.5;
+ vector<double> probabilities;
+ probabilities.push_back(p);
+ probabilities.push_back(p);
+
+ // Uniformly generated integers
+ WeightedRandomIntegerGenerator gen(probabilities);
+ int c1 = 0;
+ int c2 = 0;
+ for (int i = 0; i < repeats; ++i){
+ int n = gen();
+ if (n == 0) {
+ ++c1;
+ } else if (n == 1) {
+ ++c2;
+ }
+ }
+ double mu = repeats * p;
+ double sigma = sqrt(repeats * p * (1 - p));
+ ASSERT_TRUE(fabs(c1 - mu) < 4*sigma);
+ ASSERT_TRUE(fabs(c2 - mu) < 4*sigma);
+ }
+
+ {
+ vector<double> probabilities;
+ int c1 = 0;
+ int c2 = 0;
+ double p1 = 0.2;
+ double p2 = 0.8;
+ probabilities.push_back(p1);
+ probabilities.push_back(p2);
+ WeightedRandomIntegerGenerator gen(probabilities);
+ for (int i = 0; i < repeats; ++i) {
+ int n = gen();
+ if (n == 0) {
+ ++c1;
+ } else if (n == 1) {
+ ++c2;
+ }
+ }
+ double mu1 = repeats * p1;
+ double mu2 = repeats * p2;
+ double sigma1 = sqrt(repeats * p1 * (1 - p1));
+ double sigma2 = sqrt(repeats * p2 * (1 - p2));
+ ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
+ ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
+ }
+
+ {
+ vector<double> probabilities;
+ int c1 = 0;
+ int c2 = 0;
+ double p1 = 0.8;
+ double p2 = 0.2;
+ probabilities.push_back(p1);
+ probabilities.push_back(p2);
+ WeightedRandomIntegerGenerator gen(probabilities);
+ for (int i = 0; i < repeats; ++i) {
+ int n = gen();
+ if (n == 0) {
+ ++c1;
+ } else if (n == 1) {
+ ++c2;
+ }
+ }
+ double mu1 = repeats * p1;
+ double mu2 = repeats * p2;
+ double sigma1 = sqrt(repeats * p1 * (1 - p1));
+ double sigma2 = sqrt(repeats * p2 * (1 - p2));
+ ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
+ ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
+ }
+
+ {
+ vector<double> probabilities;
+ int c1 = 0;
+ int c2 = 0;
+ int c3 = 0;
+ double p1 = 0.5;
+ double p2 = 0.25;
+ double p3 = 0.25;
+ probabilities.push_back(p1);
+ probabilities.push_back(p2);
+ probabilities.push_back(p3);
+ WeightedRandomIntegerGenerator gen(probabilities);
+ for (int i = 0; i < repeats; ++i){
+ int n = gen();
+ if (n == 0) {
+ ++c1;
+ } else if (n == 1) {
+ ++c2;
+ } else if (n == 2) {
+ ++c3;
+ }
+ }
+ double mu1 = repeats * p1;
+ double mu2 = repeats * p2;
+ double mu3 = repeats * p3;
+ double sigma1 = sqrt(repeats * p1 * (1 - p1));
+ double sigma2 = sqrt(repeats * p2 * (1 - p2));
+ double sigma3 = sqrt(repeats * p3 * (1 - p3));
+ ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
+ ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
+ ASSERT_TRUE(fabs(c3 - mu3) < 4*sigma3);
+ }
+}
+
+// Test the reset function of generator
+TEST_F(WeightedRandomIntegerGeneratorTest, ResetProbabilities) {
+ const int repeats = 100000;
+ vector<double> probabilities;
+ int c1 = 0;
+ int c2 = 0;
+ double p1 = 0.8;
+ double p2 = 0.2;
+ probabilities.push_back(p1);
+ probabilities.push_back(p2);
+ WeightedRandomIntegerGenerator gen(probabilities);
+ for (int i = 0; i < repeats; ++i) {
+ int n = gen();
+ if (n == 0) {
+ ++c1;
+ } else if (n == 1) {
+ ++c2;
+ }
+ }
+ double mu1 = repeats * p1;
+ double mu2 = repeats * p2;
+ double sigma1 = sqrt(repeats * p1 * (1 - p1));
+ double sigma2 = sqrt(repeats * p2 * (1 - p2));
+ ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
+ ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
+
+ // Reset the probabilities
+ probabilities.clear();
+ c1 = c2 = 0;
+ p1 = 0.2;
+ p2 = 0.8;
+ probabilities.push_back(p1);
+ probabilities.push_back(p2);
+ gen.reset(probabilities);
+ for (int i = 0; i < repeats; ++i) {
+ int n = gen();
+ if (n == 0) {
+ ++c1;
+ } else if (n == 1) {
+ ++c2;
+ }
+ }
+ mu1 = repeats * p1;
+ mu2 = repeats * p2;
+ sigma1 = sqrt(repeats * p1 * (1 - p1));
+ sigma2 = sqrt(repeats * p2 * (1 - p2));
+ ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
+ ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
+}
diff --git a/src/bin/perfdhcp/tests/rate_control_unittest.cc b/src/bin/perfdhcp/tests/rate_control_unittest.cc
new file mode 100644
index 0000000..5619fd3
--- /dev/null
+++ b/src/bin/perfdhcp/tests/rate_control_unittest.cc
@@ -0,0 +1,105 @@
+// Copyright (C) 2013-2019 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <exceptions/exceptions.h>
+#include "rate_control.h"
+#include <gtest/gtest.h>
+
+
+using namespace isc;
+using namespace isc::perfdhcp;
+
+/// \brief A class which exposes protected methods and members of the
+/// RateControl class (under test).
+class NakedRateControl : public RateControl {
+public:
+
+ /// \brief Default constructor.
+ NakedRateControl()
+ : RateControl() {
+ }
+
+ /// \brief Constructor which sets up the rate.
+ ///
+ /// \param rate A rate at which messages are sent.
+ /// maximal number of messages sent in one chunk.
+ NakedRateControl(const int rate)
+ : RateControl(rate) {
+ }
+
+ using RateControl::currentTime;
+ using RateControl::start_time_;
+};
+
+// Test default constructor.
+TEST(RateControl, constructorDefault) {
+ NakedRateControl rc;
+ EXPECT_EQ(0, rc.getRate());
+}
+
+// Test the constructor which sets the rate.
+TEST(RateControl, constructor) {
+ // Call the constructor and verify that it sets the appropriate
+ // values.
+ NakedRateControl rc1(3);
+ EXPECT_EQ(3, rc1.getRate());
+
+ // Call the constructor again and make sure that different values
+ // will be set correctly.
+ NakedRateControl rc2(5);
+ EXPECT_EQ(5, rc2.getRate());
+}
+
+// Check the rate accessor.
+TEST(RateControl, getRate) {
+ RateControl rc;
+ ASSERT_EQ(0, rc.getRate());
+ rc.setRate(5);
+ ASSERT_EQ(5, rc.getRate());
+ rc.setRate(10);
+ EXPECT_EQ(10, rc.getRate());
+}
+
+// Check that the function returns the number of messages to be sent "now"
+// correctly.
+// @todo Possibly extend this test to cover more complex scenarios. Note that
+// it is quite hard to fully test this function as its behaviour strongly
+// depends on time.
+TEST(RateControl, getOutboundMessageCount) {
+ // Test that the number of outbound messages is correctly defined by the
+ // rate.
+ NakedRateControl rc(2);
+
+ // The first call to getOutboundMessageCount always returns 0 as there is
+ // no good estimate at the beginning how many packets to send.
+ uint64_t count = 0;
+ ASSERT_NO_THROW(count = rc.getOutboundMessageCount());
+ EXPECT_EQ(count, 1);
+
+ // After a given amount of time number of packets to send should
+ // allow to catch up with requested rate, ie for rate 2pks/s after 1500ms
+ // it should send 2 pkts/s * 1.5s = about 2 pkts.
+ // To simulate 1500ms lets get back start time by 1500ms.
+ rc.start_time_ -= boost::posix_time::milliseconds(1500);
+ ASSERT_NO_THROW(count = rc.getOutboundMessageCount());
+ EXPECT_EQ(count, 2);
+
+ // If elapsed time is big then a big number of packets would need to be sent.
+ // But the pkts number is capped to 3. Check it.
+ rc.start_time_ -= boost::posix_time::milliseconds(10000);
+ ASSERT_NO_THROW(count = rc.getOutboundMessageCount());
+ EXPECT_EQ(count, 3);
+}
+
+// Test the rate modifier for valid and invalid rate values.
+TEST(RateControl, setRate) {
+ NakedRateControl rc;
+ EXPECT_NO_THROW(rc.setRate(1));
+ EXPECT_NO_THROW(rc.setRate(0));
+ EXPECT_THROW(rc.setRate(-1), isc::BadValue);
+}
diff --git a/src/bin/perfdhcp/tests/receiver_unittest.cc b/src/bin/perfdhcp/tests/receiver_unittest.cc
new file mode 100644
index 0000000..1a1cb68
--- /dev/null
+++ b/src/bin/perfdhcp/tests/receiver_unittest.cc
@@ -0,0 +1,116 @@
+// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include "command_options_helper.h"
+
+#include <dhcp/iface_mgr.h>
+
+
+#include <exceptions/exceptions.h>
+#include "receiver.h"
+#include <gtest/gtest.h>
+
+
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::perfdhcp;
+
+
+/// \brief FakeReceiverPerfSocket class that mocks PerfSocket.
+///
+/// It stubs send and receive operations and collects statistics.
+class FakeReceiverPerfSocket: public BasePerfSocket {
+public:
+ /// \brief Default constructor for FakeReceiverPerfSocket.
+ FakeReceiverPerfSocket() :
+ iface_(boost::make_shared<Iface>("fake", 0)),
+ sent_cnt_(0),
+ recv_cnt_(0) {};
+
+ IfacePtr iface_; ///< Local fake interface.
+
+ int sent_cnt_; ///< Counter of sent packets
+ int recv_cnt_; ///< Counter of received packets.
+
+ /// \brief Simulate receiving DHCPv4 packet.
+ virtual dhcp::Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec) override {
+ (void)timeout_sec; // silence compile 'unused parameter' warning;
+ (void)timeout_usec; // silence compile 'unused parameter' warning;
+ recv_cnt_++;
+ // slow down receiving as receiver calls it in a loop thousands of time
+ // if null is returned
+ usleep(50);
+ return(dhcp::Pkt4Ptr());
+ };
+
+ /// \brief Simulate receiving DHCPv6 packet.
+ virtual dhcp::Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec) override {
+ (void)timeout_sec; // silence compile 'unused parameter' warning;
+ (void)timeout_usec; // silence compile 'unused parameter' warning;
+ recv_cnt_++;
+ return(dhcp::Pkt6Ptr());
+ };
+
+ /// \brief Simulate sending DHCPv4 packet.
+ virtual bool send(const dhcp::Pkt4Ptr& pkt) override {
+ sent_cnt_++;
+ pkt->updateTimestamp();
+ return true;
+ };
+
+ /// \brief Simulate sending DHCPv6 packet.
+ virtual bool send(const dhcp::Pkt6Ptr& pkt) override {
+ sent_cnt_++;
+ pkt->updateTimestamp();
+ return true;
+ };
+
+ /// \brief Override getting interface.
+ virtual IfacePtr getIface() override { return iface_; }
+
+ void reset() {
+ sent_cnt_ = 0;
+ recv_cnt_ = 0;
+ }
+};
+
+
+TEST(Receiver, singleThreaded) {
+ CommandOptions opt;
+ CommandOptionsHelper::process(opt, "perfdhcp -g single -l 127.0.0.1 all");
+ ASSERT_TRUE(opt.isSingleThreaded());
+
+ FakeReceiverPerfSocket socket;
+ Receiver receiver(socket, opt.isSingleThreaded(), opt.getIpVersion());
+
+ ASSERT_NO_THROW(receiver.start());
+
+ auto pkt = receiver.getPkt();
+
+ EXPECT_EQ(pkt, nullptr);
+
+ ASSERT_NO_THROW(receiver.stop());
+}
+
+
+TEST(Receiver, multiThreaded) {
+ CommandOptions opt;
+ CommandOptionsHelper::process(opt, "perfdhcp -g multi -l 127.0.0.1 all");
+ ASSERT_FALSE(opt.isSingleThreaded());
+
+ FakeReceiverPerfSocket socket;
+ Receiver receiver(socket, opt.isSingleThreaded(), opt.getIpVersion());
+
+ ASSERT_NO_THROW(receiver.start());
+
+ auto pkt = receiver.getPkt();
+
+ EXPECT_EQ(pkt, nullptr);
+
+ ASSERT_NO_THROW(receiver.stop());
+}
diff --git a/src/bin/perfdhcp/tests/run_unittests.cc b/src/bin/perfdhcp/tests/run_unittests.cc
new file mode 100644
index 0000000..628f79d
--- /dev/null
+++ b/src/bin/perfdhcp/tests/run_unittests.cc
@@ -0,0 +1,17 @@
+// Copyright (C) 2009-2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+
+int
+main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ return (isc::util::unittests::run_all());
+}
diff --git a/src/bin/perfdhcp/tests/stats_mgr_unittest.cc b/src/bin/perfdhcp/tests/stats_mgr_unittest.cc
new file mode 100644
index 0000000..54253d5
--- /dev/null
+++ b/src/bin/perfdhcp/tests/stats_mgr_unittest.cc
@@ -0,0 +1,598 @@
+// Copyright (C) 2012-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include "command_options_helper.h"
+
+#include <perfdhcp/stats_mgr.h>
+
+#include <exceptions/exceptions.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/pkt6.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/shared_ptr.hpp>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::perfdhcp;
+
+namespace {
+
+const uint32_t common_transid = 123;
+
+/// @brief Number of packets to be used for testing packets collecting.
+const size_t TEST_COLLECTED_PKT_NUM = 10;
+
+/// @brief DHCPv4 packet with modifiable internal values.
+///
+/// Currently the only additional modifiable value is a packet
+/// timestamp.
+class Pkt4Modifiable : public Pkt4 {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// @param msg_type DHCPv4 message type.
+ /// @param transid Transaction id.
+ Pkt4Modifiable(const uint8_t msg_type, const uint32_t transid)
+ : Pkt4(msg_type, transid) {
+ }
+
+ /// @brief Modifies packet timestamp.
+ ///
+ /// @param delta Number of seconds to be added to the current
+ /// packet time. If this number is negative, the new timestamp
+ /// will point to earlier time than the original timestamp.
+ void modifyTimestamp(const long delta) {
+ timestamp_ += boost::posix_time::seconds(delta);
+ }
+};
+
+/// @brief Pointer to the Pkt4Modifiable.
+typedef boost::shared_ptr<Pkt4Modifiable> Pkt4ModifiablePtr;
+
+class StatsMgrTest : public ::testing::Test {
+public:
+ StatsMgrTest() {
+ }
+
+ /// \brief Create DHCPv4 packet.
+ ///
+ /// Method creates DHCPv4 packet and updates its timestamp.
+ ///
+ /// \param msg_type DHCPv4 message type.
+ /// \param transid transaction id for the packet.
+ /// \return DHCPv4 packet.
+ Pkt4Modifiable* createPacket4(const uint8_t msg_type,
+ const uint32_t transid) {
+ Pkt4Modifiable* pkt = new Pkt4Modifiable(msg_type, transid);
+ // Packet timestamp is normally updated by interface
+ // manager on packets reception or send. Unit tests
+ // do not use interface manager so we need to do it
+ // ourselves.
+ pkt->updateTimestamp();
+ return (pkt);
+ }
+
+ /// \brief Create DHCPv6 packet.
+ ///
+ /// Method creates DHCPv6 packet and updates its timestamp.
+ ///
+ /// \param msg_type DHCPv6 message type.
+ /// \param transid transaction id.
+ /// \return DHCPv6 packet.
+ Pkt6* createPacket6(const uint8_t msg_type,
+ const uint32_t transid) {
+ Pkt6* pkt = new Pkt6(msg_type, transid);
+ // Packet timestamp is normally updated by interface
+ // manager on packets reception or send. Unit tests
+ // do not use interface manager so we need to do it
+ // ourselves.
+ pkt->updateTimestamp();
+ return pkt;
+ }
+
+ /// \brief Pass multiple DHCPv6 packets to Statistics Manager.
+ ///
+ /// Method simulates sending or receiving multiple DHCPv6 packets.
+ ///
+ /// \note The xchg_type parameter is passed as non-const value to avoid
+ /// false cppcheck errors which expect enum value being passed by reference.
+ /// This error is not reported when non-const enum is passed by value.
+ ///
+ /// \param stats_mgr Statistics Manager instance to be used.
+ /// \param xchg_type packet exchange types.
+ /// \param packet_type DHCPv6 packet type.
+ /// \param num_packets packets to be passed to Statistics Manager.
+ /// \param receive simulated packets are received (if true)
+ /// or sent (if false)
+ void passMultiplePackets6(const boost::shared_ptr<StatsMgr> stats_mgr,
+ ExchangeType xchg_type,
+ const uint8_t packet_type,
+ const int num_packets,
+ const bool receive = false) {
+ for (int i = 0; i < num_packets; ++i) {
+ boost::shared_ptr<Pkt6>
+ packet(createPacket6(packet_type, i));
+
+ if (receive) {
+ ASSERT_NO_THROW(
+ stats_mgr->passRcvdPacket(xchg_type, packet);
+ );
+ } else {
+ ASSERT_NO_THROW(
+ stats_mgr->passSentPacket(xchg_type, packet)
+ );
+ }
+ }
+ }
+
+ /// \brief Simulate DHCPv4 DISCOVER-OFFER with delay.
+ ///
+ /// Method simulates DHCPv4 DISCOVER-OFFER exchange. The OFFER packet
+ /// creation is delayed by the specified number of seconds. This imposes
+ /// different packet timestamps and affects delay counters in Statistics
+ /// Manager.
+ ///
+ /// \param stats_mgr Statistics Manager instance.
+ /// \param delay delay in seconds between DISCOVER and OFFER packets.
+ void passDOPacketsWithDelay(const boost::shared_ptr<StatsMgr> stats_mgr,
+ unsigned int delay,
+ uint32_t transid) {
+ boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
+ transid));
+ ASSERT_NO_THROW(
+ stats_mgr->passSentPacket(ExchangeType::DO, sent_packet)
+ );
+
+ // Simulate time difference by changing time of sent packet
+ auto ts = sent_packet->getTimestamp() - boost::posix_time::seconds(delay);
+ sent_packet->setTimestamp(ts);
+
+ boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPOFFER,
+ transid));
+ ASSERT_NO_THROW(
+ stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet);
+ );
+
+ // Calculate period between packets.
+ boost::posix_time::ptime sent_time = sent_packet->getTimestamp();
+ boost::posix_time::ptime rcvd_time = rcvd_packet->getTimestamp();
+
+ ASSERT_FALSE(sent_time.is_not_a_date_time());
+ ASSERT_FALSE(rcvd_time.is_not_a_date_time());
+ }
+
+ /// @brief This test verifies that timed out packets are collected.
+ ///
+ /// @param transid_index Index in the table of transaction ids which
+ /// points to the transaction id to be selected for the DHCPOFFER.
+ void testSendReceiveCollected(const size_t transid_index) {
+ CommandOptions opt;
+ boost::scoped_ptr<StatsMgr> stats_mgr(new StatsMgr(opt));
+ // The second parameter indicates that transactions older than
+ // 2 seconds should be removed and respective packets collected.
+ stats_mgr->addExchangeStats(ExchangeType::DO, 2);
+
+ // Transaction ids of packets to be sent. All transaction ids
+ // belong to the same bucket (match the transid & 1023 hashing
+ // function).
+ uint32_t transid[TEST_COLLECTED_PKT_NUM] =
+ { 1, 1025, 2049, 3073, 4097, 5121, 6145, 7169, 8193, 9217 };
+
+ // Simulate sending a number of packets.
+ for (unsigned int i = 0; i < TEST_COLLECTED_PKT_NUM; ++i) {
+ Pkt4ModifiablePtr sent_packet(createPacket4(DHCPDISCOVER,
+ transid[i]));
+ // For packets with low indexes we set the timestamps to
+ // 10s in the past. When DHCPOFFER is processed, the
+ // packets with timestamps older than 2s should be collected.
+ if (i < TEST_COLLECTED_PKT_NUM / 2) {
+ sent_packet->modifyTimestamp(-10);
+ }
+ ASSERT_NO_THROW(
+ stats_mgr->passSentPacket(ExchangeType::DO, sent_packet)
+ ) << "failure for transaction id " << transid[i];
+ }
+
+ // Create a server response for one of the packets sent.
+ Pkt4ModifiablePtr rcvd_packet(createPacket4(DHCPOFFER,
+ transid[transid_index]));
+ ASSERT_NO_THROW(
+ stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet);
+ );
+
+ // There is exactly one case (transaction id) for which perfdhcp
+ // will find a packet using ordered lookup. In this case, no
+ // packets will be collected. Otherwise, for any unordered lookup
+ // all packets from a bucket should be collected.
+ if (stats_mgr->getUnorderedLookups(ExchangeType::DO) > 0) {
+ // All packets in the bucket having transaction id
+ // index below TEST_COLLECTED_PKT_NUM / 2 should be removed.
+ EXPECT_EQ(TEST_COLLECTED_PKT_NUM / 2,
+ stats_mgr->getCollectedNum(ExchangeType::DO));
+ }
+
+ // Make sure that we can still use the StatsMgr. It is possible
+ // that the pointer to 'next sent' packet was invalidated
+ // during packet removal.
+ for (unsigned int i = 0; i < TEST_COLLECTED_PKT_NUM; ++i) {
+ // Increase transaction ids by 1 so as they don't duplicate
+ // with transaction ids of already sent packets.
+ Pkt4ModifiablePtr sent_packet(createPacket4(DHCPDISCOVER,
+ transid[i] + 1));
+ Pkt4ModifiablePtr rcvd_packet(createPacket4(DHCPOFFER,
+ transid[i] + 1));
+ ASSERT_NO_THROW(
+ stats_mgr->passSentPacket(ExchangeType::DO, sent_packet)
+ ) << "failure for transaction id " << transid[i];
+
+ ASSERT_NO_THROW(
+ stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet);
+ ) << "failure for transaction id " << transid[i];
+ }
+
+ // We should have processed TEST_COLLECTED_PKT_NUM but it is possible
+ // that one of them we couldn't match (orphan packet), because
+ // the matched packet had to be collected because of the transaction
+ // timeout. Therefore, we have to count both received packets and
+ // orphans.
+ EXPECT_EQ(TEST_COLLECTED_PKT_NUM + 1,
+ stats_mgr->getRcvdPacketsNum(ExchangeType::DO) +
+ stats_mgr->getOrphans(ExchangeType::DO));
+ }
+};
+
+TEST_F(StatsMgrTest, Constructor) {
+ CommandOptions opt;
+ boost::scoped_ptr<StatsMgr> stats_mgr(new StatsMgr(opt));
+ stats_mgr->addExchangeStats(ExchangeType::DO);
+ EXPECT_DOUBLE_EQ(
+ std::numeric_limits<double>::max(),
+ stats_mgr->getMinDelay(ExchangeType::DO)
+ );
+ EXPECT_DOUBLE_EQ(0, stats_mgr->getMaxDelay(ExchangeType::DO));
+ EXPECT_EQ(0, stats_mgr->getOrphans(ExchangeType::DO));
+ EXPECT_EQ(0, stats_mgr->getOrderedLookups(ExchangeType::DO));
+ EXPECT_EQ(0, stats_mgr->getUnorderedLookups(ExchangeType::DO));
+ EXPECT_EQ(0, stats_mgr->getSentPacketsNum(ExchangeType::DO));
+ EXPECT_EQ(0, stats_mgr->getRcvdPacketsNum(ExchangeType::DO));
+ EXPECT_EQ(0, stats_mgr->getCollectedNum(ExchangeType::DO));
+
+ EXPECT_THROW(stats_mgr->getAvgDelay(ExchangeType::DO), InvalidOperation);
+ EXPECT_THROW(stats_mgr->getStdDevDelay(ExchangeType::DO),
+ InvalidOperation);
+ EXPECT_THROW(stats_mgr->getAvgUnorderedLookupSetSize(ExchangeType::DO),
+ InvalidOperation);
+}
+
+TEST_F(StatsMgrTest, Exchange) {
+ CommandOptions opt;
+ boost::scoped_ptr<StatsMgr> stats_mgr(new StatsMgr(opt));
+ boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
+ common_transid));
+ boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPOFFER,
+ common_transid));
+ // This is expected to throw because XCHG_DO was not yet
+ // added to Stats Manager for tracking.
+ ASSERT_FALSE(stats_mgr->hasExchangeStats(ExchangeType::DO));
+ ASSERT_FALSE(stats_mgr->hasExchangeStats(ExchangeType::RA));
+ EXPECT_THROW(
+ stats_mgr->passSentPacket(ExchangeType::DO, sent_packet),
+ BadValue
+ );
+ EXPECT_THROW(
+ stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet),
+ BadValue
+ );
+
+
+ // Adding DISCOVER-OFFER exchanges to be tracked by Stats Manager.
+ stats_mgr->addExchangeStats(ExchangeType::DO);
+ ASSERT_TRUE(stats_mgr->hasExchangeStats(ExchangeType::DO));
+ ASSERT_FALSE(stats_mgr->hasExchangeStats(ExchangeType::RA));
+ // The following two attempts are expected to throw because
+ // invalid exchange types are passed (XCHG_RA instead of XCHG_DO)
+ EXPECT_THROW(
+ stats_mgr->passSentPacket(ExchangeType::RA, sent_packet),
+ BadValue
+ );
+ EXPECT_THROW(
+ stats_mgr->passRcvdPacket(ExchangeType::RA, rcvd_packet),
+ BadValue
+ );
+
+ // The following two attempts are expected to run fine because
+ // right exchange type is specified.
+ EXPECT_NO_THROW(
+ stats_mgr->passSentPacket(ExchangeType::DO, sent_packet)
+ );
+ EXPECT_NO_THROW(
+ stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet)
+ );
+}
+
+TEST_F(StatsMgrTest, MultipleExchanges) {
+ CommandOptions opt;
+ boost::shared_ptr<StatsMgr> stats_mgr(new StatsMgr(opt));
+ stats_mgr->addExchangeStats(ExchangeType::SA);
+ stats_mgr->addExchangeStats(ExchangeType::RR);
+
+ // Simulate sending number of solicit packets.
+ const int solicit_packets_num = 10;
+ passMultiplePackets6(stats_mgr, ExchangeType::SA, DHCPV6_SOLICIT,
+ solicit_packets_num);
+
+ // Simulate sending number of request packets. It is important that
+ // number of request packets is different then number of solicit
+ // packets. We can now check if right number packets went to
+ // the right exchange type group.
+ const int request_packets_num = 5;
+ passMultiplePackets6(stats_mgr, ExchangeType::RR, DHCPV6_REQUEST,
+ request_packets_num);
+
+ // Check if all packets are successfully passed to packet lists.
+ EXPECT_EQ(solicit_packets_num,
+ stats_mgr->getSentPacketsNum(ExchangeType::SA));
+ EXPECT_EQ(request_packets_num,
+ stats_mgr->getSentPacketsNum(ExchangeType::RR));
+
+ // Simulate reception of multiple packets for both SOLICIT-ADVERTISE
+ // and REQUEST-REPLY exchanges. Assume no packet drops.
+ const bool receive_packets = true;
+ passMultiplePackets6(stats_mgr, ExchangeType::SA, DHCPV6_ADVERTISE,
+ solicit_packets_num, receive_packets);
+
+ passMultiplePackets6(stats_mgr, ExchangeType::RR, DHCPV6_REPLY,
+ request_packets_num, receive_packets);
+
+ // Verify that all received packets are counted.
+ EXPECT_EQ(solicit_packets_num,
+ stats_mgr->getRcvdPacketsNum(ExchangeType::SA));
+ EXPECT_EQ(request_packets_num,
+ stats_mgr->getRcvdPacketsNum(ExchangeType::RR));
+}
+
+TEST_F(StatsMgrTest, SendReceiveSimple) {
+ CommandOptions opt;
+ boost::scoped_ptr<StatsMgr> stats_mgr(new StatsMgr(opt));
+ boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
+ common_transid));
+ boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPOFFER,
+ common_transid));
+ stats_mgr->addExchangeStats(ExchangeType::DO);
+ // The following attempt is expected to pass because the right
+ // exchange type is used.
+ ASSERT_NO_THROW(
+ stats_mgr->passSentPacket(ExchangeType::DO, sent_packet)
+ );
+ // It is ok, to pass to received packets here. First one will
+ // be matched with sent packet. The latter one will not be
+ // matched with sent packet but orphans counter will simply
+ // increase.
+ ASSERT_NO_THROW(
+ stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet)
+ );
+ ASSERT_NO_THROW(
+ stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet)
+ );
+ EXPECT_EQ(1, stats_mgr->getOrphans(ExchangeType::DO));
+}
+
+TEST_F(StatsMgrTest, SendReceiveUnordered) {
+ CommandOptions opt;
+ const int packets_num = 10;
+ boost::scoped_ptr<StatsMgr> stats_mgr(new StatsMgr(opt));
+ stats_mgr->addExchangeStats(ExchangeType::DO);
+
+ // Transaction ids of 10 packets to be sent and received.
+ uint32_t transid[packets_num] =
+ { 1, 1024, 2, 1025, 3, 1026, 4, 1027, 5, 1028 };
+ for (int i = 0; i < packets_num; ++i) {
+ boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
+ transid[i]));
+ ASSERT_NO_THROW(
+ stats_mgr->passSentPacket(ExchangeType::DO, sent_packet)
+ );
+ }
+
+ // We are simulating that received packets are coming in reverse order:
+ // 1028, 5, 1027 ....
+ for (int i = 0; i < packets_num; ++i) {
+ boost::shared_ptr<Pkt4>
+ rcvd_packet(createPacket4(DHCPDISCOVER,
+ transid[packets_num - 1 - i]));
+ ASSERT_NO_THROW(
+ stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet);
+ );
+ }
+ // All packets are expected to match (we did not drop any)
+ EXPECT_EQ(0, stats_mgr->getOrphans(ExchangeType::DO));
+ // Most of the time we have to do unordered lookups except for the last
+ // one. Packets are removed from the sent list every time we have a match
+ // so eventually we come up with the single packet that caching iterator
+ // is pointing to. This is counted as ordered lookup.
+ EXPECT_EQ(1, stats_mgr->getOrderedLookups(ExchangeType::DO));
+ EXPECT_EQ(9, stats_mgr->getUnorderedLookups(ExchangeType::DO));
+}
+
+TEST_F(StatsMgrTest, SendReceiveCollected) {
+ // Check that the packet collection mechanism works fine
+ // for any packet returned by the server.
+ for (unsigned int i = 0; i < TEST_COLLECTED_PKT_NUM; ++i) {
+ testSendReceiveCollected(i);
+ }
+}
+
+TEST_F(StatsMgrTest, Orphans) {
+ CommandOptions opt;
+ const int packets_num = 6;
+ boost::scoped_ptr<StatsMgr> stats_mgr(new StatsMgr(opt));
+ stats_mgr->addExchangeStats(ExchangeType::DO);
+
+ // We skip every second packet to simulate drops.
+ for (int i = 0; i < packets_num; i += 2) {
+ boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER, i));
+ ASSERT_NO_THROW(
+ stats_mgr->passSentPacket(ExchangeType::DO, sent_packet)
+ );
+ }
+ // We pass all received packets.
+ for (int i = 0; i < packets_num; ++i) {
+ boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPOFFER, i));
+ ASSERT_NO_THROW(
+ stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet);
+ );
+ }
+ // The half of received packets are expected not to have matching
+ // sent packet.
+ EXPECT_EQ(packets_num / 2, stats_mgr->getOrphans(ExchangeType::DO));
+}
+
+TEST_F(StatsMgrTest, Delays) {
+ CommandOptions opt;
+ boost::shared_ptr<StatsMgr> stats_mgr(new StatsMgr(opt));
+ stats_mgr->addExchangeStats(ExchangeType::DO, 5);
+
+ // Send DISCOVER, wait 2s and receive OFFER. This will affect
+ // counters in Stats Manager.
+ passDOPacketsWithDelay(stats_mgr, 2, common_transid);
+
+ // Initially min delay is equal to MAX_DOUBLE. After first packets
+ // are passed, it is expected to set to actual value.
+ EXPECT_LT(stats_mgr->getMinDelay(ExchangeType::DO),
+ std::numeric_limits<double>::max());
+ EXPECT_GT(stats_mgr->getMinDelay(ExchangeType::DO), 1);
+
+ // Max delay is supposed to the same value as minimum
+ // or maximum delay.
+ EXPECT_GT(stats_mgr->getMaxDelay(ExchangeType::DO), 1);
+
+ // Delay sums are now the same as minimum or maximum delay.
+ EXPECT_GT(stats_mgr->getAvgDelay(ExchangeType::DO), 1);
+
+ // Simulate another DISCOVER-OFFER exchange with delay between
+ // sent and received packets. Delay is now shorter than earlier
+ // so standard deviation of delay will now increase.
+ const unsigned int delay2 = 1;
+ passDOPacketsWithDelay(stats_mgr, delay2, common_transid + 1);
+ // Standard deviation is expected to be non-zero.
+ EXPECT_GT(stats_mgr->getStdDevDelay(ExchangeType::DO), 0);
+}
+
+TEST_F(StatsMgrTest, CustomCounters) {
+ CommandOptions opt;
+ boost::scoped_ptr<StatsMgr> stats_mgr(new StatsMgr(opt));
+
+ // Specify counter keys and names.
+ const std::string too_short_key("tooshort");
+ const std::string too_short_name("Too short packets");
+ const std::string too_late_key("toolate");
+ const std::string too_late_name("Packets sent too late");
+
+ // Add two custom counters.
+ stats_mgr->addCustomCounter(too_short_key, too_short_name);
+ stats_mgr->addCustomCounter(too_late_key, too_late_name);
+
+ // Increment one of the counters 10 times.
+ const uint64_t tooshort_num = 10;
+ for (uint64_t i = 0; i < tooshort_num; ++i) {
+ stats_mgr->incrementCounter(too_short_key);
+ }
+
+ // Increment another counter by 5 times.
+ const uint64_t toolate_num = 5;
+ for (uint64_t i = 0; i < toolate_num; ++i) {
+ stats_mgr->incrementCounter(too_late_key);
+ }
+
+ // Check counter's current value and name.
+ CustomCounterPtr tooshort_counter =
+ stats_mgr->getCounter(too_short_key);
+ EXPECT_EQ(too_short_name, tooshort_counter->getName());
+ EXPECT_EQ(tooshort_num, tooshort_counter->getValue());
+
+ // Check counter's current value and name.
+ CustomCounterPtr toolate_counter =
+ stats_mgr->getCounter(too_late_key);
+ EXPECT_EQ(too_late_name, toolate_counter->getName());
+ EXPECT_EQ(toolate_num, toolate_counter->getValue());
+
+}
+
+TEST_F(StatsMgrTest, PrintStats) {
+ std::cout << "This unit test is checking statistics printing "
+ << "capabilities. It is expected that some counters "
+ << "will be printed during this test. It may also "
+ << "cause spurious errors." << std::endl;
+ CommandOptions opt;
+ boost::shared_ptr<StatsMgr> stats_mgr(new StatsMgr(opt));
+ stats_mgr->addExchangeStats(ExchangeType::SA);
+
+ // Simulate sending and receiving one packet. Otherwise printing
+ // functions will complain about lack of packets.
+ const int packets_num = 1;
+ passMultiplePackets6(stats_mgr, ExchangeType::SA, DHCPV6_SOLICIT,
+ packets_num);
+ passMultiplePackets6(stats_mgr, ExchangeType::SA, DHCPV6_ADVERTISE,
+ packets_num, true);
+
+ // This function will print statistics even if packets are not
+ // archived because it relies on counters. There is at least one
+ // exchange needed to count the average delay and std deviation.
+ EXPECT_NO_THROW(stats_mgr->printStats());
+
+ // Printing timestamps is expected to fail because by default we
+ // disable packets archiving mode. Without packets we can't get
+ // timestamps.
+ EXPECT_THROW(stats_mgr->printTimestamps(), isc::InvalidOperation);
+
+ // Now, we create another statistics manager instance and enable timestamp
+ // printing, thus also enabling packets archiving mode.
+ CommandOptionsHelper::process(opt, "perfdhcp -x t 127.0.0.1");
+ stats_mgr.reset(new StatsMgr(opt));
+ stats_mgr->addExchangeStats(ExchangeType::SA);
+
+ // Timestamps should now get printed because packets have been preserved.
+ EXPECT_NO_THROW(stats_mgr->printTimestamps());
+
+ // Create another statistics manager instance and enable lease printing for
+ // v4, thus also enabling packets archiving mode.
+ CommandOptionsHelper::process(opt, "perfdhcp -4 -x l 127.0.0.1");
+ stats_mgr.reset(new StatsMgr(opt));
+ stats_mgr->addExchangeStats(ExchangeType::RNA);
+ stats_mgr->addExchangeStats(ExchangeType::RLA);
+ ASSERT_TRUE(stats_mgr->hasExchangeStats(ExchangeType::DO));
+ ASSERT_TRUE(stats_mgr->hasExchangeStats(ExchangeType::RA));
+ ASSERT_TRUE(stats_mgr->hasExchangeStats(ExchangeType::RNA));
+ ASSERT_TRUE(stats_mgr->hasExchangeStats(ExchangeType::RLA));
+
+ // Leases should now get printed because packets have been preserved.
+ EXPECT_NO_THROW(stats_mgr->printLeases());
+
+ // For v6 this time.
+ CommandOptionsHelper::process(opt, "perfdhcp -6 -x l 127.0.0.1");
+ stats_mgr.reset(new StatsMgr(opt));
+ stats_mgr->addExchangeStats(ExchangeType::RN);
+ stats_mgr->addExchangeStats(ExchangeType::RL);
+ ASSERT_TRUE(stats_mgr->hasExchangeStats(ExchangeType::SA));
+ ASSERT_TRUE(stats_mgr->hasExchangeStats(ExchangeType::RR));
+ ASSERT_TRUE(stats_mgr->hasExchangeStats(ExchangeType::RN));
+ ASSERT_TRUE(stats_mgr->hasExchangeStats(ExchangeType::RL));
+
+ // Leases should now get printed because packets have been preserved.
+ EXPECT_NO_THROW(stats_mgr->printLeases());
+}
+
+} // namespace
diff --git a/src/bin/perfdhcp/tests/test_control_unittest.cc b/src/bin/perfdhcp/tests/test_control_unittest.cc
new file mode 100644
index 0000000..1838a7b
--- /dev/null
+++ b/src/bin/perfdhcp/tests/test_control_unittest.cc
@@ -0,0 +1,1971 @@
+// Copyright (C) 2012-2023 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include "command_options_helper.h"
+#include "../test_control.h"
+
+#include <asiolink/io_address.h>
+#include <exceptions/exceptions.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcp/option_int.h>
+#include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_iaprefix.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+
+#include <algorithm>
+#include <cstddef>
+#include <stdint.h>
+#include <string>
+#include <vector>
+#include <fstream>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace boost::posix_time;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::perfdhcp;
+
+/// \brief FakePerfSocket class that mocks PerfSocket.
+///
+/// It stubs send and receive operations and collects statistics.
+class FakeTestControlPerfSocket: public BasePerfSocket {
+public:
+ /// \brief Default constructor for FakePerfSocket.
+ FakeTestControlPerfSocket() :
+ iface_(boost::make_shared<Iface>("fake", 0)),
+ sent_cnt_(0),
+ recv_cnt_(0) {};
+
+ IfacePtr iface_; ///< Local fake interface.
+
+ int sent_cnt_; ///< Counter of sent packets
+ int recv_cnt_; ///< Counter of received packets.
+
+ /// \brief Simulate receiving DHCPv4 packet.
+ virtual dhcp::Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec) override {
+ (void)timeout_sec; // silence compile 'unused parameter' warning;
+ (void)timeout_usec; // silence compile 'unused parameter' warning;
+ recv_cnt_++;
+ return(dhcp::Pkt4Ptr());
+ };
+
+ /// \brief Simulate receiving DHCPv6 packet.
+ virtual dhcp::Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec) override {
+ (void)timeout_sec; // silence compile 'unused parameter' warning;
+ (void)timeout_usec; // silence compile 'unused parameter' warning;
+ recv_cnt_++;
+ return(dhcp::Pkt6Ptr());
+ };
+
+ /// \brief Simulate sending DHCPv4 packet.
+ virtual bool send(const dhcp::Pkt4Ptr& pkt) override {
+ sent_cnt_++;
+ pkt->updateTimestamp();
+ sent_pkts4_.push_back(pkt);
+ return true;
+ };
+
+ /// \brief Simulate sending DHCPv6 packet.
+ virtual bool send(const dhcp::Pkt6Ptr& pkt) override {
+ sent_cnt_++;
+ pkt->updateTimestamp();
+ sent_pkts6_.push_back(pkt);
+ return true;
+ };
+
+ /// \brief Override getting interface.
+ virtual IfacePtr getIface() override { return iface_; }
+
+ void reset() {
+ sent_cnt_ = 0;
+ recv_cnt_ = 0;
+ }
+
+ std::vector<dhcp::Pkt4Ptr> sent_pkts4_; /// output v4 packets are stored here
+ std::vector<dhcp::Pkt6Ptr> sent_pkts6_; /// output v6 packets are stored here
+};
+
+
+/// \brief Test Control class with protected members made public.
+///
+/// This class makes protected TestControl class's members public
+/// to allow unit testing.
+class NakedTestControl: public TestControl {
+public:
+
+ /// \brief Incremental transaction id generator.
+ ///
+ /// This is incremental transaction id generator. It overrides
+ /// the default transaction id generator that generates transaction
+ /// ids using random function. This generator will generate values
+ /// like: 1,2,3 etc.
+ class IncrementalGenerator : public TestControl::NumberGenerator {
+ public:
+ /// \brief Default constructor.
+ IncrementalGenerator() :
+ NumberGenerator(),
+ transid_(0) {
+ }
+
+ /// \brief Generate unique transaction id.
+ ///
+ /// Generate unique transaction ids incrementally:
+ /// 1,2,3,4 etc.
+ ///
+ /// \return generated transaction id.
+ virtual uint32_t generate() {
+ return (++transid_);
+ }
+
+ /// \brief Return next transaction id value.
+ uint32_t getNext() const {
+ return (transid_ + 1);
+ }
+
+ private:
+ uint32_t transid_; ///< Last generated transaction id.
+ };
+
+ /// \brief Pointer to incremental generator.
+ typedef boost::shared_ptr<IncrementalGenerator> IncrementalGeneratorPtr;
+
+ using TestControl::createMessageFromReply;
+ using TestControl::createMessageFromAck;
+ using TestControl::factoryElapsedTime6;
+ using TestControl::factoryGeneric;
+ using TestControl::factoryIana6;
+ using TestControl::factoryOptionRequestOption6;
+ using TestControl::factoryRapidCommit6;
+ using TestControl::factoryRequestList4;
+ using TestControl::generateClientId;
+ using TestControl::generateDuid;
+ using TestControl::generateMacAddress;
+ using TestControl::getTemplateBuffer;
+ using TestControl::initPacketTemplates;
+ using TestControl::processReceivedPacket4;
+ using TestControl::processReceivedPacket6;
+ using TestControl::registerOptionFactories;
+ using TestControl::reset;
+ using TestControl::sendDiscover4;
+ using TestControl::sendRequest4;
+ using TestControl::sendPackets;
+ using TestControl::sendMultipleMessages4;
+ using TestControl::sendMultipleMessages6;
+ using TestControl::sendRequest6;
+ using TestControl::sendSolicit6;
+ using TestControl::setDefaults4;
+ using TestControl::setDefaults6;
+ using TestControl::socket_;
+ using TestControl::last_report_;
+ using TestControl::transid_gen_;
+ using TestControl::macaddr_gen_;
+ using TestControl::first_packet_serverid_;
+ using TestControl::interrupted_;
+ using TestControl::template_packets_v4_;
+ using TestControl::template_packets_v6_;
+ using TestControl::ack_storage_;
+ using TestControl::sendMessageFromAck;
+ using TestControl::options_;
+ using TestControl::stats_mgr_;
+
+ FakeTestControlPerfSocket fake_sock_;
+
+ NakedTestControl(CommandOptions &opt) : TestControl(opt, fake_sock_) {
+ uint32_t clients_num = opt.getClientsNum() == 0 ?
+ 1 : opt.getClientsNum();
+ setMacAddrGenerator(NumberGeneratorPtr(new TestControl::SequentialGenerator(clients_num)));
+ };
+};
+
+
+/// \brief Test Fixture Class
+///
+/// This test fixture class is used to perform
+/// unit tests on perfdhcp TestControl class.
+class TestControlTest : public virtual ::testing::Test
+{
+public:
+
+ typedef std::vector<uint8_t> MacAddress;
+ typedef MacAddress::iterator MacAddressIterator;
+
+ typedef std::vector<uint8_t> Duid;
+ typedef Duid::iterator DuidIterator;
+
+ /// \brief Default Constructor
+ TestControlTest() { }
+
+ /// \brief Create packet template file from binary data.
+ ///
+ /// Function creates file containing data from the provided buffer
+ /// in hexadecimal format. The size parameter specifies the maximum
+ /// size of the file. If total number of hexadecimal digits resulting
+ /// from buffer size is greater than maximum file size the file is
+ /// truncated.
+ ///
+ /// \param filename template file to be created.
+ /// \param buffer with binary data to be stored in file.
+ /// \param size target size of the file.
+ /// \param invalid_chars inject invalid chars to the template file.
+ /// \return true if file creation successful.
+ bool createTemplateFile(const std::string& filename,
+ const std::vector<uint8_t>& buf,
+ const size_t size,
+ const bool invalid_chars = false) const {
+ std::ofstream temp_file;
+ temp_file.open(filename.c_str(), ios::out | ios::trunc);
+ if (!temp_file.is_open()) {
+ return (false);
+ }
+ for (size_t i = 0; i < buf.size(); ++i) {
+ int first_digit = buf[i] / 16;
+ int second_digit = buf[i] % 16;
+ // Insert two spaces between two hexadecimal digits.
+ // Spaces are allowed in template files.
+ temp_file << std::string(2, ' ');
+ if (2 * i + 1 < size) {
+ if (!invalid_chars) {
+ temp_file << std::hex << first_digit << second_digit << std::dec;
+ } else {
+ temp_file << "XY";
+ }
+ } else if (2 * i < size) {
+ if (!invalid_chars) {
+ temp_file << std::hex << first_digit;
+ } else {
+ temp_file << "X";
+ }
+ } else {
+ break;
+ }
+ }
+ temp_file.close();
+ return (true);
+ }
+
+ /// \brief Get full path to a file in testdata directory.
+ ///
+ /// \param filename filename being appended to absolute
+ /// path to testdata directory
+ ///
+ /// \return full path to a file in testdata directory.
+ std::string getFullPath(const std::string& filename) const {
+ std::ostringstream stream;
+ stream << TEST_DATA_DIR << "/" << filename;
+ return (stream.str());
+ }
+
+ /// \brief Match requested options in the buffer with given list.
+ ///
+ /// This method iterates through options provided in the buffer
+ /// and matches them with the options specified with first parameter.
+ /// Options in both vectors may be laid in different order.
+ ///
+ /// \param requested_options reference buffer with options.
+ /// \param buf test buffer with options that will be matched.
+ /// \return number of options from the buffer matched with options
+ /// in the reference buffer.
+ int matchRequestedOptions(const dhcp::OptionBuffer& requested_options,
+ const dhcp::OptionBuffer& buf) const {
+ size_t matched_num = 0;
+ for (size_t i = 0; i < buf.size(); ++i) {
+ for (size_t j = 0; j < requested_options.size(); ++j) {
+ if (requested_options[j] == buf[i]) {
+ // Requested option has been found.
+ ++matched_num;
+ }
+ }
+ }
+ return (matched_num);
+ }
+
+ /// \brief Match requested DHCPv6 options in the buffer with given list.
+ ///
+ /// This method iterates through options provided in the buffer and
+ /// matches them with the options specified with first parameter.
+ /// Options in both vectors ma be laid in different order.
+ ///
+ /// \param requested_options reference buffer with options.
+ /// \param buf test buffer with options that will be matched.
+ /// \return number of options from the buffer matched with options in
+ /// the reference buffer or -1 if error occurred.
+ int matchRequestedOptions6(const dhcp::OptionBuffer& requested_options,
+ const dhcp::OptionBuffer& buf) const {
+ // Sanity check.
+ if ((requested_options.size() % 2 != 0) ||
+ (buf.size() % 2 != 0)) {
+ return -1;
+ }
+ size_t matched_num = 0;
+ for (size_t i = 0; i < buf.size(); i += 2) {
+ for (size_t j = 0; j < requested_options.size(); j += 2) {
+ uint16_t opt_i = (buf[i + 1] << 8) + (buf[i] & 0xFF);
+ uint16_t opt_j = (requested_options[j + 1] << 8)
+ + (requested_options[j] & 0xFF);
+ if (opt_i == opt_j) {
+ // Requested option has been found.
+ ++matched_num;
+ }
+ }
+ }
+ return (matched_num);
+ }
+
+ /// \brief Calculate the maximum vectors' mismatch position.
+ ///
+ /// This helper function calculates the maximum mismatch position
+ /// between two vectors (two different DUIDs or MAC addresses).
+ /// Calculated position is counted from the end of vectors.
+ /// Calculation is based on number of simulated clients. When number
+ /// of clients is less than 256 different DUIDs or MAC addresses can
+ /// can be coded in such a way that they differ on last vector element.
+ /// If number of clients is between 257 and 65536 they can differ
+ /// on two last positions so the returned value will be 2 and so on.
+ ///
+ /// \param clients_num number of simulated clients
+ /// \return maximum mismatch position
+ int unequalOctetPosition(int clients_num) const {
+ if (!clients_num) {
+ return (0);
+ }
+ clients_num--;
+
+ int cnt = 0;
+ while (clients_num) {
+ clients_num >>= 8;
+ ++cnt;
+ }
+
+ return (cnt);
+ }
+
+ /// \brief Test generation of multiple DUIDs
+ ///
+ /// This method checks the generation of multiple DUIDs. Number
+ /// of iterations depends on the number of simulated clients.
+ /// It is expected that DUID's size is 14 (consists of DUID-LLT
+ /// HW type field, 4 octets of time value and MAC address). The
+ /// MAC address can be randomized depending on the number of
+ /// simulated clients. The DUID-LLT and HW type are expected to
+ /// be constant. The time value has to be properly calculated
+ /// as the number of seconds since DUID time epoch. The parts
+ /// of MAC address has to change if multiple clients are simulated
+ /// and do not change if single client is simulated.
+ void testDuid(CommandOptions &opt) const {
+ int clients_num = opt.getClientsNum();
+ // Initialize Test Control class.
+ NakedTestControl tc(opt);
+ // The old duid will be holding the previously generated DUID.
+ // It will be used to compare against the new one. If we have
+ // multiple clients we want to make sure that duids differ.
+ uint8_t randomized = 0;
+ Duid old_duid(tc.generateDuid(randomized));
+ Duid new_duid(0);
+ // total_dist shows the total difference between generated duid.
+ // It has to be greater than zero if multiple clients are simulated.
+ size_t total_dist = 0;
+ // Number of unique DUIDs.
+ size_t unique_duids = 0;
+ // Holds the position if the octet on which two DUIDS can be different.
+ // If number of clients is 256 or less it is last DUID octet (except for
+ // single client when subsequent DUIDs have to be equal). If number of
+ // clients is between 257 and 65536 the last two octets can differ etc.
+ int unequal_pos = unequalOctetPosition(clients_num);
+ // Keep generated DUIDs in this container.
+ std::list<std::vector<uint8_t> > duids;
+ // Perform number of iterations to generate number of DUIDs.
+ for (int i = 0; i < 10 * clients_num; ++i) {
+ if (new_duid.empty()) {
+ new_duid = old_duid;
+ } else {
+ std::swap(old_duid, new_duid);
+ new_duid = tc.generateDuid(randomized);
+ }
+ // The DUID-LLT is expected to start with DUID_LLT value
+ // of 1 and hardware ethernet type equal to 1 (HWETHER_TYPE).
+ const uint8_t duid_llt_and_hw[4] = { 0x0, 0x1, 0x0, 0x1 };
+ // We assume DUID-LLT length 14. This includes 4 octets of
+ // DUID_LLT value, two octets of hardware type, 4 octets
+ // of time value and 6 octets of variable link layer (MAC)
+ // address.
+ const int duid_llt_size = 14;
+ ASSERT_EQ(duid_llt_size, new_duid.size());
+ // The first four octets do not change.
+ EXPECT_TRUE(std::equal(new_duid.begin(), new_duid.begin() + 4,
+ duid_llt_and_hw));
+
+ // As described in RFC 8415: 'the time value is the time
+ // that the DUID is generated represented in seconds
+ // since midnight (UTC), January 1, 2000, modulo 2^32.'
+ uint32_t duid_time = 0;
+ // Pick 4 bytes of the time from generated DUID and put them
+ // in reverse order (in DUID they are stored in network order).
+ for (int j = 4; j < 8; ++j) {
+ duid_time |= new_duid[j] << (j - 4);
+ }
+ // Calculate the duration since epoch time.
+ ptime now = microsec_clock::universal_time();
+ ptime duid_epoch(from_iso_string("20000101T000000"));
+ time_period period(duid_epoch, now);
+
+ // Current time is the same or later than time from the DUID because
+ // DUID had been generated before reference duration was calculated.
+ EXPECT_GE(period.length().total_seconds(), duid_time);
+
+ // Get the mismatch position (counting from the end) of
+ // mismatched octet between previously generated DUID
+ // and current.
+ std::pair<DuidIterator, DuidIterator> mismatch_pos =
+ std::mismatch(old_duid.begin(), old_duid.end(),
+ new_duid.begin());
+ size_t mismatch_dist =
+ std::distance(mismatch_pos.first, old_duid.end());
+ // For single client total_dist is expected to be 0 because
+ // old_duid and new_duid should always match. If we have
+ // more clients then duids have to differ except the case
+ // if randomization algorithm generates the same values but
+ // this would be an error in randomization algorithm.
+ total_dist += mismatch_dist;
+ // Mismatch may have occurred on the DUID octet position
+ // up to calculated earlier unequal_pos.
+ ASSERT_LE(mismatch_dist, unequal_pos);
+ // unique will inform if tested DUID is unique.
+ bool unique = true;
+ for (std::list<std::vector<uint8_t> >::const_iterator it =
+ duids.begin();
+ it != duids.end(); ++it) {
+ // DUIDs should be of the same size if we want to compare them.
+ ASSERT_EQ(new_duid.size(), it->size());
+ // Check if DUID is unique.
+ if (std::equal(new_duid.begin(), new_duid.end(), it->begin())) {
+ unique = false;
+ }
+ }
+ // Expecting that DUIDs will be unique only when
+ // first clients-num iterations is performed.
+ // After that, DUIDs become non unique.
+ if (unique) {
+ ++unique_duids;
+ }
+ // For number of iterations equal to clients_num,2*clients_num
+ // 3*clients_num ... we have to have number of unique duids
+ // equal to clients_num.
+ if ((i != 0) && (i % clients_num == 0)) {
+ ASSERT_EQ(clients_num, unique_duids);
+ }
+ // Remember generated DUID.
+ duids.push_back(new_duid);
+ }
+ // If we have more than one client at least one mismatch occurred.
+ if (clients_num < 2) {
+ EXPECT_EQ(0, total_dist);
+ }
+ }
+
+ /// \brief Test DHCPv4 exchanges.
+ ///
+ /// Function simulates DHCPv4 exchanges. Function caller specifies
+ /// number of exchanges to be simulated and number of simulated
+ /// responses. When number of responses is lower than number of
+ /// iterations than the difference between them is the number
+ /// of simulated packet drops. This is useful to test if program
+ /// exit conditions are handled properly (maximum number of packet
+ /// drops specified as -D<max-drops> is taken into account).
+ ///
+ /// \param iterations_num number of exchanges to simulate.
+ /// \param receive_num number of received OFFER packets.
+ /// \param tc test control instance
+ void testPkt4Exchange(int iterations_num,
+ int receive_num,
+ bool use_templates,
+ NakedTestControl& tc) const {
+ //int sock_handle = 0;
+
+ // Use templates files to crate packets.
+ if (use_templates) {
+ tc.initPacketTemplates();
+ tc.getTemplateBuffer(0);
+ tc.getTemplateBuffer(1);
+ }
+
+ // Incremental transaction id generator will generate
+ // predictable values of transaction id for each iteration.
+ // This is important because we need to simulate responses
+ // from the server and use the same transaction ids as in
+ // packets sent by client.
+ NakedTestControl::IncrementalGeneratorPtr
+ generator(new NakedTestControl::IncrementalGenerator());
+ tc.setTransidGenerator(generator);
+ for (int i = 0; i < iterations_num; ++i) {
+ // Get next transaction id, without actually using it. The same
+ // id will be used by the TestControl class for DHCPDISCOVER.
+ uint32_t transid = generator->getNext();
+ if (use_templates) {
+ tc.sendDiscover4(tc.getTemplateBuffer(0));
+ } else {
+ tc.sendDiscover4();
+ }
+
+ // Do not simulate responses for packets later
+ // that specified as receive_num. This simulates
+ // packet drops.
+ if (i < receive_num) {
+ boost::shared_ptr<Pkt4> offer_pkt4(createOfferPkt4(transid));
+ tc.processReceivedPacket4(offer_pkt4);
+ }
+ }
+ }
+
+ /// \brief Test DHCPv6 exchanges.
+ ///
+ /// Function simulates DHCPv6 exchanges. Function caller specifies
+ /// number of exchanges to be simulated and number of simulated
+ /// responses. When number of responses is lower than number of
+ /// iterations than the difference between them is the number
+ /// of simulated packet drops. This is useful to test if program
+ /// exit conditions are handled properly (maximum number of packet
+ /// drops specified as -D<max-drops> is taken into account).
+ ///
+ /// \param iterations_num number of exchanges to simulate.
+ /// \param receive_num number of received OFFER packets.
+ /// \param tc test control instance
+ void testPkt6Exchange(int iterations_num,
+ int receive_num,
+ bool use_templates,
+ NakedTestControl& tc) const {
+ //int sock_handle = 0;
+
+ // Use templates files to crate packets.
+ if (use_templates) {
+ tc.initPacketTemplates();
+ tc.getTemplateBuffer(0);
+ tc.getTemplateBuffer(1);
+ }
+
+ // Incremental transaction id generator will generate
+ // predictable values of transaction id for each iteration.
+ // This is important because we need to simulate responses
+ // from the server and use the same transaction ids as in
+ // packets sent by client.
+ TestControl::NumberGeneratorPtr
+ generator(new NakedTestControl::IncrementalGenerator());
+ tc.setTransidGenerator(generator);
+ uint32_t transid = 0;
+ for (int i = 0; i < iterations_num; ++i) {
+ // Do not simulate responses for packets later
+ // that specified as receive_num. This simulates
+ // packet drops.
+ if (use_templates) {
+ tc.sendSolicit6(tc.getTemplateBuffer(0));
+ } else {
+ tc.sendSolicit6();
+ }
+ ++transid;
+ if (i < receive_num) {
+ boost::shared_ptr<Pkt6>
+ advertise_pkt6(createAdvertisePkt6(tc, transid));
+ // Receive ADVERTISE and send REQUEST.
+ tc.processReceivedPacket6(advertise_pkt6);
+ ++transid;
+ }
+ }
+ }
+
+ /// \brief Test generation of multiple MAC addresses.
+ ///
+ /// This method validates generation of multiple MAC addresses.
+ /// The MAC address can be randomized depending on the number
+ /// of simulated clients. This test checks if different MAC
+ /// addresses are generated if number of simulated clients is
+ /// greater than 1. It also checks if the same MAC addresses is
+ /// generated if only 1 client is simulated.
+ void testMacAddress(CommandOptions &opt) const {
+ int clients_num = opt.getClientsNum();
+ // The old_mac will be holding the value of previously generated
+ // MAC address. We will be comparing the newly generated one with it
+ // to see if it changes when multiple clients are simulated or if it
+ // does not change when single client is simulated.
+ MacAddress old_mac(opt.getMacTemplate());
+ // Holds the position if the octet on which two MAC addresses can
+ // be different. If number of clients is 256 or less it is last MAC
+ // octet (except for single client when subsequent MAC addresses
+ // have to be equal). If number of clients is between 257 and 65536
+ // the last two octets can differ etc.
+ int unequal_pos = unequalOctetPosition(clients_num);
+ // Number of unique MACs.
+ size_t unique_macs = 0;
+ // Initialize Test Controller.
+ NakedTestControl tc(opt);
+ size_t total_dist = 0;
+ // Keep generated MACs in this container.
+ std::list<std::vector<uint8_t> > macs;
+ // Do many iterations to generate and test MAC address values.
+ for (int i = 0; i < clients_num * 10; ++i) {
+ // Generate new MAC address.
+ uint8_t randomized = 0;
+ MacAddress new_mac(tc.generateMacAddress(randomized));
+ // Get the mismatch position (counting from the end) of
+ // mismatched octet between previously generated MAC address
+ // and current.
+ std::pair<MacAddressIterator, MacAddressIterator> mismatch_pos =
+ std::mismatch(old_mac.begin(), old_mac.end(), new_mac.begin());
+ size_t mismatch_dist =
+ std::distance(mismatch_pos.first, old_mac.end());
+ // For single client total_dist is expected to be 0 because
+ // old_mac and new_mac should always match. If we have
+ // more clients then MAC addresses have to differ except
+ // the case if randomization algorithm generates the same
+ // values but this would be an error in randomization algorithm.
+ total_dist += mismatch_dist;
+ // Mismatch may have occurred on the MAC address's octet position
+ // up to calculated earlier unequal_pos.
+ ASSERT_LE(mismatch_dist, unequal_pos);
+ // unique will inform if tested DUID is unique.
+ bool unique = true;
+ for (std::list<std::vector<uint8_t> >::const_iterator it =
+ macs.begin();
+ it != macs.end(); ++it) {
+ // MACs should be of the same size if we want to compare them.
+ ASSERT_EQ(new_mac.size(), it->size());
+ // Check if MAC is unique.
+ if (std::equal(new_mac.begin(), new_mac.end(), it->begin())) {
+ unique = false;
+ }
+ }
+ // Expecting that MACs will be unique only when
+ // first clients-num iterations is performed.
+ // After that, MACs become non unique.
+ if (unique) {
+ ++unique_macs;
+ }
+ // For number of iterations equal to clients_num,2*clients_num
+ // 3*clients_num ... we have to have number of unique MACs
+ // equal to clients_num.
+ if ((i != 0) && (i % clients_num == 0)) {
+ ASSERT_EQ(clients_num, unique_macs);
+ }
+ // Remember generated MAC.
+ macs.push_back(new_mac);
+
+ }
+ if (clients_num < 2) {
+ EXPECT_EQ(total_dist, 0);
+ }
+ }
+
+ /// \brief Test sending DHCPv4 renews.
+ ///
+ /// This function simulates acquiring 10 leases from the server. Returned
+ /// DHCPACK messages are cached and used to send renew messages.
+ /// The maximal number of messages which can be sent is equal to the
+ /// number of leases acquired (10). This function also checks that an
+ /// attempt to send more renew messages than the number of leases acquired
+ /// will fail.
+ ///
+ /// \param msg_type A type of the message which is simulated to be sent
+ /// (DHCPREQUEST in renew state or DHCPRELEASE).
+ void testSendRenewRelease4(const uint16_t msg_type) {
+ // Build a command line. Depending on the message type, we will use
+ // -f<renew-rate> or -F<release-rate> parameter.
+ CommandOptions opt;
+ std::ostringstream s;
+ s << "perfdhcp -4 -l fake -r 10 ";
+ s << (msg_type == DHCPREQUEST ? "-f" : "-F");
+ s << " 10 -R 10 -L 10067 -n 10 127.0.0.1";
+ processCmdLine(opt, s.str());
+ // Create a test controller class.
+ NakedTestControl tc(opt);
+ // Set the transaction id generator to sequential to control to
+ // guarantee that transaction ids are predictable.
+ boost::shared_ptr<NakedTestControl::IncrementalGenerator>
+ generator(new NakedTestControl::IncrementalGenerator());
+ tc.setTransidGenerator(generator);
+
+ // Send a number of DHCPDISCOVER messages. Each generated message will
+ // be assigned a different transaction id, starting from 1 to 10.
+ tc.sendPackets(10);
+
+ // Simulate DHCPOFFER responses from the server. Each DHCPOFFER is
+ // assigned a transaction id from the range of 1 to 10, so as they
+ // match the transaction ids from the DHCPDISCOVER messages.
+ for (unsigned i = generator->getNext() - 10;
+ i < generator->getNext(); ++i) {
+ Pkt4Ptr offer(createOfferPkt4(i));
+ // If DHCPOFFER is matched with the DHCPDISCOVER the call below
+ // will trigger a corresponding DHCPREQUEST. They will be assigned
+ // transaction ids from the range from 11 to 20 (the range of
+ // 1 to 10 has been used by DHCPDISCOVER-DHCPOFFER).
+ tc.processReceivedPacket4(offer);
+ }
+
+ // Requests have been sent, so now let's simulate responses from the
+ // server. Generate corresponding DHCPACK messages with the transaction
+ // ids from the range from 11 to 20.
+ for (unsigned i = generator->getNext() - 10;
+ i < generator->getNext(); ++i) {
+ Pkt4Ptr ack(createAckPkt4(i));
+ // Each DHCPACK packet corresponds to the new lease acquired. Since
+ // -f<renew-rate> option has been specified, received Reply
+ // messages are held so as renew messages can be sent for
+ // existing leases.
+ tc.processReceivedPacket4(ack);
+ }
+
+ uint64_t msg_num;
+ // Try to send 5 messages. It should be successful because 10
+ // DHCPREQUEST messages has been received. For each of them we
+ // should be able to send renewal.
+ msg_num = tc.sendMultipleMessages4(msg_type, 5);
+ // Make sure that we have sent 5 messages.
+ EXPECT_EQ(5, msg_num);
+
+ // Try to do it again. We should still have 5 Reply packets for
+ // which renews haven't been sent yet.
+ msg_num = tc.sendMultipleMessages4(msg_type, 5);
+ EXPECT_EQ(5, msg_num);
+
+ // We used all the DHCPACK packets (we sent renew or release for each of
+ // them already). Therefore, no further renew messages should be sent
+ // before we acquire new leases.
+ msg_num = tc.sendMultipleMessages4(msg_type, 5);
+ // Make sure that no message has been sent.
+ EXPECT_EQ(0, msg_num);
+ }
+
+ /// \brief Test that the DHCPREQUEST message is created correctly and
+ /// comprises expected values.
+ ///
+ /// \param msg_type A type of the message to be tested:
+ /// DHCPREQUEST in renew state or DHCPRELEASE.
+ void testCreateRenewRelease4(const uint16_t msg_type) {
+ // This command line specifies that the Release/Renew messages should
+ // be sent with the same rate as the Solicit messages.
+ CommandOptions opt;
+ std::ostringstream s;
+ s << "perfdhcp -4 -l lo -r 10 ";
+ s << (msg_type == DHCPREQUEST ? "-F" : "-f") << " 10";
+ s << " -R 10 -L 10067 -n 10 127.0.0.1";
+ processCmdLine(opt, s.str());
+ // Create a test controller class.
+ NakedTestControl tc(opt);
+ // Set the transaction id generator which will be used by the
+ // createRenew or createRelease function to generate transaction id.
+ boost::shared_ptr<NakedTestControl::IncrementalGenerator>
+ generator(new NakedTestControl::IncrementalGenerator());
+ tc.setTransidGenerator(generator);
+
+ Pkt4Ptr ack = createAckPkt4(1);
+
+ // Create DHCPREQUEST from DHCPACK.
+ Pkt4Ptr msg;
+ msg = tc.createMessageFromAck(msg_type, ack);
+
+ // Make sure that the DHCPACK has been successfully created and that
+ // it holds expected data.
+ ASSERT_TRUE(msg);
+ EXPECT_EQ("127.0.0.1", msg->getCiaddr().toText());
+
+ // HW address.
+ HWAddrPtr hwaddr_ack = ack->getHWAddr();
+ ASSERT_TRUE(hwaddr_ack);
+ HWAddrPtr hwaddr_req = msg->getHWAddr();
+ ASSERT_TRUE(hwaddr_req);
+ EXPECT_TRUE(hwaddr_ack->hwaddr_ == hwaddr_req->hwaddr_);
+
+ // Creating message from null DHCPACK should fail.
+ EXPECT_THROW(tc.createMessageFromAck(msg_type, Pkt4Ptr()), isc::BadValue);
+
+ // Creating message from DHCPACK holding zero yiaddr should fail.
+ asiolink::IOAddress yiaddr = ack->getYiaddr();
+ ack->setYiaddr(asiolink::IOAddress::IPV4_ZERO_ADDRESS());
+ EXPECT_THROW(tc.createMessageFromAck(msg_type, ack), isc::BadValue);
+ ack->setYiaddr(yiaddr);
+ }
+
+ /// \brief Test that the DHCPv6 Release or Renew message is created
+ /// correctly and comprises expected options.
+ ///
+ /// \param msg_type A type of the message to be tested: DHCPV6_RELEASE
+ /// or DHCPV6_RENEW.
+ void testCreateRenewRelease6(const uint16_t msg_type) {
+ // This command line specifies that the Release/Renew messages should
+ // be sent with the same rate as the Solicit messages.
+ CommandOptions opt;
+ std::ostringstream s;
+ s << "perfdhcp -6 -l lo -r 10 ";
+ s << (msg_type == DHCPV6_RELEASE ? "-F" : "-f") << " 10 ";
+ s << "-R 10 -L 10547 -n 10 -e address-and-prefix ::1";
+ processCmdLine(opt, s.str());
+ // Create a test controller class.
+ NakedTestControl tc(opt);
+ // Set the transaction id generator which will be used by the
+ // createRenew or createRelease function to generate transaction id.
+ boost::shared_ptr<NakedTestControl::IncrementalGenerator>
+ generator(new NakedTestControl::IncrementalGenerator());
+ tc.setTransidGenerator(generator);
+
+ // Create a Reply packet. The createRelease or createReply function will
+ // need Reply packet to create a corresponding Release or Reply.
+ Pkt6Ptr reply = createReplyPkt6(tc, 1);
+
+ Pkt6Ptr msg;
+ // Check that the message is created.
+ msg = tc.createMessageFromReply(msg_type, reply);
+
+ ASSERT_TRUE(msg);
+ // Check that the message type and transaction id is correct.
+ EXPECT_EQ(msg_type, msg->getType());
+ EXPECT_EQ(1, msg->getTransid());
+
+ // Check that the message has expected options. These are the same for
+ // Release and Renew.
+
+ // Client Identifier.
+ OptionPtr opt_clientid = msg->getOption(D6O_CLIENTID);
+ ASSERT_TRUE(opt_clientid);
+ EXPECT_TRUE(reply->getOption(D6O_CLIENTID)->getData() ==
+ opt_clientid->getData());
+
+ // Server identifier
+ OptionPtr opt_serverid = msg->getOption(D6O_SERVERID);
+ ASSERT_TRUE(opt_serverid);
+ EXPECT_TRUE(reply->getOption(D6O_SERVERID)->getData() ==
+ opt_serverid->getData());
+
+ // IA_NA
+ OptionPtr opt_ia_na = msg->getOption(D6O_IA_NA);
+ ASSERT_TRUE(opt_ia_na);
+ EXPECT_TRUE(reply->getOption(D6O_IA_NA)->getData() ==
+ opt_ia_na->getData());
+
+ // IA_PD
+ OptionPtr opt_ia_pd = msg->getOption(D6O_IA_PD);
+ ASSERT_TRUE(opt_ia_pd);
+ EXPECT_TRUE(reply->getOption(D6O_IA_PD)->getData() ==
+ opt_ia_pd->getData());
+
+ // Make sure that exception is thrown if the Reply message is NULL.
+ EXPECT_THROW(tc.createMessageFromReply(msg_type, Pkt6Ptr()),
+ isc::BadValue);
+
+ }
+
+ /// \brief Test sending DHCPv6 Releases or Renews.
+ ///
+ /// This function simulates acquiring 10 leases from the server. Returned
+ /// Reply messages are cached and used to send Renew or Release messages.
+ /// The maximal number of Renew or Release messages which can be sent is
+ /// equal to the number of leases acquired (10). This function also checks
+ /// that an attempt to send more Renew or Release messages than the number
+ /// of leases acquired will fail.
+ ///
+ /// \param msg_type A type of the message which is simulated to be sent
+ /// (DHCPV6_RENEW or DHCPV6_RELEASE).
+ void testSendRenewRelease6(const uint16_t msg_type) {
+ // Build a command line. Depending on the message type, we will use
+ // -f<renew-rate> or -F<release-rate> parameter.
+ CommandOptions opt;
+ std::ostringstream s;
+ s << "perfdhcp -6 -l fake -r 10 ";
+ s << (msg_type == DHCPV6_RENEW ? "-f" : "-F");
+ s << " 10 -R 10 -L 10547 -n 10 ::1";
+ processCmdLine(opt, s.str());
+ // Create a test controller class.
+ NakedTestControl tc(opt);
+ // Set the transaction id generator to sequential to control to
+ // guarantee that transaction ids are predictable.
+ boost::shared_ptr<NakedTestControl::IncrementalGenerator>
+ generator(new NakedTestControl::IncrementalGenerator());
+ tc.setTransidGenerator(generator);
+
+ // Send a number of Solicit messages. Each generated Solicit will be
+ // assigned a different transaction id, starting from 1 to 10.
+ tc.sendPackets(10);
+
+ // Simulate Advertise responses from the server. Each advertise is
+ // assigned a transaction id from the range of 1 to 10, so as they
+ // match the transaction ids from the Solicit messages.
+ for (unsigned i = generator->getNext() - 10;
+ i < generator->getNext(); ++i) {
+ Pkt6Ptr advertise(createAdvertisePkt6(tc, i));
+ // If Advertise is matched with the Solicit the call below will
+ // trigger a corresponding Request. They will be assigned
+ // transaction ids from the range from 11 to 20 (the range of
+ // 1 to 10 has been used by Solicit-Advertise).
+ tc.processReceivedPacket6(advertise);
+ }
+
+ // Requests have been sent, so now let's simulate responses from the
+ // server. Generate corresponding Reply messages with the transaction
+ // ids from the range from 11 to 20.
+ for (unsigned i = generator->getNext() - 10;
+ i < generator->getNext(); ++i) {
+ Pkt6Ptr reply(createReplyPkt6(tc, i));
+ // Each Reply packet corresponds to the new lease acquired. Since
+ // -f<renew-rate> option has been specified, received Reply
+ // messages are held so as Renew messages can be sent for
+ // existing leases.
+ tc.processReceivedPacket6(reply);
+ }
+
+ uint64_t msg_num;
+ // Try to send 5 messages. It should be successful because 10 Reply
+ // messages has been received. For each of them we should be able to
+ // send Renew or Release.
+ msg_num = tc.sendMultipleMessages6(msg_type, 5);
+ // Make sure that we have sent 5 messages.
+ EXPECT_EQ(5, msg_num);
+
+ // Try to do it again. We should still have 5 Reply packets for
+ // which Renews or Releases haven't been sent yet.
+ msg_num = tc.sendMultipleMessages6(msg_type, 5);
+ EXPECT_EQ(5, msg_num);
+
+ // We used all the Reply packets (we sent Renew or Release for each of
+ // them already). Therefore, no further Renew or Release messages should
+ // be sent before we acquire new leases.
+ msg_num = tc.sendMultipleMessages6(msg_type, 5);
+ // Make sure that no message has been sent.
+ EXPECT_EQ(0, msg_num);
+
+ }
+
+ /// \brief Test counting rejected leases in Solicit-Advertise.
+ ///
+ /// This function simulates acquiring 4 leases from the server and
+ /// rejecting allocating of 6 leases
+
+ void testCountRejectedLeasesSolAdv() {
+ // Build a command line.
+ CommandOptions opt;
+ std::ostringstream s;
+ s << "perfdhcp -6 -l fake -r 10 -R 10 -L 10547 -n 10 ::1";
+ processCmdLine(opt, s.str());
+ // Create a test controller class.
+ NakedTestControl tc(opt);
+ // Set the transaction id generator to sequential to control to
+ // guarantee that transaction ids are predictable.
+ boost::shared_ptr<NakedTestControl::IncrementalGenerator>
+ generator(new NakedTestControl::IncrementalGenerator());
+ tc.setTransidGenerator(generator);
+
+ // Send a number of Solicit messages. Each generated Solicit will be
+ // assigned a different transaction id, starting from 1 to 10.
+ tc.sendPackets(10);
+
+ // Simulate Advertise responses from the server. Each advertise is
+ // assigned a transaction id from the range of 1 to 6 with incorrect IA
+ // included in the message
+ for (uint32_t i = generator->getNext() - 10; i < 7; ++i) {
+ Pkt6Ptr advertise(createAdvertisePkt6(tc, i, false));
+ tc.processReceivedPacket6(advertise);
+ }
+ // counter of rejected leases has to be 6
+ EXPECT_EQ(tc.stats_mgr_.getRejLeasesNum(ExchangeType::SA), 6);
+ // Simulate Advertise responses from the server. Each advertise is
+ // assigned a transaction id from the range of 7 to 10 with correct IA
+ // included in the message
+ for (uint32_t i = generator->getNext() - 7; i < 11; ++i) {
+ Pkt6Ptr advertise(createAdvertisePkt6(tc, i));
+ tc.processReceivedPacket6(advertise);
+ }
+ // counter of rejected leases can't change at this point
+ EXPECT_EQ(tc.stats_mgr_.getRejLeasesNum(ExchangeType::SA), 6);
+ }
+
+ /// \brief Parse command line string with CommandOptions.
+ ///
+ /// \param cmdline command line string to be parsed.
+ /// \throw isc::Unexpected if unexpected error occurred.
+ /// \throw isc::InvalidParameter if command line is invalid.
+ void processCmdLine(CommandOptions &opt, const std::string& cmdline) const {
+ CommandOptionsHelper::process(opt, cmdline);
+ }
+
+ /// \brief Create DHCPOFFER or DHCPACK packet.
+ ///
+ /// \param pkt_type DHCPOFFER or DHCPACK.
+ /// \param transid Transaction id.
+ ///
+ /// \return Instance of the packet.
+ Pkt4Ptr
+ createResponsePkt4(const uint8_t pkt_type,
+ const uint32_t transid) const {
+ Pkt4Ptr pkt(new Pkt4(pkt_type, transid));
+ OptionPtr opt_serverid = Option::factory(Option::V4,
+ DHO_DHCP_SERVER_IDENTIFIER,
+ OptionBuffer(4, 1));
+ pkt->setYiaddr(asiolink::IOAddress("127.0.0.1"));
+ pkt->addOption(opt_serverid);
+ pkt->updateTimestamp();
+ return (pkt);
+ }
+
+ /// \brief Create DHCPv4 OFFER packet.
+ ///
+ /// \param transid transaction id.
+ /// \return instance of the packet.
+ Pkt4Ptr
+ createOfferPkt4(uint32_t transid) const {
+ return (createResponsePkt4(DHCPOFFER, transid));
+ }
+
+ /// \brief Create DHCPACK packet.
+ ///
+ /// \param transid transaction id.
+ /// \return instance of the packet.
+ Pkt4Ptr
+ createAckPkt4(const uint32_t transid) const {
+ return (createResponsePkt4(DHCPACK, transid));
+ }
+
+ /// \brief Create DHCPv6 ADVERTISE packet.
+ ///
+ /// \param transid transaction id.
+ /// \return instance of the packet.
+ Pkt6Ptr
+ createAdvertisePkt6(NakedTestControl &tc, const uint32_t transid,
+ const bool validIA = true) const {
+ boost::shared_ptr<Pkt6> advertise(new Pkt6(DHCPV6_ADVERTISE, transid));
+ // Add IA_NA if requested by the client.
+ if (tc.options_.getLeaseType().includes(CommandOptions::LeaseType::ADDRESS)) {
+ OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA);
+ if (validIA) {
+ OptionPtr iaaddr(new Option6IAAddr(D6O_IAADDR,
+ isc::asiolink::IOAddress("fe80::abcd"), 300, 500));
+ opt_ia_na->addOption(iaaddr);
+ }
+ advertise->addOption(opt_ia_na);
+ }
+ // Add IA_PD if requested by the client.
+ if (tc.options_.getLeaseType().includes(CommandOptions::LeaseType::PREFIX)) {
+ OptionPtr opt_ia_pd = Option::factory(Option::V6, D6O_IA_PD);
+ if (validIA) {
+ OptionPtr iapref(new Option6IAPrefix(D6O_IAPREFIX,
+ isc::asiolink::IOAddress("fe80::"), 64, 300, 500));
+ opt_ia_pd->addOption(iapref);
+ }
+ advertise->addOption(opt_ia_pd);
+ }
+ OptionPtr opt_serverid(new Option(Option::V6, D6O_SERVERID));
+ uint8_t randomized = 0;
+ std::vector<uint8_t> duid(tc.generateDuid(randomized));
+ OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid));
+ advertise->addOption(opt_serverid);
+ advertise->addOption(opt_clientid);
+ advertise->updateTimestamp();
+ return (advertise);
+ }
+
+ Pkt6Ptr
+ createReplyPkt6(NakedTestControl &tc, const uint32_t transid,
+ const bool validIA = true) const {
+ Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, transid));
+ // Add IA_NA if requested by the client.
+ if (tc.options_.getLeaseType().includes(CommandOptions::LeaseType::ADDRESS)) {
+ OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA);
+ if (validIA) {
+ OptionPtr iaaddr(new Option6IAAddr(D6O_IAADDR,
+ isc::asiolink::IOAddress("fe80::abcd"), 300, 500));
+ opt_ia_na->addOption(iaaddr);
+ }
+ reply->addOption(opt_ia_na);
+ }
+ // Add IA_PD if requested by the client.
+ if (tc.options_.getLeaseType().includes(CommandOptions::LeaseType::PREFIX)) {
+ OptionPtr opt_ia_pd = Option::factory(Option::V6, D6O_IA_PD);
+ if (validIA) {
+ OptionPtr iapref(new Option6IAPrefix(D6O_IAPREFIX,
+ isc::asiolink::IOAddress("fe80::"), 64, 300, 500));
+ opt_ia_pd->addOption(iapref);
+ }
+ reply->addOption(opt_ia_pd);
+ }
+ OptionPtr opt_serverid(new Option(Option::V6, D6O_SERVERID));
+ uint8_t randomized = 0;
+ std::vector<uint8_t> duid(tc.generateDuid(randomized));
+ OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid));
+ reply->addOption(opt_serverid);
+ reply->addOption(opt_clientid);
+ reply->updateTimestamp();
+ return (reply);
+
+ }
+
+ /// @brief Check presence and content of v4 options 55.
+ ///
+ /// \param pkt packet to be checked
+ /// \param expected_option_requests only these option requests should be
+ /// found under option 55 in the packet, nothing more, nothing less
+ void checkOptions55(Pkt4Ptr const& pkt,
+ vector<uint8_t> const& expected_option_requests) {
+ // Sanity checks
+ ASSERT_TRUE(pkt);
+ OptionPtr const& opt(pkt->getOption(55));
+ ASSERT_TRUE(opt);
+ EXPECT_TRUE(opt->getUniverse() == Option::V4);
+
+ // Create the text of the expected option.
+ string const length(to_string(expected_option_requests.size()));
+ string const buffer(
+ TestControl::vector2Hex(expected_option_requests, ":"));
+ string const expected_option_text(boost::str(
+ boost::format("type=055, len=%03u: %s") % length % buffer));
+
+ // Compare.
+ EXPECT_EQ(opt->toText(), expected_option_text);
+ }
+
+ /// @brief check if v4 options 200 and 201 are present.
+ ///
+ /// The options are expected to have specific format, as if parameters
+ /// -o 200,abcdef1234, -o 201,00 were passed to the command line.
+ void checkOptions20x(const Pkt4Ptr& pkt) {
+ ASSERT_TRUE(pkt);
+ OptionPtr opt = pkt->getOption(200);
+ ASSERT_TRUE(opt);
+ EXPECT_TRUE(opt->getUniverse() == Option::V4);
+ EXPECT_EQ(opt->toText(), "type=200, len=005: ab:cd:ef:12:34");
+
+ opt = pkt->getOption(201);
+ ASSERT_TRUE(opt);
+ EXPECT_EQ(opt->toText(), "type=201, len=001: 00");
+ }
+
+ /// @brief check if v6 options 200 and 201 are present.
+ ///
+ /// The options are expected to have specific format, as if parameters
+ /// -o 200,abcdef1234, -o 201,00 were passed to the command line.
+ void checkOptions20x(const Pkt6Ptr& pkt) {
+ ASSERT_TRUE(pkt);
+ OptionPtr opt = pkt->getOption(200);
+ ASSERT_TRUE(opt);
+ EXPECT_TRUE(opt->getUniverse() == Option::V6);
+ EXPECT_EQ(opt->toText(), "type=00200, len=00005: ab:cd:ef:12:34");
+
+ opt = pkt->getOption(201);
+ ASSERT_TRUE(opt);
+ EXPECT_EQ(opt->toText(), "type=00201, len=00001: 00");
+ }
+
+};
+
+// This test verifies that the class members are reset to expected values.
+TEST_F(TestControlTest, reset) {
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -4 127.0.0.1");
+ NakedTestControl tc(opt);
+ tc.reset();
+ EXPECT_FALSE(tc.last_report_.is_not_a_date_time());
+ EXPECT_FALSE(tc.transid_gen_);
+ EXPECT_FALSE(tc.macaddr_gen_);
+ EXPECT_TRUE(tc.first_packet_serverid_.empty());
+ EXPECT_FALSE(tc.interrupted_);
+
+}
+
+// This test verifies that the client id is generated from the HW address.
+TEST_F(TestControlTest, generateClientId) {
+ // Generate HW address.
+ std::vector<uint8_t> hwaddr;
+ for (unsigned int i = 0; i < 6; ++i) {
+ hwaddr.push_back(i);
+ }
+ HWAddrPtr hwaddr_ptr(new HWAddr(hwaddr, 5));
+
+ // Use generated HW address to generate client id.
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -4 127.0.0.1");
+ NakedTestControl tc(opt);
+ OptionPtr opt_client_id;
+ opt_client_id = tc.generateClientId(hwaddr_ptr);
+ ASSERT_TRUE(opt_client_id);
+
+ // Extract the client id data.
+ const OptionBuffer& client_id = opt_client_id->getData();
+ ASSERT_EQ(7, client_id.size());
+
+ // Verify that the client identifier is generated correctly.
+
+ // First byte is the HW type.
+ EXPECT_EQ(5, client_id[0]);
+ // The rest of the client identifier should be equal to the HW address.
+ std::vector<uint8_t> sub(client_id.begin() + 1, client_id.end());
+ EXPECT_TRUE(hwaddr == sub);
+}
+
+TEST_F(TestControlTest, GenerateDuid) {
+ // Simple command line that simulates one client only. Always the
+ // same DUID will be generated.
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -l 127.0.0.1 all");
+ testDuid(opt);
+
+ // Simulate 50 clients. Different DUID will be generated.
+ processCmdLine(opt, "perfdhcp -l 127.0.0.1 -R 50 all");
+ testDuid(opt);
+
+ // Checks that the random mac address returned by generateDuid
+ // is in the list of mac addresses in the mac-list.txt data file
+ std::string mac_list_full_path = getFullPath("mac-list.txt");
+ std::ostringstream cmd;
+ cmd << "perfdhcp -M " << mac_list_full_path << " 127.0.0.1";
+ processCmdLine(opt, cmd.str());
+ // Initialize Test Controller.
+ NakedTestControl tc(opt);
+ uint8_t randomized = 0;
+ std::vector<uint8_t> generated_duid = tc.generateDuid(randomized);
+
+ // Check that generated_duid is DUID_LL
+ ASSERT_EQ(10, generated_duid.size());
+ DuidPtr duid(new DUID(generated_duid));
+ ASSERT_EQ(duid->getType(), DUID::DUID_LL);
+
+ // Make sure it's on the list
+ const CommandOptions::MacAddrsVector& macs = opt.getMacsFromFile();
+ // DUID LL comprises 2 bytes of duid type, 2 bytes of hardware type,
+ // then 6 bytes of HW address.
+ vector<uint8_t> mac(6);
+ std::copy(generated_duid.begin() + 4, generated_duid.begin() + 10,
+ mac.begin());
+ // Check that mac is in macs.
+ ASSERT_TRUE(std::find(macs.begin(), macs.end(), mac) != macs.end());
+}
+
+TEST_F(TestControlTest, GenerateMacAddress) {
+ CommandOptions opt;
+ // Simulate one client only. Always the same MAC address will be
+ // generated.
+ processCmdLine(opt, "perfdhcp -l 127.0.0.1 all");
+ testMacAddress(opt);
+
+ // Simulate 50 clients. Different MAC addresses will be generated.
+ processCmdLine(opt, "perfdhcp -l 127.0.0.1 -R 50 all");
+ testMacAddress(opt);
+
+ // Checks that the random mac address returned by generateMacAddress
+ // is in the list of mac addresses in the mac-list.txt data file
+ std::string mac_list_full_path = getFullPath("mac-list.txt");
+ std::ostringstream cmd;
+ cmd << "perfdhcp -M " << mac_list_full_path << " 127.0.0.1";
+ processCmdLine(opt, cmd.str());
+ // Initialize Test Controller.
+ NakedTestControl tc(opt);
+ uint8_t randomized = 0;
+ // Generate MAC address and sanity check its size.
+ std::vector<uint8_t> mac = tc.generateMacAddress(randomized);
+ ASSERT_EQ(6, mac.size());
+ // Make sure that the generated MAC address belongs to the MAC addresses
+ // read from a file.
+ const CommandOptions::MacAddrsVector& macs = opt.getMacsFromFile();
+ ASSERT_TRUE(std::find(macs.begin(), macs.end(), mac) != macs.end());
+}
+
+TEST_F(TestControlTest, Options4) {
+ using namespace isc::dhcp;
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -4 127.0.0.1");
+ NakedTestControl tc(opt);
+ // By default the IP version mode is V4 so there is no need to
+ // parse command line to override the IP version. Note that
+ // registerOptionFactories is used for both V4 and V6.
+ tc.registerOptionFactories();
+ // Create option with buffer size equal to 1 and holding DHCPDISCOVER
+ // message type.
+ OptionPtr opt_msg_type(Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE,
+ OptionBuffer(1, DHCPDISCOVER)));
+ // Validate the option type and universe.
+ EXPECT_EQ(Option::V4, opt_msg_type->getUniverse());
+ EXPECT_EQ(DHO_DHCP_MESSAGE_TYPE, opt_msg_type->getType());
+ // Validate the message type from the option we have now created.
+ uint8_t msg_type = 0;
+ msg_type = opt_msg_type->getUint8();
+ EXPECT_EQ(DHCPDISCOVER, msg_type);
+
+ // Create another option: DHCP_PARAMETER_REQUEST_LIST
+ OptionPtr
+ opt_requested_options(Option::factory(Option::V4,
+ DHO_DHCP_PARAMETER_REQUEST_LIST));
+ // Here is a list of options that we are requesting in the
+ // server's response.
+ const uint8_t requested_options[] = {
+ DHO_SUBNET_MASK,
+ DHO_BROADCAST_ADDRESS,
+ DHO_TIME_OFFSET,
+ DHO_ROUTERS,
+ DHO_DOMAIN_NAME,
+ DHO_DOMAIN_NAME_SERVERS,
+ DHO_HOST_NAME
+ };
+
+ OptionBuffer
+ requested_options_ref(requested_options,
+ requested_options + sizeof(requested_options));
+
+ // Get the option buffer. It should hold the combination of values
+ // listed in requested_options array. However their order can be
+ // different in general so we need to search each value separately.
+ const OptionBuffer& requested_options_buf =
+ opt_requested_options->getData();
+ EXPECT_EQ(requested_options_ref.size(), requested_options_buf.size());
+ size_t matched_num = matchRequestedOptions(requested_options_ref,
+ requested_options_buf);
+ // We want exactly the same requested options as listed in
+ // requested_options array - nothing more or less.
+ EXPECT_EQ(requested_options_ref.size(), matched_num);
+}
+
+TEST_F(TestControlTest, Options6) {
+ using namespace isc::dhcp;
+ CommandOptions opt;
+
+ // Lets override the IP version to test V6 options (-6 parameter)
+ processCmdLine(opt, "perfdhcp -l lo -6 ::1");
+
+ NakedTestControl tc(opt);
+ tc.registerOptionFactories();
+
+ // Validate the D6O_ELAPSED_TIME option.
+ OptionPtr opt_elapsed_time(Option::factory(Option::V6, D6O_ELAPSED_TIME));
+ // Validate the option type and universe.
+ EXPECT_EQ(Option::V6, opt_elapsed_time->getUniverse());
+ EXPECT_EQ(D6O_ELAPSED_TIME, opt_elapsed_time->getType());
+ // The default value of elapsed time is zero.
+ uint16_t elapsed_time;
+ elapsed_time = opt_elapsed_time->getUint16();
+ EXPECT_EQ(0, elapsed_time);
+
+ // With the factory function we may also specify the actual
+ // value of elapsed time. Let's make use of std::vector
+ // constructor to create the option buffer, 2 octets long
+ // with each octet initialized to 0x1.
+ size_t elapsed_time_buf_size = 2;
+ uint8_t elapsed_time_pattern = 0x1;
+ OptionPtr
+ opt_elapsed_time2(Option::factory(Option::V6, D6O_ELAPSED_TIME,
+ OptionBuffer(elapsed_time_buf_size,
+ elapsed_time_pattern)));
+
+ // Any buffer that has size neither equal to 0 nor 2 is considered invalid.
+ elapsed_time_buf_size = 1;
+ EXPECT_THROW(
+ Option::factory(Option::V6, D6O_ELAPSED_TIME,
+ OptionBuffer(elapsed_time_buf_size, elapsed_time_pattern)),
+ isc::BadValue
+ );
+
+ // Validate the option type and universe.
+ EXPECT_EQ(Option::V6, opt_elapsed_time2->getUniverse());
+ EXPECT_EQ(D6O_ELAPSED_TIME, opt_elapsed_time2->getType());
+ // Make sure the getUint16 does not throw exception. It wile throw
+ // buffer is shorter than 2 octets.
+ elapsed_time = opt_elapsed_time2->getUint16();
+ // Check the expected value of elapsed time.
+ EXPECT_EQ(0x0101, elapsed_time);
+
+ // Validate the D6O_RAPID_COMMIT option.
+ OptionPtr opt_rapid_commit(Option::factory(Option::V6, D6O_RAPID_COMMIT));
+ // Validate the option type and universe.
+ EXPECT_EQ(Option::V6, opt_rapid_commit->getUniverse());
+ EXPECT_EQ(D6O_RAPID_COMMIT, opt_rapid_commit->getType());
+ // Rapid commit has no data payload.
+ EXPECT_THROW(opt_rapid_commit->getUint8(), isc::OutOfRange);
+
+ // Validate the D6O_CLIENTID option.
+ OptionBuffer duid(opt.getDuidTemplate());
+ OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid));
+ EXPECT_EQ(Option::V6, opt_clientid->getUniverse());
+ EXPECT_EQ(D6O_CLIENTID, opt_clientid->getType());
+ const OptionBuffer& duid2 = opt_clientid->getData();
+ ASSERT_EQ(duid.size(), duid2.size());
+ // The Duid we set for option is the same we get.
+ EXPECT_TRUE(std::equal(duid.begin(), duid.end(), duid2.begin()));
+
+ // Validate the D6O_ORO (Option Request Option).
+ OptionPtr opt_oro(Option::factory(Option::V6, D6O_ORO));
+ // Prepare the reference buffer with requested options.
+ const uint8_t requested_options[] = {
+ 0, D6O_NAME_SERVERS,
+ 0, D6O_DOMAIN_SEARCH
+ };
+ // Each option code in ORO is 2 bytes long. We calculate the number of
+ // requested options by dividing the size of the buffer holding options
+ // by the size of each individual option.
+ int requested_options_num = sizeof(requested_options) / sizeof(uint16_t);
+ OptionBuffer
+ requested_options_ref(requested_options,
+ requested_options + sizeof(requested_options));
+ // Get the buffer from option.
+ const OptionBuffer& requested_options_buf = opt_oro->getData();
+ // Size of reference buffer and option buffer have to be
+ // the same for comparison.
+ EXPECT_EQ(requested_options_ref.size(), requested_options_buf.size());
+ // Check if all options in the buffer are matched with reference buffer.
+ size_t matched_num = matchRequestedOptions6(requested_options_ref,
+ requested_options_buf);
+ EXPECT_EQ(requested_options_num, matched_num);
+
+ // Validate the D6O_IA_NA option.
+ OptionPtr opt_ia_na(Option::factory(Option::V6, D6O_IA_NA));
+ EXPECT_EQ(Option::V6, opt_ia_na->getUniverse());
+ EXPECT_EQ(D6O_IA_NA, opt_ia_na->getType());
+ // Every IA_NA option is expected to start with this sequence.
+ const uint8_t opt_ia_na_array[] = {
+ 0, 0, 0, 1, // IAID = 1
+ 0, 0, 3600 >> 8, 3600 & 0xff, // T1 = 3600
+ 0, 0, 5400 >> 8, 5400 & 0xff, // T2 = 5400
+ };
+ OptionBuffer opt_ia_na_ref(opt_ia_na_array,
+ opt_ia_na_array + sizeof(opt_ia_na_array));
+ const OptionBuffer& opt_ia_na_buf = opt_ia_na->getData();
+ ASSERT_EQ(opt_ia_na_buf.size(), opt_ia_na_ref.size());
+ EXPECT_TRUE(std::equal(opt_ia_na_ref.begin(), opt_ia_na_ref.end(),
+ opt_ia_na_buf.begin()));
+
+ // @todo Add more tests for IA address options.
+}
+
+TEST_F(TestControlTest, Packet4) {
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -l fake -L 10547 all");
+ NakedTestControl tc(opt);
+ uint32_t transid = 123;
+ boost::shared_ptr<Pkt4> pkt4(new Pkt4(DHCPDISCOVER, transid));
+ // Set parameters on outgoing packet.
+ tc.setDefaults4(pkt4);
+ // Validate that packet has been setup correctly.
+ EXPECT_EQ(tc.fake_sock_.iface_->getName(), pkt4->getIface());
+ EXPECT_EQ(tc.fake_sock_.ifindex_, pkt4->getIndex());
+ EXPECT_EQ(DHCP4_CLIENT_PORT, pkt4->getLocalPort());
+ EXPECT_EQ(DHCP4_SERVER_PORT, pkt4->getRemotePort());
+ EXPECT_EQ(1, pkt4->getHops());
+ EXPECT_EQ(asiolink::IOAddress("255.255.255.255"),
+ pkt4->getRemoteAddr());
+ EXPECT_EQ(asiolink::IOAddress(tc.socket_.addr_), pkt4->getLocalAddr());
+ EXPECT_EQ(asiolink::IOAddress(tc.socket_.addr_), pkt4->getGiaddr());
+}
+
+TEST_F(TestControlTest, Packet6) {
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -6 -l fake -L 10547 servers");
+ NakedTestControl tc(opt);
+ uint32_t transid = 123;
+ boost::shared_ptr<Pkt6> pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
+ // Set packet's parameters.
+ tc.setDefaults6(pkt6);
+ // Validate if parameters have been set correctly.
+ EXPECT_EQ(tc.fake_sock_.iface_->getName(), pkt6->getIface());
+ EXPECT_EQ(tc.socket_.ifindex_, pkt6->getIndex());
+ EXPECT_EQ(DHCP6_CLIENT_PORT, pkt6->getLocalPort());
+ EXPECT_EQ(DHCP6_SERVER_PORT, pkt6->getRemotePort());
+ EXPECT_EQ(tc.socket_.addr_, pkt6->getLocalAddr());
+ EXPECT_EQ(asiolink::IOAddress("FF05::1:3"), pkt6->getRemoteAddr());
+ // Packet must not be relayed.
+ EXPECT_TRUE(pkt6->relay_info_.empty());
+}
+
+TEST_F(TestControlTest, Packet6Relayed) {
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -6 -l fake -A1 -L 10547 servers");
+ NakedTestControl tc(opt);
+ uint32_t transid = 123;
+ boost::shared_ptr<Pkt6> pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
+ // Set packet's parameters.
+ tc.setDefaults6(pkt6);
+ // Validate if parameters have been set correctly.
+ EXPECT_EQ(tc.fake_sock_.iface_->getName(), pkt6->getIface());
+ EXPECT_EQ(tc.socket_.ifindex_, pkt6->getIndex());
+ EXPECT_EQ(DHCP6_CLIENT_PORT, pkt6->getLocalPort());
+ EXPECT_EQ(DHCP6_SERVER_PORT, pkt6->getRemotePort());
+ EXPECT_EQ(tc.socket_.addr_, pkt6->getLocalAddr());
+ EXPECT_EQ(asiolink::IOAddress("FF05::1:3"), pkt6->getRemoteAddr());
+ // Packet should be relayed.
+ EXPECT_EQ(pkt6->relay_info_.size(), 1);
+ EXPECT_EQ(pkt6->relay_info_[0].hop_count_, 0);
+ EXPECT_EQ(pkt6->relay_info_[0].msg_type_, DHCPV6_RELAY_FORW);
+ EXPECT_EQ(pkt6->relay_info_[0].linkaddr_, tc.socket_.addr_);
+ EXPECT_EQ(pkt6->relay_info_[0].peeraddr_, tc.socket_.addr_);
+}
+
+TEST_F(TestControlTest, Packet6RelayedWithRelayOpts) {
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -6 -l fake -A1 --or 1:32,00000E10 -L 10547 servers");
+ NakedTestControl tc(opt);
+ uint32_t transid = 123;
+ boost::shared_ptr<Pkt6> pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
+ // Set packet's parameters.
+ tc.setDefaults6(pkt6);
+ // Validate if parameters have been set correctly.
+ EXPECT_EQ(tc.fake_sock_.iface_->getName(), pkt6->getIface());
+ EXPECT_EQ(tc.socket_.ifindex_, pkt6->getIndex());
+ EXPECT_EQ(DHCP6_CLIENT_PORT, pkt6->getLocalPort());
+ EXPECT_EQ(DHCP6_SERVER_PORT, pkt6->getRemotePort());
+ EXPECT_EQ(tc.socket_.addr_, pkt6->getLocalAddr());
+ EXPECT_EQ(asiolink::IOAddress("FF05::1:3"), pkt6->getRemoteAddr());
+ // Packet should be relayed.
+ EXPECT_EQ(pkt6->relay_info_.size(), 1);
+ EXPECT_EQ(pkt6->relay_info_[0].hop_count_, 0);
+ EXPECT_EQ(pkt6->relay_info_[0].msg_type_, DHCPV6_RELAY_FORW);
+ EXPECT_EQ(pkt6->relay_info_[0].linkaddr_, tc.socket_.addr_);
+ EXPECT_EQ(pkt6->relay_info_[0].peeraddr_, tc.socket_.addr_);
+ // Checking if relayed option is there.
+ OptionBuffer opt_data = pkt6->relay_info_[0].options_.find(32)->second->getData();
+ EXPECT_EQ(4, opt_data.size());
+ EXPECT_EQ("0x00000E10", pkt6->relay_info_[0].options_.find(32)->second->toHexString());
+}
+
+TEST_F(TestControlTest, Packet4Exchange) {
+ const int iterations_num = 100;
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -l fake -r 100 -n 10 -R 20 -L 10547 127.0.0.1");
+ bool use_templates = false;
+ NakedTestControl tc(opt);
+ testPkt4Exchange(iterations_num, iterations_num, use_templates, tc);
+
+ EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num * 2); // Discovery + Request
+ EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::DO), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::DO), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RA), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RA), 0);
+}
+
+TEST_F(TestControlTest, Packet4ExchangeFromTemplate) {
+ const int iterations_num = 100;
+ CommandOptions opt;
+
+ processCmdLine(opt, "perfdhcp -l fake -r 100 -R 20 -n 20 -L 10547"
+ " -T " + getFullPath("discover-example.hex")
+ + " -T " + getFullPath("request4-example.hex")
+ + " 127.0.0.1");
+ const int received_num = 10;
+ bool use_templates = true;
+ NakedTestControl tc(opt);
+ testPkt4Exchange(iterations_num, received_num, use_templates, tc);
+
+ EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num + received_num); // Discovery + Request
+ EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::DO), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::DO), received_num);
+ EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RA), received_num);
+ EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RA), 0);
+}
+
+TEST_F(TestControlTest, Packet6Exchange) {
+ const int iterations_num = 100;
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -l fake -6 -r 100 -n 10 -R 20 -L 10547 ::1");
+ bool use_templates = false;
+ NakedTestControl tc(opt);
+ testPkt6Exchange(iterations_num, iterations_num, use_templates, tc);
+
+ EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num * 2); // Solicit + Request
+ EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::SA), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::SA), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RR), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RR), 0);
+}
+
+TEST_F(TestControlTest, Packet6ExchangeFromTemplate) {
+ const int iterations_num = 100;
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -l fake -6 -r 100 -n 10 -R 20 -L 10547"
+ " -T " + getFullPath("solicit-example.hex")
+ + " -T " + getFullPath("request6-example.hex ::1"));
+ NakedTestControl tc(opt);
+
+ // For the first 3 packets we are simulating responses from server.
+ // For other packets we don't so packet as 4,5,6 will be dropped and
+ // then test should be interrupted and actual number of iterations will
+ // be 6.
+ const int received_num = 3;
+ // Simulate the number of Solicit-Advertise-Request-Reply (SARR) exchanges.
+ // The test function generates server's responses and passes it to the
+ // TestControl class methods for processing. All exchanged packets carry
+ // the IA_NA option to simulate the IPv6 address acquisition and to verify
+ // that the IA_NA options returned by the server are processed correctly.
+ bool use_templates = true;
+ testPkt6Exchange(iterations_num, received_num, use_templates, tc);
+
+ EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num + received_num); // Solicit + Advertise
+ EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::SA), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::SA), received_num);
+ EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RR), received_num);
+ EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RR), 0);
+}
+
+TEST_F(TestControlTest, Packet6ExchangeAddressOnly) {
+ const int iterations_num = 100;
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -l fake -e address-only"
+ " -6 -r 100 -n 10 -R 20 -L 10547 ::1");
+ // Set number of received packets equal to number of iterations.
+ // This simulates no packet drops.
+ bool use_templates = false;
+
+ // Simulate the number of Solicit-Advertise-Request-Reply (SARR) exchanges.
+ // The test function generates server's responses and passes it to the
+ // TestControl class methods for processing. All exchanged packets carry
+ // the IA_NA option to simulate the IPv6 address acquisition and to verify
+ // that the IA_NA options returned by the server are processed correctly.
+ NakedTestControl tc(opt);
+ testPkt6Exchange(iterations_num, iterations_num, use_templates, tc);
+
+ EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num * 2); // Solicit + Request
+ EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::SA), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::SA), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RR), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RR), 0);
+}
+
+TEST_F(TestControlTest, Packet6ExchangePrefixDelegation) {
+ const int iterations_num = 100;
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -l fake -e prefix-only"
+ " -6 -r 100 -n 10 -R 20 -L 10547 ::1");
+ // Set number of received packets equal to number of iterations.
+ // This simulates no packet drops.
+ bool use_templates = false;
+
+ // Simulate the number of Solicit-Advertise-Request-Reply (SARR) exchanges.
+ // The test function generates server's responses and passes it to the
+ // TestControl class methods for processing. All exchanged packets carry
+ // the IA_PD option to simulate the Prefix Delegation and to verify that
+ // the IA_PD options returned by the server are processed correctly.
+ NakedTestControl tc(opt);
+ testPkt6Exchange(iterations_num, iterations_num, use_templates, tc);
+
+ EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num * 2); // Discovery + Request
+ EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::SA), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::SA), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RR), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RR), 0);
+}
+
+TEST_F(TestControlTest, Packet6ExchangeAddressAndPrefix) {
+ const int iterations_num = 100;
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -l fake -e address-and-prefix"
+ " -6 -r 100 -n 10 -R 20 -L 10547 ::1");
+ // Set number of received packets equal to number of iterations.
+ // This simulates no packet drops.
+ bool use_templates = false;
+ // Simulate the number of Solicit-Advertise-Request-Reply (SARR) exchanges.
+ // The test function generates server's responses and passes it to the
+ // TestControl class methods for processing. All exchanged packets carry
+ // either IA_NA or IA_PD options to simulate the address and prefix
+ // acquisition with the single message and to verify that the IA_NA
+ // and IA_PD options returned by the server are processed correctly.
+ NakedTestControl tc(opt);
+ testPkt6Exchange(iterations_num, iterations_num, use_templates, tc);
+
+ EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num * 2); // Solicit + Request
+ EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::SA), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::SA), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RR), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RR), 0);
+}
+
+TEST_F(TestControlTest, PacketTemplates) {
+ std::vector<uint8_t> template1(256);
+ std::string file1("test1.hex");
+ std::vector<uint8_t> template2(233);
+ std::string file2("test2.hex");
+ for (size_t i = 0; i < template1.size(); ++i) {
+ template1[i] = static_cast<uint8_t>(random() % 256);
+ }
+ for (size_t i = 0; i < template2.size(); ++i) {
+ template2[i] = static_cast<uint8_t>(random() % 256);
+ }
+ // Size of the file is 2 times larger than binary data size.
+ ASSERT_TRUE(createTemplateFile(file1, template1, template1.size() * 2));
+ ASSERT_TRUE(createTemplateFile(file2, template2, template2.size() * 2));
+
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -l 127.0.0.1"
+ " -T " + file1 + " -T " + file2 + " all");
+ NakedTestControl tc(opt);
+
+ tc.initPacketTemplates();
+ TestControl::TemplateBuffer buf1;
+ TestControl::TemplateBuffer buf2;
+ buf1 = tc.getTemplateBuffer(0);
+ buf2 = tc.getTemplateBuffer(1);
+ ASSERT_EQ(template1.size(), buf1.size());
+ ASSERT_EQ(template2.size(), buf2.size());
+ EXPECT_TRUE(std::equal(template1.begin(), template1.end(), buf1.begin()));
+ EXPECT_TRUE(std::equal(template2.begin(), template2.end(), buf2.begin()));
+
+ // Try to read template file with odd number of digits.
+ std::string file3("test3.hex");
+ // Size of the file is 2 times larger than binary data size and it is always
+ // even number. Substracting 1 makes file size odd.
+ ASSERT_TRUE(createTemplateFile(file3, template1, template1.size() * 2 - 1));
+ processCmdLine(opt, "perfdhcp -l 127.0.0.1 -T " + file3 + " all");
+ EXPECT_THROW(tc.initPacketTemplates(), isc::OutOfRange);
+
+ // Try to read empty file.
+ std::string file4("test4.hex");
+ ASSERT_TRUE(createTemplateFile(file4, template2, 0));
+ processCmdLine(opt, "perfdhcp -l 127.0.0.1 -T " + file4 + " all");
+ EXPECT_THROW(tc.initPacketTemplates(), isc::OutOfRange);
+
+ // Try reading file with non hexadecimal characters.
+ std::string file5("test5.hex");
+ ASSERT_TRUE(createTemplateFile(file5, template1, template1.size() * 2, true));
+ processCmdLine(opt, "perfdhcp -l 127.0.0.1 -T " + file5 + " all");
+ EXPECT_THROW(tc.initPacketTemplates(), isc::BadValue);
+}
+
+// This test verifies that DHCPv4 renew (DHCPREQUEST) messages can be
+// sent for acquired leases.
+TEST_F(TestControlTest, processRenew4) {
+ testSendRenewRelease4(DHCPREQUEST);
+}
+
+// This test verifies that DHCPv4 release (DHCPRELEASE) messages can be
+// sent for acquired leases.
+TEST_F(TestControlTest, processRelease4) {
+ testSendRenewRelease4(DHCPRELEASE);
+}
+
+// This test verifies that DHCPv6 Renew messages can be sent for acquired
+// leases.
+TEST_F(TestControlTest, processRenew6) {
+ testSendRenewRelease6(DHCPV6_RENEW);
+}
+
+// This test verifies that DHCPv6 Release messages can be sent for acquired
+// leases.
+TEST_F(TestControlTest, processRelease6) {
+ testSendRenewRelease6(DHCPV6_RELEASE);
+}
+
+// This test verifies that DHCPREQUEST is created correctly from the
+// DHCPACK message.
+TEST_F(TestControlTest, createRenew4) {
+ testCreateRenewRelease4(DHCPREQUEST);
+}
+
+// This test verifies that DHCPRELEASE is created correctly from the
+// DHCPACK message.
+TEST_F(TestControlTest, createRelease4) {
+ testCreateRenewRelease4(DHCPRELEASE);
+}
+
+// This test verifies that the DHCPV6 Renew message is created correctly
+// and that it comprises all required options.
+TEST_F(TestControlTest, createRenew6) {
+ testCreateRenewRelease6(DHCPV6_RENEW);
+}
+
+// This test verifies that the DHCPv6 Release message is created correctly
+// and that it comprises all required options.
+TEST_F(TestControlTest, createRelease6) {
+ testCreateRenewRelease6(DHCPV6_RELEASE);
+}
+
+// This test verifies that the counter of rejected leases in
+// Solicit-Advertise message exchange works correctly
+TEST_F(TestControlTest, rejectedLeasesAdv) {
+ testCountRejectedLeasesSolAdv();
+}
+
+// Test checks if sendDiscover really includes custom options
+TEST_F(TestControlTest, sendDiscoverExtraOpts) {
+ // Important parameters here:
+ // -xT - save first packet of each type for templates (useful for packet inspection)
+ // -o 200,abcdef1234 - send option 200 with hex content: ab:cd:ef:12:34
+ // -o 201,00 - send option 201 with hex content: 00
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -4 -l fake -xT -L 10068"
+ " -o 200,abcdef1234 -o 201,00 -r 1 127.0.0.1");
+
+ // Create test control and set up some basic defaults.
+ NakedTestControl tc(opt);
+ tc.registerOptionFactories();
+ NakedTestControl::IncrementalGeneratorPtr gen(new NakedTestControl::IncrementalGenerator());
+ tc.setTransidGenerator(gen);
+
+ // Make tc send the packet. The first packet of each type is saved in templates.
+ tc.sendDiscover4();
+
+ // Let's find the packet and see if it includes the right option.
+ auto pkt_it = tc.template_packets_v4_.find(DHCPDISCOVER);
+ ASSERT_TRUE(pkt_it != tc.template_packets_v4_.end());
+
+ checkOptions20x(pkt_it->second);
+}
+
+// Test checks if regular packet exchange inserts the extra v4 options
+// specified on command line.
+TEST_F(TestControlTest, Packet4ExchangeExtraOpts) {
+ // Important parameters here:
+ // -xT - save first packet of each type for templates (useful for packet inspection)
+ // -o 200,abcdef1234 - send option 200 with hex content: ab:cd:ef:12:34
+ // -o 201,00 - send option 201 with hex content: 00
+ const int iterations_num = 1;
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -l fake -4 -o 200,abcdef1234 -o 201,00 "
+ "-r 100 -n 10 -R 20 -xT -L 10547 127.0.0.1");
+
+ NakedTestControl tc(opt);
+ tc.registerOptionFactories();
+
+ // Do the actual exchange.
+ testPkt4Exchange(iterations_num, iterations_num, false, tc);
+
+ EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num * 2); // Discovery + Request
+ EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::DO), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::DO), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RA), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RA), 0);
+
+ // Check if Discover was recored and if it contains options 200 and 201.
+ auto disc = tc.template_packets_v4_.find(DHCPDISCOVER);
+ ASSERT_TRUE(disc != tc.template_packets_v4_.end());
+ checkOptions20x(disc->second);
+
+ // Check if Request was recored and if it contains options 200 and 201.
+ auto req = tc.template_packets_v4_.find(DHCPREQUEST);
+ ASSERT_TRUE(req != tc.template_packets_v4_.end());
+ checkOptions20x(req->second);
+}
+
+// Test checks if regular packet exchange inserts the extra v6 options
+// specified on command line.
+TEST_F(TestControlTest, Packet6ExchangeExtraOpts) {
+ // Important parameters here:
+ // -xT - save first packet of each type for templates (useful for packet inspection)
+ // -o 200,abcdef1234 - send option 200 with hex content: ab:cd:ef:12:34
+ // -o 201,00 - send option 201 with hex content: 00
+ const int iterations_num = 1;
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -l fake"
+ " -6 -e address-only"
+ " -xT -o 200,abcdef1234 -o 201,00 "
+ " -r 100 -n 10 -R 20 -L 10547 ::1");
+
+ // Simulate the number of Solicit-Advertise-Request-Reply (SARR) exchanges.
+ // The test function generates server's responses and passes it to the
+ // TestControl class methods for processing.
+ // First packet of each type is recorded as a template packet. The check
+ // inspects this template to see if the expected options are really there.
+ NakedTestControl tc(opt);
+ testPkt6Exchange(iterations_num, iterations_num, false, tc);
+
+ EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num * 2); // Solicit + Request
+ EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::SA), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::SA), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RR), iterations_num);
+ EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RR), 0);
+
+ // Check if Solicit was recorded and if it contains options 200 and 201.
+ auto sol = tc.template_packets_v6_.find(DHCPV6_SOLICIT);
+ ASSERT_TRUE(sol != tc.template_packets_v6_.end());
+ checkOptions20x(sol->second);
+
+ // Check if Request was recorded and if it contains options 200 and 201.
+ auto req = tc.template_packets_v6_.find(DHCPV6_REQUEST);
+ ASSERT_TRUE(req != tc.template_packets_v6_.end());
+ checkOptions20x(req->second);
+}
+
+// Test checks if multiple v4 PRL options can be sent. They should be merged
+// into a single PRL option by perfdhcp.
+TEST_F(TestControlTest, sendDiscoverMultiplePRLs) {
+ // Important parameters here:
+ // -o 55,1234 - send option 55 with hex content '1234'
+ // -o 55,abcd - send option 55 with hex content 'abcd'
+ CommandOptions opt;
+ processCmdLine(
+ opt, "perfdhcp -4 -l fake -o 55,1234 -o 55,abcd -r 1 -xT 127.0.0.1");
+
+ // Create test control and set up some basic defaults.
+ NakedTestControl tc(opt);
+ tc.registerOptionFactories();
+ NakedTestControl::IncrementalGeneratorPtr gen(
+ boost::make_shared<NakedTestControl::IncrementalGenerator>());
+ tc.setTransidGenerator(gen);
+
+ // Send the packet.
+ tc.sendDiscover4();
+
+ // Let's find the packet and see if it includes the right option.
+ auto const pkt_it(tc.template_packets_v4_.find(DHCPDISCOVER));
+ ASSERT_TRUE(pkt_it != tc.template_packets_v4_.end());
+
+ checkOptions55(pkt_it->second,
+ {
+ // Added to all perfdhcp egress packets by default
+ DHO_SUBNET_MASK,
+ DHO_BROADCAST_ADDRESS,
+ DHO_TIME_OFFSET,
+ DHO_ROUTERS,
+ DHO_DOMAIN_NAME,
+ DHO_DOMAIN_NAME_SERVERS,
+ DHO_HOST_NAME,
+ // Explicitly added in this test
+ 0x12,
+ 0x34,
+ 0xab,
+ 0xcd,
+ });
+}
+
+// This test checks if HA failure can be simulated using -y and -Y options with DHCPv4.
+TEST_F(TestControlTest, haFailure4) {
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -l fake -r 1 -n 1 -R 2 -y 10 -Y 0 -L 10547 127.0.0.1");
+ NakedTestControl tc(opt);
+
+ tc.sendPackets(1); // Send one packet. It should have secs set to 1.
+ sleep(1); // wait a second...
+ tc.sendPackets(1); // and send another packet. This should have secs set to 2.
+
+ EXPECT_EQ(tc.fake_sock_.sent_cnt_, 2); // Make sure the stats are up.
+ ASSERT_EQ(tc.fake_sock_.sent_pkts4_.size(), 2); // And the packets were captured.
+ Pkt4Ptr dis1 = tc.fake_sock_.sent_pkts4_[0];
+ Pkt4Ptr dis2 = tc.fake_sock_.sent_pkts4_[1];
+ ASSERT_TRUE(dis1);
+ ASSERT_TRUE(dis2);
+
+ EXPECT_EQ(dis1->getSecs(), 1); // Make sure it has secs set to 1.
+ EXPECT_GT(dis2->getSecs(), 1); // Should be 2, but we want to avoid rare cases when the test
+ // could fall exactly on the second boundary, so checking for
+ // greater than 1.
+}
+
+// This test checks if HA failure can be simulated using -y and -Y options with DHCPv6.
+TEST_F(TestControlTest, haFailure6) {
+ CommandOptions opt;
+ processCmdLine(opt, "perfdhcp -6 -l fake -r 1 -n 1 -R 2 -y 10 -Y 0 -L 10547 all");
+ NakedTestControl tc(opt);
+
+ tc.sendPackets(1); // Send one packet. It should have secs set to 1.
+ sleep(1); // wait a second...
+ tc.sendPackets(1); // and send another packet. This should have secs set to 2.
+
+ EXPECT_EQ(tc.fake_sock_.sent_cnt_, 2); // Make sure the stats are up.
+ ASSERT_EQ(tc.fake_sock_.sent_pkts6_.size(), 2); // And the packets were captured.
+ Pkt6Ptr sol1 = tc.fake_sock_.sent_pkts6_[0];
+ Pkt6Ptr sol2 = tc.fake_sock_.sent_pkts6_[1];
+ ASSERT_TRUE(sol1);
+ ASSERT_TRUE(sol2);
+ OptionUint16Ptr elapsed1(boost::dynamic_pointer_cast<OptionUint16>(sol1->getOption(D6O_ELAPSED_TIME)));
+ OptionUint16Ptr elapsed2(boost::dynamic_pointer_cast<OptionUint16>(sol2->getOption(D6O_ELAPSED_TIME)));
+ ASSERT_TRUE(elapsed1);
+ ASSERT_TRUE(elapsed2);
+
+ EXPECT_EQ(elapsed1->getValue(), 100);
+ EXPECT_GT(elapsed2->getValue(), 100);
+}
diff --git a/src/bin/perfdhcp/tests/testdata/Makefile.am b/src/bin/perfdhcp/tests/testdata/Makefile.am
new file mode 100644
index 0000000..19cf25d
--- /dev/null
+++ b/src/bin/perfdhcp/tests/testdata/Makefile.am
@@ -0,0 +1,5 @@
+SUBDIRS = .
+
+EXTRA_DIST = discover-example.hex request4-example.hex
+EXTRA_DIST += solicit-example.hex request6-example.hex
+EXTRA_DIST += mac-list.txt relay4-list.txt relay6-list.txt
diff --git a/src/bin/perfdhcp/tests/testdata/Makefile.in b/src/bin/perfdhcp/tests/testdata/Makefile.in
new file mode 100644
index 0000000..299c09b
--- /dev/null
+++ b/src/bin/perfdhcp/tests/testdata/Makefile.in
@@ -0,0 +1,731 @@
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+VPATH = @srcdir@
+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 \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/bin/perfdhcp/tests/testdata
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \
+ $(top_srcdir)/m4macros/ax_cpp11.m4 \
+ $(top_srcdir)/m4macros/ax_cpp20.m4 \
+ $(top_srcdir)/m4macros/ax_crypto.m4 \
+ $(top_srcdir)/m4macros/ax_find_library.m4 \
+ $(top_srcdir)/m4macros/ax_gssapi.m4 \
+ $(top_srcdir)/m4macros/ax_gtest.m4 \
+ $(top_srcdir)/m4macros/ax_isc_rpath.m4 \
+ $(top_srcdir)/m4macros/ax_netconf.m4 \
+ $(top_srcdir)/m4macros/libtool.m4 \
+ $(top_srcdir)/m4macros/ltoptions.m4 \
+ $(top_srcdir)/m4macros/ltsugar.m4 \
+ $(top_srcdir)/m4macros/ltversion.m4 \
+ $(top_srcdir)/m4macros/lt~obsolete.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_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+ASCIIDOC = @ASCIIDOC@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BOOST_INCLUDES = @BOOST_INCLUDES@
+BOOST_LIBS = @BOOST_LIBS@
+BOTAN_TOOL = @BOTAN_TOOL@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CONTRIB_DIR = @CONTRIB_DIR@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRYPTO_CFLAGS = @CRYPTO_CFLAGS@
+CRYPTO_INCLUDES = @CRYPTO_INCLUDES@
+CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@
+CRYPTO_LIBS = @CRYPTO_LIBS@
+CRYPTO_PACKAGE = @CRYPTO_PACKAGE@
+CRYPTO_RPATH = @CRYPTO_RPATH@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@
+DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@
+DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@
+DISTCHECK_GSSAPI_CONFIGURE_FLAG = @DISTCHECK_GSSAPI_CONFIGURE_FLAG@
+DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@
+DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@
+DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG = @DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG@
+DISTCHECK_LIBYANG_CONFIGURE_FLAG = @DISTCHECK_LIBYANG_CONFIGURE_FLAG@
+DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@
+DISTCHECK_MYSQL_CONFIGURE_FLAG = @DISTCHECK_MYSQL_CONFIGURE_FLAG@
+DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@
+DISTCHECK_PGSQL_CONFIGURE_FLAG = @DISTCHECK_PGSQL_CONFIGURE_FLAG@
+DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@
+DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG = @DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG@
+DISTCHECK_SYSREPO_CONFIGURE_FLAG = @DISTCHECK_SYSREPO_CONFIGURE_FLAG@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GREP = @GREP@
+GSSAPI_CFLAGS = @GSSAPI_CFLAGS@
+GSSAPI_LIBS = @GSSAPI_LIBS@
+GTEST_CONFIG = @GTEST_CONFIG@
+GTEST_INCLUDES = @GTEST_INCLUDES@
+GTEST_LDADD = @GTEST_LDADD@
+GTEST_LDFLAGS = @GTEST_LDFLAGS@
+GTEST_SOURCE = @GTEST_SOURCE@
+HAVE_NETCONF = @HAVE_NETCONF@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KEA_CXXFLAGS = @KEA_CXXFLAGS@
+KEA_SRCID = @KEA_SRCID@
+KRB5_CONFIG = @KRB5_CONFIG@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBYANGCPP_CPPFLAGS = @LIBYANGCPP_CPPFLAGS@
+LIBYANGCPP_INCLUDEDIR = @LIBYANGCPP_INCLUDEDIR@
+LIBYANGCPP_LIBS = @LIBYANGCPP_LIBS@
+LIBYANGCPP_PREFIX = @LIBYANGCPP_PREFIX@
+LIBYANGCPP_VERSION = @LIBYANGCPP_VERSION@
+LIBYANG_CPPFLAGS = @LIBYANG_CPPFLAGS@
+LIBYANG_INCLUDEDIR = @LIBYANG_INCLUDEDIR@
+LIBYANG_LIBS = @LIBYANG_LIBS@
+LIBYANG_PREFIX = @LIBYANG_PREFIX@
+LIBYANG_VERSION = @LIBYANG_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@
+LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@
+MYSQL_LIBS = @MYSQL_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PDFLATEX = @PDFLATEX@
+PERL = @PERL@
+PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@
+PGSQL_LIBS = @PGSQL_LIBS@
+PKGPYTHONDIR = @PKGPYTHONDIR@
+PKG_CONFIG = @PKG_CONFIG@
+PLANTUML = @PLANTUML@
+PREMIUM_DIR = @PREMIUM_DIR@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+SED = @SED@
+SEP = @SEP@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINXBUILD = @SPHINXBUILD@
+SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@
+SR_PLUGINS_PATH = @SR_PLUGINS_PATH@
+SR_REPO_PATH = @SR_REPO_PATH@
+STRIP = @STRIP@
+SYSREPOCPP_CPPFLAGS = @SYSREPOCPP_CPPFLAGS@
+SYSREPOCPP_INCLUDEDIR = @SYSREPOCPP_INCLUDEDIR@
+SYSREPOCPP_LIBS = @SYSREPOCPP_LIBS@
+SYSREPOCPP_PREFIX = @SYSREPOCPP_PREFIX@
+SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@
+SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@
+SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@
+SYSREPO_LIBS = @SYSREPO_LIBS@
+SYSREPO_PREFIX = @SYSREPO_PREFIX@
+SYSREPO_VERSION = @SYSREPO_VERSION@
+USE_LCOV = @USE_LCOV@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@
+YACC = @YACC@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = .
+EXTRA_DIST = discover-example.hex request4-example.hex \
+ solicit-example.hex request6-example.hex mac-list.txt \
+ relay4-list.txt relay6-list.txt
+all: all-recursive
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/bin/perfdhcp/tests/testdata/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/bin/perfdhcp/tests/testdata/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+ check-am clean clean-generic clean-libtool cscopelist-am ctags \
+ ctags-am distclean distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean 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.
+.NOEXPORT:
diff --git a/src/bin/perfdhcp/tests/testdata/discover-example.hex b/src/bin/perfdhcp/tests/testdata/discover-example.hex
new file mode 100644
index 0000000..9a6e5ea
--- /dev/null
+++ b/src/bin/perfdhcp/tests/testdata/discover-example.hex
@@ -0,0 +1 @@
+01010601008b45d200000000000000000000000000000000ac100102000c0102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501013707011c02030f060cff \ No newline at end of file
diff --git a/src/bin/perfdhcp/tests/testdata/mac-list.txt b/src/bin/perfdhcp/tests/testdata/mac-list.txt
new file mode 100644
index 0000000..e9e30e0
--- /dev/null
+++ b/src/bin/perfdhcp/tests/testdata/mac-list.txt
@@ -0,0 +1,4 @@
+11:22:33:44:55:66
+11:22:33:44:55:77
+11:22:33:44:55:88
+11:22:33:44:55:99
diff --git a/src/bin/perfdhcp/tests/testdata/relay4-list.txt b/src/bin/perfdhcp/tests/testdata/relay4-list.txt
new file mode 100644
index 0000000..6d3db81
--- /dev/null
+++ b/src/bin/perfdhcp/tests/testdata/relay4-list.txt
@@ -0,0 +1,5 @@
+100.95.0.1
+20.86.12.1
+101.64.4.1
+1.86.0.1
+92.86.238.1
diff --git a/src/bin/perfdhcp/tests/testdata/relay6-list.txt b/src/bin/perfdhcp/tests/testdata/relay6-list.txt
new file mode 100644
index 0000000..ce98e94
--- /dev/null
+++ b/src/bin/perfdhcp/tests/testdata/relay6-list.txt
@@ -0,0 +1,2 @@
+3000::1
+fe80::6e2b:59ff:fe94:19d1
diff --git a/src/bin/perfdhcp/tests/testdata/request4-example.hex b/src/bin/perfdhcp/tests/testdata/request4-example.hex
new file mode 100644
index 0000000..32447d6
--- /dev/null
+++ b/src/bin/perfdhcp/tests/testdata/request4-example.hex
@@ -0,0 +1 @@
+01010601007b23f800000000000000000000000000000000ac100102000c0102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633204ac1001813501033604ac1001013707011c02030f060cff \ No newline at end of file
diff --git a/src/bin/perfdhcp/tests/testdata/request6-example.hex b/src/bin/perfdhcp/tests/testdata/request6-example.hex
new file mode 100644
index 0000000..1e3e76f
--- /dev/null
+++ b/src/bin/perfdhcp/tests/testdata/request6-example.hex
@@ -0,0 +1 @@
+03da30c60001000e0001000117cf8e76000c010203060002000e0001000117cf8a5c080027a87b3400030028000000010000000a0000000e0005001820010db800010000000000000001b568000000be000000c8000800020000 \ No newline at end of file
diff --git a/src/bin/perfdhcp/tests/testdata/solicit-example.hex b/src/bin/perfdhcp/tests/testdata/solicit-example.hex
new file mode 100644
index 0000000..41c5ad3
--- /dev/null
+++ b/src/bin/perfdhcp/tests/testdata/solicit-example.hex
@@ -0,0 +1 @@
+015f4e650001000e0001000117cf8e76000c010203040003000c0000000100000e01000015180006000400170018000800020000 \ No newline at end of file