src/lib/util/threads/Makefile
src/lib/util/threads/tests/Makefile
src/lib/util/unittests/Makefile
+ src/lib/eval/Makefile
+ src/lib/eval/tests/Makefile
tools/Makefile
tools/path_replacer.sh
])
--- /dev/null
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+lib_LTLIBRARIES = libkea-eval.la
+libkea_eval_la_SOURCES =
+libkea_eval_la_SOURCES += token.cc token.h
+
+libkea_eval_la_CXXFLAGS = $(AM_CXXFLAGS)
+libkea_eval_la_CPPFLAGS = $(AM_CPPFLAGS)
+libkea_eval_la_LIBADD = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+libkea_eval_la_LIBADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
+libkea_eval_la_LDFLAGS = -no-undefined -version-info 3:0:0
+libkea_eval_la_LDFLAGS += $(CRYPTO_LDFLAGS)
+
+EXTRA_DIST = eval.dox
+
+# Define rule to build logging source files from message file
+expr_messages.h expr_messages.cc: s-messages
+
+s-messages: expr_messages.mes
+ $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/expr/expr_messages.mes
+
+CLEANFILES = expr_messages.h expr_messages.cc
--- /dev/null
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+# Some versions of GCC warn about some versions of Boost regarding
+# missing initializer for members in its posix_time.
+# https://svn.boost.org/trac/boost/ticket/3477
+# But older GCC compilers don't have the flag.
+AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS_ENVIRONMENT = \
+ $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+TESTS =
+if HAVE_GTEST
+
+TESTS += libeval_unittests
+
+libeval_unittests_SOURCES = token_unittest.cc main.cc
+libeval_unittests_CXXFLAGS = $(AM_CXXFLAGS)
+libeval_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+libeval_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
+libeval_unittests_LDADD = $(top_builddir)/src/lib/eval/libkea-eval.la
+libeval_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
+libeval_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
+libeval_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+libeval_unittests_LDADD += $(CRYPTO_LIBS)
+libeval_unittests_LDADD += $(BOOST_LIBS) $(GTEST_LDADD)
+
+if USE_CLANGPP
+# This is to workaround unused variables tcout and tcerr in
+# log4cplus's streams.h and unused parameters from some of the
+# Boost headers.
+libeval_unittests_CXXFLAGS += -Wno-unused-variable -Wno-unused-parameter
+endif
+
+endif
+
+noinst_PROGRAMS = $(TESTS)
--- /dev/null
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#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);
+}
--- /dev/null
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <eval/token.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/option_string.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc::dhcp;
+
+namespace {
+
+/// @brief Test fixture for testing Tokens.
+///
+/// This class provides several convenience objects to be used during testing
+/// of the Token family of classes.
+class TokenTest : public ::testing::Test {
+public:
+
+ /// @brief Initializes Pkt4,Pkt6 and options that can be useful for
+ /// evaluation tests.
+ TokenTest() {
+ pkt4_.reset(new Pkt4(DHCPDISCOVER, 12345));
+ pkt6_.reset(new Pkt6(DHCPV6_SOLICIT, 12345));
+
+ // Add options with easily identifiable strings in them
+ option_str4_.reset(new OptionString(Option::V4, 100, "hundred4"));
+ option_str6_.reset(new OptionString(Option::V6, 100, "hundred6"));
+
+ pkt4_->addOption(option_str4_);
+ pkt6_->addOption(option_str6_);
+ }
+
+ TokenPtr t_; ///< Just a convenience pointer
+
+ ValueStack values_; ///< evaluated values will be stored here
+
+ Pkt4Ptr pkt4_; ///< A stub DHCPv4 packet
+ Pkt6Ptr pkt6_; ///< A stub DHCPv6 packet
+
+ OptionPtr option_str4_; ///< A string option for DHCPv4
+ OptionPtr option_str6_; ///< A string option for DHCPv6
+
+ /// @todo: Add more option types here
+};
+
+// This simple test checks that a TokenString, representing a constant string,
+// can be used in Pkt4 evaluation. (The actual packet is not used)
+TEST_F(TokenTest, string4) {
+
+ // Store constant string "foo" in the TokenString object.
+ ASSERT_NO_THROW(t_.reset(new TokenString("foo")));
+
+ // Make sure that the token can be evaluated without exceptions.
+ ASSERT_NO_THROW(t_->evaluate(*pkt4_, values_));
+
+ // Check that the evaluation put its value on the values stack.
+ ASSERT_EQ(1, values_.size());
+ EXPECT_EQ("foo", values_.top());
+}
+
+// This simple test checks that a TokenString, representing a constant string,
+// can be used in Pkt6 evaluation. (The actual packet is not used)
+TEST_F(TokenTest, string6) {
+
+ // Store constant string "foo" in the TokenString object.
+ ASSERT_NO_THROW(t_.reset(new TokenString("foo")));
+
+ // Make sure that the token can be evaluated without exceptions.
+ ASSERT_NO_THROW(t_->evaluate(*pkt6_, values_));
+
+ // Check that the evaluation put its value on the values stack.
+ ASSERT_EQ(1, values_.size());
+ EXPECT_EQ("foo", values_.top());
+}
+
+// This test checks if a token representing an option value is able to extract
+// the option from a packet and properly store the option's value.
+TEST_F(TokenTest, optionString) {
+ TokenPtr found;
+ TokenPtr not_found;
+
+ // The packets we use have option 100 with a string in them.
+ ASSERT_NO_THROW(found.reset(new TokenOption(100)));
+ ASSERT_NO_THROW(not_found.reset(new TokenOption(101)));
+
+ // This should evaluate to the content of the option 100 (i.e. "hundred4")
+ ASSERT_NO_THROW(found->evaluate(*pkt4_, values_));
+
+ // This should evaluate to "" as there is no option 101.
+ ASSERT_NO_THROW(not_found->evaluate(*pkt4_, values_));
+
+ // There should be 2 values evaluated.
+ ASSERT_EQ(2, values_.size());
+
+ // This is a stack, so the pop order is inversed. We should get the empty
+ // string first.
+ EXPECT_EQ("", values_.top());
+ values_.pop();
+
+ // Then the content of the option 100.
+ EXPECT_EQ("hundred4", values_.top());
+}
+
+// This test checks if a token representing an == operator is able to
+// compare two values (with incorrectly built stack).
+TEST_F(TokenTest, optionEqualInvalid) {
+
+ ASSERT_NO_THROW(t_.reset(new TokenEqual()));
+
+ // CASE 1: There's not enough values on the stack. == is an operator that
+ // takes two parameters. There are 0 on the stack.
+ EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
+
+ // CASE 2: One value is still not enough.
+ values_.push("foo");
+ EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
+}
+
+// This test checks if a token representing an == operator is able to
+// compare two different values.
+TEST_F(TokenTest, optionEqualFalse) {
+
+ ASSERT_NO_THROW(t_.reset(new TokenEqual()));
+
+ values_.push("foo");
+ values_.push("bar");
+ EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+
+ // After evaluation there should be a single value that represents
+ // result of "foo" == "bar" comparision.
+ ASSERT_EQ(1, values_.size());
+ EXPECT_EQ("false", values_.top());
+}
+
+// This test checks if a token representing an == operator is able to
+// compare two identical values.
+TEST_F(TokenTest, optionEqualTrue) {
+
+ ASSERT_NO_THROW(t_.reset(new TokenEqual()));
+
+ values_.push("foo");
+ values_.push("foo");
+ EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+
+ // After evaluation there should be a single value that represents
+ // result of "foo" == "foo" comparision.
+ ASSERT_EQ(1, values_.size());
+ EXPECT_EQ("true", values_.top());
+}
+
+};
--- /dev/null
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <eval/token.h>
+#include <string>
+
+using namespace isc::dhcp;
+using namespace std;
+
+void
+TokenString::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
+ // Literals only push, nothing to pop
+ values.push(value_);
+}
+
+void
+TokenOption::evaluate(const Pkt& pkt, ValueStack& values) {
+ OptionPtr opt = pkt.getOption(option_code_);
+ if (opt) {
+ values.push(opt->toString());
+ } else {
+ // Option not found, push empty string
+ values.push("");
+ }
+}
+
+void
+TokenEqual::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
+
+ if (values.size() < 2) {
+ isc_throw(EvalBadStack, "Incorrect stack order. Expected at least "
+ "2 values, got " << values.size());
+ }
+
+ string op1 = values.top();
+ values.pop();
+ string op2 = values.top();
+ values.pop(); // Dammit, std::stack interface is awkward.
+
+ if (op1 == op2)
+ values.push("true");
+ else
+ values.push("false");
+}
--- /dev/null
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef TOKEN_H
+#define TOKEN_H
+
+#include <exceptions/exceptions.h>
+#include <dhcp/pkt.h>
+#include <stack>
+
+namespace isc {
+namespace dhcp {
+
+class Token;
+
+/// @brief Pointer to a single Token
+typedef boost::shared_ptr<Token> TokenPtr;
+
+/// This is a structure that holds an expression converted to RPN
+///
+/// For example expression: option[123] == 'foo' will be converted to:
+/// [0] = option[123] (TokenOption object)
+/// [1] = 'foo' (TokenString object)
+/// [2] = == operator (TokenEqual object)
+typedef std::vector<TokenPtr> Expression;
+
+/// Evaluated values are stored as a stack of strings
+typedef std::stack<std::string> ValueStack;
+
+/// @brief EvalStackError is thrown when more or less parameters are on the
+/// stack than expected.
+class EvalBadStack : public Exception {
+public:
+ EvalBadStack(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief Base class for all tokens
+///
+/// It provides an interface for all tokens and storage for string representation
+/// (all tokens evaluate to string).
+///
+/// This class represents a single token. Examples of a token are:
+/// - "foo" (a constant string)
+/// - option[123] (a token that extracts value of option 123)
+/// - == (an operator that compares two other tokens)
+/// - substring(a,b,c) (an operator that takes three arguments: a string,
+/// first and last character)
+class Token {
+public:
+
+ /// @brief This is a generic method for evaluating a packet.
+ ///
+ /// We need to pass the packet being evaluated and possibly previous
+ /// evaluated values. Specific implementations may ignore the packet altogether
+ /// and just put its own value on the stack (constant tokens), look at the
+ /// packet and put some data extracted from it on the stack (option tokens),
+ /// or pop arguments from the stack and put back the result (operators).
+ ///
+ /// The parameters passed will be:
+ ///
+ /// @param pkt - packet being classified
+ /// @param value - stack of values with previously evaluated tokens
+ virtual void evaluate(const Pkt& pkt, ValueStack& values) = 0;
+
+ /// @brief Virtual destructor
+ virtual ~Token() {}
+};
+
+/// @brief Token representing a constant string
+///
+/// This token holds value of a constant string, e.g. it represents
+/// "MSFT" in expression option[vendor-class] == "MSFT"
+class TokenString : public Token {
+public:
+ /// Value is set during token construction.
+ ///
+ /// @param str constant string to be represented.
+ TokenString(std::string str)
+ :value_(str){
+ }
+
+ /// @brief Token evaluation (puts value of the constant string on the stack)
+ ///
+ /// @param pkt (ignored)
+ /// @param values (represented string will be pushed here)
+ void evaluate(const Pkt& pkt, ValueStack& values);
+
+protected:
+ std::string value_; ///< Constant value
+};
+
+/// @brief Token that represents a value of an option
+///
+/// This represents a reference to a given option, e.g. in the expression
+/// option[vendor-class] == "MSFT", it represents option[vendor-class]
+///
+/// During the evaluation it tries to extract the the value of specified
+/// option. If the option is not found, an empty string ("") is returned.
+class TokenOption : public Token {
+public:
+ /// @brief Constructor that takes option code as parameter
+ /// @param option_code code of the option
+ ///
+ /// Note: There is no constructor that takes option_name, as it would
+ /// introduce complex dependency of the libkea-eval on libdhcpsrv.
+ ///
+ /// @param option_code code of the option to be represented.
+ TokenOption(uint16_t option_code)
+ :option_code_(option_code) {}
+
+ /// @brief Evaluates the values of the option
+ ///
+ /// This token represents a value of the option, so this method attempts
+ /// to extract the option from the packet and put its value on the stack.
+ /// If the option is not there, an empty string ("") is put on the stack.
+ ///
+ /// @param pkt not used
+ /// @param values value of the option will be pushed here (or "")
+ void evaluate(const Pkt& pkt, ValueStack& values);
+
+private:
+ uint16_t option_code_; ///< code of the option to be extracted
+};
+
+/// @brief Token that represents equality operator (compares two other tokens)
+///
+/// For example in the expression option[vendor-class] == "MSFT" this token
+/// represents the equal (==) sign.
+class TokenEqual : public Token {
+public:
+ /// @brief Constructor (does nothing)
+ TokenEqual() {}
+
+ /// @brief Compare two values.
+ ///
+ /// Evaluation does not use packet information, but rather consumes the last
+ /// two parameters. It does simple string comparison and sets value to
+ /// either "true" or "false". It requires at least two parameters to be
+ /// present on stack.
+ ///
+ /// @throw EvalBadStack if there's less than 2 values on stack
+ ///
+ /// @brief pkt (unused)
+ /// @brief values - stack of values (2 arguments will be poped, 1 result
+ /// will be pushed)
+ void evaluate(const Pkt& pkt, ValueStack& values);
+};
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif