diff options
Diffstat (limited to 'src/hooks/dhcp/flex_option')
23 files changed, 10291 insertions, 0 deletions
diff --git a/src/hooks/dhcp/flex_option/Makefile.am b/src/hooks/dhcp/flex_option/Makefile.am new file mode 100644 index 0000000..6fa885a --- /dev/null +++ b/src/hooks/dhcp/flex_option/Makefile.am @@ -0,0 +1,87 @@ +SUBDIRS = . libloadtests tests + +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +# Ensure that the message file and doxygen file is included in the distribution +EXTRA_DIST = flex_option_messages.mes +EXTRA_DIST += flex_option.dox + +CLEANFILES = *.gcno *.gcda + +# convenience archive + +noinst_LTLIBRARIES = libflex_option.la + +libflex_option_la_SOURCES = flex_option.cc flex_option.h +libflex_option_la_SOURCES += flex_option_callouts.cc +libflex_option_la_SOURCES += flex_option_log.cc flex_option_log.h +libflex_option_la_SOURCES += flex_option_messages.cc flex_option_messages.h +libflex_option_la_SOURCES += version.cc + +libflex_option_la_CXXFLAGS = $(AM_CXXFLAGS) +libflex_option_la_CPPFLAGS = $(AM_CPPFLAGS) + +# install the shared object into $(libdir)/kea/hooks +lib_hooksdir = $(libdir)/kea/hooks +lib_hooks_LTLIBRARIES = libdhcp_flex_option.la + +libdhcp_flex_option_la_SOURCES = +libdhcp_flex_option_la_LDFLAGS = $(AM_LDFLAGS) +libdhcp_flex_option_la_LDFLAGS += -avoid-version -export-dynamic -module +libdhcp_flex_option_la_LIBADD = libflex_option.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/process/libkea-process.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/eval/libkea-eval.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/stats/libkea-stats.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/http/libkea-http.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/database/libkea-database.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/dns/libkea-dns++.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +libdhcp_flex_option_la_LIBADD += $(LOG4CPLUS_LIBS) +libdhcp_flex_option_la_LIBADD += $(CRYPTO_LIBS) +libdhcp_flex_option_la_LIBADD += $(BOOST_LIBS) + +# If we want to get rid of all generated messages files, we need to use +# make maintainer-clean. The proper way to introduce custom commands for +# that operation is to define maintainer-clean-local target. However, +# make maintainer-clean also removes Makefile, so running configure script +# is required. To make it easy to rebuild messages without going through +# reconfigure, a new target messages-clean has been added. +maintainer-clean-local: + rm -f flex_option_messages.h flex_option_messages.cc + +# To regenerate messages files, one can do: +# +# make messages-clean +# make messages +# +# This is needed only when a .mes file is modified. +messages-clean: maintainer-clean-local + +if GENERATE_MESSAGES + +# Define rule to build logging source files from message file +messages: flex_option_messages.h flex_option_messages.cc + @echo Message files regenerated + +flex_option_messages.h flex_option_messages.cc: flex_option_messages.mes + $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/hooks/dhcp/flex_option/flex_option_messages.mes + +else + +messages flex_option_messages.h flex_option_messages.cc: + @echo Messages generation disabled. Configure with --enable-generate-messages to enable it. + +endif + diff --git a/src/hooks/dhcp/flex_option/Makefile.in b/src/hooks/dhcp/flex_option/Makefile.in new file mode 100644 index 0000000..de7d0d7 --- /dev/null +++ b/src/hooks/dhcp/flex_option/Makefile.in @@ -0,0 +1,1065 @@ +# 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/hooks/dhcp/flex_option +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__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(lib_hooksdir)" +LTLIBRARIES = $(lib_hooks_LTLIBRARIES) $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +libdhcp_flex_option_la_DEPENDENCIES = libflex_option.la \ + $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la \ + $(top_builddir)/src/lib/process/libkea-process.la \ + $(top_builddir)/src/lib/eval/libkea-eval.la \ + $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la \ + $(top_builddir)/src/lib/stats/libkea-stats.la \ + $(top_builddir)/src/lib/config/libkea-cfgclient.la \ + $(top_builddir)/src/lib/http/libkea-http.la \ + $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la \ + $(top_builddir)/src/lib/hooks/libkea-hooks.la \ + $(top_builddir)/src/lib/database/libkea-database.la \ + $(top_builddir)/src/lib/cc/libkea-cc.la \ + $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ + $(top_builddir)/src/lib/dns/libkea-dns++.la \ + $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la \ + $(top_builddir)/src/lib/log/libkea-log.la \ + $(top_builddir)/src/lib/util/libkea-util.la \ + $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am_libdhcp_flex_option_la_OBJECTS = +libdhcp_flex_option_la_OBJECTS = $(am_libdhcp_flex_option_la_OBJECTS) +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 = +libdhcp_flex_option_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(libdhcp_flex_option_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +libflex_option_la_LIBADD = +am_libflex_option_la_OBJECTS = libflex_option_la-flex_option.lo \ + libflex_option_la-flex_option_callouts.lo \ + libflex_option_la-flex_option_log.lo \ + libflex_option_la-flex_option_messages.lo \ + libflex_option_la-version.lo +libflex_option_la_OBJECTS = $(am_libflex_option_la_OBJECTS) +libflex_option_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(libflex_option_la_CXXFLAGS) $(CXXFLAGS) $(AM_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)/libflex_option_la-flex_option.Plo \ + ./$(DEPDIR)/libflex_option_la-flex_option_callouts.Plo \ + ./$(DEPDIR)/libflex_option_la-flex_option_log.Plo \ + ./$(DEPDIR)/libflex_option_la-flex_option_messages.Plo \ + ./$(DEPDIR)/libflex_option_la-version.Plo +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 = $(libdhcp_flex_option_la_SOURCES) \ + $(libflex_option_la_SOURCES) +DIST_SOURCES = $(libdhcp_flex_option_la_SOURCES) \ + $(libflex_option_la_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 $(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 = . libloadtests tests +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib \ + $(BOOST_INCLUDES) +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +# Ensure that the message file and doxygen file is included in the distribution +EXTRA_DIST = flex_option_messages.mes flex_option.dox +CLEANFILES = *.gcno *.gcda + +# convenience archive +noinst_LTLIBRARIES = libflex_option.la +libflex_option_la_SOURCES = flex_option.cc flex_option.h \ + flex_option_callouts.cc flex_option_log.cc flex_option_log.h \ + flex_option_messages.cc flex_option_messages.h version.cc +libflex_option_la_CXXFLAGS = $(AM_CXXFLAGS) +libflex_option_la_CPPFLAGS = $(AM_CPPFLAGS) + +# install the shared object into $(libdir)/kea/hooks +lib_hooksdir = $(libdir)/kea/hooks +lib_hooks_LTLIBRARIES = libdhcp_flex_option.la +libdhcp_flex_option_la_SOURCES = +libdhcp_flex_option_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version \ + -export-dynamic -module +libdhcp_flex_option_la_LIBADD = libflex_option.la \ + $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la \ + $(top_builddir)/src/lib/process/libkea-process.la \ + $(top_builddir)/src/lib/eval/libkea-eval.la \ + $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la \ + $(top_builddir)/src/lib/stats/libkea-stats.la \ + $(top_builddir)/src/lib/config/libkea-cfgclient.la \ + $(top_builddir)/src/lib/http/libkea-http.la \ + $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la \ + $(top_builddir)/src/lib/hooks/libkea-hooks.la \ + $(top_builddir)/src/lib/database/libkea-database.la \ + $(top_builddir)/src/lib/cc/libkea-cc.la \ + $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ + $(top_builddir)/src/lib/dns/libkea-dns++.la \ + $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la \ + $(top_builddir)/src/lib/log/libkea-log.la \ + $(top_builddir)/src/lib/util/libkea-util.la \ + $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ + $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) $(BOOST_LIBS) +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/hooks/dhcp/flex_option/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/hooks/dhcp/flex_option/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): + +install-lib_hooksLTLIBRARIES: $(lib_hooks_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_hooks_LTLIBRARIES)'; test -n "$(lib_hooksdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(lib_hooksdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(lib_hooksdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(lib_hooksdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(lib_hooksdir)"; \ + } + +uninstall-lib_hooksLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_hooks_LTLIBRARIES)'; test -n "$(lib_hooksdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(lib_hooksdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(lib_hooksdir)/$$f"; \ + done + +clean-lib_hooksLTLIBRARIES: + -test -z "$(lib_hooks_LTLIBRARIES)" || rm -f $(lib_hooks_LTLIBRARIES) + @list='$(lib_hooks_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libdhcp_flex_option.la: $(libdhcp_flex_option_la_OBJECTS) $(libdhcp_flex_option_la_DEPENDENCIES) $(EXTRA_libdhcp_flex_option_la_DEPENDENCIES) + $(AM_V_CCLD)$(libdhcp_flex_option_la_LINK) -rpath $(lib_hooksdir) $(libdhcp_flex_option_la_OBJECTS) $(libdhcp_flex_option_la_LIBADD) $(LIBS) + +libflex_option.la: $(libflex_option_la_OBJECTS) $(libflex_option_la_DEPENDENCIES) $(EXTRA_libflex_option_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libflex_option_la_LINK) $(libflex_option_la_OBJECTS) $(libflex_option_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libflex_option_la-flex_option.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libflex_option_la-flex_option_callouts.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libflex_option_la-flex_option_log.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libflex_option_la-flex_option_messages.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libflex_option_la-version.Plo@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 $@ $< + +libflex_option_la-flex_option.lo: flex_option.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libflex_option_la_CPPFLAGS) $(CPPFLAGS) $(libflex_option_la_CXXFLAGS) $(CXXFLAGS) -MT libflex_option_la-flex_option.lo -MD -MP -MF $(DEPDIR)/libflex_option_la-flex_option.Tpo -c -o libflex_option_la-flex_option.lo `test -f 'flex_option.cc' || echo '$(srcdir)/'`flex_option.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libflex_option_la-flex_option.Tpo $(DEPDIR)/libflex_option_la-flex_option.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='flex_option.cc' object='libflex_option_la-flex_option.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libflex_option_la_CPPFLAGS) $(CPPFLAGS) $(libflex_option_la_CXXFLAGS) $(CXXFLAGS) -c -o libflex_option_la-flex_option.lo `test -f 'flex_option.cc' || echo '$(srcdir)/'`flex_option.cc + +libflex_option_la-flex_option_callouts.lo: flex_option_callouts.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libflex_option_la_CPPFLAGS) $(CPPFLAGS) $(libflex_option_la_CXXFLAGS) $(CXXFLAGS) -MT libflex_option_la-flex_option_callouts.lo -MD -MP -MF $(DEPDIR)/libflex_option_la-flex_option_callouts.Tpo -c -o libflex_option_la-flex_option_callouts.lo `test -f 'flex_option_callouts.cc' || echo '$(srcdir)/'`flex_option_callouts.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libflex_option_la-flex_option_callouts.Tpo $(DEPDIR)/libflex_option_la-flex_option_callouts.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='flex_option_callouts.cc' object='libflex_option_la-flex_option_callouts.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libflex_option_la_CPPFLAGS) $(CPPFLAGS) $(libflex_option_la_CXXFLAGS) $(CXXFLAGS) -c -o libflex_option_la-flex_option_callouts.lo `test -f 'flex_option_callouts.cc' || echo '$(srcdir)/'`flex_option_callouts.cc + +libflex_option_la-flex_option_log.lo: flex_option_log.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libflex_option_la_CPPFLAGS) $(CPPFLAGS) $(libflex_option_la_CXXFLAGS) $(CXXFLAGS) -MT libflex_option_la-flex_option_log.lo -MD -MP -MF $(DEPDIR)/libflex_option_la-flex_option_log.Tpo -c -o libflex_option_la-flex_option_log.lo `test -f 'flex_option_log.cc' || echo '$(srcdir)/'`flex_option_log.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libflex_option_la-flex_option_log.Tpo $(DEPDIR)/libflex_option_la-flex_option_log.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='flex_option_log.cc' object='libflex_option_la-flex_option_log.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libflex_option_la_CPPFLAGS) $(CPPFLAGS) $(libflex_option_la_CXXFLAGS) $(CXXFLAGS) -c -o libflex_option_la-flex_option_log.lo `test -f 'flex_option_log.cc' || echo '$(srcdir)/'`flex_option_log.cc + +libflex_option_la-flex_option_messages.lo: flex_option_messages.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libflex_option_la_CPPFLAGS) $(CPPFLAGS) $(libflex_option_la_CXXFLAGS) $(CXXFLAGS) -MT libflex_option_la-flex_option_messages.lo -MD -MP -MF $(DEPDIR)/libflex_option_la-flex_option_messages.Tpo -c -o libflex_option_la-flex_option_messages.lo `test -f 'flex_option_messages.cc' || echo '$(srcdir)/'`flex_option_messages.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libflex_option_la-flex_option_messages.Tpo $(DEPDIR)/libflex_option_la-flex_option_messages.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='flex_option_messages.cc' object='libflex_option_la-flex_option_messages.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libflex_option_la_CPPFLAGS) $(CPPFLAGS) $(libflex_option_la_CXXFLAGS) $(CXXFLAGS) -c -o libflex_option_la-flex_option_messages.lo `test -f 'flex_option_messages.cc' || echo '$(srcdir)/'`flex_option_messages.cc + +libflex_option_la-version.lo: version.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libflex_option_la_CPPFLAGS) $(CPPFLAGS) $(libflex_option_la_CXXFLAGS) $(CXXFLAGS) -MT libflex_option_la-version.lo -MD -MP -MF $(DEPDIR)/libflex_option_la-version.Tpo -c -o libflex_option_la-version.lo `test -f 'version.cc' || echo '$(srcdir)/'`version.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libflex_option_la-version.Tpo $(DEPDIR)/libflex_option_la-version.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='version.cc' object='libflex_option_la-version.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libflex_option_la_CPPFLAGS) $(CPPFLAGS) $(libflex_option_la_CXXFLAGS) $(CXXFLAGS) -c -o libflex_option_la-version.lo `test -f 'version.cc' || echo '$(srcdir)/'`version.cc + +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 $(LTLIBRARIES) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(lib_hooksdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +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-lib_hooksLTLIBRARIES clean-libtool \ + clean-noinstLTLIBRARIES mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/libflex_option_la-flex_option.Plo + -rm -f ./$(DEPDIR)/libflex_option_la-flex_option_callouts.Plo + -rm -f ./$(DEPDIR)/libflex_option_la-flex_option_log.Plo + -rm -f ./$(DEPDIR)/libflex_option_la-flex_option_messages.Plo + -rm -f ./$(DEPDIR)/libflex_option_la-version.Plo + -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-lib_hooksLTLIBRARIES + +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)/libflex_option_la-flex_option.Plo + -rm -f ./$(DEPDIR)/libflex_option_la-flex_option_callouts.Plo + -rm -f ./$(DEPDIR)/libflex_option_la-flex_option_log.Plo + -rm -f ./$(DEPDIR)/libflex_option_la-flex_option_messages.Plo + -rm -f ./$(DEPDIR)/libflex_option_la-version.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic \ + maintainer-clean-local + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-lib_hooksLTLIBRARIES + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-am clean clean-generic \ + clean-lib_hooksLTLIBRARIES clean-libtool \ + clean-noinstLTLIBRARIES 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-lib_hooksLTLIBRARIES 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 \ + maintainer-clean-local mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am \ + uninstall-lib_hooksLTLIBRARIES + +.PRECIOUS: Makefile + + +# If we want to get rid of all generated messages files, we need to use +# make maintainer-clean. The proper way to introduce custom commands for +# that operation is to define maintainer-clean-local target. However, +# make maintainer-clean also removes Makefile, so running configure script +# is required. To make it easy to rebuild messages without going through +# reconfigure, a new target messages-clean has been added. +maintainer-clean-local: + rm -f flex_option_messages.h flex_option_messages.cc + +# To regenerate messages files, one can do: +# +# make messages-clean +# make messages +# +# This is needed only when a .mes file is modified. +messages-clean: maintainer-clean-local + +# Define rule to build logging source files from message file +@GENERATE_MESSAGES_TRUE@messages: flex_option_messages.h flex_option_messages.cc +@GENERATE_MESSAGES_TRUE@ @echo Message files regenerated + +@GENERATE_MESSAGES_TRUE@flex_option_messages.h flex_option_messages.cc: flex_option_messages.mes +@GENERATE_MESSAGES_TRUE@ $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/hooks/dhcp/flex_option/flex_option_messages.mes + +@GENERATE_MESSAGES_FALSE@messages flex_option_messages.h flex_option_messages.cc: +@GENERATE_MESSAGES_FALSE@ @echo Messages generation disabled. Configure with --enable-generate-messages to enable it. + +# 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/hooks/dhcp/flex_option/flex_option.cc b/src/hooks/dhcp/flex_option/flex_option.cc new file mode 100644 index 0000000..aa6edd9 --- /dev/null +++ b/src/hooks/dhcp/flex_option/flex_option.cc @@ -0,0 +1,612 @@ +// Copyright (C) 2019-2022 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 <flex_option.h> +#include <flex_option_log.h> +#include <util/strutil.h> +#include <cc/simple_parser.h> +#include <dhcp/dhcp4.h> +#include <dhcp/libdhcp++.h> +#include <dhcp/option_definition.h> +#include <dhcp/option_space.h> +#include <dhcp/option_vendor.h> +#include <dhcpsrv/cfgmgr.h> +#include <eval/eval_context.h> + +using namespace isc; +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::eval; +using namespace isc::flex_option; +using namespace isc::log; +using namespace isc::util; +using namespace std; + +namespace { + +/// @brief Parse an action. +/// +/// @note Shared code for option and sub-option. +/// +/// @param option The option element. +/// @param opt_cfg The option configuration. +/// @param universe The universe. +/// @param name The action name. +/// @param action The action. +/// @param parser_type The type of the parser of the expression. +void +parseAction(ConstElementPtr option, + FlexOptionImpl::OptionConfigPtr opt_cfg, + Option::Universe universe, + const string& name, + FlexOptionImpl::Action action, + EvalContext::ParserType parser_type) { + ConstElementPtr elem = option->get(name); + if (elem) { + string expr_text = elem->stringValue(); + if (expr_text.empty()) { + isc_throw(BadValue, "'" << name << "' must not be empty"); + } + if (opt_cfg->getAction() != FlexOptionImpl::NONE) { + isc_throw(BadValue, "multiple actions: " << option->str()); + } + opt_cfg->setAction(action); + opt_cfg->setText(expr_text); + try { + EvalContext eval_ctx(universe); + eval_ctx.parseString(expr_text, parser_type); + ExpressionPtr expr(new Expression(eval_ctx.expression)); + opt_cfg->setExpr(expr); + } catch (const std::exception& ex) { + isc_throw(BadValue, "can't parse " << name << " expression [" + << expr_text << "] error: " << ex.what()); + } + } +} + +} // end of anonymous namespace + +namespace isc { +namespace flex_option { + +const SimpleKeywords FlexOptionImpl::OPTION_PARAMETERS = { + { "code", Element::integer }, + { "name", Element::string }, + { "space", Element::string }, + { "csv-format", Element::boolean }, + { "add", Element::string }, + { "supersede", Element::string }, + { "remove", Element::string }, + { "sub-options", Element::list }, + { "client-class", Element::string }, + { "comment", Element::string } +}; + +const SimpleKeywords FlexOptionImpl::SUB_OPTION_PARAMETERS = { + { "code", Element::integer }, + { "name", Element::string }, + { "space", Element::string }, + { "csv-format", Element::boolean }, + { "add", Element::string }, + { "supersede", Element::string }, + { "remove", Element::string }, + { "container-add", Element::boolean }, + { "container-remove", Element::boolean }, + { "client-class", Element::string }, + { "comment", Element::string } +}; + +FlexOptionImpl::OptionConfig::OptionConfig(uint16_t code, + OptionDefinitionPtr def) + : code_(code), def_(def), action_(NONE), class_("") { +} + +FlexOptionImpl::OptionConfig::~OptionConfig() { +} + +FlexOptionImpl::SubOptionConfig::SubOptionConfig(uint16_t code, + OptionDefinitionPtr def, + OptionConfigPtr container) + : OptionConfig(code, def), container_(container), vendor_id_(0), + container_action_(NONE) { + if (!container) { + isc_throw(Unexpected, "null container?"); + } +} + +FlexOptionImpl::SubOptionConfig::~SubOptionConfig() { +} + +FlexOptionImpl::FlexOptionImpl() { +} + +FlexOptionImpl::~FlexOptionImpl() { + sub_option_config_map_.clear(); + option_config_map_.clear(); +} + +void +FlexOptionImpl::configure(ConstElementPtr options) { + if (!options) { + isc_throw(BadValue, "'options' parameter is mandatory"); + } + if (options->getType() != Element::list) { + isc_throw(BadValue, "'options' parameter must be a list"); + } + if (options->empty()) { + return; + } + for (auto option : options->listValue()) { + parseOptionConfig(option); + } +} + +void +FlexOptionImpl::parseOptionConfig(ConstElementPtr option) { + uint16_t family = CfgMgr::instance().getFamily(); + if (!option) { + isc_throw(BadValue, "null option element"); + } + if (option->getType() != Element::map) { + isc_throw(BadValue, "option element is not a map"); + } + // See SimpleParser::checkKeywords + for (auto entry : option->mapValue()) { + if (OPTION_PARAMETERS.count(entry.first) == 0) { + isc_throw(BadValue, "unknown parameter '" << entry.first << "'"); + } + Element::types expected = OPTION_PARAMETERS.at(entry.first); + if (entry.second->getType() == expected) { + continue; + } + isc_throw(BadValue, "'" << entry.first << "' must be " + << (expected == Element::integer ? "an " : "a ") + << Element::typeToName(expected) + << ": " << entry.second->str()); + } + ConstElementPtr code_elem = option->get("code"); + ConstElementPtr name_elem = option->get("name"); + ConstElementPtr space_elem = option->get("space"); + ConstElementPtr csv_format_elem = option->get("csv-format"); + ConstElementPtr class_elem = option->get("client-class"); + ConstElementPtr sub_options = option->get("sub-options"); + if (!code_elem && !name_elem) { + isc_throw(BadValue, "'code' or 'name' must be specified: " + << option->str()); + } + string space; + Option::Universe universe; + if (family == AF_INET) { + space = DHCP4_OPTION_SPACE; + universe = Option::V4; + } else { + space = DHCP6_OPTION_SPACE; + universe = Option::V6; + } + if (space_elem) { + space = space_elem->stringValue(); + if (!OptionSpace::validateName(space)) { + isc_throw(BadValue, "'" << space << "' is not a valid space name"); + } + } + // The code is always set below but some compilers can't see that... + uint16_t code = 0; + if (code_elem) { + int64_t value = code_elem->intValue(); + int64_t max_code; + if (family == AF_INET) { + max_code = numeric_limits<uint8_t>::max(); + } else { + max_code = numeric_limits<uint16_t>::max(); + } + if ((value < 0) || (value > max_code)) { + isc_throw(OutOfRange, "invalid 'code' value " << value + << " not in [0.." << max_code << "]"); + } + if (space == DHCP4_OPTION_SPACE) { + if (value == DHO_PAD) { + isc_throw(BadValue, + "invalid 'code' value 0: reserved for PAD"); + } else if (value == DHO_END) { + isc_throw(BadValue, + "invalid 'code' value 255: reserved for END"); + } + } else if (space == DHCP6_OPTION_SPACE) { + if (value == 0) { + isc_throw(BadValue, "invalid 'code' value 0: reserved"); + } + } + code = static_cast<uint16_t>(value); + } + OptionDefinitionPtr def; + if (name_elem) { + string name = name_elem->stringValue(); + if (name.empty()) { + isc_throw(BadValue, "'name' must not be empty"); + } + def = LibDHCP::getOptionDef(space, name); + if (!def) { + def = LibDHCP::getRuntimeOptionDef(space, name); + } + if (!def) { + def = LibDHCP::getLastResortOptionDef(space, name); + } + if (!def) { + isc_throw(BadValue, "no known '" << name << "' option in '" + << space << "' space"); + } + if (code_elem && (def->getCode() != code)) { + isc_throw(BadValue, "option '" << name << "' is defined as code: " + << def->getCode() << ", not the specified code: " + << code); + } + code = def->getCode(); + } + + bool csv_format = false; + if (csv_format_elem) { + csv_format = csv_format_elem->boolValue(); + } + + if (!csv_format && !sub_options) { + // No definition means no csv format. + if (def) { + def.reset(); + } + } else if (!def) { + // Definition is required with csv format. + def = isc::dhcp::LibDHCP::getOptionDef(space, code); + if (!def) { + def = isc::dhcp::LibDHCP::getRuntimeOptionDef(space, code); + } + if (!def) { + def = isc::dhcp::LibDHCP::getLastResortOptionDef(space, code); + } + if (!def && csv_format) { + isc_throw(BadValue, "no known option with code '" << code + << "' in '" << space << "' space"); + } + } + + OptionConfigPtr opt_cfg(new OptionConfig(code, def)); + if (class_elem) { + opt_cfg->setClass(class_elem->stringValue()); + } + + // opt_cfg initial action is NONE. + if (sub_options) { + string action; + if (option->contains("add")) { + action = "add"; + } else if (option->contains("supersede")) { + action = "supersede"; + } else if (option->contains("remove")) { + action = "remove"; + } + if (!action.empty()) { + isc_throw(BadValue, "'sub-options' and '" << action << "' are " + << "incompatible in the same entry"); + } + parseSubOptions(sub_options, opt_cfg, universe); + } else { + parseAction(option, opt_cfg, universe, + "add", ADD, EvalContext::PARSER_STRING); + parseAction(option, opt_cfg, universe, + "supersede", SUPERSEDE, EvalContext::PARSER_STRING); + parseAction(option, opt_cfg, universe, + "remove", REMOVE, EvalContext::PARSER_BOOL); + + if (opt_cfg->getAction() == NONE) { + isc_throw(BadValue, "no action: " << option->str()); + } + + // The [] operator creates the item if it does not exist before + // returning a reference to it. + OptionConfigList& opt_lst = option_config_map_[code]; + opt_lst.push_back(opt_cfg); + } +} + +void +FlexOptionImpl::parseSubOptions(ConstElementPtr sub_options, + OptionConfigPtr opt_cfg, + Option::Universe universe) { + for (ConstElementPtr sub_option : sub_options->listValue()) { + parseSubOption(sub_option, opt_cfg, universe); + } +} + +void +FlexOptionImpl::parseSubOption(ConstElementPtr sub_option, + OptionConfigPtr opt_cfg, + Option::Universe universe) { + if (!sub_option) { + isc_throw(BadValue, "null sub-option element"); + } + if (sub_option->getType() != Element::map) { + isc_throw(BadValue, "sub-option element is not a map"); + } + // See SimpleParser::checkKeywords + for (auto entry : sub_option->mapValue()) { + if (SUB_OPTION_PARAMETERS.count(entry.first) == 0) { + isc_throw(BadValue, "unknown parameter '" << entry.first << "'"); + } + Element::types expected = SUB_OPTION_PARAMETERS.at(entry.first); + if (entry.second->getType() == expected) { + continue; + } + isc_throw(BadValue, "'" << entry.first << "' must be " + << (expected == Element::integer ? "an " : "a ") + << Element::typeToName(expected) + << ": " << entry.second->str()); + } + ConstElementPtr code_elem = sub_option->get("code"); + ConstElementPtr name_elem = sub_option->get("name"); + ConstElementPtr space_elem = sub_option->get("space"); + ConstElementPtr csv_format_elem = sub_option->get("csv-format"); + ConstElementPtr class_elem = sub_option->get("client-class"); + if (!code_elem && !name_elem) { + isc_throw(BadValue, "'code' or 'name' must be specified: " + << sub_option->str()); + } + string space; + if (space_elem) { + space = space_elem->stringValue(); + if (!OptionSpace::validateName(space)) { + isc_throw(BadValue, "'" << space << "' is not a valid space name"); + } + } else { + OptionDefinitionPtr opt_def = opt_cfg->getOptionDef(); + if (!opt_def) { + isc_throw(BadValue, "container is not defined: can't get space"); + } + space = opt_def->getEncapsulatedSpace(); + if (space.empty()) { + isc_throw(BadValue, "container does not encapsulate a space"); + } + } + uint32_t vendor_id = LibDHCP::optionSpaceToVendorId(space); + uint16_t code; + if (code_elem) { + int64_t value = code_elem->intValue(); + int64_t max_code; + if (universe == Option::V4) { + max_code = numeric_limits<uint8_t>::max(); + } else { + max_code = numeric_limits<uint16_t>::max(); + } + if ((value < 0) || (value > max_code)) { + isc_throw(OutOfRange, "invalid 'code' value " << value + << " not in [0.." << max_code << "]"); + } + code = static_cast<uint16_t>(value); + } + OptionDefinitionPtr def; + if (name_elem) { + string name = name_elem->stringValue(); + if (name.empty()) { + isc_throw(BadValue, "'name' must not be empty"); + } + def = LibDHCP::getOptionDef(space, name); + if (!def && vendor_id) { + def = LibDHCP::getVendorOptionDef(universe, vendor_id, name); + } + if (!def) { + def = LibDHCP::getRuntimeOptionDef(space, name); + } + if (!def) { + isc_throw(BadValue, "no known '" << name << "' sub-option in '" + << space << "' space"); + } + if (code_elem && (def->getCode() != code)) { + isc_throw(BadValue, "sub-option '" << name + << "' is defined as code: " << def->getCode() + << ", not the specified code: " << code); + } + code = def->getCode(); + } + + bool csv_format = false; + if (csv_format_elem) { + csv_format = csv_format_elem->boolValue(); + } + + if (!csv_format) { + // No definition means no csv format. + if (def) { + def.reset(); + } + } else if (!def) { + // Definition is required with csv format. + def = isc::dhcp::LibDHCP::getOptionDef(space, code); + if (!def && vendor_id) { + def = LibDHCP::getVendorOptionDef(universe, vendor_id, code); + } + if (!def) { + def = isc::dhcp::LibDHCP::getRuntimeOptionDef(space, code); + } + if (!def) { + isc_throw(BadValue, "no known sub-option with code '" << code + << "' in '" << space << "' space"); + } + } + + SubOptionConfigPtr sub_cfg(new SubOptionConfig(code, def, opt_cfg)); + if (vendor_id) { + if (((universe == Option::V4) && + (opt_cfg->getCode() == DHO_VIVSO_SUBOPTIONS)) || + ((universe == Option::V6) && + (opt_cfg->getCode() == D6O_VENDOR_OPTS))) { + sub_cfg->setVendorId(vendor_id); + } + } + if (class_elem) { + sub_cfg->setClass(class_elem->stringValue()); + } + + // sub_cfg initial action is NONE. + parseAction(sub_option, sub_cfg, universe, + "add", ADD, EvalContext::PARSER_STRING); + parseAction(sub_option, sub_cfg, universe, + "supersede", SUPERSEDE, EvalContext::PARSER_STRING); + parseAction(sub_option, sub_cfg, universe, + "remove", REMOVE, EvalContext::PARSER_BOOL); + + if (sub_cfg->getAction() == NONE) { + isc_throw(BadValue, "no action: " << sub_option->str()); + } + + ConstElementPtr container_add = sub_option->get("container-add"); + ConstElementPtr container_remove = sub_option->get("container-remove"); + if ((sub_cfg->getAction() == ADD) || (sub_cfg->getAction() == SUPERSEDE)) { + sub_cfg->setContainerAction(ADD); + if (container_add && !container_add->boolValue()) { + sub_cfg->setContainerAction(NONE); + } + } else if (sub_cfg->getAction() == REMOVE) { + sub_cfg->setContainerAction(REMOVE); + if (container_remove && !container_remove->boolValue()) { + sub_cfg->setContainerAction(NONE); + } + } + + // The [] operator creates the item if it does not exist before + // returning a reference to it. + uint16_t opt_code = opt_cfg->getCode(); + SubOptionConfigMap& sub_map = sub_option_config_map_[opt_code]; + if (sub_map.count(code)) { + isc_throw(BadValue, "sub-option " << code << " of option " << opt_code + << " was already specified"); + } + sub_map[code] = sub_cfg; +} + +void +FlexOptionImpl::logClass(const ClientClass& client_class, uint16_t code) { + LOG_DEBUG(flex_option_logger, DBGLVL_TRACE_BASIC, + FLEX_OPTION_PROCESS_CLIENT_CLASS) + .arg(client_class) + .arg(code); + return; +} + +void +FlexOptionImpl::logAction(Action action, uint16_t code, + const string& value) { + if (action == NONE) { + return; + } + if (action == REMOVE) { + LOG_DEBUG(flex_option_logger, DBGLVL_TRACE_BASIC, + FLEX_OPTION_PROCESS_REMOVE) + .arg(code); + return; + } + ostringstream repr; + if (str::isPrintable(value)) { + repr << "'" << value << "'"; + } else { + repr << "0x" << hex; + for (const char& ch : value) { + repr << setw(2) << setfill('0') << static_cast<unsigned>(ch); + } + } + if (action == SUPERSEDE) { + LOG_DEBUG(flex_option_logger, DBGLVL_TRACE_BASIC, + FLEX_OPTION_PROCESS_SUPERSEDE) + .arg(code) + .arg(repr.str()); + } else { + LOG_DEBUG(flex_option_logger, DBGLVL_TRACE_BASIC, + FLEX_OPTION_PROCESS_ADD) + .arg(code) + .arg(repr.str()); + } +} + +void +FlexOptionImpl::logAction(Action action, uint16_t code, + uint32_t vendor_id) { + if (action == SUPERSEDE) { + LOG_DEBUG(flex_option_logger, DBGLVL_TRACE_BASIC, + FLEX_OPTION_PROCESS_SUPERSEDE) + .arg(code) + .arg(vendor_id); + } else { + LOG_DEBUG(flex_option_logger, DBGLVL_TRACE_BASIC, + FLEX_OPTION_PROCESS_ADD) + .arg(code) + .arg(vendor_id); + } +} + +void +FlexOptionImpl::logSubClass(const ClientClass& client_class, uint16_t code, + uint16_t container_code) { + LOG_DEBUG(flex_option_logger, DBGLVL_TRACE_BASIC, + FLEX_OPTION_PROCESS_SUB_CLIENT_CLASS) + .arg(client_class) + .arg(code) + .arg(container_code); + return; +} + +void +FlexOptionImpl::logSubAction(Action action, uint16_t code, + uint16_t container_code, + const string& value) { + if (action == NONE) { + return; + } + if (action == REMOVE) { + LOG_DEBUG(flex_option_logger, DBGLVL_TRACE_BASIC, + FLEX_OPTION_PROCESS_SUB_REMOVE) + .arg(code) + .arg(container_code); + return; + } + ostringstream repr; + if (str::isPrintable(value)) { + repr << "'" << value << "'"; + } else { + repr << "0x" << hex; + for (const char& ch : value) { + repr << setw(2) << setfill('0') << static_cast<unsigned>(ch); + } + } + if (action == SUPERSEDE) { + LOG_DEBUG(flex_option_logger, DBGLVL_TRACE_BASIC, + FLEX_OPTION_PROCESS_SUB_SUPERSEDE) + .arg(code) + .arg(container_code) + .arg(repr.str()); + } else { + LOG_DEBUG(flex_option_logger, DBGLVL_TRACE_BASIC, + FLEX_OPTION_PROCESS_SUB_ADD) + .arg(code) + .arg(container_code) + .arg(repr.str()); + } +} + +bool +FlexOptionImpl::checkVendor(OptionPtr opt, uint32_t vendor_id) { + OptionVendorPtr vendor = boost::dynamic_pointer_cast<OptionVendor>(opt); + bool ret = (!vendor || (vendor->getVendorId() == vendor_id)); + if (!ret) { + LOG_DEBUG(flex_option_logger, DBGLVL_TRACE_BASIC, + FLEX_OPTION_PROCESS_VENDOR_ID_MISMATCH) + .arg(opt->getType()) + .arg(vendor->getVendorId()) + .arg(vendor_id); + } + return (ret); +} + +} // end of namespace flex_option +} // end of namespace isc diff --git a/src/hooks/dhcp/flex_option/flex_option.dox b/src/hooks/dhcp/flex_option/flex_option.dox new file mode 100644 index 0000000..9fc84d6 --- /dev/null +++ b/src/hooks/dhcp/flex_option/flex_option.dox @@ -0,0 +1,142 @@ +// Copyright (C) 2019-2022 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/. + +/** + +@page libdhcp_flex_option Kea Flexible Option Hooks Library + +@section libdhcp_flex_optionIntro Introduction + +Welcome to Kea Flexible Option Hooks Library. This documentation is +addressed to developers who are interested in the internal operation +of the Flexible Option library. This file provides information needed +to understand and perhaps extend this library. + +This documentation is stand-alone: you should have read and understood +the <a href="https://reports.kea.isc.org/dev_guide/">Kea +Developer's Guide</a> and in particular its section about hooks. + +@section libdhcp_flex_optionUser Now To Use libdhcp_flex_option +## Introduction +libdhcp_flex_option is a hooks library which customize the option value +setting mechanism by introducing values from expression evaluation. +Instead of relying on static configured values, it allows user to define +expressions and use values of that expressions computed for options +added to response packets. + +## Configuring the DHCP Modules + +It must be configured as a hook library for the desired DHCP server +modules. Note that the flex_option library is installed alongside the +Kea libraries in "<install-dir>/lib" where <install-dir> is determined +by the --prefix option of the configure script. It defaults to +"/usr/local". Assuming the default value then, configuring kea-dhcp4 +to load the flex_option library could be done with the following Kea4 +configuration: + +@code +"Dhcp4": { + "hooks-libraries": [ + { "library": "/usr/local/lib/libdhcp_flex_option.so", + "parameters": { + "options": [ + { + "code": 100, + "add": "concat(relay4[2].hex, 'abc')", + "csv-format": false + } + ] + } + }, + ... + ] +} +@endcode + +To configure it for kea-dhcp6, the commands are simply as shown below: + +@code +"Dhcp6": { + "hooks-libraries": [ + { "library": "/usr/local/lib/libdhcp_flex_option.so", + "parameters": { + "options": [ + { + "code": 100, + "add": "concat(relay6[0].option[37].hex, 'abc')", + "csv-format": false + } + ] + } + }, + ... + ] +} +@endcode + +The sole parameter is a options list of options with: + - @b code - Specifies the option code. + - @b name - Specifies the option name, at least the option code or name + must be given. + - @b add - Specifies the add action: it takes a string expression on + the query packet. If the option does not already exist in the response + packet and the expression evaluates to a not empty value, the option + with the value is added to the response. + - @b supersede - Specifies the supersede action: it takes a string expression + on the query packet. If the expression evaluates to a not empty value, + the option with the value is added to the response. If it already exists + it is overwritten. + - @b remove - Specifies the remove action: it takes a boolean expression + on the query packet. If the expression evaluates to true and the option + already exists in the response packet, the option is removed from the + response. Only one action can be specified. + - @b csv-format - Specifies the option format used for input. If not + specified, it will default to false (raw data). When enabled, the option + data will be parsed using csv format and packed according to the option + definition. + - @b client-class - Specifies the guard i.e. the client class the query + must belong to. + +Note for the rare options which can be empty this mechanism does not work. +The proposed solution in this case is to use a client class to set the +empty value to the option in a option-data clause. + +The sub-option support is similar to the option one with in addition: + - @b container-add - Specifies if the container option should be created + when it does not exist (default behavior) in add and supersede actions. + - @b container-remove - Specifies if the container option should be + removed when the sub-option removal left it empty (default behavior) + in remove action. +Sub-option configuration is a list in a @b sub-option entry of the container +option configuration. + +## Internal operation + +The first function called in @ref load() located in the +flex_option_callouts.cc. It checks if the necessary parameter is passed and +decodes the option configurations. @ref unload() free the configuration. + +Kea engine checks if the library has functions that match known hook point +names. This library has two such functions: @ref pkt4_send and @ref pkt6_send, +all located in flex_option_callouts.cc. + +kea-dhcp4 server calls @ref pkt4_send (and kea-dhcp6 @ref pkt6_send) with +the query and response packets. For each configured option and sub-option +the action is applied by the template @c process located in flex_option.h. +When required the expression is evaluated on the query packet and the result +is used by the action for instance to add a new option. + +In case any other library sets the SKIP flag before pkt4_send or pkt6_send, an +exception with the message "the packet pack already handled" will be thrown, to +indicate that the action can not be properly performed. +To fix this, all other libraries which might set the SKIP flag must appear +in the server configuration after this library. + +@section libdhcp_flex_optionMTCompatibility Multi-Threading Compatibility + +The libdhcp_flex_option hooks library is compatible with multi-threading. + +*/ diff --git a/src/hooks/dhcp/flex_option/flex_option.h b/src/hooks/dhcp/flex_option/flex_option.h new file mode 100644 index 0000000..bda8ca1 --- /dev/null +++ b/src/hooks/dhcp/flex_option/flex_option.h @@ -0,0 +1,649 @@ +// Copyright (C) 2019-2022 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 FLEX_OPTION_H +#define FLEX_OPTION_H + +#include <cc/data.h> +#include <cc/simple_parser.h> +#include <dhcp/classify.h> +#include <dhcp/libdhcp++.h> +#include <dhcp/option.h> +#include <dhcp/option_definition.h> +#include <dhcp/option_vendor.h> +#include <dhcp/std_option_defs.h> +#include <eval/evaluate.h> +#include <eval/token.h> +#include <util/strutil.h> + +#include <boost/algorithm/string/split.hpp> +#include <boost/algorithm/string/classification.hpp> + +#include <map> +#include <string> + +namespace isc { +namespace flex_option { + +/// @brief Flex Option implementation. +/// +/// The implementation can be divided into two parts: +/// - the configuration parsed and stored by load() +/// - the response packet processing performed by the process method +/// +class FlexOptionImpl { +public: + + /// @brief Action. + /// + /// Currently supported actions are: + /// - add (if not already existing) + /// - supersede (as add but also when already existing) + /// - remove + enum Action { + NONE, + ADD, + SUPERSEDE, + REMOVE + }; + + /// @brief Base option configuration. + /// + /// Per option configuration. + class OptionConfig { + public: + /// @brief Constructor. + /// + /// @param code the option code. + /// @param def the option definition. + OptionConfig(uint16_t code, isc::dhcp::OptionDefinitionPtr def); + + /// @brief Destructor. + virtual ~OptionConfig(); + + /// @brief Return option code. + /// + /// @return option code. + uint16_t getCode() const { + return (code_); + } + + /// @brief Return option definition. + /// + /// @return option definition. + isc::dhcp::OptionDefinitionPtr getOptionDef() const { + return (def_); + } + + /// @brief Set action. + /// + /// @param action the action. + void setAction(Action action) { + action_ = action; + } + + /// @brief Return action. + /// + /// @return action. + Action getAction() const { + return (action_); + } + + /// @brief Set textual expression. + /// + /// @param text the textual expression. + void setText(const std::string& text) { + text_ = text; + }; + + /// @brief Get textual expression. + /// + /// @return textual expression. + const std::string& getText() const { + return (text_); + } + + /// @brief Set match expression. + /// + /// @param expr the match expression. + void setExpr(const isc::dhcp::ExpressionPtr& expr) { + expr_ = expr; + } + + /// @brief Get match expression. + /// + /// @return the match expression. + const isc::dhcp::ExpressionPtr& getExpr() const { + return (expr_); + } + + /// @brief Set client class. + /// + /// @param class_name the client class aka guard name. + void setClass(const isc::dhcp::ClientClass& class_name) { + class_ = class_name; + } + + /// @brief Get client class. + /// + /// @return client class aka guard name. + const isc::dhcp::ClientClass& getClass() const { + return (class_); + } + + private: + /// @brief The code. + uint16_t code_; + + /// @brief The option definition. + /// @note This value is set only when csv-format is true. + isc::dhcp::OptionDefinitionPtr def_; + + /// @brief The action. + Action action_; + + /// @brief The textual expression. + std::string text_; + + /// @brief The match expression. + isc::dhcp::ExpressionPtr expr_; + + /// @brief The client class aka guard name. + isc::dhcp::ClientClass class_; + }; + + /// @brief The type of shared pointers to option config. + typedef boost::shared_ptr<OptionConfig> OptionConfigPtr; + + /// @brief The type of lists of shared pointers to option config. + typedef std::list<OptionConfigPtr> OptionConfigList; + + /// @brief The type of the option config map. + typedef std::map<uint16_t, OptionConfigList> OptionConfigMap; + + /// @brief Sub-option configuration. + /// + /// Per sub-option configuration. + class SubOptionConfig : public OptionConfig { + public: + /// @brief Constructor. + /// + /// @param code the sub-option code. + /// @param def the sub-option definition. + /// @param container pointer to the container option. + SubOptionConfig(uint16_t code, isc::dhcp::OptionDefinitionPtr def, + OptionConfigPtr container); + + /// @brief Destructor. + virtual ~SubOptionConfig(); + + /// @brief Set vendor id. + /// + /// @param vendor_id the vendor id. + void setVendorId(uint32_t vendor_id) { + vendor_id_ = vendor_id; + } + + /// @brief Return vendor id. + /// + /// @return vendor id. + uint32_t getVendorId() const { + return (vendor_id_); + } + + /// @brief Return container code. + /// + /// @return container code. + uint16_t getContainerCode() const { + return (container_->getCode()); + } + + /// @brief Return container definition. + /// + /// @return container definition. + isc::dhcp::OptionDefinitionPtr getContainerDef() const { + return (container_->getOptionDef()); + } + + /// @brief Return container client class. + /// + /// @return container client class name. + const isc::dhcp::ClientClass& getContainerClass() const { + return (container_->getClass()); + } + + /// @brief Set action on the container. + /// + /// @param action the action. + void setContainerAction(Action action) { + container_action_ = action; + } + + /// @brief Return action on the container. + /// + /// @return action on the container. + Action getContainerAction() const { + return (container_action_); + } + + private: + /// @brief Pointer to the container option configuration. + OptionConfigPtr container_; + + /// @brief Vendor id (0 when the container is not a vendor one). + uint32_t vendor_id_; + + /// @brief The action on the container. + Action container_action_; + }; + + /// @brief The type of shared pointers to sub-option config. + typedef boost::shared_ptr<SubOptionConfig> SubOptionConfigPtr; + + /// @brief The type of the sub-option config map. + /// @note the index is the sub-option code. + typedef std::map<uint16_t, SubOptionConfigPtr> SubOptionConfigMap; + + /// @brief The type of the map of sub-option config maps. + /// @note the index is the container option code. + typedef std::map<uint16_t, SubOptionConfigMap> SubOptionConfigMapMap; + + /// @brief Constructor. + FlexOptionImpl(); + + /// @brief Destructor. + ~FlexOptionImpl(); + + /// @brief Get the option config map. + /// + /// @return The option config map. + const OptionConfigMap& getOptionConfigMap() const { + return (option_config_map_); + } + + /// @brief Get the sub-option config map of maps. + /// + /// @return The sub-option config map of maps. + const SubOptionConfigMapMap& getSubOptionConfigMap() const { + return (sub_option_config_map_); + } + + /// @brief Configure the Flex Option implementation. + /// + /// @param options The element with option config list. + /// @throw BadValue and similar exceptions on error. + void configure(isc::data::ConstElementPtr options); + + /// @brief Process a query / response pair. + /// + /// @tparam PktType The type of pointers to packets: Pkt4Ptr or Pkt6Ptr. + /// @param universe The option universe: Option::V4 or Option::V6. + /// @param query The query packet. + /// @param response The response packet. + template <typename PktType> + void process(isc::dhcp::Option::Universe universe, + PktType query, PktType response) { + for (auto pair : getOptionConfigMap()) { + for (const OptionConfigPtr& opt_cfg : pair.second) { + const isc::dhcp::ClientClass& client_class = + opt_cfg->getClass(); + if (!client_class.empty()) { + if (!query->inClass(client_class)) { + logClass(client_class, opt_cfg->getCode()); + continue; + } + } + std::string value; + isc::dhcp::OptionBuffer buffer; + uint16_t code = opt_cfg->getCode(); + isc::dhcp::OptionPtr opt = response->getOption(code); + isc::dhcp::OptionDefinitionPtr def = opt_cfg->getOptionDef(); + switch (opt_cfg->getAction()) { + case NONE: + break; + case ADD: + // Don't add if option is already there. + if (opt) { + break; + } + // Do nothing is the expression evaluates to empty. + value = isc::dhcp::evaluateString(*opt_cfg->getExpr(), + *query); + if (value.empty()) { + break; + } + // Set the value. + if (def) { + std::vector<std::string> split_vec = + isc::util::str::tokens(value, ",", true); + opt = def->optionFactory(universe, code, split_vec); + } else { + buffer.assign(value.begin(), value.end()); + opt.reset(new isc::dhcp::Option(universe, code, + buffer)); + } + // Add the option. + response->addOption(opt); + logAction(ADD, code, value); + break; + case SUPERSEDE: + // Do nothing is the expression evaluates to empty. + value = isc::dhcp::evaluateString(*opt_cfg->getExpr(), + *query); + if (value.empty()) { + break; + } + // Set the value. + if (def) { + std::vector<std::string> split_vec = + isc::util::str::tokens(value, ",", true); + opt = def->optionFactory(universe, code, + split_vec); + } else { + buffer.assign(value.begin(), value.end()); + opt.reset(new isc::dhcp::Option(universe, + code, + buffer)); + } + // Remove the option if already there. + while (response->getOption(code)) { + response->delOption(code); + } + // Add the option. + response->addOption(opt); + logAction(SUPERSEDE, code, value); + break; + case REMOVE: + // Nothing to remove if option is not present. + if (!opt) { + break; + } + // Do nothing is the expression evaluates to false. + if (!isc::dhcp::evaluateBool(*opt_cfg->getExpr(), *query)) { + break; + } + // Remove the option. + while (response->getOption(code)) { + response->delOption(code); + } + logAction(REMOVE, code, ""); + break; + } + } + } + for (auto pair : getSubOptionConfigMap()) { + for (auto sub_pair : pair.second) { + const SubOptionConfigPtr& sub_cfg = sub_pair.second; + uint16_t sub_code = sub_cfg->getCode(); + uint16_t opt_code = sub_cfg->getContainerCode(); + const isc::dhcp::ClientClass& opt_class = + sub_cfg->getContainerClass(); + if (!opt_class.empty()) { + if (!query->inClass(opt_class)) { + logClass(opt_class, opt_code); + continue; + } + } + const isc::dhcp::ClientClass& sub_class = + sub_cfg->getClass(); + if (!sub_class.empty()) { + if (!query->inClass(sub_class)) { + logSubClass(sub_class, sub_code, opt_code); + continue; + } + } + std::string value; + isc::dhcp::OptionBuffer buffer; + isc::dhcp::OptionPtr opt = response->getOption(opt_code); + isc::dhcp::OptionPtr sub; + isc::dhcp::OptionDefinitionPtr def = sub_cfg->getOptionDef(); + uint32_t vendor_id = sub_cfg->getVendorId(); + switch (sub_cfg->getAction()) { + case NONE: + break; + case ADD: + // If no container and no magic add return + if (!opt && (sub_cfg->getContainerAction() != ADD)) { + break; + } + // Do nothing is the expression evaluates to empty. + value = isc::dhcp::evaluateString(*sub_cfg->getExpr(), + *query); + if (value.empty()) { + break; + } + // Check vendor id mismatch. + if (opt && vendor_id && !checkVendor(opt, vendor_id)) { + break; + } + // Don't add if sub-option is already there. + if (opt && opt->getOption(sub_code)) { + break; + } + // Set the value. + if (def) { + std::vector<std::string> split_vec = + isc::util::str::tokens(value, ",", true); + sub = def->optionFactory(universe, sub_code, + split_vec); + } else { + buffer.assign(value.begin(), value.end()); + sub.reset(new isc::dhcp::Option(universe, sub_code, + buffer)); + } + // If the container does not exist add it. + if (!opt) { + if (!vendor_id) { + opt.reset(new isc::dhcp::Option(universe, + opt_code)); + } else { + opt.reset(new isc::dhcp::OptionVendor(universe, + vendor_id)); + } + response->addOption(opt); + if (vendor_id) { + logAction(ADD, opt_code, vendor_id); + } else { + logAction(ADD, opt_code, ""); + } + } + // Add the sub-option. + opt->addOption(sub); + logSubAction(ADD, sub_code, opt_code, value); + break; + case SUPERSEDE: + // If no container and no magic add return + if (!opt && (sub_cfg->getContainerAction() != ADD)) { + break; + } + // Do nothing is the expression evaluates to empty. + value = isc::dhcp::evaluateString(*sub_cfg->getExpr(), + *query); + if (value.empty()) { + break; + } + // Check vendor id mismatch. + if (opt && vendor_id && !checkVendor(opt, vendor_id)) { + break; + } + // Set the value. + if (def) { + std::vector<std::string> split_vec = + isc::util::str::tokens(value, ",", true); + sub = def->optionFactory(universe, sub_code, + split_vec); + } else { + buffer.assign(value.begin(), value.end()); + sub.reset(new isc::dhcp::Option(universe, sub_code, + buffer)); + } + // Remove the sub-option if already there. + if (opt) { + while (opt->getOption(sub_code)) { + opt->delOption(sub_code); + } + } + // If the container does not exist add it. + if (!opt) { + if (!vendor_id) { + opt.reset(new isc::dhcp::Option(universe, + opt_code)); + } else { + opt.reset(new isc::dhcp::OptionVendor(universe, + vendor_id)); + } + response->addOption(opt); + if (vendor_id) { + logAction(ADD, opt_code, vendor_id); + } else { + logAction(ADD, opt_code, ""); + } + } + // Add the sub-option. + opt->addOption(sub); + logSubAction(SUPERSEDE, sub_code, opt_code, value); + break; + case REMOVE: + // Nothing to remove if container is not present. + if (!opt) { + break; + } + sub = opt->getOption(sub_code); + // Nothing to remove if sub-option is not present. + if (!sub) { + break; + } + // Do nothing is the expression evaluates to false. + if (!isc::dhcp::evaluateBool(*sub_cfg->getExpr(), *query)) { + break; + } + // Check vendor id mismatch. + if (opt && vendor_id && !checkVendor(opt, vendor_id)) { + break; + } + // Remove the sub-option. + while (opt->getOption(sub_code)) { + opt->delOption(sub_code); + } + logSubAction(REMOVE, sub_code, opt_code, ""); + // Remove the empty container when wanted. + if ((sub_cfg->getContainerAction() == REMOVE) && + opt->getOptions().empty()) { + response->delOption(opt_code); + logAction(REMOVE, opt_code, ""); + } + break; + } + } + } + } + + + /// @brief Log the client class for option. + /// + /// @param client_class The client class aka guard name. + /// @param code The option code. + static void logClass(const isc::dhcp::ClientClass &client_class, + uint16_t code); + + /// @brief Log the action for option. + /// + /// @param action The action. + /// @param code The option code. + /// @param value The option value ("" for remove). + static void logAction(Action action, uint16_t code, + const std::string& value); + + /// @brief Log the action for option. + /// + /// @param action The action. + /// @param code The option code. + /// @param vendor_id The vendor option vendor id. + static void logAction(Action action, uint16_t code, uint32_t vendor_id); + + /// @brief Log the client class for sub-option. + /// + /// @param client_class The client class aka guard name. + /// @param code The sub-option code. + /// @param container_code The container option code. + static void logSubClass(const isc::dhcp::ClientClass &client_class, + uint16_t code, uint16_t container_code); + + /// @brief Log the action for sub-option. + /// + /// @param action The action. + /// @param code The sub-option code. + /// @param container_code The container option code. + /// @param value The option value ("" for remove). + static void logSubAction(Action action, uint16_t code, + uint16_t container_code, + const std::string& value); + + /// @brief Check vendor option vendor id mismatch. + /// + /// @param opt The pointer to the option. + /// @param vendor_id The vendor option vendor id. + static bool checkVendor(isc::dhcp::OptionPtr opt, uint32_t vendor_id); + +protected: + /// @brief Get a mutable reference to the option config map + /// + /// @return The option config map. + OptionConfigMap& getMutableOptionConfigMap() { + return (option_config_map_); + } + + /// @brief Get a mutable reference to the sub-option config map of maps. + /// + /// @return The sub-option config map of maps. + SubOptionConfigMapMap& getMutableSubOptionConfigMap() { + return (sub_option_config_map_); + } + +private: + /// @brief Option parameters. + static const data::SimpleKeywords OPTION_PARAMETERS; + + /// @brief Sub-option parameters. + static const data::SimpleKeywords SUB_OPTION_PARAMETERS; + + /// @brief The option config map (code and pointer to option config). + OptionConfigMap option_config_map_; + + /// @brief The sub-option config map of maps. + SubOptionConfigMapMap sub_option_config_map_; + + /// @brief Parse an option config. + /// + /// @param option The element with option config. + /// @throw BadValue and similar exceptions on error. + void parseOptionConfig(isc::data::ConstElementPtr option); + + /// @brief Parse a sub-option. + /// + /// @param sub_option The sub-option element. + /// @param opt_cfg The container option configuration. + /// @param universe The universe. + void parseSubOption(isc::data::ConstElementPtr sub_option, + OptionConfigPtr opt_cfg, + isc::dhcp::Option::Universe universe); + + /// @brief Parse sub-options. + /// + /// @param sub_options The sub-option list. + /// @param opt_cfg The container option configuration. + /// @param universe The universe. + void parseSubOptions(isc::data::ConstElementPtr sub_options, + OptionConfigPtr opt_cfg, + isc::dhcp::Option::Universe universe); +}; + +/// @brief The type of shared pointers to Flex Option implementations. +typedef boost::shared_ptr<FlexOptionImpl> FlexOptionImplPtr; + +} // end of namespace flex_option +} // end of namespace isc +#endif diff --git a/src/hooks/dhcp/flex_option/flex_option_callouts.cc b/src/hooks/dhcp/flex_option/flex_option_callouts.cc new file mode 100644 index 0000000..97a90c2 --- /dev/null +++ b/src/hooks/dhcp/flex_option/flex_option_callouts.cc @@ -0,0 +1,169 @@ +// Copyright (C) 2019-2022 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 <flex_option.h> +#include <flex_option_log.h> +#include <cc/command_interpreter.h> +#include <hooks/hooks.h> +#include <dhcp/pkt4.h> +#include <dhcp/pkt6.h> +#include <dhcpsrv/cfgmgr.h> +#include <process/daemon.h> + +namespace isc { +namespace flex_option { + +FlexOptionImplPtr impl; + +} // end of namespace flex_option +} // end of namespace isc + +using namespace isc; +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::flex_option; +using namespace isc::hooks; +using namespace isc::process; + +// Functions accessed by the hooks framework use C linkage to avoid the name +// mangling that accompanies use of the C++ compiler as well as to avoid +// issues related to namespaces. +extern "C" { + +/// @brief This callout is called at the "pkt4_send" hook. +/// +/// It retrieves v4 query and response packets, and then adds, supersedes +/// or removes option values in the response according to expressions +/// evaluated on the query. +/// +/// @param handle CalloutHandle. +/// +/// @return 0 upon success, non-zero otherwise +int pkt4_send(CalloutHandle& handle) { + CalloutHandle::CalloutNextStep status = handle.getStatus(); + if (status == CalloutHandle::NEXT_STEP_DROP) { + return (0); + } + + // Sanity. + if (!impl) { + return (0); + } + + // Get the parameters. + Pkt4Ptr query; + Pkt4Ptr response; + handle.getArgument("query4", query); + handle.getArgument("response4", response); + + if (status == CalloutHandle::NEXT_STEP_SKIP) { + isc_throw(InvalidOperation, "packet pack already handled"); + } + + try { + impl->process<Pkt4Ptr>(Option::V4, query, response); + } catch (const std::exception& ex) { + LOG_ERROR(flex_option_logger, FLEX_OPTION_PROCESS_ERROR) + .arg(query->getLabel()) + .arg(ex.what()); + } + + return (0); +} + +/// @brief This callout is called at the "pkt6_send" hook. +/// +/// It retrieves v6 query and response packets, and then adds, supersedes +/// or removes option values in the response according to expressions +/// evaluated on the query. +/// +/// @param handle CalloutHandle. +/// +/// @return 0 upon success, non-zero otherwise +int pkt6_send(CalloutHandle& handle) { + CalloutHandle::CalloutNextStep status = handle.getStatus(); + if (status == CalloutHandle::NEXT_STEP_DROP) { + return (0); + } + + // Sanity. + if (!impl) { + return (0); + } + + if (status == CalloutHandle::NEXT_STEP_SKIP) { + isc_throw(InvalidOperation, "packet pack already handled"); + } + + // Get the parameters. + Pkt6Ptr query; + Pkt6Ptr response; + handle.getArgument("query6", query); + handle.getArgument("response6", response); + + try { + impl->process<Pkt6Ptr>(Option::V6, query, response); + } catch (const std::exception& ex) { + LOG_ERROR(flex_option_logger, FLEX_OPTION_PROCESS_ERROR) + .arg(query->getLabel()) + .arg(ex.what()); + } + + return (0); +} + +/// @brief This function is called when the library is loaded. +/// +/// @param handle library handle +/// @return 0 when initialization is successful, 1 otherwise +int load(LibraryHandle& handle) { + try { + // Make the hook library not loadable by d2 or ca. + uint16_t family = CfgMgr::instance().getFamily(); + const std::string& proc_name = Daemon::getProcName(); + if (family == AF_INET) { + if (proc_name != "kea-dhcp4") { + isc_throw(isc::Unexpected, "Bad process name: " << proc_name + << ", expected kea-dhcp4"); + } + } else { + if (proc_name != "kea-dhcp6") { + isc_throw(isc::Unexpected, "Bad process name: " << proc_name + << ", expected kea-dhcp6"); + } + } + + impl.reset(new FlexOptionImpl()); + ConstElementPtr options = handle.getParameter("options"); + impl->configure(options); + } catch (const std::exception& ex) { + LOG_ERROR(flex_option_logger, FLEX_OPTION_LOAD_ERROR) + .arg(ex.what()); + return (1); + } + + return (0); +} + +/// @brief This function is called when the library is unloaded. +/// +/// @return always 0. +int unload() { + impl.reset(); + LOG_INFO(flex_option_logger, FLEX_OPTION_UNLOAD); + return (0); +} + +/// @brief This function is called to retrieve the multi-threading compatibility. +/// +/// @return 1 which means compatible with multi-threading. +int multi_threading_compatible() { + return (1); +} + +} // end extern "C" diff --git a/src/hooks/dhcp/flex_option/flex_option_log.cc b/src/hooks/dhcp/flex_option/flex_option_log.cc new file mode 100644 index 0000000..6ff0106 --- /dev/null +++ b/src/hooks/dhcp/flex_option/flex_option_log.cc @@ -0,0 +1,17 @@ +// 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 <flex_option_log.h> + +namespace isc { +namespace flex_option { + +isc::log::Logger flex_option_logger("flex-option-hooks"); + +} // namespace flex_option +} // namespace isc diff --git a/src/hooks/dhcp/flex_option/flex_option_log.h b/src/hooks/dhcp/flex_option/flex_option_log.h new file mode 100644 index 0000000..d8c5569 --- /dev/null +++ b/src/hooks/dhcp/flex_option/flex_option_log.h @@ -0,0 +1,22 @@ +// 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/. + +#ifndef FLEX_OPTION_LOG_H +#define FLEX_OPTION_LOG_H + +#include <log/logger_support.h> +#include <log/macros.h> +#include <log/log_dbglevels.h> +#include <flex_option_messages.h> + +namespace isc { +namespace flex_option { + +extern isc::log::Logger flex_option_logger; + +} // end of namespace flex_option +} // end of namespace isc +#endif diff --git a/src/hooks/dhcp/flex_option/flex_option_messages.cc b/src/hooks/dhcp/flex_option/flex_option_messages.cc new file mode 100644 index 0000000..7b7a08a --- /dev/null +++ b/src/hooks/dhcp/flex_option/flex_option_messages.cc @@ -0,0 +1,41 @@ +// File created from ../../../../src/hooks/dhcp/flex_option/flex_option_messages.mes + +#include <cstddef> +#include <log/message_types.h> +#include <log/message_initializer.h> + +extern const isc::log::MessageID FLEX_OPTION_LOAD_ERROR = "FLEX_OPTION_LOAD_ERROR"; +extern const isc::log::MessageID FLEX_OPTION_PROCESS_ADD = "FLEX_OPTION_PROCESS_ADD"; +extern const isc::log::MessageID FLEX_OPTION_PROCESS_CLIENT_CLASS = "FLEX_OPTION_PROCESS_CLIENT_CLASS"; +extern const isc::log::MessageID FLEX_OPTION_PROCESS_ERROR = "FLEX_OPTION_PROCESS_ERROR"; +extern const isc::log::MessageID FLEX_OPTION_PROCESS_REMOVE = "FLEX_OPTION_PROCESS_REMOVE"; +extern const isc::log::MessageID FLEX_OPTION_PROCESS_SUB_ADD = "FLEX_OPTION_PROCESS_SUB_ADD"; +extern const isc::log::MessageID FLEX_OPTION_PROCESS_SUB_CLIENT_CLASS = "FLEX_OPTION_PROCESS_SUB_CLIENT_CLASS"; +extern const isc::log::MessageID FLEX_OPTION_PROCESS_SUB_REMOVE = "FLEX_OPTION_PROCESS_SUB_REMOVE"; +extern const isc::log::MessageID FLEX_OPTION_PROCESS_SUB_SUPERSEDE = "FLEX_OPTION_PROCESS_SUB_SUPERSEDE"; +extern const isc::log::MessageID FLEX_OPTION_PROCESS_SUPERSEDE = "FLEX_OPTION_PROCESS_SUPERSEDE"; +extern const isc::log::MessageID FLEX_OPTION_PROCESS_VENDOR_ID_MISMATCH = "FLEX_OPTION_PROCESS_VENDOR_ID_MISMATCH"; +extern const isc::log::MessageID FLEX_OPTION_UNLOAD = "FLEX_OPTION_UNLOAD"; + +namespace { + +const char* values[] = { + "FLEX_OPTION_LOAD_ERROR", "loading Flex Option hooks library failed: %1", + "FLEX_OPTION_PROCESS_ADD", "Added the option code %1 with value %2", + "FLEX_OPTION_PROCESS_CLIENT_CLASS", "Skip processing of the option code %1 for class '%2'", + "FLEX_OPTION_PROCESS_ERROR", "An error occurred processing query %1: %2", + "FLEX_OPTION_PROCESS_REMOVE", "Removed option code %1", + "FLEX_OPTION_PROCESS_SUB_ADD", "Added the sub-option code %1 in option code %2 with value %3", + "FLEX_OPTION_PROCESS_SUB_CLIENT_CLASS", "Skip processing of the sub-option code %1 in option code %2 for class '%3'", + "FLEX_OPTION_PROCESS_SUB_REMOVE", "Removed sub-option code %1 in option code %2", + "FLEX_OPTION_PROCESS_SUB_SUPERSEDE", "Supersedes the sub-option code %1 in option code %2 with value %3", + "FLEX_OPTION_PROCESS_SUPERSEDE", "Supersedes the option code %1 with value %2", + "FLEX_OPTION_PROCESS_VENDOR_ID_MISMATCH", "Skip processing of vendor option code %1 with vendor id %2 not matching wanted %3", + "FLEX_OPTION_UNLOAD", "Flex Option hooks library has been unloaded", + NULL +}; + +const isc::log::MessageInitializer initializer(values); + +} // Anonymous namespace + diff --git a/src/hooks/dhcp/flex_option/flex_option_messages.h b/src/hooks/dhcp/flex_option/flex_option_messages.h new file mode 100644 index 0000000..0ace6cf --- /dev/null +++ b/src/hooks/dhcp/flex_option/flex_option_messages.h @@ -0,0 +1,21 @@ +// File created from ../../../../src/hooks/dhcp/flex_option/flex_option_messages.mes + +#ifndef FLEX_OPTION_MESSAGES_H +#define FLEX_OPTION_MESSAGES_H + +#include <log/message_types.h> + +extern const isc::log::MessageID FLEX_OPTION_LOAD_ERROR; +extern const isc::log::MessageID FLEX_OPTION_PROCESS_ADD; +extern const isc::log::MessageID FLEX_OPTION_PROCESS_CLIENT_CLASS; +extern const isc::log::MessageID FLEX_OPTION_PROCESS_ERROR; +extern const isc::log::MessageID FLEX_OPTION_PROCESS_REMOVE; +extern const isc::log::MessageID FLEX_OPTION_PROCESS_SUB_ADD; +extern const isc::log::MessageID FLEX_OPTION_PROCESS_SUB_CLIENT_CLASS; +extern const isc::log::MessageID FLEX_OPTION_PROCESS_SUB_REMOVE; +extern const isc::log::MessageID FLEX_OPTION_PROCESS_SUB_SUPERSEDE; +extern const isc::log::MessageID FLEX_OPTION_PROCESS_SUPERSEDE; +extern const isc::log::MessageID FLEX_OPTION_PROCESS_VENDOR_ID_MISMATCH; +extern const isc::log::MessageID FLEX_OPTION_UNLOAD; + +#endif // FLEX_OPTION_MESSAGES_H diff --git a/src/hooks/dhcp/flex_option/flex_option_messages.mes b/src/hooks/dhcp/flex_option/flex_option_messages.mes new file mode 100644 index 0000000..f922c25 --- /dev/null +++ b/src/hooks/dhcp/flex_option/flex_option_messages.mes @@ -0,0 +1,59 @@ +# Copyright (C) 2019-2022 Internet Systems Consortium, Inc. ("ISC") + +% FLEX_OPTION_LOAD_ERROR loading Flex Option hooks library failed: %1 +This error message indicates an error during loading the Flex Option +hooks library. The details of the error are provided as argument of +the log message. + +% FLEX_OPTION_PROCESS_ADD Added the option code %1 with value %2 +This debug message is printed when an option was added into the response +packet. The option code and the value (between quotes if printable, in +hexadecimal if not) are provided. + +% FLEX_OPTION_PROCESS_CLIENT_CLASS Skip processing of the option code %1 for class '%2' +This debug message is printed when the processing for an option is skipped +because the query does not belongs to the client class. The option code and +the client class name are provided. + +% FLEX_OPTION_PROCESS_ERROR An error occurred processing query %1: %2 +This error message indicates an error during processing of a query +by the Flex Option hooks library. The client identification information +from the query and the details of the error are provided as arguments +of the log message. + +% FLEX_OPTION_PROCESS_REMOVE Removed option code %1 +This debug message is printed when an option was removed from the response +packet. The option code is provided. + +% FLEX_OPTION_PROCESS_SUB_ADD Added the sub-option code %1 in option code %2 with value %3 +This debug message is printed when an sub-option was added into the response +packet. The sub-option and container option codes, and the value +(between quotes if printable, in hexadecimal if not) are provided. + +% FLEX_OPTION_PROCESS_SUB_CLIENT_CLASS Skip processing of the sub-option code %1 in option code %2 for class '%3' +This debug message is printed when the processing for a sub-option is skipped +because the query does not belongs to the client class. The sub-option and +container option codes, and the client class name are provided. + +% FLEX_OPTION_PROCESS_SUB_REMOVE Removed sub-option code %1 in option code %2 +This debug message is printed when a sub-option was removed from the response +packet. The sub-option and container option codes are provided. + +% FLEX_OPTION_PROCESS_SUB_SUPERSEDE Supersedes the sub-option code %1 in option code %2 with value %3 +This debug message is printed when a sub-option was superseded into the +response packet. The sub-option and container option codes, and the value +(between quotes if printable, in hexadecimal if not) are provided. + +% FLEX_OPTION_PROCESS_SUPERSEDE Supersedes the option code %1 with value %2 +This debug message is printed when an option was superseded into the response +packet. The option code and the value (between quotes if printable, in +hexadecimal if not) are provided. + +% FLEX_OPTION_PROCESS_VENDOR_ID_MISMATCH Skip processing of vendor option code %1 with vendor id %2 not matching wanted %3 +This debug message is printed when a sub-option of a vendor option is +processed but vendor ids do not match. The code of the vendor option +and the two vendor ids are provided. + +% FLEX_OPTION_UNLOAD Flex Option hooks library has been unloaded +This info message indicates that the Flex Option hooks library has been +unloaded. diff --git a/src/hooks/dhcp/flex_option/libloadtests/Makefile.am b/src/hooks/dhcp/flex_option/libloadtests/Makefile.am new file mode 100644 index 0000000..8b23b0b --- /dev/null +++ b/src/hooks/dhcp/flex_option/libloadtests/Makefile.am @@ -0,0 +1,61 @@ +SUBDIRS = . + +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +AM_CPPFLAGS += -I$(top_builddir)/src/hooks/dhcp/flex_option -I$(top_srcdir)/src/hooks/dhcp/flex_option +AM_CPPFLAGS += $(BOOST_INCLUDES) +AM_CPPFLAGS += -DFLEX_OPTION_LIB_SO=\"$(abs_top_builddir)/src/hooks/dhcp/flex_option/.libs/libdhcp_flex_option.so\" +AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\" + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +if USE_STATIC_LINK +AM_LDFLAGS = -static +endif + +# Unit test data files need to get installed. +EXTRA_DIST = + +CLEANFILES = *.gcno *.gcda + +TESTS_ENVIRONMENT = $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) + +LOG_COMPILER = $(LIBTOOL) +AM_LOG_FLAGS = --mode=execute + +TESTS = +if HAVE_GTEST +TESTS += flex_option_unittests + +flex_option_unittests_SOURCES = run_unittests.cc +flex_option_unittests_SOURCES += callout_unittests.cc +flex_option_unittests_SOURCES += load_unload_unittests.cc + +flex_option_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES) + +flex_option_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS) + +flex_option_unittests_CXXFLAGS = $(AM_CXXFLAGS) + +flex_option_unittests_LDADD = $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/process/libkea-process.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/eval/libkea-eval.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/http/libkea-http.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/database/libkea-database.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +flex_option_unittests_LDADD += $(LOG4CPLUS_LIBS) +flex_option_unittests_LDADD += $(CRYPTO_LIBS) +flex_option_unittests_LDADD += $(BOOST_LIBS) +flex_option_unittests_LDADD += $(GTEST_LDADD) +endif +noinst_PROGRAMS = $(TESTS) diff --git a/src/hooks/dhcp/flex_option/libloadtests/Makefile.in b/src/hooks/dhcp/flex_option/libloadtests/Makefile.in new file mode 100644 index 0000000..4178df1 --- /dev/null +++ b/src/hooks/dhcp/flex_option/libloadtests/Makefile.in @@ -0,0 +1,1054 @@ +# 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 = flex_option_unittests +noinst_PROGRAMS = $(am__EXEEXT_2) +subdir = src/hooks/dhcp/flex_option/libloadtests +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 = flex_option_unittests$(EXEEXT) +am__EXEEXT_2 = $(am__EXEEXT_1) +PROGRAMS = $(noinst_PROGRAMS) +am__flex_option_unittests_SOURCES_DIST = run_unittests.cc \ + callout_unittests.cc load_unload_unittests.cc +@HAVE_GTEST_TRUE@am_flex_option_unittests_OBJECTS = flex_option_unittests-run_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ flex_option_unittests-callout_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ flex_option_unittests-load_unload_unittests.$(OBJEXT) +flex_option_unittests_OBJECTS = $(am_flex_option_unittests_OBJECTS) +am__DEPENDENCIES_1 = +@HAVE_GTEST_TRUE@flex_option_unittests_DEPENDENCIES = $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/libkea-process.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/eval/libkea-eval.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/stats/libkea-stats.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/config/libkea-cfgclient.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/http/libkea-http.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/database/libkea-database.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/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__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 = +flex_option_unittests_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) \ + $(flex_option_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)/flex_option_unittests-callout_unittests.Po \ + ./$(DEPDIR)/flex_option_unittests-load_unload_unittests.Po \ + ./$(DEPDIR)/flex_option_unittests-run_unittests.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 = +SOURCES = $(flex_option_unittests_SOURCES) +DIST_SOURCES = $(am__flex_option_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='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + 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 = . +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib \ + -I$(top_builddir)/src/hooks/dhcp/flex_option \ + -I$(top_srcdir)/src/hooks/dhcp/flex_option $(BOOST_INCLUDES) \ + -DFLEX_OPTION_LIB_SO=\"$(abs_top_builddir)/src/hooks/dhcp/flex_option/.libs/libdhcp_flex_option.so\" \ + -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\" +AM_CXXFLAGS = $(KEA_CXXFLAGS) +@USE_STATIC_LINK_TRUE@AM_LDFLAGS = -static + +# Unit test data files need to get installed. +EXTRA_DIST = +CLEANFILES = *.gcno *.gcda +TESTS_ENVIRONMENT = $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) +LOG_COMPILER = $(LIBTOOL) +AM_LOG_FLAGS = --mode=execute +@HAVE_GTEST_TRUE@flex_option_unittests_SOURCES = run_unittests.cc \ +@HAVE_GTEST_TRUE@ callout_unittests.cc load_unload_unittests.cc +@HAVE_GTEST_TRUE@flex_option_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES) +@HAVE_GTEST_TRUE@flex_option_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS) +@HAVE_GTEST_TRUE@flex_option_unittests_CXXFLAGS = $(AM_CXXFLAGS) +@HAVE_GTEST_TRUE@flex_option_unittests_LDADD = $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/libkea-process.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/eval/libkea-eval.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/stats/libkea-stats.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/config/libkea-cfgclient.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/http/libkea-http.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/database/libkea-database.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/libkea-util.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ +@HAVE_GTEST_TRUE@ $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) \ +@HAVE_GTEST_TRUE@ $(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/hooks/dhcp/flex_option/libloadtests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/hooks/dhcp/flex_option/libloadtests/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 + +flex_option_unittests$(EXEEXT): $(flex_option_unittests_OBJECTS) $(flex_option_unittests_DEPENDENCIES) $(EXTRA_flex_option_unittests_DEPENDENCIES) + @rm -f flex_option_unittests$(EXEEXT) + $(AM_V_CXXLD)$(flex_option_unittests_LINK) $(flex_option_unittests_OBJECTS) $(flex_option_unittests_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flex_option_unittests-callout_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flex_option_unittests-load_unload_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flex_option_unittests-run_unittests.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 $@ $< + +flex_option_unittests-run_unittests.o: run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -MT flex_option_unittests-run_unittests.o -MD -MP -MF $(DEPDIR)/flex_option_unittests-run_unittests.Tpo -c -o flex_option_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/flex_option_unittests-run_unittests.Tpo $(DEPDIR)/flex_option_unittests-run_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='flex_option_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) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -c -o flex_option_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc + +flex_option_unittests-run_unittests.obj: run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -MT flex_option_unittests-run_unittests.obj -MD -MP -MF $(DEPDIR)/flex_option_unittests-run_unittests.Tpo -c -o flex_option_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)/flex_option_unittests-run_unittests.Tpo $(DEPDIR)/flex_option_unittests-run_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='flex_option_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) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -c -o flex_option_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi` + +flex_option_unittests-callout_unittests.o: callout_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -MT flex_option_unittests-callout_unittests.o -MD -MP -MF $(DEPDIR)/flex_option_unittests-callout_unittests.Tpo -c -o flex_option_unittests-callout_unittests.o `test -f 'callout_unittests.cc' || echo '$(srcdir)/'`callout_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/flex_option_unittests-callout_unittests.Tpo $(DEPDIR)/flex_option_unittests-callout_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='callout_unittests.cc' object='flex_option_unittests-callout_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) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -c -o flex_option_unittests-callout_unittests.o `test -f 'callout_unittests.cc' || echo '$(srcdir)/'`callout_unittests.cc + +flex_option_unittests-callout_unittests.obj: callout_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -MT flex_option_unittests-callout_unittests.obj -MD -MP -MF $(DEPDIR)/flex_option_unittests-callout_unittests.Tpo -c -o flex_option_unittests-callout_unittests.obj `if test -f 'callout_unittests.cc'; then $(CYGPATH_W) 'callout_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/callout_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/flex_option_unittests-callout_unittests.Tpo $(DEPDIR)/flex_option_unittests-callout_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='callout_unittests.cc' object='flex_option_unittests-callout_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) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -c -o flex_option_unittests-callout_unittests.obj `if test -f 'callout_unittests.cc'; then $(CYGPATH_W) 'callout_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/callout_unittests.cc'; fi` + +flex_option_unittests-load_unload_unittests.o: load_unload_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -MT flex_option_unittests-load_unload_unittests.o -MD -MP -MF $(DEPDIR)/flex_option_unittests-load_unload_unittests.Tpo -c -o flex_option_unittests-load_unload_unittests.o `test -f 'load_unload_unittests.cc' || echo '$(srcdir)/'`load_unload_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/flex_option_unittests-load_unload_unittests.Tpo $(DEPDIR)/flex_option_unittests-load_unload_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='load_unload_unittests.cc' object='flex_option_unittests-load_unload_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) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -c -o flex_option_unittests-load_unload_unittests.o `test -f 'load_unload_unittests.cc' || echo '$(srcdir)/'`load_unload_unittests.cc + +flex_option_unittests-load_unload_unittests.obj: load_unload_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -MT flex_option_unittests-load_unload_unittests.obj -MD -MP -MF $(DEPDIR)/flex_option_unittests-load_unload_unittests.Tpo -c -o flex_option_unittests-load_unload_unittests.obj `if test -f 'load_unload_unittests.cc'; then $(CYGPATH_W) 'load_unload_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/load_unload_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/flex_option_unittests-load_unload_unittests.Tpo $(DEPDIR)/flex_option_unittests-load_unload_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='load_unload_unittests.cc' object='flex_option_unittests-load_unload_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) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -c -o flex_option_unittests-load_unload_unittests.obj `if test -f 'load_unload_unittests.cc'; then $(CYGPATH_W) 'load_unload_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/load_unload_unittests.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)/flex_option_unittests-callout_unittests.Po + -rm -f ./$(DEPDIR)/flex_option_unittests-load_unload_unittests.Po + -rm -f ./$(DEPDIR)/flex_option_unittests-run_unittests.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)/flex_option_unittests-callout_unittests.Po + -rm -f ./$(DEPDIR)/flex_option_unittests-load_unload_unittests.Po + -rm -f ./$(DEPDIR)/flex_option_unittests-run_unittests.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/hooks/dhcp/flex_option/libloadtests/callout_unittests.cc b/src/hooks/dhcp/flex_option/libloadtests/callout_unittests.cc new file mode 100644 index 0000000..623419b --- /dev/null +++ b/src/hooks/dhcp/flex_option/libloadtests/callout_unittests.cc @@ -0,0 +1,181 @@ +// Copyright (C) 2019-2022 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/. + +/// @file This file contains tests which exercise the pkt[46]_send callouis +/// called by the flexible option hook library. In order to test the callouts +/// one must be able to pass to the load function it hook library parameters +/// because the only way to populate these parameters is by actually loading +/// the library via HooksManager::loadLibraries(). + +#include <config.h> + +#include <flex_option.h> +#include <hooks/hooks.h> +#include <hooks/hooks_manager.h> +#include <hooks/callout_manager.h> +#include <dhcp/pkt4.h> +#include <dhcp/pkt6.h> +#include <dhcpsrv/cfgmgr.h> +#include <process/daemon.h> + +#include <gtest/gtest.h> +#include <errno.h> + +using namespace std; +using namespace isc; +using namespace isc::hooks; +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::process; + +namespace { + +/// @brief Structure that holds registered hook indexes. +struct TestHooks { + /// @brief Index of pkt4_send callout. + int hook_index_pkt4_send_; + + /// @brief Index of pkt6_send callout. + int hook_index_pkt6_send_; + + /// @brief Constructor + /// + /// The constructor registers hook points for callout tests. + TestHooks() { + hook_index_pkt4_send_ = HooksManager::registerHook("pkt4_send"); + hook_index_pkt6_send_ = HooksManager::registerHook("pkt6_send"); + } +}; + +TestHooks testHooks; + +/// @brief Test fixture for testing callouts called by the flex-option library +class CalloutTest : public ::testing::Test { +public: + /// @brief Constructor + CalloutTest() { + reset(); + } + + /// @brief Destructor + /// Removes files that may be left over from previous tests + virtual ~CalloutTest() { + reset(); + } + + /// @brief Removes files that may be left over from previous tests + virtual void reset() { + HooksManager::unloadLibraries(); + } + + void addLib(const std::string& lib, ConstElementPtr params) { + libraries_.push_back(make_pair(lib, params)); + } + + void loadLibs() { + EXPECT_TRUE(HooksManager::loadLibraries(libraries_)); + } + + void unloadLibs() { + EXPECT_NO_THROW(HooksManager::unloadLibraries()); + } + + HookLibsCollection libraries_; +}; + +// Simple test which exercises the pkt4_send callout. +TEST_F(CalloutTest, pkt4Send) { + // Prepare load() parameters. + ElementPtr params = Element::createMap(); + ElementPtr options = Element::createList(); + params->set("options", options); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr add = Element::create(string("'abc'")); + option->set("add", add); + + // Set family and proc name. + CfgMgr::instance().setFamily(AF_INET); + Daemon::setProcName("kea-dhcp4"); + + // Load the library. + addLib(FLEX_OPTION_LIB_SO, params); + loadLibs(); + + // Prepare packets. + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + EXPECT_FALSE(response->getOption(DHO_HOST_NAME)); + + // Get and setup the callout handle. + EXPECT_TRUE(HooksManager::calloutsPresent(testHooks.hook_index_pkt4_send_)); + CalloutHandlePtr handle = HooksManager::createCalloutHandle(); + handle->setArgument("query4", query); + handle->setArgument("response4", response); + + // Execute the callout. + EXPECT_NO_THROW(HooksManager::callCallouts(testHooks.hook_index_pkt4_send_, + *handle)); + EXPECT_EQ(0, handle->getStatus()); + + // Check the result. + OptionPtr opt = response->getOption(DHO_HOST_NAME); + ASSERT_TRUE(opt); + EXPECT_EQ(DHO_HOST_NAME, opt->getType()); + const OptionBuffer& buffer = opt->getData(); + ASSERT_EQ(3, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3)); +} + +// Simple test which exercises the pkt6_send callout. +TEST_F(CalloutTest, pkt6Send) { + // Prepare load() parameters. + ElementPtr params = Element::createMap(); + ElementPtr options = Element::createList(); + params->set("options", options); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_BOOTFILE_URL); + option->set("code", code); + ElementPtr supersede = Element::create(string("'abc'")); + option->set("supersede", supersede); + + // Set family and proc name. + CfgMgr::instance().setFamily(AF_INET6); + Daemon::setProcName("kea-dhcp6"); + + // Load the library. + addLib(FLEX_OPTION_LIB_SO, params); + loadLibs(); + + // Prepare packets. + Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345)); + Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345)); + EXPECT_FALSE(response->getOption(D6O_BOOTFILE_URL)); + + // Get and setup the callout handle. + EXPECT_TRUE(HooksManager::calloutsPresent(testHooks.hook_index_pkt6_send_)); + CalloutHandlePtr handle = HooksManager::createCalloutHandle(); + handle->setArgument("query6", query); + handle->setArgument("response6", response); + + // Execute the callout. + EXPECT_NO_THROW(HooksManager::callCallouts(testHooks.hook_index_pkt6_send_, + *handle)); + EXPECT_EQ(0, handle->getStatus()); + + // Check the result. + OptionPtr opt = response->getOption(D6O_BOOTFILE_URL); + ASSERT_TRUE(opt); + EXPECT_EQ(D6O_BOOTFILE_URL, opt->getType()); + const OptionBuffer& buffer = opt->getData(); + ASSERT_EQ(3, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3)); +} + +} // end of anonymous namespace diff --git a/src/hooks/dhcp/flex_option/libloadtests/load_unload_unittests.cc b/src/hooks/dhcp/flex_option/libloadtests/load_unload_unittests.cc new file mode 100644 index 0000000..c786b86 --- /dev/null +++ b/src/hooks/dhcp/flex_option/libloadtests/load_unload_unittests.cc @@ -0,0 +1,76 @@ +// Copyright (C) 2019-2022 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/. + +/// @file This file contains tests which exercise the load and unload +/// functions in the flexible option hook library. In order to test the load +/// function, one must be able to pass it hook library parameters. The +/// the only way to populate these parameters is by actually loading the +/// library via HooksManager::loadLibraries(). + +#include <config.h> + +#include <flex_option.h> +#include <dhcpsrv/cfgmgr.h> +#include <hooks/hooks_manager.h> +#include <process/daemon.h> +#include <testutils/lib_load_test_fixture.h> + +#include <gtest/gtest.h> +#include <errno.h> + +using namespace std; +using namespace isc; +using namespace isc::hooks; +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::process; + +namespace { + +/// @brief Test fixture for testing loading and unloading the flex-option library +class FlexOptionLibLoadTest : public isc::test::LibLoadTest { +public: + /// @brief Constructor + FlexOptionLibLoadTest() : LibLoadTest(FLEX_OPTION_LIB_SO) { + } + + /// @brief Destructor + virtual ~FlexOptionLibLoadTest() { + } + + /// @brief Creates a set configuration parameters valid for the library. + virtual ElementPtr validConfigParams() { + ElementPtr params = Element::createMap(); + ElementPtr options = Element::createList(); + params->set("options", options); + return (params); + } +}; + +// Simple V4 test that checks the library can be loaded and unloaded several times. +TEST_F(FlexOptionLibLoadTest, validLoadDhcp4) { + validDaemonTest("kea-dhcp4", AF_INET, valid_params_); +} + +// Simple test that checks the library can be loaded in a DHCPv6 server. +TEST_F(FlexOptionLibLoadTest, validLoadDhcp6) { + validDaemonTest("kea-dhcp6", AF_INET6, valid_params_); +} + +// Simple test that checks the library cannot by loaded by invalid daemons. +TEST_F(FlexOptionLibLoadTest, invalidDaemonLoad) { + // V4 is invalid when family is AF_INET6 + invalidDaemonTest("kea-dhcp4", AF_INET6, valid_params_); + + // V6 is invalid when family is AF_INET + invalidDaemonTest("kea-dhcp6", AF_INET, valid_params_); + + invalidDaemonTest("kea-ctrl-agent", AF_INET, valid_params_); + invalidDaemonTest("kea-dhcp-ddns", AF_INET, valid_params_); + invalidDaemonTest("bogus", AF_INET, valid_params_); +} + +} // end of anonymous namespace diff --git a/src/hooks/dhcp/flex_option/libloadtests/run_unittests.cc b/src/hooks/dhcp/flex_option/libloadtests/run_unittests.cc new file mode 100644 index 0000000..5805b42 --- /dev/null +++ b/src/hooks/dhcp/flex_option/libloadtests/run_unittests.cc @@ -0,0 +1,19 @@ +// 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 <log/logger_support.h> +#include <gtest/gtest.h> + +int +main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + isc::log::initLogger(); + int result = RUN_ALL_TESTS(); + + return (result); +} diff --git a/src/hooks/dhcp/flex_option/tests/Makefile.am b/src/hooks/dhcp/flex_option/tests/Makefile.am new file mode 100644 index 0000000..5089125 --- /dev/null +++ b/src/hooks/dhcp/flex_option/tests/Makefile.am @@ -0,0 +1,63 @@ +SUBDIRS = . + +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +AM_CPPFLAGS += -I$(top_builddir)/src/hooks/dhcp/flex_option -I$(top_srcdir)/src/hooks/dhcp/flex_option +AM_CPPFLAGS += $(BOOST_INCLUDES) +AM_CPPFLAGS += -DFLEX_OPTION_LIB_SO=\"$(abs_top_builddir)/src/hooks/dhcp/flex_option/.libs/libdhcp_flex_option.so\" +AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\" + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +if USE_STATIC_LINK +AM_LDFLAGS = -static +endif + +# Unit test data files need to get installed. +EXTRA_DIST = + +CLEANFILES = *.gcno *.gcda + +TESTS_ENVIRONMENT = $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) + +LOG_COMPILER = $(LIBTOOL) +AM_LOG_FLAGS = --mode=execute + +TESTS = +if HAVE_GTEST +TESTS += flex_option_unittests + +flex_option_unittests_SOURCES = run_unittests.cc +flex_option_unittests_SOURCES += test_flex_option.h +flex_option_unittests_SOURCES += flex_option_unittests.cc +flex_option_unittests_SOURCES += sub_option_unittests.cc + +flex_option_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES) + +flex_option_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS) + +flex_option_unittests_CXXFLAGS = $(AM_CXXFLAGS) + +flex_option_unittests_LDADD = $(top_builddir)/src/hooks/dhcp/flex_option/libflex_option.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/process/libkea-process.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/eval/libkea-eval.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/http/libkea-http.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/database/libkea-database.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +flex_option_unittests_LDADD += $(LOG4CPLUS_LIBS) +flex_option_unittests_LDADD += $(CRYPTO_LIBS) +flex_option_unittests_LDADD += $(BOOST_LIBS) +flex_option_unittests_LDADD += $(GTEST_LDADD) +endif +noinst_PROGRAMS = $(TESTS) diff --git a/src/hooks/dhcp/flex_option/tests/Makefile.in b/src/hooks/dhcp/flex_option/tests/Makefile.in new file mode 100644 index 0000000..30a4059 --- /dev/null +++ b/src/hooks/dhcp/flex_option/tests/Makefile.in @@ -0,0 +1,1076 @@ +# 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 = flex_option_unittests +noinst_PROGRAMS = $(am__EXEEXT_2) +subdir = src/hooks/dhcp/flex_option/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 = flex_option_unittests$(EXEEXT) +am__EXEEXT_2 = $(am__EXEEXT_1) +PROGRAMS = $(noinst_PROGRAMS) +am__flex_option_unittests_SOURCES_DIST = run_unittests.cc \ + test_flex_option.h flex_option_unittests.cc \ + sub_option_unittests.cc +@HAVE_GTEST_TRUE@am_flex_option_unittests_OBJECTS = flex_option_unittests-run_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ flex_option_unittests-flex_option_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ flex_option_unittests-sub_option_unittests.$(OBJEXT) +flex_option_unittests_OBJECTS = $(am_flex_option_unittests_OBJECTS) +am__DEPENDENCIES_1 = +@HAVE_GTEST_TRUE@flex_option_unittests_DEPENDENCIES = $(top_builddir)/src/hooks/dhcp/flex_option/libflex_option.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/libkea-process.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/eval/libkea-eval.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/stats/libkea-stats.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/config/libkea-cfgclient.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/http/libkea-http.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/database/libkea-database.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/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__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 = +flex_option_unittests_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) \ + $(flex_option_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)/flex_option_unittests-flex_option_unittests.Po \ + ./$(DEPDIR)/flex_option_unittests-run_unittests.Po \ + ./$(DEPDIR)/flex_option_unittests-sub_option_unittests.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 = $(flex_option_unittests_SOURCES) +DIST_SOURCES = $(am__flex_option_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='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + 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 = . +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib \ + -I$(top_builddir)/src/hooks/dhcp/flex_option \ + -I$(top_srcdir)/src/hooks/dhcp/flex_option $(BOOST_INCLUDES) \ + -DFLEX_OPTION_LIB_SO=\"$(abs_top_builddir)/src/hooks/dhcp/flex_option/.libs/libdhcp_flex_option.so\" \ + -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\" +AM_CXXFLAGS = $(KEA_CXXFLAGS) +@USE_STATIC_LINK_TRUE@AM_LDFLAGS = -static + +# Unit test data files need to get installed. +EXTRA_DIST = +CLEANFILES = *.gcno *.gcda +TESTS_ENVIRONMENT = $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) +LOG_COMPILER = $(LIBTOOL) +AM_LOG_FLAGS = --mode=execute +@HAVE_GTEST_TRUE@flex_option_unittests_SOURCES = run_unittests.cc \ +@HAVE_GTEST_TRUE@ test_flex_option.h flex_option_unittests.cc \ +@HAVE_GTEST_TRUE@ sub_option_unittests.cc +@HAVE_GTEST_TRUE@flex_option_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES) +@HAVE_GTEST_TRUE@flex_option_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS) +@HAVE_GTEST_TRUE@flex_option_unittests_CXXFLAGS = $(AM_CXXFLAGS) +@HAVE_GTEST_TRUE@flex_option_unittests_LDADD = $(top_builddir)/src/hooks/dhcp/flex_option/libflex_option.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/libkea-process.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/eval/libkea-eval.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/stats/libkea-stats.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/config/libkea-cfgclient.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/http/libkea-http.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/database/libkea-database.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/libkea-util.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ +@HAVE_GTEST_TRUE@ $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) \ +@HAVE_GTEST_TRUE@ $(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/hooks/dhcp/flex_option/tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/hooks/dhcp/flex_option/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 + +flex_option_unittests$(EXEEXT): $(flex_option_unittests_OBJECTS) $(flex_option_unittests_DEPENDENCIES) $(EXTRA_flex_option_unittests_DEPENDENCIES) + @rm -f flex_option_unittests$(EXEEXT) + $(AM_V_CXXLD)$(flex_option_unittests_LINK) $(flex_option_unittests_OBJECTS) $(flex_option_unittests_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flex_option_unittests-flex_option_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flex_option_unittests-run_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flex_option_unittests-sub_option_unittests.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 $@ $< + +flex_option_unittests-run_unittests.o: run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -MT flex_option_unittests-run_unittests.o -MD -MP -MF $(DEPDIR)/flex_option_unittests-run_unittests.Tpo -c -o flex_option_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/flex_option_unittests-run_unittests.Tpo $(DEPDIR)/flex_option_unittests-run_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='flex_option_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) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -c -o flex_option_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc + +flex_option_unittests-run_unittests.obj: run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -MT flex_option_unittests-run_unittests.obj -MD -MP -MF $(DEPDIR)/flex_option_unittests-run_unittests.Tpo -c -o flex_option_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)/flex_option_unittests-run_unittests.Tpo $(DEPDIR)/flex_option_unittests-run_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='flex_option_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) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -c -o flex_option_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi` + +flex_option_unittests-flex_option_unittests.o: flex_option_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -MT flex_option_unittests-flex_option_unittests.o -MD -MP -MF $(DEPDIR)/flex_option_unittests-flex_option_unittests.Tpo -c -o flex_option_unittests-flex_option_unittests.o `test -f 'flex_option_unittests.cc' || echo '$(srcdir)/'`flex_option_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/flex_option_unittests-flex_option_unittests.Tpo $(DEPDIR)/flex_option_unittests-flex_option_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='flex_option_unittests.cc' object='flex_option_unittests-flex_option_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) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -c -o flex_option_unittests-flex_option_unittests.o `test -f 'flex_option_unittests.cc' || echo '$(srcdir)/'`flex_option_unittests.cc + +flex_option_unittests-flex_option_unittests.obj: flex_option_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -MT flex_option_unittests-flex_option_unittests.obj -MD -MP -MF $(DEPDIR)/flex_option_unittests-flex_option_unittests.Tpo -c -o flex_option_unittests-flex_option_unittests.obj `if test -f 'flex_option_unittests.cc'; then $(CYGPATH_W) 'flex_option_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/flex_option_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/flex_option_unittests-flex_option_unittests.Tpo $(DEPDIR)/flex_option_unittests-flex_option_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='flex_option_unittests.cc' object='flex_option_unittests-flex_option_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) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -c -o flex_option_unittests-flex_option_unittests.obj `if test -f 'flex_option_unittests.cc'; then $(CYGPATH_W) 'flex_option_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/flex_option_unittests.cc'; fi` + +flex_option_unittests-sub_option_unittests.o: sub_option_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -MT flex_option_unittests-sub_option_unittests.o -MD -MP -MF $(DEPDIR)/flex_option_unittests-sub_option_unittests.Tpo -c -o flex_option_unittests-sub_option_unittests.o `test -f 'sub_option_unittests.cc' || echo '$(srcdir)/'`sub_option_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/flex_option_unittests-sub_option_unittests.Tpo $(DEPDIR)/flex_option_unittests-sub_option_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='sub_option_unittests.cc' object='flex_option_unittests-sub_option_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) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -c -o flex_option_unittests-sub_option_unittests.o `test -f 'sub_option_unittests.cc' || echo '$(srcdir)/'`sub_option_unittests.cc + +flex_option_unittests-sub_option_unittests.obj: sub_option_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -MT flex_option_unittests-sub_option_unittests.obj -MD -MP -MF $(DEPDIR)/flex_option_unittests-sub_option_unittests.Tpo -c -o flex_option_unittests-sub_option_unittests.obj `if test -f 'sub_option_unittests.cc'; then $(CYGPATH_W) 'sub_option_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/sub_option_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/flex_option_unittests-sub_option_unittests.Tpo $(DEPDIR)/flex_option_unittests-sub_option_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='sub_option_unittests.cc' object='flex_option_unittests-sub_option_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) $(flex_option_unittests_CPPFLAGS) $(CPPFLAGS) $(flex_option_unittests_CXXFLAGS) $(CXXFLAGS) -c -o flex_option_unittests-sub_option_unittests.obj `if test -f 'sub_option_unittests.cc'; then $(CYGPATH_W) 'sub_option_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/sub_option_unittests.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)/flex_option_unittests-flex_option_unittests.Po + -rm -f ./$(DEPDIR)/flex_option_unittests-run_unittests.Po + -rm -f ./$(DEPDIR)/flex_option_unittests-sub_option_unittests.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)/flex_option_unittests-flex_option_unittests.Po + -rm -f ./$(DEPDIR)/flex_option_unittests-run_unittests.Po + -rm -f ./$(DEPDIR)/flex_option_unittests-sub_option_unittests.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/hooks/dhcp/flex_option/tests/flex_option_unittests.cc b/src/hooks/dhcp/flex_option/tests/flex_option_unittests.cc new file mode 100644 index 0000000..c5149ba --- /dev/null +++ b/src/hooks/dhcp/flex_option/tests/flex_option_unittests.cc @@ -0,0 +1,1586 @@ +// Copyright (C) 2019-2022 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/. + +/// @file This file contains tests which verify flexible option. + +#include <config.h> +#include <flex_option.h> +#include <flex_option_log.h> +#include <dhcp/option_custom.h> +#include <dhcp/option_string.h> +#include <dhcp/libdhcp++.h> +#include <dhcpsrv/cfgmgr.h> +#include <eval/eval_context.h> +#include <hooks/callout_manager.h> +#include <hooks/hooks.h> + +#include <tests/test_flex_option.h> +#include <gtest/gtest.h> +#include <sstream> + +using namespace std; +using namespace isc; +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::eval; +using namespace isc::hooks; +using namespace isc::flex_option; +using namespace isc::flex_option::test; + +namespace { + +/// @brief Test fixture for testing the Flex Option library. +class FlexOptionTest : public BaseFlexOptionTest { }; + +// Verify that the configuration must exist. +TEST_F(FlexOptionTest, noConfig) { + ElementPtr options; + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'options' parameter is mandatory", impl_->getErrMsg()); +} + +// Verify that the configuration must be a list. +TEST_F(FlexOptionTest, configNotList) { + ElementPtr options = Element::createMap(); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'options' parameter must be a list", impl_->getErrMsg()); +} + +// Verify that the configuration can be the empty list. +TEST_F(FlexOptionTest, configEmpty) { + ElementPtr options = Element::createList(); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + EXPECT_TRUE(impl_->getOptionConfigMap().empty()); +} + +// Verify that an option configuration must exist. +TEST_F(FlexOptionTest, noOptionConfig) { + ElementPtr options = Element::createList(); + ElementPtr option; + options->add(option); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("null option element", impl_->getErrMsg()); +} + +// Verify that an option configuration must be a map. +TEST_F(FlexOptionTest, optionConfigNotMap) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createList(); + options->add(option); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("option element is not a map", impl_->getErrMsg()); +} + +// Verify that an unknown option keyword is rejected. +TEST_F(FlexOptionTest, optionConfigUnknown) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr unknown = Element::create(string("'ab'")); + // The right keyword is remove... + option->set("delete", unknown); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("unknown parameter 'delete'", impl_->getErrMsg()); +} + +// Verify that an option configuration must have code or name. +TEST_F(FlexOptionTest, optionConfigNoCodeName) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + ostringstream errmsg; + errmsg << "'code' or 'name' must be specified: " << option->str(); + EXPECT_EQ(errmsg.str(), impl_->getErrMsg()); +} + +// Verify that the v4 option code must be in [1..254]. +TEST_F(FlexOptionTest, optionConfigBadCode4) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr add = Element::create(string("'ab'")); + option->set("add", add); + ElementPtr code = Element::create(false); + option->set("code", code); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'code' must be an integer: false", impl_->getErrMsg()); + + code = Element::create(-1); + option->set("code", code); + EXPECT_THROW(impl_->testConfigure(options), OutOfRange); + EXPECT_EQ("invalid 'code' value -1 not in [0..255]", impl_->getErrMsg()); + + code = Element::create(DHO_PAD); + option->set("code", code); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("invalid 'code' value 0: reserved for PAD", impl_->getErrMsg()); + + code = Element::create(DHO_END); + option->set("code", code); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("invalid 'code' value 255: reserved for END", impl_->getErrMsg()); + + code = Element::create(256); + option->set("code", code); + EXPECT_THROW(impl_->testConfigure(options), OutOfRange); + EXPECT_EQ("invalid 'code' value 256 not in [0..255]", impl_->getErrMsg()); + + code = Element::create(1); + option->set("code", code); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + code = Element::create(254); + option->set("code", code); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); +} + +// Verify that the v6 option code must be in [1..65535]. +TEST_F(FlexOptionTest, optionConfigBadCode6) { + CfgMgr::instance().setFamily(AF_INET6); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr add = Element::create(string("'ab'")); + option->set("add", add); + ElementPtr code = Element::create(false); + option->set("code", code); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'code' must be an integer: false", impl_->getErrMsg()); + + code = Element::create(-1); + option->set("code", code); + EXPECT_THROW(impl_->testConfigure(options), OutOfRange); + EXPECT_EQ("invalid 'code' value -1 not in [0..65535]", impl_->getErrMsg()); + + code = Element::create(0); + option->set("code", code); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("invalid 'code' value 0: reserved", impl_->getErrMsg()); + + code = Element::create(65536); + option->set("code", code); + EXPECT_THROW(impl_->testConfigure(options), OutOfRange); + EXPECT_EQ("invalid 'code' value 65536 not in [0..65535]", impl_->getErrMsg()); + + code = Element::create(1); + option->set("code", code); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + code = Element::create(65535); + option->set("code", code); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); +} + +// Verify that the space must be a string. +TEST_F(FlexOptionTest, optionConfigBadSpace) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr add = Element::create(string("'ab'")); + option->set("add", add); + ElementPtr code = Element::create(222); + option->set("code", code); + ElementPtr space = Element::create(true); + option->set("space", space); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'space' must be a string: true", impl_->getErrMsg()); +} + +// Verify that the space must be valid. +TEST_F(FlexOptionTest, optionConfigInvalidSpace) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr add = Element::create(string("'ab'")); + option->set("add", add); + ElementPtr code = Element::create(222); + option->set("code", code); + ElementPtr space = Element::create(string("-bad-")); + option->set("space", space); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'-bad-' is not a valid space name", impl_->getErrMsg()); +} + +// Verify that the name must be a string. +TEST_F(FlexOptionTest, optionConfigBadName) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr add = Element::create(string("'ab'")); + option->set("add", add); + ElementPtr name = Element::create(true); + option->set("name", name); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'name' must be a string: true", impl_->getErrMsg()); +} + +// Verify that the name must not be empty. +TEST_F(FlexOptionTest, optionConfigEmptyName) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr add = Element::create(string("'ab'")); + option->set("add", add); + ElementPtr name = Element::create(string()); + option->set("name", name); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'name' must not be empty", impl_->getErrMsg()); +} + +// Verify that the name must be a known option. +TEST_F(FlexOptionTest, optionConfigUnknownName) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr add = Element::create(string("'ab'")); + option->set("add", add); + ElementPtr name = Element::create(string("foobar")); + option->set("name", name); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("no known 'foobar' option in 'dhcp4' space", impl_->getErrMsg()); +} + +// Verify that the space must be a known space. +TEST_F(FlexOptionTest, optionConfigUnknownSpace) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr add = Element::create(string("'ab'")); + option->set("add", add); + ElementPtr name = Element::create(string("host-name")); + option->set("name", name); + ElementPtr space = Element::create(string("foobar")); + option->set("space", space); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("no known 'host-name' option in 'foobar' space", + impl_->getErrMsg()); +} + +// Verify that the definition is not required when csv-format is not specified. +TEST_F(FlexOptionTest, optionConfigUnknownCodeNoCSVFormat) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr add = Element::create(string("'ab'")); + option->set("add", add); + ElementPtr code = Element::create(109); + option->set("code", code); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getOptionConfigMap(); + EXPECT_EQ(1, map.count(109)); + auto opt_lst = map[109]; + EXPECT_EQ(1, opt_lst.size()); +} + +// Verify that the definition is not required when csv-format is false. +TEST_F(FlexOptionTest, optionConfigUnknownCodeDisableCSVFormat) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr add = Element::create(string("'ab'")); + option->set("add", add); + ElementPtr code = Element::create(109); + option->set("code", code); + // Disable csv-format. + option->set("csv-format", Element::create(false)); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getOptionConfigMap(); + EXPECT_EQ(1, map.count(109)); + auto opt_lst = map[109]; + EXPECT_EQ(1, opt_lst.size()); +} + +// Verify that the code must be a known option when csv-format is true. +TEST_F(FlexOptionTest, optionConfigUnknownCodeEnableCSVFormat) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr add = Element::create(string("'ab'")); + option->set("add", add); + ElementPtr code = Element::create(109); + option->set("code", code); + // Enable csv-format. + option->set("csv-format", Element::create(true)); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("no known option with code '109' in 'dhcp4' space", impl_->getErrMsg()); +} + +// Verify that the name can be a standard option. +TEST_F(FlexOptionTest, optionConfigStandardName) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr add = Element::create(string("'ab'")); + option->set("add", add); + ElementPtr name = Element::create(string("host-name")); + option->set("name", name); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getOptionConfigMap(); + EXPECT_EQ(1, map.count(DHO_HOST_NAME)); + auto opt_lst = map[DHO_HOST_NAME]; + EXPECT_EQ(1, opt_lst.size()); +} + +// Verify that the name can be an user defined option. +TEST_F(FlexOptionTest, optionConfigDefinedName) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-option", 222, + DHCP4_OPTION_SPACE, "string")); + defs.addItem(def); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr add = Element::create(string("'ab'")); + option->set("add", add); + ElementPtr name = Element::create(string("my-option")); + option->set("name", name); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getOptionConfigMap(); + EXPECT_EQ(1, map.count(222)); + auto opt_lst = map[222]; + EXPECT_EQ(1, opt_lst.size()); +} + +// Verify that the name can be an user defined option in a custom space. +TEST_F(FlexOptionTest, optionConfigDefinedSpace) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-option", 222, + "my-space", "string")); + defs.addItem(def); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr add = Element::create(string("'ab'")); + option->set("add", add); + ElementPtr name = Element::create(string("my-option")); + option->set("name", name); + ElementPtr space = Element::create(string("my-space")); + option->set("space", space); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getOptionConfigMap(); + EXPECT_EQ(1, map.count(222)); + auto opt_lst = map[222]; + EXPECT_EQ(1, opt_lst.size()); +} + +// Last resort is only option 43... + +// Verify that the name must match the code. +TEST_F(FlexOptionTest, optionConfigCodeNameMismatch) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr add = Element::create(string("'ab'")); + option->set("add", add); + ElementPtr code = Element::create(DHO_HOST_NAME + 1); + option->set("code", code); + ElementPtr name = Element::create(string("host-name")); + option->set("name", name); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + string expected = "option 'host-name' is defined as code: 12, "; + expected += "not the specified code: 13"; + EXPECT_EQ(expected, impl_->getErrMsg()); +} + +// Verify that the csv-format must be a boolean. +TEST_F(FlexOptionTest, optionConfigBadCSVFormat) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr csv_format = Element::create(123); + option->set("csv-format", csv_format); + ElementPtr code = Element::create(12); + option->set("code", code); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'csv-format' must be a boolean: 123", impl_->getErrMsg()); +} + +// Verify that an option can be configured more than once. +TEST_F(FlexOptionTest, optionConfigTwice) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr add = Element::create(string("'ab'")); + option->set("add", add); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + + // Add it a second time. + options->add(option); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getOptionConfigMap(); + EXPECT_EQ(1, map.count(DHO_HOST_NAME)); + auto opt_lst = map[DHO_HOST_NAME]; + EXPECT_EQ(2, opt_lst.size()); +} + +// Verify that the add value must be a string. +TEST_F(FlexOptionTest, optionConfigAddNotString) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr add = Element::create(true); + option->set("add", add); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'add' must be a string: true", impl_->getErrMsg()); +} + +// Verify that the add value must not be empty. +TEST_F(FlexOptionTest, optionConfigEmptyAdd) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr add = Element::create(string()); + option->set("add", add); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'add' must not be empty", impl_->getErrMsg()); +} + +// Verify that the add value must parse. +TEST_F(FlexOptionTest, optionConfigBadAdd) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr add = Element::create(string("ifelse('a','b','c')")); + option->set("add", add); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + string expected = "can't parse add expression [ifelse('a','b','c')] "; + expected += "error: <string>:1.11: syntax error, "; + expected += "unexpected \",\", expecting == or +"; + EXPECT_EQ(expected, impl_->getErrMsg()); +} + +// Verify that a valid v4 add value is accepted. +TEST_F(FlexOptionTest, optionConfigAdd4) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr add = Element::create(string("'abc'")); + option->set("add", add); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getOptionConfigMap(); + FlexOptionImpl::OptionConfigList opt_lst; + ASSERT_NO_THROW(opt_lst = map.at(DHO_HOST_NAME)); + ASSERT_FALSE(opt_lst.empty()); + EXPECT_EQ(1, opt_lst.size()); + FlexOptionImpl::OptionConfigPtr opt_cfg; + ASSERT_NO_THROW(opt_cfg = opt_lst.front()); + + ASSERT_TRUE(opt_cfg); + EXPECT_EQ(DHO_HOST_NAME, opt_cfg->getCode()); + EXPECT_EQ(FlexOptionImpl::ADD, opt_cfg->getAction()); + EXPECT_EQ("'abc'", opt_cfg->getText()); + + ExpressionPtr expr = opt_cfg->getExpr(); + ASSERT_TRUE(expr); + ASSERT_EQ(1, expr->size()); + Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 12345)); + ValueStack values; + EXPECT_NO_THROW(expr->at(0)->evaluate(*pkt4, values)); + ASSERT_EQ(1, values.size()); + EXPECT_EQ("abc", values.top()); +} + +// Verify that a valid v6 add value is accepted. +TEST_F(FlexOptionTest, optionConfigAdd6) { + CfgMgr::instance().setFamily(AF_INET6); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_BOOTFILE_URL); + option->set("code", code); + ElementPtr add = Element::create(string("'abc'")); + option->set("add", add); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getOptionConfigMap(); + FlexOptionImpl::OptionConfigList opt_lst; + ASSERT_NO_THROW(opt_lst = map.at(D6O_BOOTFILE_URL)); + ASSERT_FALSE(opt_lst.empty()); + EXPECT_EQ(1, opt_lst.size()); + FlexOptionImpl::OptionConfigPtr opt_cfg; + ASSERT_NO_THROW(opt_cfg = opt_lst.front()); + + ASSERT_TRUE(opt_cfg); + EXPECT_EQ(D6O_BOOTFILE_URL, opt_cfg->getCode()); + EXPECT_EQ(FlexOptionImpl::ADD, opt_cfg->getAction()); + EXPECT_EQ("'abc'", opt_cfg->getText()); + + ExpressionPtr expr = opt_cfg->getExpr(); + ASSERT_TRUE(expr); + ASSERT_EQ(1, expr->size()); + Pkt6Ptr pkt6(new Pkt6(DHCPV6_SOLICIT, 12345)); + ValueStack values; + EXPECT_NO_THROW(expr->at(0)->evaluate(*pkt6, values)); + ASSERT_EQ(1, values.size()); + EXPECT_EQ("abc", values.top()); +} + +// Verify that the supersede value must be a string. +TEST_F(FlexOptionTest, optionConfigSupersedeNotString) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr supersede = Element::create(123); + option->set("supersede", supersede); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'supersede' must be a string: 123", impl_->getErrMsg()); +} + +// Verify that the supersede value must not be empty. +TEST_F(FlexOptionTest, optionConfigEmptySupersede) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr supersede = Element::create(string()); + option->set("supersede", supersede); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'supersede' must not be empty", impl_->getErrMsg()); +} + +// Verify that the supersede value must parse. +TEST_F(FlexOptionTest, optionConfigBadSupersede) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr supersede = Element::create(string("ifelse('a','b','c')")); + option->set("supersede", supersede); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + string expected = "can't parse supersede expression [ifelse('a','b','c')] "; + expected += "error: <string>:1.11: syntax error, "; + expected += "unexpected \",\", expecting == or +"; + EXPECT_EQ(expected, impl_->getErrMsg()); +} + +// Verify that a valid v4 supersede value is accepted. +TEST_F(FlexOptionTest, optionConfigSupersede4) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr supersede = Element::create(string("'abc'")); + option->set("supersede", supersede); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getOptionConfigMap(); + FlexOptionImpl::OptionConfigList opt_lst; + ASSERT_NO_THROW(opt_lst = map.at(DHO_HOST_NAME)); + ASSERT_FALSE(opt_lst.empty()); + EXPECT_EQ(1, opt_lst.size()); + FlexOptionImpl::OptionConfigPtr opt_cfg; + ASSERT_NO_THROW(opt_cfg = opt_lst.front()); + + ASSERT_TRUE(opt_cfg); + EXPECT_EQ(DHO_HOST_NAME, opt_cfg->getCode()); + EXPECT_EQ(FlexOptionImpl::SUPERSEDE, opt_cfg->getAction()); + EXPECT_EQ("'abc'", opt_cfg->getText()); + + ExpressionPtr expr = opt_cfg->getExpr(); + ASSERT_TRUE(expr); + ASSERT_EQ(1, expr->size()); + Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 12345)); + ValueStack values; + EXPECT_NO_THROW(expr->at(0)->evaluate(*pkt4, values)); + ASSERT_EQ(1, values.size()); + EXPECT_EQ("abc", values.top()); +} + +// Verify that a valid v6 supersede value is accepted. +TEST_F(FlexOptionTest, optionConfigSupersede6) { + CfgMgr::instance().setFamily(AF_INET6); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_BOOTFILE_URL); + option->set("code", code); + ElementPtr supersede = Element::create(string("'abc'")); + option->set("supersede", supersede); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getOptionConfigMap(); + FlexOptionImpl::OptionConfigList opt_lst; + ASSERT_NO_THROW(opt_lst = map.at(D6O_BOOTFILE_URL)); + ASSERT_FALSE(opt_lst.empty()); + EXPECT_EQ(1, opt_lst.size()); + FlexOptionImpl::OptionConfigPtr opt_cfg; + ASSERT_NO_THROW(opt_cfg = opt_lst.front()); + + ASSERT_TRUE(opt_cfg); + EXPECT_EQ(D6O_BOOTFILE_URL, opt_cfg->getCode()); + EXPECT_EQ(FlexOptionImpl::SUPERSEDE, opt_cfg->getAction()); + EXPECT_EQ("'abc'", opt_cfg->getText()); + + ExpressionPtr expr = opt_cfg->getExpr(); + ASSERT_TRUE(expr); + ASSERT_EQ(1, expr->size()); + Pkt6Ptr pkt6(new Pkt6(DHCPV6_SOLICIT, 12345)); + ValueStack values; + EXPECT_NO_THROW(expr->at(0)->evaluate(*pkt6, values)); + ASSERT_EQ(1, values.size()); + EXPECT_EQ("abc", values.top()); +} + +// Verify that the remove value must be a string. +TEST_F(FlexOptionTest, optionConfigRemoveNotString) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr remove = Element::createMap(); + option->set("remove", remove); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'remove' must be a string: { }", impl_->getErrMsg()); +} + +// Verify that the remove value must not be empty. +TEST_F(FlexOptionTest, optionConfigEmptyRemove) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr remove = Element::create(string()); + option->set("remove", remove); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'remove' must not be empty", impl_->getErrMsg()); +} + +// Verify that the remove value must parse. +TEST_F(FlexOptionTest, optionConfigBadRemove) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr remove = Element::create(string("'abc'")); + option->set("remove", remove); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + string expected = "can't parse remove expression ['abc'] error: "; + expected += "<string>:1.6: syntax error, unexpected end of file, "; + expected += "expecting == or +"; + EXPECT_EQ(expected, impl_->getErrMsg()); +} + +// Verify that a valid v4 remove value is accepted. +TEST_F(FlexOptionTest, optionConfigRemove4) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr remove = Element::create(string("'abc' == 'abc'")); + option->set("remove", remove); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getOptionConfigMap(); + FlexOptionImpl::OptionConfigList opt_lst; + ASSERT_NO_THROW(opt_lst = map.at(DHO_HOST_NAME)); + ASSERT_FALSE(opt_lst.empty()); + EXPECT_EQ(1, opt_lst.size()); + FlexOptionImpl::OptionConfigPtr opt_cfg; + ASSERT_NO_THROW(opt_cfg = opt_lst.front()); + + ASSERT_TRUE(opt_cfg); + EXPECT_EQ(DHO_HOST_NAME, opt_cfg->getCode()); + EXPECT_EQ(FlexOptionImpl::REMOVE, opt_cfg->getAction()); + EXPECT_EQ("'abc' == 'abc'", opt_cfg->getText()); + + ExpressionPtr expr = opt_cfg->getExpr(); + ASSERT_TRUE(expr); + ASSERT_EQ(3, expr->size()); + Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 12345)); + ValueStack values; + EXPECT_NO_THROW(expr->at(0)->evaluate(*pkt4, values)); + ASSERT_EQ(1, values.size()); + EXPECT_EQ("abc", values.top()); + EXPECT_NO_THROW(expr->at(1)->evaluate(*pkt4, values)); + ASSERT_EQ(2, values.size()); + EXPECT_NO_THROW(expr->at(2)->evaluate(*pkt4, values)); + ASSERT_EQ(1, values.size()); + EXPECT_EQ("true", values.top()); +} + +// Verify that a valid v6 remove value is accepted. +TEST_F(FlexOptionTest, optionConfigRemove6) { + CfgMgr::instance().setFamily(AF_INET6); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_BOOTFILE_URL); + option->set("code", code); + ElementPtr remove = Element::create(string("'abc' == 'abc'")); + option->set("remove", remove); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getOptionConfigMap(); + FlexOptionImpl::OptionConfigList opt_lst; + ASSERT_NO_THROW(opt_lst = map.at(D6O_BOOTFILE_URL)); + ASSERT_FALSE(opt_lst.empty()); + EXPECT_EQ(1, opt_lst.size()); + FlexOptionImpl::OptionConfigPtr opt_cfg; + ASSERT_NO_THROW(opt_cfg = opt_lst.front()); + + ASSERT_TRUE(opt_cfg); + EXPECT_EQ(D6O_BOOTFILE_URL, opt_cfg->getCode()); + EXPECT_EQ(FlexOptionImpl::REMOVE, opt_cfg->getAction()); + EXPECT_EQ("'abc' == 'abc'", opt_cfg->getText()); + + ExpressionPtr expr = opt_cfg->getExpr(); + ASSERT_TRUE(expr); + ASSERT_EQ(3, expr->size()); + Pkt6Ptr pkt6(new Pkt6(DHCPV6_SOLICIT, 12345)); + ValueStack values; + EXPECT_NO_THROW(expr->at(0)->evaluate(*pkt6, values)); + ASSERT_EQ(1, values.size()); + EXPECT_EQ("abc", values.top()); + EXPECT_NO_THROW(expr->at(1)->evaluate(*pkt6, values)); + ASSERT_EQ(2, values.size()); + EXPECT_NO_THROW(expr->at(2)->evaluate(*pkt6, values)); + ASSERT_EQ(1, values.size()); + EXPECT_EQ("true", values.top()); +} + +// Verify that multiple actions are not accepted. +TEST_F(FlexOptionTest, optionConfigMultipleAction) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + + // add and supersede. + ElementPtr add = Element::create(string("'abc'")); + option->set("add", add); + ElementPtr supersede = Element::create(string("'abc'")); + option->set("supersede", supersede); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + ostringstream errmsg; + errmsg << "multiple actions: " << option->str(); + EXPECT_EQ(errmsg.str(), impl_->getErrMsg()); + + // supersede and remove. + option->remove("add"); + ElementPtr remove = Element::create(string("'abc' == 'abc'")); + option->set("remove", remove); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + errmsg.str(""); + errmsg << "multiple actions: " << option->str(); + EXPECT_EQ(errmsg.str(), impl_->getErrMsg()); + + // add and remove. + option->remove("supersede"); + option->set("add", add); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + errmsg.str(""); + errmsg << "multiple actions: " << option->str(); + EXPECT_EQ(errmsg.str(), impl_->getErrMsg()); +} + +// Verify that multiple options are accepted. +TEST_F(FlexOptionTest, optionConfigList) { + ElementPtr options = Element::createList(); + + ElementPtr option1 = Element::createMap(); + options->add(option1); + ElementPtr code1 = Element::create(DHO_HOST_NAME); + option1->set("code", code1); + ElementPtr add1 = Element::create(string("'abc'")); + option1->set("add", add1); + + ElementPtr option2 = Element::createMap(); + options->add(option2); + ElementPtr code2 = Element::create(DHO_ROOT_PATH); + option2->set("code", code2); + ElementPtr supersede2 = Element::create(string("'/'")); + option2->set("supersede", supersede2); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getOptionConfigMap(); + EXPECT_EQ(2, map.size()); + + FlexOptionImpl::OptionConfigList opt1_lst; + ASSERT_NO_THROW(opt1_lst = map.at(DHO_HOST_NAME)); + ASSERT_FALSE(opt1_lst.empty()); + EXPECT_EQ(1, opt1_lst.size()); + FlexOptionImpl::OptionConfigPtr opt1_cfg; + ASSERT_NO_THROW(opt1_cfg = opt1_lst.front()); + + ASSERT_TRUE(opt1_cfg); + EXPECT_EQ(DHO_HOST_NAME, opt1_cfg->getCode()); + EXPECT_EQ(FlexOptionImpl::ADD, opt1_cfg->getAction()); + EXPECT_EQ("'abc'", opt1_cfg->getText()); + + FlexOptionImpl::OptionConfigList opt2_lst; + ASSERT_NO_THROW(opt2_lst = map.at(DHO_ROOT_PATH)); + ASSERT_FALSE(opt2_lst.empty()); + EXPECT_EQ(1, opt2_lst.size()); + FlexOptionImpl::OptionConfigPtr opt2_cfg; + ASSERT_NO_THROW(opt2_cfg = opt2_lst.front()); + + ASSERT_TRUE(opt2_cfg); + EXPECT_EQ(DHO_ROOT_PATH, opt2_cfg->getCode()); + EXPECT_EQ(FlexOptionImpl::SUPERSEDE, opt2_cfg->getAction()); + EXPECT_EQ("'/'", opt2_cfg->getText()); +} + +// Verify that empty option config list does nothing. +TEST_F(FlexOptionTest, processEmpty) { + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + string response_txt = response->toText(); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); +} + +// Verify that NONE action really does nothing. +TEST_F(FlexOptionTest, processNone) { + CfgMgr::instance().setFamily(AF_INET6); + + OptionDefinitionPtr def = LibDHCP::getOptionDef(DHCP6_OPTION_SPACE, D6O_BOOTFILE_URL); + FlexOptionImpl::OptionConfigPtr + opt_cfg(new FlexOptionImpl::OptionConfig(D6O_BOOTFILE_URL, def)); + EXPECT_EQ(FlexOptionImpl::NONE, opt_cfg->getAction()); + auto map = impl_->getMutableOptionConfigMap(); + auto& opt_lst = map[DHO_HOST_NAME]; + opt_lst.push_back(opt_cfg); + + Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345)); + Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345)); + string response_txt = response->toText(); + + EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response)); + + EXPECT_EQ(response_txt, response->toText()); +} + +// Verify that ADD action adds the specified option in csv format. +TEST_F(FlexOptionTest, processAddEnableCSVFormat) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr add = Element::create(string("'abc'")); + option->set("add", add); + + option = Element::createMap(); + options->add(option); + code = Element::create(DHO_DOMAIN_SEARCH); + option->set("code", code); + add = Element::create(string("'example.com'")); + option->set("add", add); + // fqdn option data is parsed using option definition in csv format. + option->set("csv-format", Element::create(true)); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + EXPECT_FALSE(response->getOption(DHO_HOST_NAME)); + EXPECT_FALSE(response->getOption(DHO_DOMAIN_SEARCH)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + OptionPtr opt = response->getOption(DHO_HOST_NAME); + ASSERT_TRUE(opt); + EXPECT_EQ(DHO_HOST_NAME, opt->getType()); + const OptionBuffer& buffer = opt->getData(); + ASSERT_EQ(3, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3)); + + opt = response->getOption(DHO_DOMAIN_SEARCH); + ASSERT_TRUE(opt); + EXPECT_EQ(DHO_DOMAIN_SEARCH, opt->getType()); + const OptionBuffer& buffer_fqdn = opt->getData(); + ASSERT_EQ(13, buffer_fqdn.size()); + EXPECT_EQ(7, buffer_fqdn[0]); + EXPECT_EQ(0, memcmp(&buffer_fqdn[1], "example", 7)); + EXPECT_EQ(3, buffer_fqdn[8]); + EXPECT_EQ(0, memcmp(&buffer_fqdn[9], "com", 3)); + EXPECT_EQ(0, buffer_fqdn[12]); +} + +// Verify that ADD action adds the specified option in raw format. +TEST_F(FlexOptionTest, processAddDisableCSVFormat) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr add = Element::create(string("'abc'")); + option->set("add", add); + + option = Element::createMap(); + options->add(option); + code = Element::create(DHO_DOMAIN_SEARCH); + option->set("code", code); + add = Element::create(string("0x076578616d706c6503636f6d00")); + option->set("add", add); + // fqdn option data is specified in raw format. + option->set("csv-format", Element::create(false)); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + EXPECT_FALSE(response->getOption(DHO_HOST_NAME)); + EXPECT_FALSE(response->getOption(DHO_DOMAIN_SEARCH)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + OptionPtr opt = response->getOption(DHO_HOST_NAME); + ASSERT_TRUE(opt); + EXPECT_EQ(DHO_HOST_NAME, opt->getType()); + const OptionBuffer& buffer = opt->getData(); + ASSERT_EQ(3, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3)); + + opt = response->getOption(DHO_DOMAIN_SEARCH); + ASSERT_TRUE(opt); + EXPECT_EQ(DHO_DOMAIN_SEARCH, opt->getType()); + const OptionBuffer& buffer_fqdn = opt->getData(); + ASSERT_EQ(13, buffer_fqdn.size()); + EXPECT_EQ(7, buffer_fqdn[0]); + EXPECT_EQ(0, memcmp(&buffer_fqdn[1], "example", 7)); + EXPECT_EQ(3, buffer_fqdn[8]); + EXPECT_EQ(0, memcmp(&buffer_fqdn[9], "com", 3)); + EXPECT_EQ(0, buffer_fqdn[12]); +} + +// Verify that ADD action does not add an already existing option. +TEST_F(FlexOptionTest, processAddExisting) { + CfgMgr::instance().setFamily(AF_INET6); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_BOOTFILE_URL); + option->set("code", code); + ElementPtr add = Element::create(string("'abc'")); + option->set("add", add); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345)); + Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345)); + OptionStringPtr str(new OptionString(Option::V6, D6O_BOOTFILE_URL, "http")); + response->addOption(str); + + EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response)); + + OptionPtr opt = response->getOption(D6O_BOOTFILE_URL); + ASSERT_TRUE(opt); + EXPECT_EQ(D6O_BOOTFILE_URL, opt->getType()); + EXPECT_EQ("http", opt->toString()); +} + +// Verify that ADD action does not add an empty value. +TEST_F(FlexOptionTest, processAddEmpty) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr add = Element::create(string("''")); + option->set("add", add); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + string response_txt = response->toText(); + EXPECT_FALSE(response->getOption(DHO_HOST_NAME)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + EXPECT_FALSE(response->getOption(DHO_HOST_NAME)); +} + +// Verify that SUPERSEDE action supersedes the specified option in csv format. +TEST_F(FlexOptionTest, processSupersedeEnableCSVFormat) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr supersede = Element::create(string("'abc'")); + option->set("supersede", supersede); + + option = Element::createMap(); + options->add(option); + code = Element::create(DHO_DOMAIN_SEARCH); + option->set("code", code); + supersede = Element::create(string("'example.com'")); + option->set("supersede", supersede); + // fqdn option data is parsed using option definition in csv format. + option->set("csv-format", Element::create(true)); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + EXPECT_FALSE(response->getOption(DHO_HOST_NAME)); + EXPECT_FALSE(response->getOption(DHO_DOMAIN_SEARCH)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + OptionPtr opt = response->getOption(DHO_HOST_NAME); + ASSERT_TRUE(opt); + EXPECT_EQ(DHO_HOST_NAME, opt->getType()); + const OptionBuffer& buffer = opt->getData(); + ASSERT_EQ(3, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3)); + + opt = response->getOption(DHO_DOMAIN_SEARCH); + ASSERT_TRUE(opt); + EXPECT_EQ(DHO_DOMAIN_SEARCH, opt->getType()); + const OptionBuffer& buffer_fqdn = opt->getData(); + ASSERT_EQ(13, buffer_fqdn.size()); + EXPECT_EQ(7, buffer_fqdn[0]); + EXPECT_EQ(0, memcmp(&buffer_fqdn[1], "example", 7)); + EXPECT_EQ(3, buffer_fqdn[8]); + EXPECT_EQ(0, memcmp(&buffer_fqdn[9], "com", 3)); + EXPECT_EQ(0, buffer_fqdn[12]); +} + +// Verify that SUPERSEDE action supersedes the specified option in raw format. +TEST_F(FlexOptionTest, processSupersedeDisableCSVFormat) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr supersede = Element::create(string("'abc'")); + option->set("supersede", supersede); + + option = Element::createMap(); + options->add(option); + code = Element::create(DHO_DOMAIN_SEARCH); + option->set("code", code); + supersede = Element::create(string("0x076578616d706c6503636f6d00")); + option->set("supersede", supersede); + // fqdn option data is specified in raw format. + option->set("csv-format", Element::create(false)); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + EXPECT_FALSE(response->getOption(DHO_HOST_NAME)); + EXPECT_FALSE(response->getOption(DHO_DOMAIN_SEARCH)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + OptionPtr opt = response->getOption(DHO_HOST_NAME); + ASSERT_TRUE(opt); + EXPECT_EQ(DHO_HOST_NAME, opt->getType()); + const OptionBuffer& buffer = opt->getData(); + ASSERT_EQ(3, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3)); + + opt = response->getOption(DHO_DOMAIN_SEARCH); + ASSERT_TRUE(opt); + EXPECT_EQ(DHO_DOMAIN_SEARCH, opt->getType()); + const OptionBuffer& buffer_fqdn = opt->getData(); + ASSERT_EQ(13, buffer_fqdn.size()); + EXPECT_EQ(7, buffer_fqdn[0]); + EXPECT_EQ(0, memcmp(&buffer_fqdn[1], "example", 7)); + EXPECT_EQ(3, buffer_fqdn[8]); + EXPECT_EQ(0, memcmp(&buffer_fqdn[9], "com", 3)); + EXPECT_EQ(0, buffer_fqdn[12]); +} + +// Verify that SUPERSEDE action supersedes an already existing option. +TEST_F(FlexOptionTest, processSupersedeExisting) { + CfgMgr::instance().setFamily(AF_INET6); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_BOOTFILE_URL); + option->set("code", code); + ElementPtr supersede = Element::create(string("0xabcdef")); + option->set("supersede", supersede); + + option = Element::createMap(); + options->add(option); + code = Element::create(D6O_DOMAIN_SEARCH); + option->set("code", code); + supersede = Element::create(string("'example.com'")); + option->set("supersede", supersede); + // fqdn option data is parsed using option definition in csv format. + option->set("csv-format", Element::create(true)); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345)); + Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345)); + OptionStringPtr str(new OptionString(Option::V6, D6O_BOOTFILE_URL, "http")); + response->addOption(str); + OptionDefinition def("domain-name", D6O_DOMAIN_SEARCH, DHCP6_OPTION_SPACE, + OPT_FQDN_TYPE); + OptionCustomPtr option_domain_name(new OptionCustom(def, Option::V6)); + option_domain_name->writeFqdn("old.example.com"); + response->addOption(option_domain_name); + + EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response)); + + OptionPtr opt = response->getOption(D6O_BOOTFILE_URL); + ASSERT_TRUE(opt); + EXPECT_EQ(D6O_BOOTFILE_URL, opt->getType()); + const OptionBuffer& buffer = opt->getData(); + ASSERT_EQ(3, buffer.size()); + uint8_t expected[] = { 0xab, 0xcd, 0xef }; + EXPECT_EQ(0, memcmp(&buffer[0], expected, 3)); + + opt = response->getOption(D6O_DOMAIN_SEARCH); + ASSERT_TRUE(opt); + EXPECT_EQ(D6O_DOMAIN_SEARCH, opt->getType()); + const OptionBuffer& buffer_fqdn = opt->getData(); + ASSERT_EQ(13, buffer_fqdn.size()); + EXPECT_EQ(7, buffer_fqdn[0]); + EXPECT_EQ(0, memcmp(&buffer_fqdn[1], "example", 7)); + EXPECT_EQ(3, buffer_fqdn[8]); + EXPECT_EQ(0, memcmp(&buffer_fqdn[9], "com", 3)); + EXPECT_EQ(0, buffer_fqdn[12]); +} + +// Verify that SUPERSEDE action does not supersede an empty value. +TEST_F(FlexOptionTest, processSupersedeEmpty) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr supersede = Element::create(string("''")); + option->set("supersede", supersede); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + string response_txt = response->toText(); + EXPECT_FALSE(response->getOption(DHO_HOST_NAME)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + EXPECT_FALSE(response->getOption(DHO_HOST_NAME)); + + // Empty value does not remove existing values. + OptionStringPtr str(new OptionString(Option::V4, DHO_HOST_NAME, "abc")); + response->addOption(str); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + OptionPtr opt = response->getOption(DHO_HOST_NAME); + ASSERT_TRUE(opt); + EXPECT_EQ(DHO_HOST_NAME, opt->getType()); + const OptionBuffer& buffer = opt->getData(); + ASSERT_EQ(3, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3)); +} + +// Verify that SUPERSEDE if exists + ADD adds a not yet existing option. +TEST_F(FlexOptionTest, processSupersedeAddNotExisting) { + CfgMgr::instance().setFamily(AF_INET6); + + ElementPtr options = Element::createList(); + ElementPtr option1 = Element::createMap(); + options->add(option1); + ElementPtr code = Element::create(D6O_BOOTFILE_URL); + option1->set("code", code); + string action = "ifelse(option[bootfile-url].exists,'supersede','')"; + ElementPtr supersede = Element::create(action); + option1->set("supersede", supersede); + ElementPtr option2 = Element::createMap(); + options->add(option2); + option2->set("code", code); + ElementPtr add = Element::create(string("'add'")); + option2->set("add", add); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345)); + Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345)); + EXPECT_FALSE(response->getOption(D6O_BOOTFILE_URL)); + + EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response)); + + OptionPtr opt = response->getOption(D6O_BOOTFILE_URL); + ASSERT_TRUE(opt); + EXPECT_EQ(D6O_BOOTFILE_URL, opt->getType()); + const OptionBuffer& buffer = opt->getData(); + ASSERT_EQ(3, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "add", 3)); +} + +// Verify that SUPERSEDE if exists + ADD supersedes an existing option. +TEST_F(FlexOptionTest, processSupersedeAddExisting) { + ElementPtr options = Element::createList(); + ElementPtr option1 = Element::createMap(); + options->add(option1); + ElementPtr code = Element::create(DHO_HOST_NAME); + option1->set("code", code); + string action = "ifelse(option[host-name].exists,'supersede','')"; + ElementPtr supersede = Element::create(action); + option1->set("supersede", supersede); + ElementPtr option2 = Element::createMap(); + options->add(option2); + option2->set("code", code); + ElementPtr add = Element::create(string("'add'")); + option2->set("add", add); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + OptionStringPtr str(new OptionString(Option::V4, DHO_HOST_NAME, "foobar")); + // Be careful here: the expression is related to the query. + query->addOption(str); + response->addOption(str); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + OptionPtr opt = response->getOption(DHO_HOST_NAME); + ASSERT_TRUE(opt); + EXPECT_EQ(DHO_HOST_NAME, opt->getType()); + const OptionBuffer& buffer = opt->getData(); + ASSERT_EQ(9, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "supersede", 9)); +} + +// Verify that REMOVE action removes an already existing option. +TEST_F(FlexOptionTest, processRemove) { + CfgMgr::instance().setFamily(AF_INET6); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_BOOTFILE_URL); + option->set("code", code); + ElementPtr remove = Element::create(string("'abc' == 'abc'")); + option->set("remove", remove); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345)); + Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345)); + OptionStringPtr str(new OptionString(Option::V6, D6O_BOOTFILE_URL, "http")); + response->addOption(str); + + EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response)); + + EXPECT_FALSE(response->getOption(D6O_BOOTFILE_URL)); +} + +// Verify that REMOVE action does nothing if the option is not present. +TEST_F(FlexOptionTest, processRemoveNoOption) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr remove = Element::create(string("'abc' == 'abc'")); + option->set("remove", remove); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + string response_txt = response->toText(); + EXPECT_FALSE(response->getOption(DHO_HOST_NAME)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + EXPECT_FALSE(response->getOption(DHO_HOST_NAME)); +} + +// Verify that REMOVE action does nothing when the expression evaluates to false. +TEST_F(FlexOptionTest, processRemoveFalse) { + CfgMgr::instance().setFamily(AF_INET6); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_BOOTFILE_URL); + option->set("code", code); + ElementPtr remove = Element::create(string("'abc' == 'xyz'")); + option->set("remove", remove); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345)); + Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345)); + OptionStringPtr str(new OptionString(Option::V6, D6O_BOOTFILE_URL, "http")); + response->addOption(str); + string response_txt = response->toText(); + + EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + EXPECT_TRUE(response->getOption(D6O_BOOTFILE_URL)); +} + +// A more complex check... +TEST_F(FlexOptionTest, processFullTest) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_BOOT_FILE_NAME); + option->set("code", code); + string expr = "ifelse(option[host-name].exists,"; + expr += "concat(option[host-name].text,'.boot'),'')"; + ElementPtr add = Element::create(expr); + option->set("add", add); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + OptionStringPtr str(new OptionString(Option::V4, DHO_HOST_NAME, "foo")); + query->addOption(str); + EXPECT_FALSE(response->getOption(DHO_BOOT_FILE_NAME)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + OptionPtr opt = response->getOption(DHO_BOOT_FILE_NAME); + ASSERT_TRUE(opt); + EXPECT_EQ(DHO_BOOT_FILE_NAME, opt->getType()); + const OptionBuffer& buffer = opt->getData(); + ASSERT_EQ(8, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "foo.boot", 8)); +} + +// Verify that complex strings with escaped characters are properly parsed on add. +TEST_F(FlexOptionTest, processFullAddWithComplexString) { + CfgMgr::instance().setFamily(AF_INET6); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_NEW_POSIX_TIMEZONE); + option->set("code", code); + string expr = "ifelse(option[39].exists,'EST5EDT4\\,M3.2.0/02:00\\,M11.1.0/02:00','')"; + ElementPtr add = Element::create(expr); + option->set("add", add); + // strings with escape characters are parsed in csv format. + option->set("csv-format", Element::create(true)); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345)); + Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345)); + OptionDefinitionPtr def = isc::dhcp::LibDHCP::getOptionDef(DHCP6_OPTION_SPACE, D6O_CLIENT_FQDN); + OptionCustomPtr str(new OptionCustom(*def, Option::V6)); + query->addOption(str); + EXPECT_FALSE(response->getOption(D6O_NEW_POSIX_TIMEZONE)); + + EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response)); + + OptionPtr opt = response->getOption(D6O_NEW_POSIX_TIMEZONE); + ASSERT_TRUE(opt); + EXPECT_EQ(D6O_NEW_POSIX_TIMEZONE, opt->getType()); + const OptionBuffer& buffer = opt->getData(); + EXPECT_EQ(35, buffer.size()); + std::string data("EST5EDT4,M3.2.0/02:00,M11.1.0/02:00"); + EXPECT_EQ(0, memcmp(&buffer[0], &data[0], buffer.size())); +} + +// Verify that complex strings with escaped characters are properly parsed on supersede. +TEST_F(FlexOptionTest, processFullSupersedeWithComplexString) { + CfgMgr::instance().setFamily(AF_INET6); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_NEW_POSIX_TIMEZONE); + option->set("code", code); + string expr = "ifelse(option[39].exists,'EST5EDT4\\,M3.2.0/02:00\\,M11.1.0/02:00','')"; + ElementPtr supersede = Element::create(expr); + option->set("supersede", supersede); + // strings with escape characters are parsed in csv format. + option->set("csv-format", Element::create(true)); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345)); + Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345)); + OptionDefinitionPtr def = isc::dhcp::LibDHCP::getOptionDef(DHCP6_OPTION_SPACE, D6O_CLIENT_FQDN); + OptionCustomPtr str(new OptionCustom(*def, Option::V6)); + query->addOption(str); + EXPECT_FALSE(response->getOption(D6O_NEW_POSIX_TIMEZONE)); + + EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response)); + + OptionPtr opt = response->getOption(D6O_NEW_POSIX_TIMEZONE); + ASSERT_TRUE(opt); + EXPECT_EQ(D6O_NEW_POSIX_TIMEZONE, opt->getType()); + const OptionBuffer& buffer = opt->getData(); + EXPECT_EQ(35, buffer.size()); + std::string data("EST5EDT4,M3.2.0/02:00,M11.1.0/02:00"); + EXPECT_EQ(0, memcmp(&buffer[0], &data[0], buffer.size())); +} + +// Verify that the client class must be a string. +TEST_F(FlexOptionTest, optionConfigBadClass) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr remove = Element::create(string("'abc' == 'abc'")); + option->set("remove", remove); + option->set("client-class", Element::create(true)); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'client-class' must be a string: true", impl_->getErrMsg()); +} + +// Verify that a valid client class is accepted. +TEST_F(FlexOptionTest, optionConfigGuardValid) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr remove = Element::create(string("'abc' == 'abc'")); + option->set("remove", remove); + option->set("client-class", Element::create(string("foobar"))); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getOptionConfigMap(); + FlexOptionImpl::OptionConfigList opt_lst; + ASSERT_NO_THROW(opt_lst = map.at(DHO_HOST_NAME)); + ASSERT_FALSE(opt_lst.empty()); + EXPECT_EQ(1, opt_lst.size()); + FlexOptionImpl::OptionConfigPtr opt_cfg; + ASSERT_NO_THROW(opt_cfg = opt_lst.front()); + + ASSERT_TRUE(opt_cfg); + EXPECT_EQ(DHO_HOST_NAME, opt_cfg->getCode()); + EXPECT_EQ("foobar", opt_cfg->getClass()); +} + +// Verify that a guarded action is skipped when query does not belong to the +// client class. +TEST_F(FlexOptionTest, optionConfigGuardNoMatch) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr remove = Element::create(string("'abc' == 'abc'")); + option->set("remove", remove); + option->set("client-class", Element::create(string("foobar"))); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + OptionStringPtr str(new OptionString(Option::V4, DHO_HOST_NAME, "foo")); + response->addOption(str); + EXPECT_TRUE(response->getOption(DHO_HOST_NAME)); + string response_txt = response->toText(); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + EXPECT_TRUE(response->getOption(DHO_HOST_NAME)); +} + +// Verify that a guarded action is applied when query belongs to the class. +TEST_F(FlexOptionTest, optionConfigGuardMatch) { + CfgMgr::instance().setFamily(AF_INET6); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_BOOTFILE_URL); + option->set("code", code); + ElementPtr remove = Element::create(string("'abc' == 'abc'")); + option->set("remove", remove); + option->set("client-class", Element::create(string("foobar"))); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345)); + query->addClass("foobar"); + Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345)); + string response_txt = response->toText(); + OptionStringPtr str(new OptionString(Option::V6, D6O_BOOTFILE_URL, "http")); + response->addOption(str); + + EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + EXPECT_FALSE(response->getOption(D6O_BOOTFILE_URL)); +} + +} // end of anonymous namespace diff --git a/src/hooks/dhcp/flex_option/tests/run_unittests.cc b/src/hooks/dhcp/flex_option/tests/run_unittests.cc new file mode 100644 index 0000000..5805b42 --- /dev/null +++ b/src/hooks/dhcp/flex_option/tests/run_unittests.cc @@ -0,0 +1,19 @@ +// 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 <log/logger_support.h> +#include <gtest/gtest.h> + +int +main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + isc::log::initLogger(); + int result = RUN_ALL_TESTS(); + + return (result); +} diff --git a/src/hooks/dhcp/flex_option/tests/sub_option_unittests.cc b/src/hooks/dhcp/flex_option/tests/sub_option_unittests.cc new file mode 100644 index 0000000..e593239 --- /dev/null +++ b/src/hooks/dhcp/flex_option/tests/sub_option_unittests.cc @@ -0,0 +1,3173 @@ +// Copyright (C) 2019-2022 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/. + +/// @file This file contains tests which verify flexible sub-option. + +#include <config.h> +#include <flex_option.h> +#include <flex_option_log.h> +#include <dhcp/docsis3_option_defs.h> +#include <dhcp/libdhcp++.h> +#include <dhcp/option4_addrlst.h> +#include <dhcp/option6_addrlst.h> +#include <dhcp/option_custom.h> +#include <dhcp/option_string.h> +#include <dhcpsrv/cfgmgr.h> +#include <eval/eval_context.h> +#include <hooks/callout_manager.h> +#include <hooks/hooks.h> + +#include <tests/test_flex_option.h> +#include <gtest/gtest.h> +#include <sstream> + +using namespace std; +using namespace isc; +using namespace isc::asiolink; +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::eval; +using namespace isc::hooks; +using namespace isc::flex_option; +using namespace isc::flex_option::test; + +namespace { + +/// @brief Test fixture for testing the Flex Option library. +class FlexSubOptionTest : public BaseFlexOptionTest { }; + +// Verify that the sub-options configuration must be a list. +TEST_F(FlexSubOptionTest, configNotList) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createMap(); + option->set("sub-options", sub_options); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'sub-options' must be a list: { }", + impl_->getErrMsg()); +} + +// Verify that the sub-options configuration can be the empty list. +TEST_F(FlexSubOptionTest, configEmpty) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + EXPECT_TRUE(impl_->getOptionConfigMap().empty()); + EXPECT_TRUE(impl_->getSubOptionConfigMap().empty()); +} + +// Verify that a sub-option configuration must exist. +TEST_F(FlexSubOptionTest, noSubOptionConfig) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option; + sub_options->add(sub_option); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("null sub-option element", impl_->getErrMsg()); +} + +// Verify that a sub-option configuration must be a map. +TEST_F(FlexSubOptionTest, subOptionConfigNotMap) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createList(); + sub_options->add(sub_option); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("sub-option element is not a map", impl_->getErrMsg()); +} + +// Verify that multiple actions are not accepted. +TEST_F(FlexSubOptionTest, configMultipleAction) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + + // add and sub-options. + ElementPtr add = Element::create(string("'abc'")); + option->set("add", add); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + + EXPECT_THROW(impl_->testConfigure(options), BadValue); + ostringstream errmsg; + errmsg << "'sub-options' and 'add' are incompatible in the same entry"; + EXPECT_EQ(errmsg.str(), impl_->getErrMsg()); +} + +// Verify that an unknown option keyword is rejected. +TEST_F(FlexSubOptionTest, subOptionConfigUnknown) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr unknown = Element::create(string("'ab'")); + // The right keyword is remove... + sub_option->set("delete", unknown); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("unknown parameter 'delete'", impl_->getErrMsg()); +} + +// Verify that a sub-option configuration must have code or name. +TEST_F(FlexSubOptionTest, subOptionConfigNoCodeName) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + ostringstream errmsg; + errmsg << "'code' or 'name' must be specified: " << sub_option->str(); + EXPECT_EQ(errmsg.str(), impl_->getErrMsg()); +} + +// Verify that a sub-option configuration must retrieve a space. +TEST_F(FlexSubOptionTest, subOptionConfigNoSpace) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + sub_option->set("code", code); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("container is not defined: can't get space", impl_->getErrMsg()); +} + +// Verify that the v4 sub-option code must be an integer in [0..255]. +TEST_F(FlexSubOptionTest, subOptionConfigBadCode4) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + code = Element::create(false); + sub_option->set("code", code); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'code' must be an integer: false", impl_->getErrMsg()); + + code = Element::create(-1); + sub_option->set("code", code); + EXPECT_THROW(impl_->testConfigure(options), OutOfRange); + EXPECT_EQ("invalid 'code' value -1 not in [0..255]", impl_->getErrMsg()); + + code = Element::create(256); + sub_option->set("code", code); + EXPECT_THROW(impl_->testConfigure(options), OutOfRange); + EXPECT_EQ("invalid 'code' value 256 not in [0..255]", impl_->getErrMsg()); + + code = Element::create(1); + sub_option->set("code", code); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + code = Element::create(254); + sub_option->set("code", code); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); +} + +// Verify that the v6 option code must be an integer in [0..65535]. +TEST_F(FlexSubOptionTest, subOptionConfigBadCode6) { + CfgMgr::instance().setFamily(AF_INET6); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + code = Element::create(false); + sub_option->set("code", code); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'code' must be an integer: false", impl_->getErrMsg()); + + code = Element::create(-1); + sub_option->set("code", code); + EXPECT_THROW(impl_->testConfigure(options), OutOfRange); + EXPECT_EQ("invalid 'code' value -1 not in [0..65535]", impl_->getErrMsg()); + + code = Element::create(65536); + sub_option->set("code", code); + EXPECT_THROW(impl_->testConfigure(options), OutOfRange); + EXPECT_EQ("invalid 'code' value 65536 not in [0..65535]", impl_->getErrMsg()); + + code = Element::create(1); + sub_option->set("code", code); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + code = Element::create(65535); + sub_option->set("code", code); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); +} + +// Verify that the space must be a string. +TEST_F(FlexSubOptionTest, subOptionConfigBadSpace) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(true); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + sub_option->set("code", code); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'space' must be a string: true", impl_->getErrMsg()); +} + +// Verify that the space must be valid. +TEST_F(FlexSubOptionTest, subOptionConfigInvalidSpace) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("-bad-")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + sub_option->set("code", code); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'-bad-' is not a valid space name", impl_->getErrMsg()); +} + +// Verify that the name must be a string. +TEST_F(FlexSubOptionTest, subOptionConfigBadName) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + ElementPtr name = Element::create(true); + sub_option->set("name", name); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'name' must be a string: true", impl_->getErrMsg()); +} + +// Verify that the name must not be empty. +TEST_F(FlexSubOptionTest, subOptionConfigEmptyName) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + ElementPtr name = Element::create(string()); + sub_option->set("name", name); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'name' must not be empty", impl_->getErrMsg()); +} + +// Verify that the name must be a known option. +TEST_F(FlexSubOptionTest, subOptionConfigUnknownName) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + ElementPtr name = Element::create(string("foobar")); + sub_option->set("name", name); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("no known 'foobar' sub-option in 'my-space' space", + impl_->getErrMsg()); +} + +// Verify that the definition is not required when csv-format is not specified. +TEST_F(FlexSubOptionTest, subOptionConfigUnknownCodeNoCSVFormat) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + ElementPtr sub_code = Element::create(222); + sub_option->set("code", sub_code); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getSubOptionConfigMap(); + EXPECT_EQ(1, map.count(109)); + auto smap = map[109]; + EXPECT_EQ(1, smap.count(222)); +} + +// Verify that the definition is not required when csv-format is false. +TEST_F(FlexSubOptionTest, subOptionConfigUnknownCodeDisableCSVFormat) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + ElementPtr sub_code = Element::create(222); + sub_option->set("code", sub_code); + // Disable csv-format. + sub_option->set("csv-format", Element::create(false)); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getSubOptionConfigMap(); + EXPECT_EQ(1, map.count(109)); + auto smap = map[109]; + EXPECT_EQ(1, smap.count(222)); +} + +// Verify that the code must be a known sub-option when csv-format is true. +TEST_F(FlexSubOptionTest, subOptionConfigUnknownCodeEnableCSVFormat) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + ElementPtr sub_code = Element::create(222); + sub_option->set("code", sub_code); + // Enable csv-format. + sub_option->set("csv-format", Element::create(true)); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("no known sub-option with code '222' in 'my-space' space", + impl_->getErrMsg()); +} + +// Verify that the name can be an user defined sub-option. +TEST_F(FlexSubOptionTest, subOptionConfigDefinedName) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-option", 222, + "my-space", "string")); + defs.addItem(def); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getSubOptionConfigMap(); + EXPECT_EQ(1, map.count(109)); + auto smap = map[109]; + EXPECT_EQ(1, smap.count(222)); +} + +// Verify that the name can be a last resort space defined sub-option. +TEST_F(FlexSubOptionTest, subOptionConfigLastResortName) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-option", 222, + VENDOR_ENCAPSULATED_OPTION_SPACE, + "string")); + defs.addItem(def); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_VENDOR_ENCAPSULATED_OPTIONS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + ElementPtr sub_code = Element::create(222); + sub_option->set("code", sub_code); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getSubOptionConfigMap(); + EXPECT_EQ(1, map.count(DHO_VENDOR_ENCAPSULATED_OPTIONS)); + auto smap = map[DHO_VENDOR_ENCAPSULATED_OPTIONS]; + EXPECT_EQ(1, smap.count(222)); +} + +// Verify that the sub-option definition can be fetched from the last +// resort space. +TEST_F(FlexSubOptionTest, subOptionConfigLastResortCode) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_VENDOR_ENCAPSULATED_OPTIONS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + ElementPtr sub_code = Element::create(222); + sub_option->set("code", sub_code); + // Enable csv-format. + sub_option->set("csv-format", Element::create(true)); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + string msg = "no known sub-option with code '222' in '"; + msg += VENDOR_ENCAPSULATED_OPTION_SPACE; + msg += "' space"; + EXPECT_EQ(msg, impl_->getErrMsg()); + + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-option", 222, + VENDOR_ENCAPSULATED_OPTION_SPACE, + "string")); + defs.addItem(def); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + auto map = impl_->getSubOptionConfigMap(); + EXPECT_EQ(1, map.count(DHO_VENDOR_ENCAPSULATED_OPTIONS)); + auto smap = map[DHO_VENDOR_ENCAPSULATED_OPTIONS]; + EXPECT_EQ(1, smap.count(222)); +} + +// Verify that the name can be a vendor defined sub-option. +TEST_F(FlexSubOptionTest, subOptionConfigVendorName) { + CfgMgr::instance().setFamily(AF_INET6); + + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-option", 222, + "vendor-1234", "string")); + defs.addItem(def); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_VENDOR_OPTS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("vendor-1234")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getSubOptionConfigMap(); + EXPECT_EQ(1, map.count(D6O_VENDOR_OPTS)); + auto smap = map[D6O_VENDOR_OPTS]; + EXPECT_EQ(1, smap.count(222)); +} + +// Verify that the sub-option definition can be fetched from a custom +// vendor space. +TEST_F(FlexSubOptionTest, subOptionConfigVendorCode) { + CfgMgr::instance().setFamily(AF_INET6); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_VENDOR_OPTS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("vendor-1234")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + ElementPtr sub_code = Element::create(222); + sub_option->set("code", sub_code); + // Enable csv-format. + sub_option->set("csv-format", Element::create(true)); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("no known sub-option with code '222' in 'vendor-1234' space", + impl_->getErrMsg()); + + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-option", 222, + "vendor-1234", "string")); + defs.addItem(def); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getSubOptionConfigMap(); + EXPECT_EQ(1, map.count(D6O_VENDOR_OPTS)); + auto smap = map[D6O_VENDOR_OPTS]; + EXPECT_EQ(1, smap.count(222)); +} + +// Verify that the name can be a vendor standard sub-option. +TEST_F(FlexSubOptionTest, subOptionConfigDosSISName) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + // VENDOR_ID_CABLE_LABS is 4491 + ElementPtr space = Element::create(string("vendor-4491")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + ElementPtr name = Element::create(string("tftp-servers")); + sub_option->set("name", name); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getSubOptionConfigMap(); + EXPECT_EQ(1, map.count(DHO_VIVSO_SUBOPTIONS)); + auto smap = map[DHO_VIVSO_SUBOPTIONS]; + // DOCSIS3_V4_TFTP_SERVERS is 2 + EXPECT_EQ(1, smap.count(2)); +} + +// Verify that the sub-option definition can be fetched from a standard +// vendor space. +TEST_F(FlexSubOptionTest, subOptionConfigDosSISCode) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + // VENDOR_ID_CABLE_LABS is 4491 + ElementPtr space = Element::create(string("vendor-4491")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + ElementPtr sub_code = Element::create(DOCSIS3_V4_TFTP_SERVERS); + sub_option->set("code", sub_code); + // Enable csv-format. + sub_option->set("csv-format", Element::create(true)); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getSubOptionConfigMap(); + EXPECT_EQ(1, map.count(DHO_VIVSO_SUBOPTIONS)); + auto smap = map[DHO_VIVSO_SUBOPTIONS]; + // DOCSIS3_V4_TFTP_SERVERS is 2 + EXPECT_EQ(1, smap.count(2)); +} + +// Verify that the name must match the code. +TEST_F(FlexSubOptionTest, subOptionConfigCodeNameMismatch) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + // VENDOR_ID_CABLE_LABS is 4491 + ElementPtr space = Element::create(string("vendor-4491")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + ElementPtr name = Element::create(string("tftp-servers")); + sub_option->set("name", name); + // DOCSIS3_V4_TFTP_SERVERS is 2 + ElementPtr sub_code = Element::create(222); + sub_option->set("code", sub_code); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + string expected = "sub-option 'tftp-servers' is defined as code: 2, "; + expected += "not the specified code: 222"; + EXPECT_EQ(expected, impl_->getErrMsg()); +} + +// Verify that the csv-format must be a boolean. +TEST_F(FlexSubOptionTest, subOptionConfigBadCSVFormat) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + ElementPtr sub_code = Element::create(222); + sub_option->set("code", sub_code); + ElementPtr csv_format = Element::create(123); + sub_option->set("csv-format", csv_format); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'csv-format' must be a boolean: 123", impl_->getErrMsg()); +} + +// Verify that the container-add must be a boolean. +TEST_F(FlexSubOptionTest, subOptionConfigBadContainerAdd) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + ElementPtr sub_code = Element::create(222); + sub_option->set("code", sub_code); + ElementPtr container_add = Element::create(123); + sub_option->set("container-add", container_add); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'container-add' must be a boolean: 123", impl_->getErrMsg()); +} + +// Verify that the container-remove must be a boolean. +TEST_F(FlexSubOptionTest, subOptionConfigBadContainerRemove) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + ElementPtr sub_code = Element::create(222); + sub_option->set("code", sub_code); + ElementPtr container_remove = Element::create(123); + sub_option->set("container-remove", container_remove); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'container-remove' must be a boolean: 123", impl_->getErrMsg()); +} + +// Verify that multiple sub-option actions are not accepted. +TEST_F(FlexSubOptionTest, subOptionConfigMultipleAction) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + ElementPtr sub_code = Element::create(222); + sub_option->set("code", sub_code); + + // Add it a second time. + sub_options->add(sub_option); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("sub-option 222 of option 109 was already specified", + impl_->getErrMsg()); +} + +// Verify that the add value must be a string. +TEST_F(FlexSubOptionTest, subOptionConfigAddNotString) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr sub_code = Element::create(222); + sub_option->set("code", sub_code); + ElementPtr add = Element::create(true); + sub_option->set("add", add); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'add' must be a string: true", impl_->getErrMsg()); +} + +// Verify that the add value must not be empty. +TEST_F(FlexSubOptionTest, subOptionConfigEmptyAdd) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr sub_code = Element::create(222); + sub_option->set("code", sub_code); + ElementPtr add = Element::create(string()); + sub_option->set("add", add); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'add' must not be empty", impl_->getErrMsg()); +} + +// Verify that the add value must parse. +TEST_F(FlexSubOptionTest, subOptionConfigBadAdd) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr sub_code = Element::create(222); + sub_option->set("code", sub_code); + ElementPtr add = Element::create(string("ifelse('a','b','c')")); + sub_option->set("add", add); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + string expected = "can't parse add expression [ifelse('a','b','c')] "; + expected += "error: <string>:1.11: syntax error, "; + expected += "unexpected \",\", expecting == or +"; + EXPECT_EQ(expected, impl_->getErrMsg()); +} + +// Verify that the supersede value must be a string. +TEST_F(FlexSubOptionTest, subOptionConfigSupersedeNotString) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr sub_code = Element::create(222); + sub_option->set("code", sub_code); + ElementPtr supersede = Element::create(true); + sub_option->set("supersede", supersede); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'supersede' must be a string: true", impl_->getErrMsg()); +} + +// Verify that the supersede value must not be empty. +TEST_F(FlexSubOptionTest, subOptionConfigEmptySupersede) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr sub_code = Element::create(222); + sub_option->set("code", sub_code); + ElementPtr supersede = Element::create(string()); + sub_option->set("supersede", supersede); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'supersede' must not be empty", impl_->getErrMsg()); +} + +// Verify that the supersede value must parse. +TEST_F(FlexSubOptionTest, subOptionConfigBadSupersede) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr sub_code = Element::create(222); + sub_option->set("code", sub_code); + ElementPtr supersede = Element::create(string("ifelse('a','b','c')")); + sub_option->set("supersede", supersede); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + string expected = "can't parse supersede expression [ifelse('a','b','c')] "; + expected += "error: <string>:1.11: syntax error, "; + expected += "unexpected \",\", expecting == or +"; + EXPECT_EQ(expected, impl_->getErrMsg()); +} + +// Verify that the remove value must be a string. +TEST_F(FlexSubOptionTest, subOptionConfigRemoveNotString) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr sub_code = Element::create(222); + sub_option->set("code", sub_code); + ElementPtr remove = Element::createMap(); + sub_option->set("remove", remove); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'remove' must be a string: { }", impl_->getErrMsg()); +} + +// Verify that the remove value must not be empty. +TEST_F(FlexSubOptionTest, subOptionConfigEmptyRemove) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr sub_code = Element::create(222); + sub_option->set("code", sub_code); + ElementPtr remove = Element::create(string()); + sub_option->set("remove", remove); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'remove' must not be empty", impl_->getErrMsg()); +} + +// Verify that the remove value must parse. +TEST_F(FlexSubOptionTest, subOptionConfigBadRemove) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr sub_code = Element::create(222); + sub_option->set("code", sub_code); + ElementPtr remove = Element::create(string("'abc'")); + sub_option->set("remove", remove); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + string expected = "can't parse remove expression ['abc'] error: "; + expected += "<string>:1.6: syntax error, unexpected end of file, "; + expected += "expecting == or +"; + EXPECT_EQ(expected, impl_->getErrMsg()); +} + +// Verify that a complex example must parse. +TEST_F(FlexSubOptionTest, subOptionConfigComplex) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + + ElementPtr sub_option1 = Element::createMap(); + sub_options->add(sub_option1); + ElementPtr space = Element::create(string("my-space")); + sub_option1->set("space", space); + ElementPtr sub_code = Element::create(1); + sub_option1->set("code", sub_code); + ElementPtr add = Element::create(string("'abc'")); + sub_option1->set("add", add); + + ElementPtr sub_option2 = Element::createMap(); + sub_options->add(sub_option2); + sub_option2->set("space", space); + sub_code = Element::create(2); + sub_option2->set("code", sub_code); + ElementPtr supersede = Element::create(string("'def'")); + sub_option2->set("supersede", supersede); + sub_option2->set("container-add", Element::create(false)); + + ElementPtr sub_option3 = Element::createMap(); + sub_options->add(sub_option3); + sub_option3->set("space", space); + sub_code = Element::create(3); + sub_option3->set("code", sub_code); + ElementPtr remove = Element::create(string("'a' == 'b'")); + sub_option3->set("remove", remove); + + ElementPtr sub_option4 = Element::createMap(); + sub_options->add(sub_option4); + sub_option4->set("space", space); + sub_code = Element::create(4); + sub_option4->set("code", sub_code); + remove = Element::create(string("'b' == 'a'")); + sub_option4->set("remove", remove); + sub_option4->set("container-remove", Element::create(false)); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getSubOptionConfigMap(); + EXPECT_EQ(1, map.count(109)); + auto smap = map[109]; + FlexOptionImpl::SubOptionConfigPtr sub_cfg; + ASSERT_NO_THROW(sub_cfg = smap.at(1)); + ASSERT_TRUE(sub_cfg); + EXPECT_EQ(1, sub_cfg->getCode()); + EXPECT_EQ(FlexOptionImpl::ADD, sub_cfg->getAction()); + EXPECT_EQ(FlexOptionImpl::ADD, sub_cfg->getContainerAction()); + EXPECT_EQ("'abc'", sub_cfg->getText()); + EXPECT_EQ(109, sub_cfg->getContainerCode()); + + ASSERT_NO_THROW(sub_cfg = smap.at(2)); + ASSERT_TRUE(sub_cfg); + EXPECT_EQ(2, sub_cfg->getCode()); + EXPECT_EQ(FlexOptionImpl::SUPERSEDE, sub_cfg->getAction()); + EXPECT_EQ(FlexOptionImpl::NONE, sub_cfg->getContainerAction()); + EXPECT_EQ("'def'", sub_cfg->getText()); + EXPECT_EQ(109, sub_cfg->getContainerCode()); + + ASSERT_NO_THROW(sub_cfg = smap.at(3)); + ASSERT_TRUE(sub_cfg); + EXPECT_EQ(3, sub_cfg->getCode()); + EXPECT_EQ(FlexOptionImpl::REMOVE, sub_cfg->getAction()); + EXPECT_EQ(FlexOptionImpl::REMOVE, sub_cfg->getContainerAction()); + EXPECT_EQ("'a' == 'b'", sub_cfg->getText()); + EXPECT_EQ(109, sub_cfg->getContainerCode()); + + ASSERT_NO_THROW(sub_cfg = smap.at(4)); + ASSERT_TRUE(sub_cfg); + EXPECT_EQ(4, sub_cfg->getCode()); + EXPECT_EQ(FlexOptionImpl::REMOVE, sub_cfg->getAction()); + EXPECT_EQ(FlexOptionImpl::NONE, sub_cfg->getContainerAction()); + EXPECT_EQ("'b' == 'a'", sub_cfg->getText()); + EXPECT_EQ(109, sub_cfg->getContainerCode()); +} + +// Empty sub-option config list doing nothing is the same as empty option list. + +// Verify that NONE action really does nothing. +TEST_F(FlexSubOptionTest, subProcessNone) { + auto rai_def = LibDHCP::getOptionDef(DHCP4_OPTION_SPACE, + DHO_DHCP_AGENT_OPTIONS); + FlexOptionImpl::OptionConfigPtr + opt_cfg(new FlexOptionImpl::OptionConfig(DHO_DHCP_AGENT_OPTIONS, + rai_def)); + // RAI_OPTION_AGENT_CIRCUIT_ID is 1 but has no definition. + OptionDefinitionPtr def; + FlexOptionImpl::SubOptionConfigPtr + sub_cfg(new FlexOptionImpl::SubOptionConfig(1, def, opt_cfg)); + auto& map = impl_->getMutableSubOptionConfigMap(); + auto& smap = map[DHO_DHCP_AGENT_OPTIONS]; + smap[1] = sub_cfg; + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + string response_txt = response->toText(); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); +} + +// Verify that ADD action adds the specified sub-option in csv format. +TEST_F(FlexSubOptionTest, subProcessAddEnableCSVFormat) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-container", 222, + DHCP4_OPTION_SPACE, "empty", + "my-space")); + defs.addItem(def); + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space", + "fqdn", true)); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(222); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'example.com'")); + sub_option->set("add", add); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + sub_option->set("csv-format", Element::create(true)); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + EXPECT_FALSE(response->getOption(222)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + OptionPtr opt = response->getOption(222); + ASSERT_TRUE(opt); + EXPECT_EQ(222, opt->getType()); + OptionPtr sub = opt->getOption(1); + ASSERT_TRUE(sub); + EXPECT_EQ(1, sub->getType()); + // The fqdn array is the most complex encoding of one element... + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(13, buffer.size()); + EXPECT_EQ(7, buffer[0]); + EXPECT_EQ(0, memcmp(&buffer[1], "example", 7)); + EXPECT_EQ(3, buffer[8]); + EXPECT_EQ(0, memcmp(&buffer[9], "com", 3)); + EXPECT_EQ(0, buffer[12]); +} + +// Verify that ADD action does nothing when the container does not exist and +// container-add is false. +TEST_F(FlexSubOptionTest, subProcessAddNoContainer) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-container", 222, + DHCP4_OPTION_SPACE, "empty", + "my-space")); + defs.addItem(def); + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space", + "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(222); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'abc'")); + sub_option->set("add", add); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + sub_option->set("container-add", Element::create(false)); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + string response_txt = response->toText(); + EXPECT_FALSE(response->getOption(222)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + EXPECT_FALSE(response->getOption(222)); +} + +// Verify that ADD action adds the specified sub-option in raw format. +TEST_F(FlexSubOptionTest, subProcessAddDisableCSVFormat) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-container", 222, + DHCP4_OPTION_SPACE, "empty", + "my-space")); + defs.addItem(def); + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space", + "fqdn", true)); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(222); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("0x076578616d706c6503636f6d00")); + sub_option->set("add", add); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + // csv-format is disabled by default. + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + EXPECT_FALSE(response->getOption(222)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + OptionPtr opt = response->getOption(222); + ASSERT_TRUE(opt); + EXPECT_EQ(222, opt->getType()); + OptionPtr sub = opt->getOption(1); + ASSERT_TRUE(sub); + EXPECT_EQ(1, sub->getType()); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(13, buffer.size()); + EXPECT_EQ(7, buffer[0]); + EXPECT_EQ(0, memcmp(&buffer[1], "example", 7)); + EXPECT_EQ(3, buffer[8]); + EXPECT_EQ(0, memcmp(&buffer[9], "com", 3)); + EXPECT_EQ(0, buffer[12]); +} + +// Verify that ADD action adds the specified sub-option in an already +// existing container option. +TEST_F(FlexSubOptionTest, subProcessAdd) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-container", 222, + DHCP4_OPTION_SPACE, "empty", + "my-space")); + defs.addItem(def); + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space", + "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(222); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'abc'")); + sub_option->set("add", add); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + OptionPtr container(new Option(Option::V4, 222)); + response->addOption(container); + EXPECT_TRUE(response->getOption(222)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + // Only one option with code 222. + EXPECT_EQ(1, response->options_.count(222)); + + OptionPtr opt = response->getOption(222); + ASSERT_TRUE(opt); + EXPECT_EQ(222, opt->getType()); + OptionPtr sub = opt->getOption(1); + ASSERT_TRUE(sub); + EXPECT_EQ(1, sub->getType()); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(3, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3)); + + // Only one sub-option. + auto const& opts = opt->getOptions(); + EXPECT_EQ(1, opts.size()); +} + +// Verify that ADD action does not add an already existing sub-option. +TEST_F(FlexSubOptionTest, subProcessAddExisting) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-container", 222, + DHCP4_OPTION_SPACE, "empty", + "my-space")); + defs.addItem(def); + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space", + "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(222); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'abc'")); + sub_option->set("add", add); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + OptionPtr container(new Option(Option::V4, 222)); + response->addOption(container); + EXPECT_TRUE(response->getOption(222)); + OptionStringPtr str(new OptionString(Option::V4, 1, "xyzt")); + container->addOption(str); + string response_txt = response->toText(); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + OptionPtr opt = response->getOption(222); + ASSERT_TRUE(opt); + EXPECT_EQ(222, opt->getType()); + OptionPtr sub = opt->getOption(1); + ASSERT_TRUE(sub); + EXPECT_EQ(1, sub->getType()); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(4, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "xyzt", 4)); + + // Only one sub-option. + auto const& opts = opt->getOptions(); + EXPECT_EQ(1, opts.size()); +} + +// Verify that ADD action does not add an empty value. +TEST_F(FlexSubOptionTest, subProcessAddEmpty) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-container", 222, + DHCP4_OPTION_SPACE, "empty", + "my-space")); + defs.addItem(def); + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space", + "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(222); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("''")); + sub_option->set("add", add); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + string response_txt = response->toText(); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + EXPECT_FALSE(response->getOption(222)); +} + +// Verify that ADD action can handle vendor-encapsulated-options 43. +TEST_F(FlexSubOptionTest, subProcessAdd43) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, + VENDOR_ENCAPSULATED_OPTION_SPACE, + "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_VENDOR_ENCAPSULATED_OPTIONS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr add = Element::create(string("'foobar'")); + sub_option->set("add", add); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + OptionPtr opt = response->getOption(DHO_VENDOR_ENCAPSULATED_OPTIONS); + ASSERT_TRUE(opt); + EXPECT_EQ(DHO_VENDOR_ENCAPSULATED_OPTIONS, opt->getType()); + OptionPtr sub = opt->getOption(1); + ASSERT_TRUE(sub); + EXPECT_EQ(1, sub->getType()); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(6, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "foobar", 6)); +} + +// Verify that ADD action can handle DocSIS Vivso. +TEST_F(FlexSubOptionTest, subProcessAddDocSISVIVSO) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr add = Element::create(string("'10.1.2.3'")); + sub_option->set("add", add); + // DocSIS is 4491 + ElementPtr space = Element::create(string("vendor-4491")); + sub_option->set("space", space); + ElementPtr name = Element::create(string("tftp-servers")); + sub_option->set("name", name); + sub_option->set("csv-format", Element::create(true)); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + OptionPtr opt = response->getOption(DHO_VIVSO_SUBOPTIONS); + ASSERT_TRUE(opt); + OptionVendorPtr vendor = boost::dynamic_pointer_cast<OptionVendor>(opt); + ASSERT_TRUE(vendor); + EXPECT_EQ(VENDOR_ID_CABLE_LABS, vendor->getVendorId()); + OptionPtr sub = vendor->getOption(DOCSIS3_V4_TFTP_SERVERS); + ASSERT_TRUE(sub); + Option4AddrLstPtr addr = boost::dynamic_pointer_cast<Option4AddrLst>(sub); + ASSERT_TRUE(addr); + auto const& addrs = addr->getAddresses(); + ASSERT_EQ(1, addrs.size()); + EXPECT_EQ("10.1.2.3", addrs[0].toText()); +} + +// Verify that ADD action can handle DocSIS vendor-opts. +TEST_F(FlexSubOptionTest, subProcessAddDocSISVendorOps) { + CfgMgr::instance().setFamily(AF_INET6); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_VENDOR_OPTS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr add = Element::create(string("'foobar'")); + sub_option->set("add", add); + // DocSIS is 4491 + ElementPtr space = Element::create(string("vendor-4491")); + sub_option->set("space", space); + code = Element::create(DOCSIS3_V6_VENDOR_NAME); + sub_option->set("code", code); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345)); + Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345)); + + EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response)); + + OptionPtr opt = response->getOption(D6O_VENDOR_OPTS); + ASSERT_TRUE(opt); + OptionVendorPtr vendor = boost::dynamic_pointer_cast<OptionVendor>(opt); + ASSERT_TRUE(vendor); + EXPECT_EQ(VENDOR_ID_CABLE_LABS, vendor->getVendorId()); + OptionPtr sub = vendor->getOption(DOCSIS3_V6_VENDOR_NAME); + ASSERT_TRUE(sub); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(6, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "foobar", 6)); +} + +// Verify that ADD action can handle the Vivso option. +TEST_F(FlexSubOptionTest, subProcessAddVivso) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, + "vendor-123456", "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr add = Element::create(string("'foobar'")); + sub_option->set("add", add); + ElementPtr space = Element::create(string("vendor-123456")); + sub_option->set("space", space); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + OptionPtr opt = response->getOption(DHO_VIVSO_SUBOPTIONS); + ASSERT_TRUE(opt); + OptionVendorPtr vendor = boost::dynamic_pointer_cast<OptionVendor>(opt); + ASSERT_TRUE(vendor); + EXPECT_EQ(123456, vendor->getVendorId()); + OptionPtr sub = vendor->getOption(1); + ASSERT_TRUE(sub); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(6, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "foobar", 6)); +} + +// Verify that ADD action can handle the Vivso option with vendor mismatch. +TEST_F(FlexSubOptionTest, subProcessAddVivsoMismatch) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, + "vendor-123456", "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr add = Element::create(string("'foobar'")); + sub_option->set("add", add); + ElementPtr space = Element::create(string("vendor-123456")); + sub_option->set("space", space); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + OptionVendorPtr vendor(new OptionVendor(Option::V4, 67890)); + response->addOption(vendor); + OptionStringPtr str(new OptionString(Option::V4, 2, "xyzt")); + vendor->addOption(str); + string response_txt = response->toText(); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + OptionPtr opt = response->getOption(DHO_VIVSO_SUBOPTIONS); + ASSERT_TRUE(opt); + vendor = boost::dynamic_pointer_cast<OptionVendor>(opt); + ASSERT_TRUE(vendor); + EXPECT_EQ(67890, vendor->getVendorId()); + OptionPtr sub = vendor->getOption(1); + EXPECT_FALSE(sub); + sub = vendor->getOption(2); + ASSERT_TRUE(sub); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(4, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "xyzt", 4)); +} + +// Verify that ADD action can handle the vendor-opts option. +TEST_F(FlexSubOptionTest, subProcessAddVendorOpts) { + CfgMgr::instance().setFamily(AF_INET6); + + OptionDefSpaceContainer defs; + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, + "vendor-123456", "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_VENDOR_OPTS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr add = Element::create(string("'foobar'")); + sub_option->set("add", add); + ElementPtr space = Element::create(string("vendor-123456")); + sub_option->set("space", space); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345)); + Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345)); + + EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response)); + + OptionPtr opt = response->getOption(D6O_VENDOR_OPTS); + ASSERT_TRUE(opt); + OptionVendorPtr vendor = boost::dynamic_pointer_cast<OptionVendor>(opt); + ASSERT_TRUE(vendor); + EXPECT_EQ(123456, vendor->getVendorId()); + OptionPtr sub = vendor->getOption(1); + ASSERT_TRUE(sub); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(6, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "foobar", 6)); +} + +// Verify that ADD action can handle the vendor-opts option with vendor mismatch. +TEST_F(FlexSubOptionTest, subProcessAddVendorOptsMismatch) { + CfgMgr::instance().setFamily(AF_INET6); + + OptionDefSpaceContainer defs; + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, + "vendor-123456", "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_VENDOR_OPTS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr add = Element::create(string("'foobar'")); + sub_option->set("add", add); + ElementPtr space = Element::create(string("vendor-123456")); + sub_option->set("space", space); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345)); + Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345)); + OptionVendorPtr vendor(new OptionVendor(Option::V6, 67890)); + response->addOption(vendor); + OptionStringPtr str(new OptionString(Option::V6, 2, "xyzt")); + vendor->addOption(str); + string response_txt = response->toText(); + + EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + OptionPtr opt = response->getOption(D6O_VENDOR_OPTS); + ASSERT_TRUE(opt); + vendor = boost::dynamic_pointer_cast<OptionVendor>(opt); + ASSERT_TRUE(vendor); + EXPECT_EQ(67890, vendor->getVendorId()); + OptionPtr sub = vendor->getOption(1); + EXPECT_FALSE(sub); + sub = vendor->getOption(2); + ASSERT_TRUE(sub); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(4, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "xyzt", 4)); +} + +// Verify that SUPERSEDE action adds the specified sub-option in csv format. +TEST_F(FlexSubOptionTest, subProcessSupersedeEnableCSVFormat) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-container", 222, + DHCP4_OPTION_SPACE, "empty", + "my-space")); + defs.addItem(def); + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space", + "fqdn", true)); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(222); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr supersede = Element::create(string("'example.com'")); + sub_option->set("supersede", supersede); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + sub_option->set("csv-format", Element::create(true)); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + EXPECT_FALSE(response->getOption(222)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + OptionPtr opt = response->getOption(222); + ASSERT_TRUE(opt); + EXPECT_EQ(222, opt->getType()); + OptionPtr sub = opt->getOption(1); + ASSERT_TRUE(sub); + EXPECT_EQ(1, sub->getType()); + // The fqdn array is the most complex encoding of one element... + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(13, buffer.size()); + EXPECT_EQ(7, buffer[0]); + EXPECT_EQ(0, memcmp(&buffer[1], "example", 7)); + EXPECT_EQ(3, buffer[8]); + EXPECT_EQ(0, memcmp(&buffer[9], "com", 3)); + EXPECT_EQ(0, buffer[12]); +} + +// Verify that SUPERSEDE action does nothing when the container does not exist +// and container-add is false. +TEST_F(FlexSubOptionTest, subProcessSupersedeNoContainer) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-container", 222, + DHCP4_OPTION_SPACE, "empty", + "my-space")); + defs.addItem(def); + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space", + "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(222); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr supersede = Element::create(string("'abc'")); + sub_option->set("supersede", supersede); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + sub_option->set("container-add", Element::create(false)); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + string response_txt = response->toText(); + EXPECT_FALSE(response->getOption(222)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + EXPECT_FALSE(response->getOption(222)); +} + +// Verify that SUPERSEDE action adds the specified sub-option in raw format. +TEST_F(FlexSubOptionTest, subProcessSupersedeDisableCSVFormat) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-container", 222, + DHCP4_OPTION_SPACE, "empty", + "my-space")); + defs.addItem(def); + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space", + "fqdn", true)); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(222); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr supersede = Element::create(string("0x076578616d706c6503636f6d00")); + sub_option->set("supersede", supersede); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + // csv-format is disabled by default. + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + EXPECT_FALSE(response->getOption(222)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + OptionPtr opt = response->getOption(222); + ASSERT_TRUE(opt); + EXPECT_EQ(222, opt->getType()); + OptionPtr sub = opt->getOption(1); + ASSERT_TRUE(sub); + EXPECT_EQ(1, sub->getType()); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(13, buffer.size()); + EXPECT_EQ(7, buffer[0]); + EXPECT_EQ(0, memcmp(&buffer[1], "example", 7)); + EXPECT_EQ(3, buffer[8]); + EXPECT_EQ(0, memcmp(&buffer[9], "com", 3)); + EXPECT_EQ(0, buffer[12]); +} + +// Verify that SUPERSEDE action adds the specified sub-option in an already +// existing container option. +TEST_F(FlexSubOptionTest, subProcessSupersede) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-container", 222, + DHCP4_OPTION_SPACE, "empty", + "my-space")); + defs.addItem(def); + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space", + "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(222); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr supersede = Element::create(string("'abc'")); + sub_option->set("supersede", supersede); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + OptionPtr container(new Option(Option::V4, 222)); + response->addOption(container); + EXPECT_TRUE(response->getOption(222)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + // Only one option with code 222. + EXPECT_EQ(1, response->options_.count(222)); + + OptionPtr opt = response->getOption(222); + ASSERT_TRUE(opt); + EXPECT_EQ(222, opt->getType()); + OptionPtr sub = opt->getOption(1); + ASSERT_TRUE(sub); + EXPECT_EQ(1, sub->getType()); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(3, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3)); + + // Only one sub-option. + auto const& opts = opt->getOptions(); + EXPECT_EQ(1, opts.size()); +} + +// Verify that SUPERSEDE action replaces an already existing sub-option. +TEST_F(FlexSubOptionTest, subProcessSupersedeExisting) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-container", 222, + DHCP4_OPTION_SPACE, "empty", + "my-space")); + defs.addItem(def); + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space", + "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(222); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr supersede = Element::create(string("'abc'")); + sub_option->set("supersede", supersede); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + OptionPtr container(new Option(Option::V4, 222)); + response->addOption(container); + EXPECT_TRUE(response->getOption(222)); + OptionStringPtr str(new OptionString(Option::V4, 1, "xyzt")); + container->addOption(str); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + // Only one option with code 222. + EXPECT_EQ(1, response->options_.count(222)); + + OptionPtr opt = response->getOption(222); + ASSERT_TRUE(opt); + EXPECT_EQ(222, opt->getType()); + OptionPtr sub = opt->getOption(1); + ASSERT_TRUE(sub); + EXPECT_EQ(1, sub->getType()); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(3, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3)); + + // Only one sub-option. + auto const& opts = opt->getOptions(); + EXPECT_EQ(1, opts.size()); +} + +// Verify that SUPERSEDE action does not add an empty value. +TEST_F(FlexSubOptionTest, subProcessSupersedeEmpty) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-container", 222, + DHCP4_OPTION_SPACE, "empty", + "my-space")); + defs.addItem(def); + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space", + "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(222); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr supersede = Element::create(string("''")); + sub_option->set("supersede", supersede); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + string response_txt = response->toText(); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + EXPECT_FALSE(response->getOption(222)); +} + +// Verify that SUPERSEDE action can handle vendor-encapsulated-options 43. +TEST_F(FlexSubOptionTest, subProcessSupersede43) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, + VENDOR_ENCAPSULATED_OPTION_SPACE, + "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_VENDOR_ENCAPSULATED_OPTIONS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr supersede = Element::create(string("'foobar'")); + sub_option->set("supersede", supersede); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + OptionPtr opt = response->getOption(DHO_VENDOR_ENCAPSULATED_OPTIONS); + ASSERT_TRUE(opt); + EXPECT_EQ(DHO_VENDOR_ENCAPSULATED_OPTIONS, opt->getType()); + OptionPtr sub = opt->getOption(1); + ASSERT_TRUE(sub); + EXPECT_EQ(1, sub->getType()); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(6, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "foobar", 6)); +} + +// Verify that SUPERSEDE action can handle DocSIS Vivso. +TEST_F(FlexSubOptionTest, subProcessSupersedeDocSISVIVSO) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr supersede = Element::create(string("10.1.2.3")); + sub_option->set("supersede", supersede); + // DocSIS is 4491 + ElementPtr space = Element::create(string("vendor-4491")); + sub_option->set("space", space); + ElementPtr name = Element::create(string("tftp-servers")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + OptionPtr opt = response->getOption(DHO_VIVSO_SUBOPTIONS); + ASSERT_TRUE(opt); + OptionVendorPtr vendor = boost::dynamic_pointer_cast<OptionVendor>(opt); + ASSERT_TRUE(vendor); + EXPECT_EQ(VENDOR_ID_CABLE_LABS, vendor->getVendorId()); + OptionPtr sub = vendor->getOption(DOCSIS3_V4_TFTP_SERVERS); + ASSERT_TRUE(sub); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(4, buffer.size()); + uint8_t expected[] = { 10, 1, 2, 3 }; + EXPECT_EQ(0, memcmp(&buffer[0], expected, 4)); +} + +// Verify that SUPERSEDE action can handle DocSIS vendor-opts. +TEST_F(FlexSubOptionTest, subProcessSupersedeDocSISVendorOps) { + CfgMgr::instance().setFamily(AF_INET6); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_VENDOR_OPTS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr supersede = Element::create(string("'foobar'")); + sub_option->set("supersede", supersede); + // DocSIS is 4491 + ElementPtr space = Element::create(string("vendor-4491")); + sub_option->set("space", space); + code = Element::create(DOCSIS3_V6_VENDOR_NAME); + sub_option->set("code", code); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345)); + Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345)); + + EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response)); + + OptionPtr opt = response->getOption(D6O_VENDOR_OPTS); + ASSERT_TRUE(opt); + OptionVendorPtr vendor = boost::dynamic_pointer_cast<OptionVendor>(opt); + ASSERT_TRUE(vendor); + EXPECT_EQ(VENDOR_ID_CABLE_LABS, vendor->getVendorId()); + OptionPtr sub = vendor->getOption(DOCSIS3_V6_VENDOR_NAME); + ASSERT_TRUE(sub); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(6, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "foobar", 6)); +} + +// Verify that SUPERSEDE action can handle the Vivso option. +TEST_F(FlexSubOptionTest, subProcessSupersedeVivso) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, + "vendor-123456", "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr supersede = Element::create(string("'foobar'")); + sub_option->set("supersede", supersede); + ElementPtr space = Element::create(string("vendor-123456")); + sub_option->set("space", space); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + OptionPtr opt = response->getOption(DHO_VIVSO_SUBOPTIONS); + ASSERT_TRUE(opt); + OptionVendorPtr vendor = boost::dynamic_pointer_cast<OptionVendor>(opt); + ASSERT_TRUE(vendor); + EXPECT_EQ(123456, vendor->getVendorId()); + OptionPtr sub = vendor->getOption(1); + ASSERT_TRUE(sub); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(6, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "foobar", 6)); +} + +// Verify that SUPERSEDE action can handle the Vivso option with vendor mismatch. +TEST_F(FlexSubOptionTest, subProcessSupersedeVivsoMismatch) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, + "vendor-123456", "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr supersede = Element::create(string("'foobar'")); + sub_option->set("supersede", supersede); + ElementPtr space = Element::create(string("vendor-123456")); + sub_option->set("space", space); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + OptionVendorPtr vendor(new OptionVendor(Option::V4, 67890)); + response->addOption(vendor); + OptionStringPtr str(new OptionString(Option::V4, 2, "xyzt")); + vendor->addOption(str); + string response_txt = response->toText(); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + OptionPtr opt = response->getOption(DHO_VIVSO_SUBOPTIONS); + ASSERT_TRUE(opt); + vendor = boost::dynamic_pointer_cast<OptionVendor>(opt); + ASSERT_TRUE(vendor); + EXPECT_EQ(67890, vendor->getVendorId()); + OptionPtr sub = vendor->getOption(1); + EXPECT_FALSE(sub); + sub = vendor->getOption(2); + ASSERT_TRUE(sub); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(4, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "xyzt", 4)); +} + +// Verify that SUPERSEDE action can handle the vendor-opts option. +TEST_F(FlexSubOptionTest, subProcessSupersedeVendorOpts) { + CfgMgr::instance().setFamily(AF_INET6); + + OptionDefSpaceContainer defs; + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, + "vendor-123456", "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_VENDOR_OPTS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr supersede = Element::create(string("'foobar'")); + sub_option->set("supersede", supersede); + ElementPtr space = Element::create(string("vendor-123456")); + sub_option->set("space", space); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345)); + Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345)); + + EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response)); + + OptionPtr opt = response->getOption(D6O_VENDOR_OPTS); + ASSERT_TRUE(opt); + OptionVendorPtr vendor = boost::dynamic_pointer_cast<OptionVendor>(opt); + ASSERT_TRUE(vendor); + EXPECT_EQ(123456, vendor->getVendorId()); + OptionPtr sub = vendor->getOption(1); + ASSERT_TRUE(sub); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(6, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "foobar", 6)); +} + +// Verify that SUPERSEDE action can handle the vendor-opts option with vendor mismatch. +TEST_F(FlexSubOptionTest, subProcessSupersedeVendorOptsMismatch) { + CfgMgr::instance().setFamily(AF_INET6); + + OptionDefSpaceContainer defs; + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, + "vendor-123456", "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_VENDOR_OPTS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr supersede = Element::create(string("'foobar'")); + sub_option->set("supersede", supersede); + ElementPtr space = Element::create(string("vendor-123456")); + sub_option->set("space", space); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345)); + Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345)); + OptionVendorPtr vendor(new OptionVendor(Option::V6, 67890)); + response->addOption(vendor); + OptionStringPtr str(new OptionString(Option::V6, 2, "xyzt")); + vendor->addOption(str); + string response_txt = response->toText(); + + EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + OptionPtr opt = response->getOption(D6O_VENDOR_OPTS); + ASSERT_TRUE(opt); + vendor = boost::dynamic_pointer_cast<OptionVendor>(opt); + ASSERT_TRUE(vendor); + EXPECT_EQ(67890, vendor->getVendorId()); + OptionPtr sub = vendor->getOption(1); + EXPECT_FALSE(sub); + sub = vendor->getOption(2); + ASSERT_TRUE(sub); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(4, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "xyzt", 4)); +} + +// Verify that REMOVE action removes an already existing sub-option. +TEST_F(FlexSubOptionTest, subProcessRemove) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-container", 222, + DHCP4_OPTION_SPACE, "empty", + "my-space")); + defs.addItem(def); + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space", + "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(222); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr remove = Element::create(string("'abc' == 'abc'")); + sub_option->set("remove", remove); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + string response_txt = response->toText(); + OptionPtr container(new Option(Option::V4, 222)); + response->addOption(container); + EXPECT_TRUE(response->getOption(222)); + OptionStringPtr str(new OptionString(Option::V4, 1, "xyzt")); + container->addOption(str); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + EXPECT_FALSE(response->getOption(222)); +} + +// Verify that REMOVE action removes an already existing sub-option but +// leaves the container option when container-remove is false. +TEST_F(FlexSubOptionTest, subProcessRemoveLeaveContainer) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-container", 222, + DHCP4_OPTION_SPACE, "empty", + "my-space")); + defs.addItem(def); + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space", + "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(222); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr remove = Element::create(string("'abc' == 'abc'")); + sub_option->set("remove", remove); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + sub_option->set("container-remove", Element::create(false)); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + OptionPtr container(new Option(Option::V4, 222)); + response->addOption(container); + string response_txt = response->toText(); + EXPECT_TRUE(response->getOption(222)); + OptionStringPtr str(new OptionString(Option::V4, 1, "xyzt")); + container->addOption(str); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + OptionPtr opt = response->getOption(222); + ASSERT_TRUE(opt); + EXPECT_FALSE(opt->getOption(1)); + EXPECT_TRUE(opt->getOptions().empty()); +} + +// Verify that REMOVE action removes an already existing sub-option but +// leaves the container option when it is not empty. +TEST_F(FlexSubOptionTest, subProcessRemoveContainerNotEmpty) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-container", 222, + DHCP4_OPTION_SPACE, "empty", + "my-space")); + defs.addItem(def); + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 2, "my-space", + "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(222); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr remove = Element::create(string("'abc' == 'abc'")); + sub_option->set("remove", remove); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + OptionPtr container(new Option(Option::V4, 222)); + response->addOption(container); + EXPECT_TRUE(response->getOption(222)); + OptionStringPtr str(new OptionString(Option::V4, 1, "abc")); + container->addOption(str); + string response_txt = response->toText(); + str.reset(new OptionString(Option::V4, 2, "xyzt")); + container->addOption(str); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + OptionPtr opt = response->getOption(222); + ASSERT_TRUE(opt); + EXPECT_FALSE(opt->getOption(2)); + EXPECT_EQ(1, opt->getOptions().size()); + OptionPtr sub = opt->getOption(1); + ASSERT_TRUE(sub); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(3, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3)); +} + +// Verify that REMOVE action does not removes the container option when the +// sub-option does not exist. +TEST_F(FlexSubOptionTest, subProcessRemoveContainerNoSubOption) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-container", 222, + DHCP4_OPTION_SPACE, "empty", + "my-space")); + defs.addItem(def); + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space", + "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(222); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr remove = Element::create(string("'abc' == 'abc'")); + sub_option->set("remove", remove); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + sub_option->set("container-remove", Element::create(false)); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + OptionPtr container(new Option(Option::V4, 222)); + response->addOption(container); + EXPECT_TRUE(response->getOption(222)); + string response_txt = response->toText(); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + EXPECT_TRUE(response->getOption(222)); +} + +// Verify that REMOVE action does nothing when the expression evaluates to false. +TEST_F(FlexSubOptionTest, subProcessRemoveFalse) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-container", 222, + DHCP4_OPTION_SPACE, "empty", + "my-space")); + defs.addItem(def); + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space", + "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(222); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr remove = Element::create(string("'abc' == 'xyz'")); + sub_option->set("remove", remove); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + OptionPtr container(new Option(Option::V4, 222)); + response->addOption(container); + EXPECT_TRUE(response->getOption(222)); + OptionStringPtr str(new OptionString(Option::V4, 1, "abc")); + container->addOption(str); + string response_txt = response->toText(); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + OptionPtr opt = response->getOption(222); + ASSERT_TRUE(opt); + EXPECT_EQ(222, opt->getType()); + OptionPtr sub = opt->getOption(1); + ASSERT_TRUE(sub); + EXPECT_EQ(1, sub->getType()); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(3, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3)); +} + +// Verify that REMOVE action can handle vendor-encapsulated-options 43. +TEST_F(FlexSubOptionTest, subProcessRemove43) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, + VENDOR_ENCAPSULATED_OPTION_SPACE, + "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_VENDOR_ENCAPSULATED_OPTIONS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr remove = Element::create(string("'abc' == 'abc'")); + sub_option->set("remove", remove); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + string response_txt = response->toText(); + OptionPtr container(new Option(Option::V4, DHO_VENDOR_ENCAPSULATED_OPTIONS)); + response->addOption(container); + EXPECT_TRUE(response->getOption(DHO_VENDOR_ENCAPSULATED_OPTIONS)); + OptionStringPtr str(new OptionString(Option::V4, 1, "abc")); + container->addOption(str); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + EXPECT_FALSE(response->getOption(DHO_VENDOR_ENCAPSULATED_OPTIONS)); +} + +// Verify that REMOVE action can handle DocSIS Vivso. +TEST_F(FlexSubOptionTest, subProcessRemoveDocSISVIVSO) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr remove = Element::create(string("member('ALL')")); + sub_option->set("remove", remove); + // DocSIS is 4491 + ElementPtr space = Element::create(string("vendor-4491")); + sub_option->set("space", space); + ElementPtr name = Element::create(string("tftp-servers")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + query->addClass("ALL"); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + string response_txt = response->toText(); + OptionVendorPtr vendor(new OptionVendor(Option::V4, 4491)); + response->addOption(vendor); + Option4AddrLstPtr tftp(new Option4AddrLst(DOCSIS3_V4_TFTP_SERVERS, + IOAddress("10.1.2.3"))); + vendor->addOption(tftp); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + EXPECT_FALSE(response->getOption(DHO_VIVSO_SUBOPTIONS)); +} + +// Verify that REMOVE action can handle DocSIS vendor-opts. +TEST_F(FlexSubOptionTest, subProcessRemoveDocSISVendorOps) { + CfgMgr::instance().setFamily(AF_INET6); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_VENDOR_OPTS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr remove = Element::create(string("'abc' == 'abc'")); + sub_option->set("remove", remove); + // DocSIS is 4491 + ElementPtr space = Element::create(string("vendor-4491")); + sub_option->set("space", space); + code = Element::create(DOCSIS3_V6_VENDOR_NAME); + sub_option->set("code", code); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345)); + Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345)); + string response_txt = response->toText(); + OptionVendorPtr vendor(new OptionVendor(Option::V6, 4491)); + response->addOption(vendor); + OptionStringPtr str(new OptionString(Option::V6, DOCSIS3_V6_VENDOR_NAME, + "foobar")); + vendor->addOption(str); + EXPECT_TRUE(response->getOption(D6O_VENDOR_OPTS)); + + EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + EXPECT_FALSE(response->getOption(D6O_VENDOR_OPTS)); +} + +// Verify that REMOVE action can handle the Vivso option. +TEST_F(FlexSubOptionTest, subProcessRemoveVivso) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, + "vendor-123456", "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr remove = Element::create(string("'abc' == 'abc'")); + sub_option->set("remove", remove); + ElementPtr space = Element::create(string("vendor-123456")); + sub_option->set("space", space); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + string response_txt = response->toText(); + OptionVendorPtr vendor(new OptionVendor(Option::V4, 123456)); + response->addOption(vendor); + OptionStringPtr str(new OptionString(Option::V4, 1, "foobar")); + vendor->addOption(str); + EXPECT_TRUE(response->getOption(DHO_VIVSO_SUBOPTIONS)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + EXPECT_FALSE(response->getOption(DHO_VIVSO_SUBOPTIONS)); +} + +// Verify that REMOVE action can handle the Vivso option with vendor mismatch. +TEST_F(FlexSubOptionTest, subProcessRemoveVivsoMismatch) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, + "vendor-123456", "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr remove = Element::create(string("member('ALL')")); + sub_option->set("remove", remove); + ElementPtr space = Element::create(string("vendor-123456")); + sub_option->set("space", space); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + query->addClass("ALL"); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + OptionVendorPtr vendor(new OptionVendor(Option::V4, 67890)); + response->addOption(vendor); + OptionStringPtr str(new OptionString(Option::V4, 2, "xyzt")); + vendor->addOption(str); + string response_txt = response->toText(); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + OptionPtr opt = response->getOption(DHO_VIVSO_SUBOPTIONS); + ASSERT_TRUE(opt); + vendor = boost::dynamic_pointer_cast<OptionVendor>(opt); + ASSERT_TRUE(vendor); + EXPECT_EQ(67890, vendor->getVendorId()); + OptionPtr sub = vendor->getOption(1); + EXPECT_FALSE(sub); + sub = vendor->getOption(2); + ASSERT_TRUE(sub); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(4, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "xyzt", 4)); +} + +// Verify that REMOVE action can handle the vendor-opts option. +TEST_F(FlexSubOptionTest, subProcessRemoveVendorOpts) { + CfgMgr::instance().setFamily(AF_INET6); + + OptionDefSpaceContainer defs; + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, + "vendor-123456", "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_VENDOR_OPTS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr remove = Element::create(string("'abc' == 'abc'")); + sub_option->set("remove", remove); + ElementPtr space = Element::create(string("vendor-123456")); + sub_option->set("space", space); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345)); + Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345)); + string response_txt = response->toText(); + OptionVendorPtr vendor(new OptionVendor(Option::V6, 123456)); + response->addOption(vendor); + OptionStringPtr str(new OptionString(Option::V6, 1, "foobar")); + vendor->addOption(str); + EXPECT_TRUE(response->getOption(D6O_VENDOR_OPTS)); + + EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + EXPECT_FALSE(response->getOption(D6O_VENDOR_OPTS)); +} + +// Verify that REMOVE action can handle the vendor-opts option with vendor mismatch. +TEST_F(FlexSubOptionTest, subProcessRemoveVendorOptsMismatch) { + CfgMgr::instance().setFamily(AF_INET6); + + OptionDefSpaceContainer defs; + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, + "vendor-123456", "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_VENDOR_OPTS); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr remove = Element::create(string("'abc' == 'abc'")); + sub_option->set("remove", remove); + ElementPtr space = Element::create(string("vendor-123456")); + sub_option->set("space", space); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345)); + Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345)); + OptionVendorPtr vendor(new OptionVendor(Option::V6, 67890)); + response->addOption(vendor); + OptionStringPtr str(new OptionString(Option::V6, 2, "xyzt")); + vendor->addOption(str); + string response_txt = response->toText(); + + EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + OptionPtr opt = response->getOption(D6O_VENDOR_OPTS); + ASSERT_TRUE(opt); + vendor = boost::dynamic_pointer_cast<OptionVendor>(opt); + ASSERT_TRUE(vendor); + EXPECT_EQ(67890, vendor->getVendorId()); + OptionPtr sub = vendor->getOption(1); + EXPECT_FALSE(sub); + sub = vendor->getOption(2); + ASSERT_TRUE(sub); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(4, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "xyzt", 4)); +} + +// Verify that the client class must be a string. +TEST_F(FlexSubOptionTest, subOptionConfigBadClass) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(true); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + sub_option->set("code", code); + sub_option->set("client-class", Element::create(true)); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'client-class' must be a string: true", impl_->getErrMsg()); +} + +// Verify that a valid client class is accepted. +TEST_F(FlexSubOptionTest, subOptionConfigGuardValid) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(109); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'ab'")); + sub_option->set("add", add); + ElementPtr sub_code = Element::create(222); + sub_option->set("code", sub_code); + sub_option->set("client-class", Element::create(string("foobar"))); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + auto map = impl_->getSubOptionConfigMap(); + EXPECT_EQ(1, map.count(109)); + auto smap = map[109]; + FlexOptionImpl::SubOptionConfigPtr sub_cfg; + ASSERT_NO_THROW(sub_cfg = smap.at(222)); + ASSERT_TRUE(sub_cfg); + EXPECT_EQ(222, sub_cfg->getCode()); + EXPECT_EQ(109, sub_cfg->getContainerCode()); + EXPECT_EQ("foobar", sub_cfg->getClass()); +} + +// Verify that a guarded action is skipped when query does not belong to the +// client class of the container option. +TEST_F(FlexSubOptionTest, subOptionConfigGuardOptiondNoMatch) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-container", 222, + DHCP4_OPTION_SPACE, "empty", + "my-space")); + defs.addItem(def); + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space", + "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(222); + option->set("code", code); + option->set("client-class", Element::create(string("foobar"))); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'abc'")); + sub_option->set("add", add); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + string response_txt = response->toText(); + EXPECT_FALSE(response->getOption(222)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + EXPECT_FALSE(response->getOption(222)); +} + +// Verify that a guarded action is applied when query belongs to the class +// class of the container option. +TEST_F(FlexSubOptionTest, subOptionConfigGuardOptiondMatch) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-container", 222, + DHCP4_OPTION_SPACE, "empty", + "my-space")); + defs.addItem(def); + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space", + "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(222); + option->set("code", code); + option->set("client-class", Element::create(string("foobar"))); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'abc'")); + sub_option->set("add", add); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + query->addClass("foobar"); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + EXPECT_FALSE(response->getOption(222)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + OptionPtr opt = response->getOption(222); + ASSERT_TRUE(opt); + EXPECT_EQ(222, opt->getType()); + OptionPtr sub = opt->getOption(1); + ASSERT_TRUE(sub); + EXPECT_EQ(1, sub->getType()); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(3, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3)); +} + +// Verify that a guarded action is skipped when query does not belong to the +// client class of the sub-option. +TEST_F(FlexSubOptionTest, subOptionConfigGuardSubOptiondNoMatch) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-container", 222, + DHCP4_OPTION_SPACE, "empty", + "my-space")); + defs.addItem(def); + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space", + "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(222); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'abc'")); + sub_option->set("add", add); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + sub_option->set("client-class", Element::create(string("foobar"))); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + string response_txt = response->toText(); + EXPECT_FALSE(response->getOption(222)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + EXPECT_EQ(response_txt, response->toText()); + EXPECT_FALSE(response->getOption(222)); +} + +// Verify that a guarded action is applied when query belongs to the class +// class of the sub-option. +TEST_F(FlexSubOptionTest, subOptionConfigGuardSubOptiondMatch) { + OptionDefSpaceContainer defs; + OptionDefinitionPtr def(new OptionDefinition("my-container", 222, + DHCP4_OPTION_SPACE, "empty", + "my-space")); + defs.addItem(def); + OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space", + "string")); + defs.addItem(sdef); + EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs)); + + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(222); + option->set("code", code); + ElementPtr sub_options = Element::createList(); + option->set("sub-options", sub_options); + ElementPtr sub_option = Element::createMap(); + sub_options->add(sub_option); + ElementPtr space = Element::create(string("my-space")); + sub_option->set("space", space); + ElementPtr add = Element::create(string("'abc'")); + sub_option->set("add", add); + ElementPtr name = Element::create(string("my-option")); + sub_option->set("name", name); + sub_option->set("client-class", Element::create(string("foobar"))); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg(); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + query->addClass("foobar"); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + EXPECT_FALSE(response->getOption(222)); + + EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response)); + + OptionPtr opt = response->getOption(222); + ASSERT_TRUE(opt); + EXPECT_EQ(222, opt->getType()); + OptionPtr sub = opt->getOption(1); + ASSERT_TRUE(sub); + EXPECT_EQ(1, sub->getType()); + const OptionBuffer& buffer = sub->getData(); + ASSERT_EQ(3, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3)); +} + +} // end of anonymous namespace diff --git a/src/hooks/dhcp/flex_option/tests/test_flex_option.h b/src/hooks/dhcp/flex_option/tests/test_flex_option.h new file mode 100644 index 0000000..df0a9bf --- /dev/null +++ b/src/hooks/dhcp/flex_option/tests/test_flex_option.h @@ -0,0 +1,82 @@ +// Copyright (C) 2019-2022 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/. + +/// @file This file contains common part of flexible option tests. + +#ifndef TEST_FLEX_OPTION_H +#define TEST_FLEX_OPTION_H + +#include <flex_option.h> +#include <dhcp/libdhcp++.h> +#include <dhcpsrv/cfgmgr.h> +#include <gtest/gtest.h> + +namespace isc { +namespace flex_option { +namespace test { + +/// @brief Test class derived from FlexOptionImpl +class TestFlexOptionImpl : public FlexOptionImpl { +public: + /// Export getMutableOptionConfigMap. + using FlexOptionImpl::getMutableOptionConfigMap; + + /// Export getMutableSubOptionConfigMap. + using FlexOptionImpl::getMutableSubOptionConfigMap; + + /// @Brief Configure clone which records the error. + /// + /// @param options The element with option config list. + void testConfigure(isc::data::ConstElementPtr options) { + err_msg_.clear(); + try { + configure(options); + } catch (const std::exception& ex) { + err_msg_ = std::string(ex.what()); + throw; + } + } + + /// @brief Get the last error message. + /// + /// @return The last error message. + const std::string& getErrMsg() const { + return (err_msg_); + } + +private: + /// @brief Last error message. + std::string err_msg_; +}; + +/// @brief The type of shared pointers to TestFlexOptionImpl +typedef boost::shared_ptr<TestFlexOptionImpl> TestFlexOptionImplPtr; + +/// @brief Base test fixture for testing the Flex Option library. +class BaseFlexOptionTest : public ::testing::Test { +public: + /// @brief Constructor. + BaseFlexOptionTest() { + impl_.reset(new TestFlexOptionImpl()); + isc::dhcp::CfgMgr::instance().setFamily(AF_INET); + } + + /// @brief Destructor. + virtual ~BaseFlexOptionTest() { + LibDHCP::clearRuntimeOptionDefs(); + isc::dhcp::CfgMgr::instance().setFamily(AF_INET); + impl_.reset(); + } + + /// @brief Flex Option implementation. + TestFlexOptionImplPtr impl_; +}; + +} // end of namespace test +} // end of namespace flex_option +} // end of namespace isc + +#endif // TEST_FLEX_OPTION_H diff --git a/src/hooks/dhcp/flex_option/version.cc b/src/hooks/dhcp/flex_option/version.cc new file mode 100644 index 0000000..4250193 --- /dev/null +++ b/src/hooks/dhcp/flex_option/version.cc @@ -0,0 +1,17 @@ +// 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 <hooks/hooks.h> + +extern "C" { + +/// @brief returns Kea hooks version. +int version() { + return (KEA_HOOKS_VERSION); +} + +} |