Browse Source

[master] Merge branch 'trac4271' (vendor options support in classification)

# Conflicts:
#	src/lib/eval/lexer.cc
#	src/lib/eval/lexer.ll
#	src/lib/eval/location.hh
#	src/lib/eval/parser.cc
#	src/lib/eval/parser.h
#	src/lib/eval/parser.yy
#	src/lib/eval/position.hh
#	src/lib/eval/stack.hh
#	src/lib/eval/tests/context_unittest.cc
#	src/lib/eval/tests/token_unittest.cc
#	src/lib/eval/token.cc
#	src/lib/eval/token.h
Tomek Mrugalski 8 years ago
parent
commit
ef676368f9

+ 113 - 3
doc/guide/classify.xml

@@ -202,7 +202,7 @@
 -->
             <row>
               <entry>Option existence</entry>
-              <entry>option[123].exist</entry>
+              <entry>option[123].exists</entry>
               <entry>'true'</entry>
               <entry>If the option with given code is present in the
               packet "true" else "false"</entry>
@@ -315,6 +315,88 @@
               <entry>The value of the transaction id in the DHCPv6
               packet.</entry>
             </row>
+
+            <row>
+              <entry>Vendor option existence (any vendor)</entry>
+              <entry>vendor[*].exists</entry>
+              <entry>true</entry>
+              <entry>Returns whether a vendor option from any vendor
+              is present ('true') or absent ('false').</entry>
+            </row>
+            <row>
+              <entry>Vendor option existence (specific vendor)</entry>
+              <entry>vendor[4491].exists</entry>
+              <entry>true</entry>
+              <entry>Returns whether a vendor option from specified
+              vendor (determined by its enterprise-id)
+              is present ('true') or absent ('false').</entry>
+            </row>
+            <row>
+              <entry>Enterprise-id from vendor option</entry>
+              <entry>vendor.enterprise</entry>
+              <entry>0x0000118b</entry>
+              <entry>If the vendor option is present, it returns the
+              value of the enterprise-id field padded to 4
+              bytes. Returns '' otherwise.</entry>
+            </row>
+            <row>
+              <entry>Vendor sub-option existence</entry>
+              <entry>vendor[4491].option[1].exists</entry>
+              <entry>true</entry>
+              <entry>Returns 'true' if there is vendor option with
+              specified enterprise-id and given sub-option is present.
+              Returns 'false' otherwise.</entry>
+            </row>
+            <row>
+              <entry>Vendor sub-option content</entry>
+              <entry>vendor[4491].option[1].hex</entry>
+              <entry>docsis3.0</entry>
+              <entry>Returns content of the specified sub-option of
+              a vendor option with specified enterprise id. Returns
+              '' if no such option or sub-option is present.
+              </entry>
+            </row>
+
+            <row>
+              <entry>Vendor class option existence (any vendor)</entry>
+              <entry>vendor-class[*].exists</entry>
+              <entry>true</entry>
+              <entry>Returns whether a vendor class option from any vendor
+              is present ('true') or absent ('false').</entry>
+            </row>
+            <row>
+              <entry>Vendor class option existence (specific vendor)</entry>
+              <entry>vendor-class[4491].exists</entry>
+              <entry>true</entry>
+              <entry>Returns whether a vendor class option from specified
+              vendor (determined by its enterprise-id)
+              is present ('true') or absent ('false').</entry>
+            </row>
+            <row>
+              <entry>Enterprise-id from vendor class option</entry>
+              <entry>vendor-class.enterprise</entry>
+              <entry>0x0000118b</entry>
+              <entry>If the vendor option is present, it returns the
+              value of the enterprise-id field padded to 4
+              bytes. Returns '' otherwise.</entry>
+            </row>
+            <row>
+              <entry>First data chunk from vendor class option</entry>
+              <entry>vendor-class[4491].data</entry>
+              <entry>docsis3.0</entry>
+              <entry>Returns content of the first data chunk from
+              the vendor class option with specified enterprise-id.
+              Returns '' if missing.</entry>
+            </row>
+            <row>
+              <entry>Specific data chunk from vendor class option</entry>
+              <entry>vendor-class[4491].data[3]</entry>
+              <entry>docsis3.0</entry>
+              <entry>Returns content of the specified data chunk of
+              a vendor class option with specified enterprise id. Returns
+              '' if no such option or data chunk is present.
+              </entry>
+            </row>
           </tbody>
           </tgroup>
         </table>
@@ -341,7 +423,7 @@
       </para>
 
       <para>
-      "option[code].exist" checks if an option with the code "code" is present
+      "option[code].exists" checks if an option with the code "code" is present
       in the incoming packet. It can be used with empty options.
       </para>
 
@@ -388,6 +470,33 @@
       </para>
 
       <para>
+        Vendor option means Vendor-Identifying Vendor-specific Information
+        option (code 125, see Section 4 of RFC3925) in DHCPv4 and
+        Vendor-specific Information Option (code 17, defined in Section 22.17 of
+        RFC3315) in DHCPv6. Vendor class option means Vendor-Identifying Vendor
+        Class Option (code 124, see Section 3 of RFC3925) in DHCPv4 and Vendor
+        Class Option (code 16, see Section 22.16 of RFC3315). Vendor options may
+        have sub-options that are referenced by their codes. Vendor class
+        options do not have sub-options, but rather data chunks, which are
+        referenced by index value. Index 0 means the first data chunk, Index 1
+        is for the second data chunk (if present), etc.
+      </para>
+
+      <para>In the vendor and vendor-class constructs Asterisk (*) or 0 can be
+      used to specify a wildcard enterprise-id value, i.e. it will match any
+      enterprise-id value.</para>
+
+      <para>Vendor Class Identifier (option 60 in DHCPv4) can be
+      accessed using option[60] expression.</para>
+
+      <para>RFC3925 and RFC3315 allow for multiple instances of vendor options
+      to appear in a single message. The client classification code currently
+      examines the first instance if more than one appear. For vendor.enterprise
+      and vendor-class.enterprise expressions, the value from the first instance
+      is returned. Please submit a feature request on Kea website if you need
+      support for multiple instances.</para>
+
+      <para>
         <table frame="all" id="classification-expressions-list">
           <title>List of Classification Expressions</title>
           <tgroup cols='3'>
@@ -407,7 +516,8 @@
 <row><entry>And</entry> <entry>('foo' == 'bar') and ('bar' == 'foo')</entry><entry>Logical and</entry></row>
 <row><entry>Or</entry> <entry>('foo' == 'bar') or ('bar' == 'foo')</entry><entry>Logical or</entry></row>
 <row><entry>Substring</entry><entry>substring('foobar',0,3)</entry><entry>Return the requested substring</entry></row>
-<row><entry>Concat</entry><entry>concat('foo','bar')</entry><entry>Return the concatenation of the strings</entry></row>
+<row><entry>Concat</entry><entry>concat('foo','bar')</entry><entry>Return the
+concatenation of the strings</entry></row>
           </tbody>
           </tgroup>
         </table>

+ 0 - 6
doc/guide/dhcp4-srv.xml

@@ -3584,9 +3584,6 @@ src/lib/dhcpsrv/cfg_host_operations.cc -->
             <simpara>Host reservation (static addresses) is not supported yet.</simpara>
           </listitem>
           <listitem>
-            <simpara>Full featured client classification is not supported yet.</simpara>
-          </listitem>
-          <listitem>
             <simpara>
               BOOTP (<ulink url="http://tools.ietf.org/html/rfc951">RFC 951</ulink>)
               is not supported. This is a design choice. BOOTP support is not planned.
@@ -3609,9 +3606,6 @@ src/lib/dhcpsrv/cfg_host_operations.cc -->
             allocating server should verify that address is not used by
             sending ICMP echo request.</simpara>
           </listitem>
-          <listitem>
-            <simpara>Address duplication report (DECLINE) is not supported yet.</simpara>
-          </listitem>
       </itemizedlist>
     </section>
 

+ 1 - 2
doc/guide/dhcp6-srv.xml

@@ -3828,8 +3828,7 @@ should include options from the isc option space:
 
         <listitem>
           <simpara>
-            Duplication report (DECLINE) and client reconfiguration (RECONFIGURE) are
-            not yet supported.
+            Client reconfiguration (RECONFIGURE) is not yet supported.
           </simpara>
         </listitem>
       </itemizedlist>

+ 5 - 0
src/lib/eval/Makefile.am

@@ -85,6 +85,11 @@ parser: lexer.cc location.hh position.hh stack.hh parser.cc parser.h
 	@echo "Flex/bison files regenerated"
 
 # --- Flex/Bison stuff below --------------------------------------------------
+# When debugging grammar issues, it's useful to add -v to bison parameters.
+# bison will generate parser.output file that explains the whole grammar.
+# It can be used to manually follow what's going on in the parser.
+# This is especially useful if yydebug_ is set to 1 as that variable
+# will cause parser to print out its internal state.
 location.hh position.hh stack.hh parser.cc parser.h: parser.yy
 	$(YACC) --defines=parser.h -o parser.cc parser.yy
 

+ 38 - 7
src/lib/eval/eval_context.cc

@@ -13,6 +13,7 @@
 #include <exceptions/exceptions.h>
 #include <boost/lexical_cast.hpp>
 #include <fstream>
+#include <limits>
 
 EvalContext::EvalContext(const Option::Universe& option_universe)
   : trace_scanning_(false), trace_parsing_(false),
@@ -97,14 +98,9 @@ uint8_t
 EvalContext::convertNestLevelNumber(const std::string& nest_level,
                                     const isc::eval::location& loc)
 {
-    int n = 0;
-    try {
-        n  = boost::lexical_cast<int>(nest_level);
-    } catch (const boost::bad_lexical_cast &) {
-        error(loc, "Nest level has invalid value in " + nest_level);
-    }
+    uint8_t n = convertUint8(nest_level, loc);
     if (option_universe_ == Option::V6) {
-        if (n < 0 || n >= HOP_COUNT_LIMIT) {
+        if (n >= HOP_COUNT_LIMIT) {
             error(loc, "Nest level has invalid value in "
                       + nest_level + ". Allowed range: 0..31");
         }
@@ -112,9 +108,44 @@ EvalContext::convertNestLevelNumber(const std::string& nest_level,
         error(loc, "Nest level invalid for DHCPv4 packets");
     }
 
+    return (n);
+}
+
+uint8_t
+EvalContext::convertUint8(const std::string& number,
+                          const isc::eval::location& loc)
+{
+    int n = 0;
+    try {
+        n  = boost::lexical_cast<int>(number);
+    } catch (const boost::bad_lexical_cast &) {
+        error(loc, "Invalid integer value in " + number);
+    }
+    if (n < 0 || n >= std::numeric_limits<uint8_t>::max()) {
+        error(loc, "Invalid value in "
+              + number + ". Allowed range: 0..255");
+    }
+
     return (static_cast<uint8_t>(n));
 }
 
+uint32_t
+EvalContext::convertUint32(const std::string& number,
+                          const isc::eval::location& loc)
+{
+    uint64_t n = 0;
+    try {
+        n  = boost::lexical_cast<uint64_t>(number);
+    } catch (const boost::bad_lexical_cast &) {
+        error(loc, "Invalid value in " + number);
+    }
+    if (n >= std::numeric_limits<uint32_t>::max()) {
+        error(loc, "Invalid value in "
+              + number + ". Allowed range: 0..4294967295");
+    }
+
+    return (static_cast<uint32_t>(n));
+}
 
 void
 EvalContext::fatal (const std::string& what)

+ 20 - 2
src/lib/eval/eval_context.h

@@ -96,16 +96,34 @@ public:
     ///
     /// @param option_name the option name
     /// @param loc the location of the token
-    /// @result the option code
+    /// @return the option code
     /// @throw calls the syntax error function if the name cannot be resolved
     uint16_t convertOptionName(const std::string& option_name,
                                const isc::eval::location& loc);
 
+    /// @brief Attempts to convert string to unsigned 32bit integer
+    ///
+    /// @param number string to be converted
+    /// @param loc the location of the token
+    /// @return the integer value
+    /// @throw EvalParseError if conversion fails or the value is out of range.
+    uint32_t convertUint32(const std::string& number,
+                           const isc::eval::location& loc);
+
+    /// @brief Attempts to convert string to unsigned 8bit integer
+    ///
+    /// @param number string to be converted
+    /// @param loc the location of the token
+    /// @return the integer value
+    /// @throw EvalParseError if conversion fails or the value is out of range.
+    uint8_t convertUint8(const std::string& number,
+                         const isc::eval::location& loc);
+
     /// @brief Nest level conversion
     ///
     /// @param nest_level a string representing the integer nesting level
     /// @param loc the location of the token
-    /// @result the nesting level
+    /// @return the nesting level
     /// @throw calls the syntax error function if the value is not in
     ///        the range 0..31
     uint8_t convertNestLevelNumber(const std::string& nest_level,

+ 45 - 0
src/lib/eval/eval_messages.mes

@@ -113,6 +113,51 @@ string and an empty result will be pushed onto the stack.  The start,
 length and string are still popped from the stack and the result is
 still pushed.  The strings are displayed in hex.
 
+% EVAL_DEBUG_VENDOR_CLASS_DATA Data %1 (out of %2 received) in vendor class found, pushing result '%3'
+This debug message indicates that vendor class option was found and passed
+enterprise-id checks and has sufficient number of data chunks. The total number
+of chunks and value pushed are reported as debugging aid.
+
+% EVAL_DEBUG_VENDOR_CLASS_DATA_NOT_FOUND Requested data index %1, but option with enterprise-id %2 has only %3 data tuple(s), pushing result '%4'
+This debug message indicates that vendor class option was found and passed
+enterprise-id checks, but does not have sufficient number of data chunks.
+Note that the index starts at 0, so there has to be at least (index + 1)
+data chunks.
+
+% EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID Pushing enterprise-id %1 as result 0x%2
+This debug message indicates that the expression has been evaluated and vendor
+class option was found and its enterprise-id is being reported.
+
+% EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID_MISMATCH Was looking for %1, option had %2, pushing result '%3'
+This debug message indicates that the expression has been evaluated
+and vendor class option was found, but has different enterprise-id than specified
+in the expression.
+
+% EVAL_DEBUG_VENDOR_CLASS_EXISTS Option with enterprise-id %1 found, pushing result '%2'
+This debug message indicates that the expression has been evaluated and vendor
+class option was found.
+
+% EVAL_DEBUG_VENDOR_CLASS_NO_OPTION Option with code %1 missing, pushing result '%2'
+This debug message indicates that the expression has been evaluated
+and vendor class option was not found.
+
+% EVAL_DEBUG_VENDOR_ENTERPRISE_ID Pushing enterprise-id %1 as result 0x%2
+This debug message indicates that the expression has been evaluated and vendor
+option was found and its enterprise-id is being reported.
+
+% EVAL_DEBUG_VENDOR_ENTERPRISE_ID_MISMATCH Was looking for %1, option had %2, pushing result '%3'
+This debug message indicates that the expression has been evaluated
+and vendor option was found, but has different enterprise-id than specified
+in the expression.
+
+% EVAL_DEBUG_VENDOR_EXISTS Option with enterprise-id %1 found, pushing result '%2'
+This debug message indicates that the expression has been evaluated and vendor
+option was found.
+
+% EVAL_DEBUG_VENDOR_NO_OPTION Option with code %1 missing, pushing result '%2'
+This debug message indicates that the expression has been evaluated
+and vendor option was not found.
+
 % EVAL_RESULT Expression %1 evaluated to %2
 This debug message indicates that the expression has been evaluated
 to said value. This message is mostly useful during debugging of the

+ 252 - 207
src/lib/eval/lexer.cc

@@ -284,7 +284,7 @@ struct yy_buffer_state
 	/* Number of characters read into yy_ch_buf, not including EOB
 	 * characters.
 	 */
-	yy_size_t yy_n_chars;
+	int yy_n_chars;
 
 	/* Whether we "own" the buffer - i.e., we know we created it,
 	 * and can realloc() it to grow it, and should free() it to
@@ -368,7 +368,7 @@ static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
 
 /* yy_hold_char holds the character lost when yytext is formed. */
 static char yy_hold_char;
-static yy_size_t yy_n_chars;		/* number of characters read into yy_ch_buf */
+static int yy_n_chars;		/* number of characters read into yy_ch_buf */
 yy_size_t yyleng;
 
 /* Points to current character in buffer. */
@@ -483,8 +483,8 @@ static void yy_fatal_error (yyconst char msg[]  );
 	(yy_c_buf_p) = yy_cp;
 
 /* %% [4.0] data tables for the DFA and the user's section 1 definitions go here */
-#define YY_NUM_RULES 46
-#define YY_END_OF_BUFFER 47
+#define YY_NUM_RULES 51
+#define YY_END_OF_BUFFER 52
 /* This struct is not used in this scanner,
    but its presence is necessary. */
 struct yy_trans_info
@@ -492,58 +492,64 @@ struct yy_trans_info
 	flex_int32_t yy_verify;
 	flex_int32_t yy_nxt;
 	};
-static yyconst flex_int16_t yy_acclist[248] =
+static yyconst flex_int16_t yy_acclist[280] =
     {   0,
-       47,   45,   46,    1,   45,   46,    2,   46,   45,   46,
-       40,   45,   46,   41,   45,   46,   44,   45,   46,   45,
-       46,   39,   45,   46,    5,   45,   46,    5,   45,   46,
-       45,   46,   45,   46,   45,   46,16390,   45,   46,16390,
-       42,   45,   46,   43,   45,   46,   45,   46,16390,   45,
-       46,16390,   45,   46,16390,   45,   46,16390,   45,   46,
-    16390,   45,   46,16390,   45,   46,16390,   45,   46,16390,
-       45,   46,16390,   45,   46,16390,   45,   46,16390,   45,
-       46,16390,   45,   46,16390,   45,   46,16390,   45,   46,
-    16390,   45,   46,16390,    1,    2,    3,    5,    5,    7,
-
-        8,16390,16390, 8198,16390,16390,16390,16390,16390,16390,
+       52,   50,   51,    1,   50,   51,    2,   51,   50,   51,
+       44,   50,   51,   45,   50,   51,   49,   50,   51,   48,
+       50,   51,   50,   51,   43,   50,   51,    5,   50,   51,
+        5,   50,   51,   50,   51,   50,   51,   50,   51,16390,
+       50,   51,16390,   46,   50,   51,   47,   50,   51,   50,
+       51,16390,   50,   51,16390,   50,   51,16390,   50,   51,
+    16390,   50,   51,16390,   50,   51,16390,   50,   51,16390,
+       50,   51,16390,   50,   51,16390,   50,   51,16390,   50,
+       51,16390,   50,   51,16390,   50,   51,16390,   50,   51,
+    16390,   50,   51,16390,   50,   51,16390,   50,   51,16390,
+
+        1,    2,    3,    5,    5,    7,    8,16390,16390, 8198,
     16390,16390,16390,16390,16390,16390,16390,16390,16390,16390,
-    16390,   38,16390,16390,16390,16390,16390,16390,16390,16390,
-    16390,16390,    4,    7,   34,16390,   37,16390,16390,16390,
-       20,16390,16390,16390,   15,16390,16390,16390,16390,   21,
-    16390,16390,   23,16390,16390,   36,16390,16390,16390,   17,
-    16390,16390,16390,   19,16390,16390,16390,16390,16390,16390,
-    16390,16390,16390,   24,16390,16390,16390,16390,16390,16390,
-    16390,   22,16390,   30,16390,16390,16390,16390,   14,16390,
-    16390,16390,16390,16390,16390,16390,   25,16390,   18,16390,
-
-    16390,16390,16390,16390,16390,16390,16390,16390,16390,   26,
-    16390,   35,16390,   16,16390,   27,16390,16390,16390,    9,
-    16390,16390,   10,16390,   11,16390,   29,16390,16390,16390,
-       28,16390,    7,16390,   31,16390,16390,16390,   32,16390,
-       13,16390,   12,16390,16390,   33,16390
+    16390,16390,16390,16390,16390,16390,16390,16390,16390,   42,
+    16390,16390,16390,16390,16390,16390,16390,16390,16390,16390,
+    16390,    4,    7,   38,16390,   41,16390,16390,16390,16390,
+       20,16390,16390,16390,16390,   15,16390,16390,16390,16390,
+       21,16390,16390,   23,16390,16390,   40,16390,16390,16390,
+       17,16390,16390,16390,   19,16390,16390,16390,16390,16390,
+    16390,16390,16390,   35,16390,16390,16390,16390,   24,16390,
+    16390,16390,16390,16390,16390,16390,   22,16390,   30,16390,
+
+    16390,16390,16390,   14,16390,16390,16390,16390,16390,16390,
+    16390,16390,16390,   25,16390,   18,16390,16390,16390,16390,
+    16390,16390,16390,16390,16390,16390,16390,   26,16390,   39,
+    16390,16390,   16,16390,   27,16390,16390,16390,    9,16390,
+    16390,   10,16390,   11,16390,   29,16390,16390,16390,   33,
+    16390,   28,16390,    7,16390,16390,   31,16390,16390,16390,
+       32,16390,16390,   13,16390,   12,16390,16390,16390,16390,
+       37,16390,16390,   36,16390,16390,16390,   34,16390
     } ;
 
-static yyconst flex_int16_t yy_accept[174] =
+static yyconst flex_int16_t yy_accept[199] =
     {   0,
         1,    1,    1,    2,    4,    7,    9,   11,   14,   17,
-       20,   22,   25,   28,   31,   33,   35,   38,   41,   44,
+       20,   23,   25,   28,   31,   34,   36,   38,   41,   44,
        47,   50,   53,   56,   59,   62,   65,   68,   71,   74,
-       77,   80,   83,   86,   89,   92,   95,   96,   97,   97,
-       98,   99,   99,  100,  100,  100,  100,  100,  101,  102,
-      102,  102,  103,  104,  105,  106,  107,  108,  109,  110,
-      111,  112,  113,  114,  115,  116,  117,  118,  119,  120,
-      121,  122,  124,  125,  126,  127,  128,  129,  130,  131,
-      132,  133,  133,  134,  135,  137,  139,  140,  141,  143,
-      144,  145,  147,  148,  149,  150,  152,  153,  155,  156,
-
-      158,  159,  160,  162,  163,  164,  166,  167,  168,  169,
-      170,  170,  171,  172,  173,  174,  176,  177,  178,  179,
-      180,  181,  182,  184,  186,  187,  188,  189,  191,  192,
-      193,  193,  194,  195,  196,  197,  199,  201,  202,  203,
-      204,  205,  206,  207,  208,  209,  210,  210,  212,  214,
-      216,  218,  219,  220,  222,  223,  225,  227,  229,  230,
-      231,  233,  234,  235,  237,  238,  239,  241,  243,  245,
-      246,  248,  248
+       77,   80,   83,   86,   89,   92,   95,   98,  101,  102,
+      103,  103,  104,  105,  105,  106,  106,  106,  106,  106,
+      107,  108,  108,  108,  109,  110,  111,  112,  113,  114,
+      115,  116,  117,  118,  119,  120,  121,  122,  123,  124,
+      125,  126,  127,  128,  129,  130,  132,  133,  134,  135,
+      136,  137,  138,  139,  140,  141,  142,  142,  143,  144,
+      146,  148,  149,  150,  151,  153,  154,  155,  156,  158,
+
+      159,  160,  161,  163,  164,  166,  167,  169,  170,  171,
+      173,  174,  175,  177,  178,  179,  180,  181,  182,  182,
+      183,  184,  186,  187,  188,  189,  191,  192,  193,  194,
+      195,  196,  197,  199,  201,  202,  203,  204,  206,  207,
+      208,  209,  209,  210,  211,  212,  213,  214,  216,  218,
+      219,  220,  221,  222,  223,  224,  225,  226,  227,  228,
+      228,  230,  232,  233,  235,  237,  238,  239,  241,  242,
+      244,  246,  248,  249,  250,  252,  254,  255,  256,  257,
+      259,  260,  261,  263,  263,  264,  266,  268,  269,  270,
+      271,  273,  274,  276,  277,  278,  280,  280
+
     } ;
 
 static yyconst YY_CHAR yy_ec[256] =
@@ -552,16 +558,16 @@ static yyconst YY_CHAR yy_ec[256] =
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    2,    1,    1,    1,    1,    1,    1,    4,    5,
-        6,    1,    1,    7,    8,    9,    1,   10,   11,   11,
-       11,   12,   11,   13,   11,   11,   11,   14,    1,    1,
-       15,    1,    1,    1,   16,   16,   16,   16,   16,   16,
-       17,   17,   17,   17,   17,   17,   17,   17,   17,   17,
-       17,   17,   17,   17,   17,   17,   17,   18,   17,   17,
-       19,    1,   20,    1,   21,    1,   22,   23,   24,   25,
-
-       26,   27,   28,   29,   30,   17,   31,   32,   33,   34,
-       35,   36,   17,   37,   38,   39,   40,   17,   17,   41,
-       42,   17,    1,    1,    1,    1,    1,    1,    1,    1,
+        6,    7,    1,    8,    9,   10,    1,   11,   12,   12,
+       12,   13,   12,   14,   12,   12,   12,   15,    1,    1,
+       16,    1,    1,    1,   17,   17,   17,   17,   17,   17,
+       18,   18,   18,   18,   18,   18,   18,   18,   18,   18,
+       18,   18,   18,   18,   18,   18,   18,   19,   18,   18,
+       20,    1,   21,    1,   22,    1,   23,   24,   25,   26,
+
+       27,   28,   29,   30,   31,   18,   32,   33,   34,   35,
+       36,   37,   18,   38,   39,   40,   41,   42,   18,   43,
+       44,   18,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
@@ -578,158 +584,172 @@ static yyconst YY_CHAR yy_ec[256] =
         1,    1,    1,    1,    1
     } ;
 
-static yyconst YY_CHAR yy_meta[43] =
+static yyconst YY_CHAR yy_meta[45] =
     {   0,
-        1,    1,    2,    1,    1,    1,    1,    1,    3,    4,
-        4,    4,    4,    5,    1,    4,    1,    1,    1,    1,
-        1,    4,    4,    4,    4,    4,    4,    1,    1,    1,
+        1,    1,    2,    1,    1,    1,    1,    1,    1,    3,
+        4,    4,    4,    4,    5,    1,    4,    1,    1,    1,
+        1,    1,    4,    4,    4,    4,    4,    4,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
-        1,    1
+        1,    1,    1,    1
     } ;
 
-static yyconst flex_uint16_t yy_base[178] =
+static yyconst flex_uint16_t yy_base[203] =
     {   0,
-        0,    0,  281,  282,  278,  276,  274,  282,  282,  282,
-       33,  282,   38,   35,  263,  261,   78,  111,  282,  282,
-       23,   36,  237,  233,  243,   43,  245,   42,   36,  236,
-       41,  100,  244,  102,   33,  239,  266,  264,  262,  282,
-      133,  137,  110,  251,  250,    0,  249,    0,  282,  138,
-      150,    0,    0,  282,  230,  236,  238,  225,  219,  227,
-      234,  214,  228,  211,  230,  217,  216,  225,  220,  208,
-      207,    0,  219,  205,  211,  220,  217,  217,  198,  216,
-      215,  150,    0,    0,    0,    0,  211,  211,    0,  196,
-      208,    0,  198,  195,  206,    0,  198,    0,  189,    0,
-
-      197,  189,   71,  203,  199,    0,  185,  183,  187,  195,
-      144,  194,  196,  178,  191,    0,  189,  188,  191,  170,
-      176,  188,    0,    0,  167,  183,  168,    0,  168,  180,
-      155,  167,  164,  164,  164,    0,    0,  175,  163,  164,
-      172,  117,  159,  158,  164,  156,  161,    0,    0,    0,
-        0,  167,  165,    0,  155,    0,    0,    0,  139,   72,
-        0,  165,   56,    0,   50,   51,    0,    0,    0,   45,
-        0,  282,  178,  180,  182,   63,  185
+        0,    0,  310,  311,  307,  305,  303,  311,  311,  311,
+      311,   34,  311,   39,   36,  291,  289,   81,  115,  311,
+      311,   24,   37,   37,   26,  273,   45,  275,   43,   48,
+      266,   43,   59,  274,  106,   50,  273,  268,  296,  294,
+      292,  311,  122,  137,  112,  280,  279,    0,  278,    0,
+      311,  143,  150,    0,    0,  311,  259,  265,  267,  254,
+      248,  247,  246,  254,  261,  240,  255,  237,  257,  244,
+      243,  252,  247,  235,  234,    0,  246,  232,  238,  247,
+      244,  244,  224,  243,  230,  241,  146,    0,    0,    0,
+        0,  237,  237,  238,    0,  233,  220,  232,    0,  222,
+
+      219,  230,    0,  222,    0,  213,    0,  221,  213,  148,
+      227,  223,    0,  209,  207,  211,  219,  218,  154,  217,
+      219,    0,  203,  200,  213,    0,  211,  210,  213,  191,
+      198,  210,    0,    0,  188,  205,  190,    0,  190,  192,
+      201,  162,  188,  185,  187,  184,  184,    0,    0,  195,
+      173,  172,  180,  156,  167,  165,  171,  163,  162,  166,
+        0,    0,  161,    0,    0,  172,  170,    0,  170,    0,
+        0,    0,  164,  168,  184,    0,  170,  161,  153,    0,
+      152,  154,    0,  183,  149,    0,    0,  158,  130,  127,
+        0,   78,    0,   58,   50,    0,  311,  208,  210,  212,
+
+       71,  215
     } ;
 
-static yyconst flex_int16_t yy_def[178] =
+static yyconst flex_int16_t yy_def[203] =
     {   0,
-      172,    1,  172,  172,  172,  172,  173,  172,  172,  172,
-      172,  172,  172,   13,  174,  172,  172,   17,  172,  172,
-       17,   17,   17,   17,   18,   18,   18,   18,   18,   18,
-       18,   18,   18,   18,   18,   18,  172,  172,  173,  172,
-      172,  172,   13,  174,  175,  176,  174,  177,  172,  172,
-       18,   17,   18,  172,   18,   18,   18,   18,   18,   18,
-       18,   18,   18,   18,   18,   18,   18,   18,   18,   18,
-       18,   18,   18,   18,   18,   18,   18,   18,   18,   18,
-       18,  172,  176,  177,   18,   18,   18,   18,   18,   18,
-       18,   18,   18,   18,   18,   18,   18,   18,   18,   18,
-
-       18,   18,   18,   18,   18,   18,   18,   18,   18,   18,
-      172,   18,   18,   18,   18,   18,   18,   18,   18,   18,
-       18,   18,   18,   18,   18,   18,   18,   18,   18,   18,
-      172,   18,   18,   18,   18,   18,   18,   18,   18,   18,
-       18,   18,   18,   18,   18,   18,  172,   18,   18,   18,
-       18,   18,   18,   18,   18,   18,   18,   18,   18,   18,
-       18,  172,   18,   18,   18,   18,   18,   18,   18,   18,
-       18,    0,  172,  172,  172,  172,  172
+      197,    1,  197,  197,  197,  197,  198,  197,  197,  197,
+      197,  197,  197,  197,   14,  199,  197,  197,   18,  197,
+      197,   18,   18,   18,   18,   19,   19,   19,   19,   19,
+       19,   19,   19,   19,   19,   19,   19,   19,  197,  197,
+      198,  197,  197,  197,   14,  199,  200,  201,  199,  202,
+      197,  197,   19,   18,   19,  197,   19,   19,   19,   19,
+       18,   19,   19,   19,   19,   19,   19,   19,   19,   19,
+       19,   19,   19,   19,   19,   19,   19,   19,   19,   19,
+       19,   19,   19,   19,   19,   19,  197,  201,  202,   19,
+       19,   19,   19,   19,   19,   19,   19,   19,   19,   19,
+
+       19,   19,   19,   19,   19,   19,   19,   19,   19,   19,
+       19,   19,   19,   19,   19,   19,   19,   19,  197,   19,
+       19,   19,   19,   19,   19,   19,   19,   19,   19,   19,
+       19,   19,   19,   19,   19,   19,   19,   19,   19,   19,
+       19,  197,   19,   19,   19,   19,   19,   19,   19,   19,
+       19,   19,   19,   19,   19,   19,   19,   19,   19,  197,
+       19,   19,   19,   19,   19,   19,   19,   19,   19,   19,
+       19,   19,   19,   19,   19,   19,  197,   19,   19,   19,
+       19,   19,   19,   19,   19,   19,   19,   19,   19,   19,
+       19,   19,   19,   19,   19,   19,    0,  197,  197,  197,
+
+      197,  197
     } ;
 
-static yyconst flex_uint16_t yy_nxt[325] =
+static yyconst flex_uint16_t yy_nxt[356] =
     {   0,
         4,    5,    6,    7,    8,    9,   10,   11,   12,   13,
-       14,   14,   14,   15,   16,   17,   18,   18,   19,   20,
-        4,   21,   17,   22,   23,   24,   17,   25,   26,   27,
-       18,   28,   29,   30,   31,   32,   33,   34,   35,   18,
-       18,   36,   41,   41,   41,   41,   42,   43,   43,   43,
-       43,   44,  172,   45,   55,   46,   56,   68,   79,   45,
-       45,   45,   45,   45,   45,   57,   83,   66,   62,   80,
-       58,   67,  171,   69,   63,  172,   71,   72,   46,   50,
-       50,   64,  123,  124,  170,   51,  169,   52,   52,   52,
-       52,   44,  168,   52,   53,   53,  167,   54,   51,   52,
-
-       52,   52,   52,   52,   52,   53,   53,   53,   53,   53,
-       53,   53,   53,   53,   53,   53,   53,   53,   53,   53,
-       53,   53,   53,   53,  172,   73,   53,  172,  156,  157,
-       74,   76,   53,   53,   53,   53,   53,   53,   77,   50,
-       50,   78,   41,   41,   41,   41,   82,   82,   82,   82,
-      172,  172,  172,  131,  131,  131,  131,   54,  111,   82,
-       82,   82,   82,  147,  131,  131,  131,  131,  166,  172,
-      162,  162,  162,  162,  162,  162,  162,  162,   39,  165,
-       39,   39,   39,   47,   47,   45,   45,   84,   84,   84,
-      164,  163,  161,  160,  159,  158,  155,  154,  153,  152,
-
-      151,  150,  149,  148,  146,  145,  144,  143,  142,  141,
-      140,  139,  138,  137,  136,  135,  134,  133,  132,  130,
-      129,  128,  127,  126,  125,  122,  121,  120,  119,  118,
-      117,  116,  115,  114,  113,  112,  110,  109,  108,  107,
-      106,  105,  104,  103,  102,  101,  100,   99,   98,   97,
-       96,   95,   94,   93,   92,   91,   90,   89,   88,   87,
-       86,   85,   48,   44,   48,   40,   38,   37,   81,   75,
-       70,   65,   61,   60,   59,   49,   48,   40,   38,   37,
-      172,    3,  172,  172,  172,  172,  172,  172,  172,  172,
-      172,  172,  172,  172,  172,  172,  172,  172,  172,  172,
-
-      172,  172,  172,  172,  172,  172,  172,  172,  172,  172,
-      172,  172,  172,  172,  172,  172,  172,  172,  172,  172,
-      172,  172,  172,  172
+       14,   15,   15,   15,   16,   17,   18,   19,   19,   20,
+       21,    4,   22,   18,   23,   24,   25,   18,   26,   27,
+       28,   19,   29,   30,   31,   32,   33,   34,   35,   36,
+       19,   37,   19,   38,   43,   43,   43,   43,   44,   45,
+       45,   45,   45,   46,  197,   47,   57,   48,   58,   61,
+       63,   47,   47,   47,   47,   47,   47,   59,   64,   70,
+       72,   66,   60,   71,   88,   62,   83,   67,  197,   75,
+       76,   48,   52,   52,   68,   77,   73,   84,  196,   53,
+       78,   54,   54,   54,   54,   46,  195,   54,   55,   55,
+
+      194,   56,   53,   54,   54,   54,   54,   54,   54,   55,
+       55,   55,   55,   55,   55,   55,   55,   55,   55,   55,
+       55,   55,   55,   55,   55,   55,   55,   55,   55,  197,
+      197,   55,   43,   43,   43,   43,   80,   55,   55,   55,
+       55,   55,   55,   81,   52,   52,   82,   87,   87,   87,
+       87,  197,  197,  193,  197,  119,   87,   87,   87,   87,
+      133,  134,  192,   56,  142,  142,  142,  142,  170,  171,
+      197,  160,  142,  142,  142,  142,  177,  177,  177,  177,
+      177,  177,  177,  177,  197,  197,  191,  190,  188,  187,
+      186,  185,  184,  183,  182,  181,  180,  179,  178,  176,
+
+      175,  174,  173,  197,  172,  169,  168,  189,   41,  167,
+       41,   41,   41,   49,   49,   47,   47,   89,   89,   89,
+      166,  165,  164,  163,  162,  161,  159,  158,  157,  156,
+      155,  154,  153,  152,  151,  150,  149,  148,  147,  146,
+      145,  144,  143,  141,  140,  139,  138,  137,  136,  135,
+      132,  131,  130,  129,  128,  127,  126,  125,  124,  123,
+      122,  121,  120,  118,  117,  116,  115,  114,  113,  112,
+      111,  110,  109,  108,  107,  106,  105,  104,  103,  102,
+      101,  100,   99,   98,   97,   96,   95,   94,   93,   92,
+       91,   90,   50,   46,   50,   42,   40,   39,   86,   85,
+
+       79,   74,   69,   65,   51,   50,   42,   40,   39,  197,
+        3,  197,  197,  197,  197,  197,  197,  197,  197,  197,
+      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
+      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
+      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
+      197,  197,  197,  197,  197
     } ;
 
-static yyconst flex_int16_t yy_chk[325] =
+static yyconst flex_int16_t yy_chk[356] =
     {   0,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
-        1,    1,   11,   11,   11,   11,   13,   13,   13,   13,
-       13,   13,   14,   13,   21,   13,   21,   29,   35,   13,
-       13,   13,   13,   13,   13,   22,  176,   28,   26,   35,
-       22,   28,  170,   29,   26,   14,   31,   31,   13,   17,
-       17,   26,  103,  103,  166,   17,  165,   17,   17,   17,
-       17,   17,  163,   17,   17,   17,  160,   17,   17,   17,
-
-       17,   17,   17,   17,   17,   17,   17,   17,   17,   17,
-       17,   17,   17,   17,   17,   17,   17,   17,   17,   17,
-       18,   18,   18,   18,   18,   32,   18,   43,  142,  142,
-       32,   34,   18,   18,   18,   18,   18,   18,   34,   50,
-       50,   34,   41,   41,   41,   41,   42,   42,   42,   42,
-       43,   51,   51,  111,  111,  111,  111,   50,   82,   82,
-       82,   82,   82,  131,  131,  131,  131,  131,  159,   51,
-      147,  147,  147,  147,  162,  162,  162,  162,  173,  155,
-      173,  173,  173,  174,  174,  175,  175,  177,  177,  177,
-      153,  152,  146,  145,  144,  143,  141,  140,  139,  138,
-
-      135,  134,  133,  132,  130,  129,  127,  126,  125,  122,
-      121,  120,  119,  118,  117,  115,  114,  113,  112,  110,
-      109,  108,  107,  105,  104,  102,  101,   99,   97,   95,
-       94,   93,   91,   90,   88,   87,   81,   80,   79,   78,
-       77,   76,   75,   74,   73,   71,   70,   69,   68,   67,
-       66,   65,   64,   63,   62,   61,   60,   59,   58,   57,
-       56,   55,   47,   45,   44,   39,   38,   37,   36,   33,
-       30,   27,   25,   24,   23,   16,   15,    7,    6,    5,
-        3,  172,  172,  172,  172,  172,  172,  172,  172,  172,
-      172,  172,  172,  172,  172,  172,  172,  172,  172,  172,
-
-      172,  172,  172,  172,  172,  172,  172,  172,  172,  172,
-      172,  172,  172,  172,  172,  172,  172,  172,  172,  172,
-      172,  172,  172,  172
+        1,    1,    1,    1,   12,   12,   12,   12,   14,   14,
+       14,   14,   14,   14,   15,   14,   22,   14,   22,   24,
+       25,   14,   14,   14,   14,   14,   14,   23,   25,   29,
+       30,   27,   23,   29,  201,   24,   36,   27,   15,   32,
+       32,   14,   18,   18,   27,   33,   30,   36,  195,   18,
+       33,   18,   18,   18,   18,   18,  194,   18,   18,   18,
+
+      192,   18,   18,   18,   18,   18,   18,   18,   18,   18,
+       18,   18,   18,   18,   18,   18,   18,   18,   18,   18,
+       18,   18,   18,   18,   18,   19,   19,   19,   19,   19,
+       45,   19,   43,   43,   43,   43,   35,   19,   19,   19,
+       19,   19,   19,   35,   52,   52,   35,   44,   44,   44,
+       44,   53,   53,  190,   45,   87,   87,   87,   87,   87,
+      110,  110,  189,   52,  119,  119,  119,  119,  154,  154,
+       53,  142,  142,  142,  142,  142,  160,  160,  160,  160,
+      177,  177,  177,  177,  184,  184,  188,  185,  182,  181,
+      179,  178,  175,  174,  173,  169,  167,  166,  163,  159,
+
+      158,  157,  156,  184,  155,  153,  152,  184,  198,  151,
+      198,  198,  198,  199,  199,  200,  200,  202,  202,  202,
+      150,  147,  146,  145,  144,  143,  141,  140,  139,  137,
+      136,  135,  132,  131,  130,  129,  128,  127,  125,  124,
+      123,  121,  120,  118,  117,  116,  115,  114,  112,  111,
+      109,  108,  106,  104,  102,  101,  100,   98,   97,   96,
+       94,   93,   92,   86,   85,   84,   83,   82,   81,   80,
+       79,   78,   77,   75,   74,   73,   72,   71,   70,   69,
+       68,   67,   66,   65,   64,   63,   62,   61,   60,   59,
+       58,   57,   49,   47,   46,   41,   40,   39,   38,   37,
+
+       34,   31,   28,   26,   17,   16,    7,    6,    5,    3,
+      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
+      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
+      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
+      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
+      197,  197,  197,  197,  197
     } ;
 
 /* Table of booleans, true if rule could match eol. */
-static yyconst flex_int32_t yy_rule_can_match_eol[47] =
+static yyconst flex_int32_t yy_rule_can_match_eol[52] =
     {   0,
 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-    0, 0, 0, 0, 0, 0, 0,     };
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,     };
 
 extern int yy_flex_debug;
 int yy_flex_debug = 1;
 
-static yyconst flex_int16_t yy_rule_linenum[46] =
+static yyconst flex_int16_t yy_rule_linenum[51] =
     {   0,
        82,   86,   92,  102,  108,  122,  129,  143,  144,  145,
       146,  147,  148,  149,  150,  151,  152,  153,  154,  155,
       156,  157,  158,  159,  160,  161,  162,  163,  164,  165,
       166,  167,  168,  169,  170,  171,  172,  173,  174,  175,
-      176,  177,  178,  179,  181
+      176,  177,  178,  179,  180,  181,  182,  183,  184,  185
     } ;
 
 static yy_state_type *yy_state_buf=0, *yy_state_ptr=0;
@@ -808,7 +828,7 @@ static isc::eval::location loc;
 // by moving it ahead by yyleng bytes. yyleng specifies the length of the
 // currently matched token.
 #define YY_USER_ACTION  loc.columns(yyleng);
-#line 812 "lexer.cc"
+#line 832 "lexer.cc"
 
 #define INITIAL 0
 
@@ -1105,7 +1125,7 @@ YY_DECL
     loc.step();
 
 
-#line 1109 "lexer.cc"
+#line 1129 "lexer.cc"
 
 	while ( /*CONSTCOND*/1 )		/* loops until end-of-file is reached */
 		{
@@ -1133,14 +1153,14 @@ yy_match:
 			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 				{
 				yy_current_state = (int) yy_def[yy_current_state];
-				if ( yy_current_state >= 173 )
+				if ( yy_current_state >= 198 )
 					yy_c = yy_meta[(unsigned int) yy_c];
 				}
 			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
 			*(yy_state_ptr)++ = yy_current_state;
 			++yy_cp;
 			}
-		while ( yy_current_state != 172 );
+		while ( yy_current_state != 197 );
 
 yy_find_action:
 /* %% [10.0] code to find the action number goes here */
@@ -1203,13 +1223,13 @@ do_action:	/* This label is used only to access EOF actions. */
 			{
 			if ( yy_act == 0 )
 				fprintf( stderr, "--scanner backing up\n" );
-			else if ( yy_act < 46 )
+			else if ( yy_act < 51 )
 				fprintf( stderr, "--accepting rule at line %ld (\"%s\")\n",
 				         (long)yy_rule_linenum[yy_act], yytext );
-			else if ( yy_act == 46 )
+			else if ( yy_act == 51 )
 				fprintf( stderr, "--accepting default rule (\"%s\")\n",
 				         yytext );
-			else if ( yy_act == 47 )
+			else if ( yy_act == 52 )
 				fprintf( stderr, "--(end of buffer or a NUL)\n" );
 			else
 				fprintf( stderr, "--EOF (start condition %d)\n", YY_START );
@@ -1431,78 +1451,103 @@ return isc::eval::EvalParser::make_TRANSID(loc);
 case 33:
 YY_RULE_SETUP
 #line 168 "lexer.ll"
-return isc::eval::EvalParser::make_SUBSTRING(loc);
+return isc::eval::EvalParser::make_VENDOR(loc);
 	YY_BREAK
 case 34:
 YY_RULE_SETUP
 #line 169 "lexer.ll"
-return isc::eval::EvalParser::make_ALL(loc);
+return isc::eval::EvalParser::make_VENDOR_CLASS(loc);
 	YY_BREAK
 case 35:
 YY_RULE_SETUP
 #line 170 "lexer.ll"
-return isc::eval::EvalParser::make_CONCAT(loc);
+return isc::eval::EvalParser::make_DATA(loc);
 	YY_BREAK
 case 36:
 YY_RULE_SETUP
 #line 171 "lexer.ll"
-return isc::eval::EvalParser::make_NOT(loc);
+return isc::eval::EvalParser::make_ENTERPRISE(loc);
 	YY_BREAK
 case 37:
 YY_RULE_SETUP
 #line 172 "lexer.ll"
-return isc::eval::EvalParser::make_AND(loc);
+return isc::eval::EvalParser::make_SUBSTRING(loc);
 	YY_BREAK
 case 38:
 YY_RULE_SETUP
 #line 173 "lexer.ll"
-return isc::eval::EvalParser::make_OR(loc);
+return isc::eval::EvalParser::make_ALL(loc);
 	YY_BREAK
 case 39:
 YY_RULE_SETUP
 #line 174 "lexer.ll"
-return isc::eval::EvalParser::make_DOT(loc);
+return isc::eval::EvalParser::make_CONCAT(loc);
 	YY_BREAK
 case 40:
 YY_RULE_SETUP
 #line 175 "lexer.ll"
-return isc::eval::EvalParser::make_LPAREN(loc);
+return isc::eval::EvalParser::make_NOT(loc);
 	YY_BREAK
 case 41:
 YY_RULE_SETUP
 #line 176 "lexer.ll"
-return isc::eval::EvalParser::make_RPAREN(loc);
+return isc::eval::EvalParser::make_AND(loc);
 	YY_BREAK
 case 42:
 YY_RULE_SETUP
 #line 177 "lexer.ll"
-return isc::eval::EvalParser::make_LBRACKET(loc);
+return isc::eval::EvalParser::make_OR(loc);
 	YY_BREAK
 case 43:
 YY_RULE_SETUP
 #line 178 "lexer.ll"
-return isc::eval::EvalParser::make_RBRACKET(loc);
+return isc::eval::EvalParser::make_DOT(loc);
 	YY_BREAK
 case 44:
 YY_RULE_SETUP
 #line 179 "lexer.ll"
-return isc::eval::EvalParser::make_COMA(loc);
+return isc::eval::EvalParser::make_LPAREN(loc);
 	YY_BREAK
 case 45:
 YY_RULE_SETUP
+#line 180 "lexer.ll"
+return isc::eval::EvalParser::make_RPAREN(loc);
+	YY_BREAK
+case 46:
+YY_RULE_SETUP
 #line 181 "lexer.ll"
+return isc::eval::EvalParser::make_LBRACKET(loc);
+	YY_BREAK
+case 47:
+YY_RULE_SETUP
+#line 182 "lexer.ll"
+return isc::eval::EvalParser::make_RBRACKET(loc);
+	YY_BREAK
+case 48:
+YY_RULE_SETUP
+#line 183 "lexer.ll"
+return isc::eval::EvalParser::make_COMA(loc);
+	YY_BREAK
+case 49:
+YY_RULE_SETUP
+#line 184 "lexer.ll"
+return isc::eval::EvalParser::make_ANY(loc);
+	YY_BREAK
+case 50:
+YY_RULE_SETUP
+#line 185 "lexer.ll"
 driver.error (loc, "Invalid character: " + std::string(yytext));
 	YY_BREAK
 case YY_STATE_EOF(INITIAL):
-#line 182 "lexer.ll"
+#line 186 "lexer.ll"
 return isc::eval::EvalParser::make_END(loc);
 	YY_BREAK
-case 46:
+case 51:
 YY_RULE_SETUP
-#line 183 "lexer.ll"
+#line 187 "lexer.ll"
 ECHO;
 	YY_BREAK
-#line 1506 "lexer.cc"
+#line 1551 "lexer.cc"
 
 	case YY_END_OF_BUFFER:
 		{
@@ -1744,9 +1789,9 @@ static int yy_get_next_buffer (void)
 	else
 		ret_val = EOB_ACT_CONTINUE_SCAN;
 
-	if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+	if ((int) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
 		/* Extend the array by 50%, plus the number we really need. */
-		yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+		int new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
 		YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size  );
 		if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
 			YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
@@ -1787,7 +1832,7 @@ static int yy_get_next_buffer (void)
 		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 			{
 			yy_current_state = (int) yy_def[yy_current_state];
-			if ( yy_current_state >= 173 )
+			if ( yy_current_state >= 198 )
 				yy_c = yy_meta[(unsigned int) yy_c];
 			}
 		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
@@ -1815,11 +1860,11 @@ static int yy_get_next_buffer (void)
 	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 		{
 		yy_current_state = (int) yy_def[yy_current_state];
-		if ( yy_current_state >= 173 )
+		if ( yy_current_state >= 198 )
 			yy_c = yy_meta[(unsigned int) yy_c];
 		}
 	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
-	yy_is_jam = (yy_current_state == 172);
+	yy_is_jam = (yy_current_state == 197);
 	if ( ! yy_is_jam )
 		*(yy_state_ptr)++ = yy_current_state;
 
@@ -2212,7 +2257,7 @@ static void yyensure_buffer_stack (void)
 		 * scanner will even need a stack. We use 2 instead of 1 to avoid an
 		 * immediate realloc on the next call.
          */
-		num_to_alloc = 1; // After all that talk, this was set to 1 anyways...
+		num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */
 		(yy_buffer_stack) = (struct yy_buffer_state**)yyalloc
 								(num_to_alloc * sizeof(struct yy_buffer_state*)
 								);
@@ -2585,7 +2630,7 @@ void yyfree (void * ptr )
 
 /* %ok-for-header */
 
-#line 183 "lexer.ll"
+#line 187 "lexer.ll"
 
 
 

+ 42 - 38
src/lib/eval/lexer.ll

@@ -140,44 +140,48 @@ addr6 [0-9a-fA-F]*\:[0-9a-fA-F]*\:[0-9a-fA-F:.]*
     return isc::eval::EvalParser::make_IP_ADDRESS(yytext, loc);
 }
 
-"=="        return isc::eval::EvalParser::make_EQUAL(loc);
-"option"    return isc::eval::EvalParser::make_OPTION(loc);
-"relay4"    return isc::eval::EvalParser::make_RELAY4(loc);
-"relay6"    return isc::eval::EvalParser::make_RELAY6(loc);
-"peeraddr"  return isc::eval::EvalParser::make_PEERADDR(loc);
-"linkaddr"  return isc::eval::EvalParser::make_LINKADDR(loc);
-"text"      return isc::eval::EvalParser::make_TEXT(loc);
-"hex"       return isc::eval::EvalParser::make_HEX(loc);
-"exists"    return isc::eval::EvalParser::make_EXISTS(loc);
-"pkt"       return isc::eval::EvalParser::make_PKT(loc);
-"iface"     return isc::eval::EvalParser::make_IFACE(loc);
-"src"       return isc::eval::EvalParser::make_SRC(loc);
-"dst"       return isc::eval::EvalParser::make_DST(loc);
-"len"       return isc::eval::EvalParser::make_LEN(loc);
-"pkt4"      return isc::eval::EvalParser::make_PKT4(loc);
-"mac"       return isc::eval::EvalParser::make_CHADDR(loc);
-"hlen"      return isc::eval::EvalParser::make_HLEN(loc);
-"htype"     return isc::eval::EvalParser::make_HTYPE(loc);
-"ciaddr"    return isc::eval::EvalParser::make_CIADDR(loc);
-"giaddr"    return isc::eval::EvalParser::make_GIADDR(loc);
-"yiaddr"    return isc::eval::EvalParser::make_YIADDR(loc);
-"siaddr"    return isc::eval::EvalParser::make_SIADDR(loc);
-"pkt6"      return isc::eval::EvalParser::make_PKT6(loc);
-"msgtype"   return isc::eval::EvalParser::make_MSGTYPE(loc);
-"transid"   return isc::eval::EvalParser::make_TRANSID(loc);
-"substring" return isc::eval::EvalParser::make_SUBSTRING(loc);
-"all"       return isc::eval::EvalParser::make_ALL(loc);
-"concat"    return isc::eval::EvalParser::make_CONCAT(loc);
-"not"       return isc::eval::EvalParser::make_NOT(loc);
-"and"       return isc::eval::EvalParser::make_AND(loc);
-"or"        return isc::eval::EvalParser::make_OR(loc);
-"."         return isc::eval::EvalParser::make_DOT(loc);
-"("         return isc::eval::EvalParser::make_LPAREN(loc);
-")"         return isc::eval::EvalParser::make_RPAREN(loc);
-"["         return isc::eval::EvalParser::make_LBRACKET(loc);
-"]"         return isc::eval::EvalParser::make_RBRACKET(loc);
-","         return isc::eval::EvalParser::make_COMA(loc);
-
+"=="           return isc::eval::EvalParser::make_EQUAL(loc);
+"option"       return isc::eval::EvalParser::make_OPTION(loc);
+"relay4"       return isc::eval::EvalParser::make_RELAY4(loc);
+"relay6"       return isc::eval::EvalParser::make_RELAY6(loc);
+"peeraddr"     return isc::eval::EvalParser::make_PEERADDR(loc);
+"linkaddr"     return isc::eval::EvalParser::make_LINKADDR(loc);
+"text"         return isc::eval::EvalParser::make_TEXT(loc);
+"hex"          return isc::eval::EvalParser::make_HEX(loc);
+"exists"       return isc::eval::EvalParser::make_EXISTS(loc);
+"pkt"          return isc::eval::EvalParser::make_PKT(loc);
+"iface"        return isc::eval::EvalParser::make_IFACE(loc);
+"src"          return isc::eval::EvalParser::make_SRC(loc);
+"dst"          return isc::eval::EvalParser::make_DST(loc);
+"len"          return isc::eval::EvalParser::make_LEN(loc);
+"pkt4"         return isc::eval::EvalParser::make_PKT4(loc);
+"mac"          return isc::eval::EvalParser::make_CHADDR(loc);
+"hlen"         return isc::eval::EvalParser::make_HLEN(loc);
+"htype"        return isc::eval::EvalParser::make_HTYPE(loc);
+"ciaddr"       return isc::eval::EvalParser::make_CIADDR(loc);
+"giaddr"       return isc::eval::EvalParser::make_GIADDR(loc);
+"yiaddr"       return isc::eval::EvalParser::make_YIADDR(loc);
+"siaddr"       return isc::eval::EvalParser::make_SIADDR(loc);
+"pkt6"         return isc::eval::EvalParser::make_PKT6(loc);
+"msgtype"      return isc::eval::EvalParser::make_MSGTYPE(loc);
+"transid"      return isc::eval::EvalParser::make_TRANSID(loc);
+"vendor"       return isc::eval::EvalParser::make_VENDOR(loc);
+"vendor-class" return isc::eval::EvalParser::make_VENDOR_CLASS(loc);
+"data"         return isc::eval::EvalParser::make_DATA(loc);
+"enterprise"   return isc::eval::EvalParser::make_ENTERPRISE(loc);
+"substring"    return isc::eval::EvalParser::make_SUBSTRING(loc);
+"all"          return isc::eval::EvalParser::make_ALL(loc);
+"concat"       return isc::eval::EvalParser::make_CONCAT(loc);
+"not"          return isc::eval::EvalParser::make_NOT(loc);
+"and"          return isc::eval::EvalParser::make_AND(loc);
+"or"           return isc::eval::EvalParser::make_OR(loc);
+"."            return isc::eval::EvalParser::make_DOT(loc);
+"("            return isc::eval::EvalParser::make_LPAREN(loc);
+")"            return isc::eval::EvalParser::make_RPAREN(loc);
+"["            return isc::eval::EvalParser::make_LBRACKET(loc);
+"]"            return isc::eval::EvalParser::make_RBRACKET(loc);
+","            return isc::eval::EvalParser::make_COMA(loc);
+"*"            return isc::eval::EvalParser::make_ANY(loc);
 .          driver.error (loc, "Invalid character: " + std::string(yytext));
 <<EOF>>    return isc::eval::EvalParser::make_END(loc);
 %%

+ 0 - 1
src/lib/eval/location.hh

@@ -1,4 +1,3 @@
-// Generated 201608161508
 // A Bison parser, made by GNU Bison 3.0.4.
 
 // Locations for Bison parsers in C++

File diff suppressed because it is too large
+ 458 - 283
src/lib/eval/parser.cc


+ 168 - 84
src/lib/eval/parser.h

@@ -320,8 +320,11 @@ namespace isc { namespace eval {
       // option_code
       char dummy7[sizeof(uint16_t)];
 
+      // enterprise_id
+      char dummy8[sizeof(uint32_t)];
+
       // nest_level
-      char dummy8[sizeof(uint8_t)];
+      char dummy9[sizeof(uint8_t)];
 };
 
     /// Symbol semantic values.
@@ -375,18 +378,23 @@ namespace isc { namespace eval {
         TOKEN_GIADDR = 285,
         TOKEN_YIADDR = 286,
         TOKEN_SIADDR = 287,
-        TOKEN_PKT6 = 288,
-        TOKEN_MSGTYPE = 289,
-        TOKEN_TRANSID = 290,
-        TOKEN_SUBSTRING = 291,
-        TOKEN_ALL = 292,
-        TOKEN_COMA = 293,
-        TOKEN_CONCAT = 294,
-        TOKEN_STRING = 295,
-        TOKEN_INTEGER = 296,
-        TOKEN_HEXSTRING = 297,
-        TOKEN_OPTION_NAME = 298,
-        TOKEN_IP_ADDRESS = 299
+        TOKEN_SUBSTRING = 288,
+        TOKEN_ALL = 289,
+        TOKEN_COMA = 290,
+        TOKEN_CONCAT = 291,
+        TOKEN_PKT6 = 292,
+        TOKEN_MSGTYPE = 293,
+        TOKEN_TRANSID = 294,
+        TOKEN_VENDOR_CLASS = 295,
+        TOKEN_VENDOR = 296,
+        TOKEN_ANY = 297,
+        TOKEN_DATA = 298,
+        TOKEN_ENTERPRISE = 299,
+        TOKEN_STRING = 300,
+        TOKEN_INTEGER = 301,
+        TOKEN_HEXSTRING = 302,
+        TOKEN_OPTION_NAME = 303,
+        TOKEN_IP_ADDRESS = 304
       };
     };
 
@@ -438,6 +446,8 @@ namespace isc { namespace eval {
 
   basic_symbol (typename Base::kind_type t, const uint16_t v, const location_type& l);
 
+  basic_symbol (typename Base::kind_type t, const uint32_t v, const location_type& l);
+
   basic_symbol (typename Base::kind_type t, const uint8_t v, const location_type& l);
 
 
@@ -633,6 +643,22 @@ namespace isc { namespace eval {
 
     static inline
     symbol_type
+    make_SUBSTRING (const location_type& l);
+
+    static inline
+    symbol_type
+    make_ALL (const location_type& l);
+
+    static inline
+    symbol_type
+    make_COMA (const location_type& l);
+
+    static inline
+    symbol_type
+    make_CONCAT (const location_type& l);
+
+    static inline
+    symbol_type
     make_PKT6 (const location_type& l);
 
     static inline
@@ -645,19 +671,23 @@ namespace isc { namespace eval {
 
     static inline
     symbol_type
-    make_SUBSTRING (const location_type& l);
+    make_VENDOR_CLASS (const location_type& l);
 
     static inline
     symbol_type
-    make_ALL (const location_type& l);
+    make_VENDOR (const location_type& l);
 
     static inline
     symbol_type
-    make_COMA (const location_type& l);
+    make_ANY (const location_type& l);
 
     static inline
     symbol_type
-    make_CONCAT (const location_type& l);
+    make_DATA (const location_type& l);
+
+    static inline
+    symbol_type
+    make_ENTERPRISE (const location_type& l);
 
     static inline
     symbol_type
@@ -746,7 +776,7 @@ namespace isc { namespace eval {
     // Tables.
   // YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
   // STATE-NUM.
-  static const signed char yypact_[];
+  static const short int yypact_[];
 
   // YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
   // Performed when YYTABLE does not specify something else to do.  Zero
@@ -757,14 +787,14 @@ namespace isc { namespace eval {
   static const signed char yypgoto_[];
 
   // YYDEFGOTO[NTERM-NUM].
-  static const signed char yydefgoto_[];
+  static const short int yydefgoto_[];
 
   // YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM.  If
   // positive, shift that token.  If negative, reduce the rule whose
   // number is the opposite.  If YYTABLE_NINF, syntax error.
   static const unsigned char yytable_[];
 
-  static const signed char yycheck_[];
+  static const short int yycheck_[];
 
   // YYSTOS[STATE-NUM] -- The (internal number of the) accessing
   // symbol of state STATE-NUM.
@@ -884,12 +914,12 @@ namespace isc { namespace eval {
     enum
     {
       yyeof_ = 0,
-      yylast_ = 121,     ///< Last index in yytable_.
-      yynnts_ = 13,  ///< Number of nonterminal symbols.
-      yyfinal_ = 27, ///< Termination state number.
+      yylast_ = 171,     ///< Last index in yytable_.
+      yynnts_ = 14,  ///< Number of nonterminal symbols.
+      yyfinal_ = 33, ///< Termination state number.
       yyterror_ = 1,
       yyerrcode_ = 256,
-      yyntokens_ = 45  ///< Number of tokens.
+      yyntokens_ = 50  ///< Number of tokens.
     };
 
 
@@ -935,9 +965,10 @@ namespace isc { namespace eval {
        5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
       15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
       25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
-      35,    36,    37,    38,    39,    40,    41,    42,    43,    44
+      35,    36,    37,    38,    39,    40,    41,    42,    43,    44,
+      45,    46,    47,    48,    49
     };
-    const unsigned int user_token_number_max_ = 299;
+    const unsigned int user_token_number_max_ = 304;
     const token_number_type undef_token_ = 2;
 
     if (static_cast<int>(t) <= yyeof_)
@@ -970,39 +1001,43 @@ namespace isc { namespace eval {
   {
       switch (other.type_get ())
     {
-      case 50: // option_repr_type
+      case 55: // option_repr_type
         value.copy< TokenOption::RepresentationType > (other.value);
         break;
 
-      case 53: // pkt4_field
+      case 59: // pkt4_field
         value.copy< TokenPkt4::FieldType > (other.value);
         break;
 
-      case 54: // pkt6_field
+      case 60: // pkt6_field
         value.copy< TokenPkt6::FieldType > (other.value);
         break;
 
-      case 52: // pkt_metadata
+      case 57: // pkt_metadata
         value.copy< TokenPkt::MetadataType > (other.value);
         break;
 
-      case 55: // relay6_field
+      case 61: // relay6_field
         value.copy< TokenRelay6Field::FieldType > (other.value);
         break;
 
-      case 40: // "constant string"
-      case 41: // "integer"
-      case 42: // "constant hexstring"
-      case 43: // "option name"
-      case 44: // "ip address"
+      case 45: // "constant string"
+      case 46: // "integer"
+      case 47: // "constant hexstring"
+      case 48: // "option name"
+      case 49: // "ip address"
         value.copy< std::string > (other.value);
         break;
 
-      case 49: // option_code
+      case 54: // option_code
         value.copy< uint16_t > (other.value);
         break;
 
-      case 51: // nest_level
+      case 58: // enterprise_id
+        value.copy< uint32_t > (other.value);
+        break;
+
+      case 56: // nest_level
         value.copy< uint8_t > (other.value);
         break;
 
@@ -1023,39 +1058,43 @@ namespace isc { namespace eval {
     (void) v;
       switch (this->type_get ())
     {
-      case 50: // option_repr_type
+      case 55: // option_repr_type
         value.copy< TokenOption::RepresentationType > (v);
         break;
 
-      case 53: // pkt4_field
+      case 59: // pkt4_field
         value.copy< TokenPkt4::FieldType > (v);
         break;
 
-      case 54: // pkt6_field
+      case 60: // pkt6_field
         value.copy< TokenPkt6::FieldType > (v);
         break;
 
-      case 52: // pkt_metadata
+      case 57: // pkt_metadata
         value.copy< TokenPkt::MetadataType > (v);
         break;
 
-      case 55: // relay6_field
+      case 61: // relay6_field
         value.copy< TokenRelay6Field::FieldType > (v);
         break;
 
-      case 40: // "constant string"
-      case 41: // "integer"
-      case 42: // "constant hexstring"
-      case 43: // "option name"
-      case 44: // "ip address"
+      case 45: // "constant string"
+      case 46: // "integer"
+      case 47: // "constant hexstring"
+      case 48: // "option name"
+      case 49: // "ip address"
         value.copy< std::string > (v);
         break;
 
-      case 49: // option_code
+      case 54: // option_code
         value.copy< uint16_t > (v);
         break;
 
-      case 51: // nest_level
+      case 58: // enterprise_id
+        value.copy< uint32_t > (v);
+        break;
+
+      case 56: // nest_level
         value.copy< uint8_t > (v);
         break;
 
@@ -1124,6 +1163,13 @@ namespace isc { namespace eval {
   {}
 
   template <typename Base>
+  EvalParser::basic_symbol<Base>::basic_symbol (typename Base::kind_type t, const uint32_t v, const location_type& l)
+    : Base (t)
+    , value (v)
+    , location (l)
+  {}
+
+  template <typename Base>
   EvalParser::basic_symbol<Base>::basic_symbol (typename Base::kind_type t, const uint8_t v, const location_type& l)
     : Base (t)
     , value (v)
@@ -1156,39 +1202,43 @@ namespace isc { namespace eval {
     // Type destructor.
     switch (yytype)
     {
-      case 50: // option_repr_type
+      case 55: // option_repr_type
         value.template destroy< TokenOption::RepresentationType > ();
         break;
 
-      case 53: // pkt4_field
+      case 59: // pkt4_field
         value.template destroy< TokenPkt4::FieldType > ();
         break;
 
-      case 54: // pkt6_field
+      case 60: // pkt6_field
         value.template destroy< TokenPkt6::FieldType > ();
         break;
 
-      case 52: // pkt_metadata
+      case 57: // pkt_metadata
         value.template destroy< TokenPkt::MetadataType > ();
         break;
 
-      case 55: // relay6_field
+      case 61: // relay6_field
         value.template destroy< TokenRelay6Field::FieldType > ();
         break;
 
-      case 40: // "constant string"
-      case 41: // "integer"
-      case 42: // "constant hexstring"
-      case 43: // "option name"
-      case 44: // "ip address"
+      case 45: // "constant string"
+      case 46: // "integer"
+      case 47: // "constant hexstring"
+      case 48: // "option name"
+      case 49: // "ip address"
         value.template destroy< std::string > ();
         break;
 
-      case 49: // option_code
+      case 54: // option_code
         value.template destroy< uint16_t > ();
         break;
 
-      case 51: // nest_level
+      case 58: // enterprise_id
+        value.template destroy< uint32_t > ();
+        break;
+
+      case 56: // nest_level
         value.template destroy< uint8_t > ();
         break;
 
@@ -1215,39 +1265,43 @@ namespace isc { namespace eval {
     super_type::move(s);
       switch (this->type_get ())
     {
-      case 50: // option_repr_type
+      case 55: // option_repr_type
         value.move< TokenOption::RepresentationType > (s.value);
         break;
 
-      case 53: // pkt4_field
+      case 59: // pkt4_field
         value.move< TokenPkt4::FieldType > (s.value);
         break;
 
-      case 54: // pkt6_field
+      case 60: // pkt6_field
         value.move< TokenPkt6::FieldType > (s.value);
         break;
 
-      case 52: // pkt_metadata
+      case 57: // pkt_metadata
         value.move< TokenPkt::MetadataType > (s.value);
         break;
 
-      case 55: // relay6_field
+      case 61: // relay6_field
         value.move< TokenRelay6Field::FieldType > (s.value);
         break;
 
-      case 40: // "constant string"
-      case 41: // "integer"
-      case 42: // "constant hexstring"
-      case 43: // "option name"
-      case 44: // "ip address"
+      case 45: // "constant string"
+      case 46: // "integer"
+      case 47: // "constant hexstring"
+      case 48: // "option name"
+      case 49: // "ip address"
         value.move< std::string > (s.value);
         break;
 
-      case 49: // option_code
+      case 54: // option_code
         value.move< uint16_t > (s.value);
         break;
 
-      case 51: // nest_level
+      case 58: // enterprise_id
+        value.move< uint32_t > (s.value);
+        break;
+
+      case 56: // nest_level
         value.move< uint8_t > (s.value);
         break;
 
@@ -1310,7 +1364,7 @@ namespace isc { namespace eval {
      265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
      275,   276,   277,   278,   279,   280,   281,   282,   283,   284,
      285,   286,   287,   288,   289,   290,   291,   292,   293,   294,
-     295,   296,   297,   298,   299
+     295,   296,   297,   298,   299,   300,   301,   302,   303,   304
     };
     return static_cast<token_type> (yytoken_number_[type]);
   }
@@ -1502,6 +1556,30 @@ namespace isc { namespace eval {
   }
 
   EvalParser::symbol_type
+  EvalParser::make_SUBSTRING (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_SUBSTRING, l);
+  }
+
+  EvalParser::symbol_type
+  EvalParser::make_ALL (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_ALL, l);
+  }
+
+  EvalParser::symbol_type
+  EvalParser::make_COMA (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_COMA, l);
+  }
+
+  EvalParser::symbol_type
+  EvalParser::make_CONCAT (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_CONCAT, l);
+  }
+
+  EvalParser::symbol_type
   EvalParser::make_PKT6 (const location_type& l)
   {
     return symbol_type (token::TOKEN_PKT6, l);
@@ -1520,27 +1598,33 @@ namespace isc { namespace eval {
   }
 
   EvalParser::symbol_type
-  EvalParser::make_SUBSTRING (const location_type& l)
+  EvalParser::make_VENDOR_CLASS (const location_type& l)
   {
-    return symbol_type (token::TOKEN_SUBSTRING, l);
+    return symbol_type (token::TOKEN_VENDOR_CLASS, l);
   }
 
   EvalParser::symbol_type
-  EvalParser::make_ALL (const location_type& l)
+  EvalParser::make_VENDOR (const location_type& l)
   {
-    return symbol_type (token::TOKEN_ALL, l);
+    return symbol_type (token::TOKEN_VENDOR, l);
   }
 
   EvalParser::symbol_type
-  EvalParser::make_COMA (const location_type& l)
+  EvalParser::make_ANY (const location_type& l)
   {
-    return symbol_type (token::TOKEN_COMA, l);
+    return symbol_type (token::TOKEN_ANY, l);
   }
 
   EvalParser::symbol_type
-  EvalParser::make_CONCAT (const location_type& l)
+  EvalParser::make_DATA (const location_type& l)
   {
-    return symbol_type (token::TOKEN_CONCAT, l);
+    return symbol_type (token::TOKEN_DATA, l);
+  }
+
+  EvalParser::symbol_type
+  EvalParser::make_ENTERPRISE (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_ENTERPRISE, l);
   }
 
   EvalParser::symbol_type
@@ -1576,7 +1660,7 @@ namespace isc { namespace eval {
 
 #line 13 "parser.yy" // lalr1.cc:377
 } } // isc::eval
-#line 1580 "parser.h" // lalr1.cc:377
+#line 1664 "parser.h" // lalr1.cc:377
 
 
 

+ 108 - 10
src/lib/eval/parser.yy

@@ -67,13 +67,18 @@ using namespace isc::eval;
   GIADDR "giaddr"
   YIADDR "yiaddr"
   SIADDR "siaddr"
-  PKT6 "pkt6"
-  MSGTYPE "msgtype"
-  TRANSID "transid"
   SUBSTRING "substring"
   ALL "all"
   COMA ","
   CONCAT "concat"
+  PKT6 "pkt6"
+  MSGTYPE "msgtype"
+  TRANSID "transid"
+  VENDOR_CLASS "vendor-class"
+  VENDOR "vendor"
+  ANY "*"
+  DATA "data"
+  ENTERPRISE "enterprise"
 ;
 
 %token <std::string> STRING "constant string"
@@ -83,6 +88,7 @@ using namespace isc::eval;
 %token <std::string> IP_ADDRESS "ip address"
 
 %type <uint16_t> option_code
+%type <uint32_t> enterprise_id
 %type <TokenOption::RepresentationType> option_repr_type
 %type <TokenRelay6Field::FieldType> relay6_field
 %type <uint8_t> nest_level
@@ -166,6 +172,34 @@ bool_expr : "(" bool_expr ")"
                         error(@1, "relay6 can only be used in DHCPv6.");
                     }
                 }
+          | VENDOR_CLASS "[" enterprise_id "]" "." EXISTS
+              {
+                  // Expression: vendor-class[1234].exists
+                  //
+                  // This token will find option 124 (DHCPv4) or 16 (DHCPv6),
+                  // and will check if enterprise-id equals specified value.
+                  TokenPtr exist(new TokenVendorClass(ctx.getUniverse(), $3, TokenOption::EXISTS));
+                  ctx.expression.push_back(exist);
+              }
+          | VENDOR "[" enterprise_id "]" "." EXISTS
+              {
+                  // Expression: vendor[1234].exists
+                  //
+                  // This token will find option 125 (DHCPv4) or 17 (DHCPv6),
+                  // and will check if enterprise-id equals specified value.
+                  TokenPtr exist(new TokenVendor(ctx.getUniverse(), $3, TokenOption::EXISTS));
+                  ctx.expression.push_back(exist);
+              }
+          | VENDOR "[" enterprise_id "]" "." OPTION "[" option_code "]" "." EXISTS
+              {
+                  // Expression vendor[1234].option[123].exists
+                  //
+                  // This token will check if specified vendor option
+                  // exists, has specified enterprise-id and if has
+                  // specified suboption.
+                  TokenPtr exist(new TokenVendor(ctx.getUniverse(), $3, TokenOption::EXISTS, $8));
+                  ctx.expression.push_back(exist);
+               }
           ;
 
 string_expr : STRING
@@ -282,6 +316,60 @@ string_expr : STRING
                       TokenPtr conc(new TokenConcat());
                       ctx.expression.push_back(conc);
                   }
+            | VENDOR "." ENTERPRISE
+                {
+                    // expression: vendor.enterprise
+                    //
+                    // This token will return enterprise-id number of
+                    // received vendor option.
+                    TokenPtr vendor(new TokenVendor(ctx.getUniverse(), 0, TokenVendor::ENTERPRISE_ID));
+                    ctx.expression.push_back(vendor);
+                }
+            | VENDOR_CLASS "." ENTERPRISE
+                {
+                    // expression: vendor-class.enterprise
+                    //
+                    // This token will return enterprise-id number of
+                    // received vendor class option.
+                    TokenPtr vendor(new TokenVendorClass(ctx.getUniverse(), 0,
+                                                         TokenVendor::ENTERPRISE_ID));
+                    ctx.expression.push_back(vendor);
+                }
+            | VENDOR "[" enterprise_id "]" "." OPTION "[" option_code "]" "." option_repr_type
+                {
+                    // This token will search for vendor option with
+                    // specified enterprise-id.  If found, will search
+                    // for specified suboption and finally will return
+                    // its content.
+                    TokenPtr opt(new TokenVendor(ctx.getUniverse(), $3, $11, $8));
+                    ctx.expression.push_back(opt);
+                }
+            | VENDOR_CLASS "[" enterprise_id "]" "." DATA
+                {
+                    // expression: vendor-class[1234].data
+                    //
+                    // Vendor class option does not have suboptions,
+                    // but chunks of data (typically 1, but the option
+                    // structure allows multiple of them). If chunk
+                    // offset is not specified, we assume the first (0th)
+                    // is requested.
+                    TokenPtr vendor_class(new TokenVendorClass(ctx.getUniverse(), $3,
+                                                               TokenVendor::DATA, 0));
+                    ctx.expression.push_back(vendor_class);
+                }
+            | VENDOR_CLASS "[" enterprise_id "]" "." DATA "[" INTEGER "]"
+                {
+                    // expression: vendor-class[1234].data[5]
+                    //
+                    // Vendor class option does not have suboptions,
+                    // but chunks of data (typically 1, but the option
+                    // structure allows multiple of them). This syntax
+                    // specifies which data chunk (tuple) we want.
+                    uint8_t index = ctx.convertUint8($8, @8);
+                    TokenPtr vendor_class(new TokenVendorClass(ctx.getUniverse(), $3,
+                                                               TokenVendor::DATA, index));
+                    ctx.expression.push_back(vendor_class);
+                }
             ;
 
 option_code : INTEGER
@@ -331,6 +419,16 @@ pkt_metadata : IFACE
                   }
              ;
 
+enterprise_id : INTEGER
+                   {
+                       $$ = ctx.convertUint32($1, @1);
+                   }
+              | "*"
+                   {
+                       $$ = 0;
+                   }
+              ;
+
 pkt4_field : CHADDR
                 {
                     $$ = TokenPkt4::CHADDR;
@@ -382,17 +480,17 @@ relay6_field : PEERADDR
              ;
 
 start_expr : INTEGER
+                {
+                    TokenPtr str(new TokenString($1));
+                    ctx.expression.push_back(str);
+                }
+           ;
+
+length_expr : INTEGER
                  {
                      TokenPtr str(new TokenString($1));
                      ctx.expression.push_back(str);
                  }
-           ;
-
-length_expr : INTEGER
-                  {
-                      TokenPtr str(new TokenString($1));
-                      ctx.expression.push_back(str);
-                  }
             | ALL
                  {
                      TokenPtr str(new TokenString("all"));

+ 0 - 1
src/lib/eval/position.hh

@@ -1,4 +1,3 @@
-// Generated 201608161508
 // A Bison parser, made by GNU Bison 3.0.4.
 
 // Positions for Bison parsers in C++

+ 0 - 1
src/lib/eval/stack.hh

@@ -1,4 +1,3 @@
-// Generated 201608161508
 // A Bison parser, made by GNU Bison 3.0.4.
 
 // Stack handling for Bison parsers in C++

+ 274 - 3
src/lib/eval/tests/context_unittest.cc

@@ -417,8 +417,189 @@ public:
         universe_ = universe;
     }
 
-    Option::Universe universe_;
+    /// @brief Checks if the given token is TokenVendor and has expected characteristics
+    /// @param token token to be checked
+    /// @param exp_vendor_id expected vendor-id (aka enterprise number)
+    /// @param exp_repr expected representation (either 'exists' or 'hex')
+    /// @param exp_option_code expected option code (ignored if 0)
+    void checkTokenVendor(const TokenPtr& token, uint32_t exp_vendor_id,
+                          uint16_t exp_option_code,
+                          TokenOption::RepresentationType exp_repr) {
+        ASSERT_TRUE(token);
+
+        boost::shared_ptr<TokenVendor> vendor =
+            boost::dynamic_pointer_cast<TokenVendor>(token);
+
+        ASSERT_TRUE(vendor);
+
+        EXPECT_EQ(exp_vendor_id, vendor->getVendorId());
+        EXPECT_EQ(exp_repr, vendor->getRepresentation());
+        EXPECT_EQ(exp_option_code, vendor->getCode());
+    }
+
+    /// @brief Tests if specified token vendor expression can be parsed
+    ///
+    /// This test assumes the first token will be token vendor. Any additional
+    /// tokens are ignored. Tests expressions:
+    /// vendor[1234].option[234].hex
+    /// vendor[1234].option[234].exists
+    ///
+    /// @param expr expression to be parsed
+    /// @param u universe (V4 or V6)
+    /// @param vendor_id expected vendor-id (aka enterprise number)
+    /// @param option_code expected option code (ignored if 0)
+    /// @param expected_repr expected representation (either 'exists' or 'hex')
+    void testVendor(std::string expr, Option::Universe u, uint32_t vendor_id,
+                    uint16_t option_code, TokenOption::RepresentationType expected_repr) {
+        EvalContext eval(u);
+
+        EXPECT_NO_THROW(parsed_ = eval.parseString(expr));
+        EXPECT_TRUE(parsed_);
+
+        // We need at least one token, we will evaluate the first one.
+        ASSERT_FALSE(eval.expression.empty());
+
+        checkTokenVendor(eval.expression.at(0), vendor_id, option_code, expected_repr);
+    }
+
+    /// @brief Checks if token is really a TokenVendor, that the vendor_id was
+    /// stored properly and that it has expected representation
+    ///
+    /// This test is able to handle expressions similar to:
+    /// vendor[4491].option[1].hex
+    /// vendor[4491].option[1].exists
+    /// vendor[4491].exists
+    /// vendor[*].exists
+    ///
+    /// @param expr expression to be parsed
+    /// @param u universe (V4 or V6)
+    /// @param vendor_id expected vendor-id (aka enterprise number)
+    /// @param expected_repr expected representation (either 'exists' or 'hex')
+    void testVendor(std::string expr, Option::Universe u, uint32_t vendor_id,
+                    TokenOption::RepresentationType expected_repr) {
+        testVendor(expr, u, vendor_id, 0, expected_repr);
+    }
+
+    /// @brief Tests if the expression parses into token vendor that returns enterprise-id
+    ///
+    /// This test is able to handle expressions similar to:
+    /// vendor.enterprise
+    ///
+    /// @param expr expression to be parsed
+    /// @param u universe (V4 or V6)
+    void testVendorEnterprise(std::string expr, Option::Universe u) {
+        EvalContext eval(u);
+
+        EXPECT_NO_THROW(parsed_ = eval.parseString(expr));
+        EXPECT_TRUE(parsed_);
+
+        ASSERT_FALSE(eval.expression.empty());
+
+        boost::shared_ptr<TokenVendor> vendor =
+            boost::dynamic_pointer_cast<TokenVendor>(eval.expression.at(0));
+
+        ASSERT_TRUE(vendor);
+        EXPECT_EQ(TokenVendor::ENTERPRISE_ID, vendor->getField());
+    }
+
+    /// @brief This test checks if vendor-class token is correct
+    ///
+    /// This test checks if EXISTS representation is set correctly.
+    /// It covers cases like:
+    /// - vendor-class[4491].exists
+    /// - vendor-class[*].exists
+    ///
+    /// @param expr expression to be parsed
+    /// @param u universe (V4 or V6)
+    /// @param vendor_id expected vendor-id (aka enterprise number)
+    void testVendorClass(std::string expr, Option::Universe u, uint32_t vendor_id) {
+        EvalContext eval(u);
+
+        EXPECT_NO_THROW(parsed_ = eval.parseString(expr));
+        EXPECT_TRUE(parsed_);
+
+        ASSERT_EQ(1, eval.expression.size());
+        checkTokenVendorClass(eval.expression.at(0), vendor_id, 0, TokenOption::EXISTS,
+                              TokenVendor::EXISTS);
+    }
+
+    /// @brief Tests if specified token vendor class expression can be parsed
+    ///
+    /// This test assumes the first token will be token vendor-class.
+    /// Any additional tokens are ignored. Tests expressions:
+    /// - vendor-class[4491].exists
+    /// - vendor-class[*].exists
+    /// - vendor-class[4491].data
+    /// - vendor-class[4491].data[3]
+    ///
+    /// @param expr expression to be parsed
+    /// @param u universe (V4 or V6)
+    /// @param vendor_id expected vendor-id (aka enterprise number)
+    /// @param index expected data index
+    void testVendorClass(std::string expr, Option::Universe u, uint32_t vendor_id,
+                         uint16_t index) {
+        EvalContext eval(u);
+
+        EXPECT_NO_THROW(parsed_ = eval.parseString(expr));
+        EXPECT_TRUE(parsed_);
+
+        // Make sure there's at least one token
+        ASSERT_FALSE(eval.expression.empty());
+
+        // The first token should be TokenVendorClass, let's take a closer look.
+        checkTokenVendorClass(eval.expression.at(0), vendor_id, index,
+                              TokenOption::HEXADECIMAL, TokenVendor::DATA);
+
+    }
+
+    /// @brief Tests if the expression parses into vendor class token that
+    ///        returns enterprise-id.
+    ///
+    /// This test is able to handle expressions similar to:
+    /// - vendor-class.enterprise
+    ///
+    /// @param expr expression to be parsed
+    /// @param u universe (V4 or V6)
+    void testVendorClassEnterprise(std::string expr, Option::Universe u) {
+        EvalContext eval(u);
+
+        EXPECT_NO_THROW(parsed_ = eval.parseString(expr));
+        EXPECT_TRUE(parsed_);
+
+        // Make sure there's at least one token
+        ASSERT_FALSE(eval.expression.empty());
+
+        // The first token should be TokenVendorClass, let's take a closer look.
+        checkTokenVendorClass(eval.expression.at(0), 0, 0, TokenOption::HEXADECIMAL,
+                              TokenVendor::ENTERPRISE_ID);
+    }
+
+    /// @brief Checks if the given token is TokenVendorClass and has expected characteristics
+    ///
+    /// @param token token to be checked
+    /// @param vendor_id expected vendor-id (aka enterprise number)
+    /// @param index expected index (used for data field only)
+    /// @param repr expected representation (either 'exists' or 'hex')
+    /// @param field expected field (none, enterprise or data)
+    void checkTokenVendorClass(const TokenPtr& token, uint32_t vendor_id,
+                               uint16_t index, TokenOption::RepresentationType repr,
+                               TokenVendor::FieldType field) {
+        ASSERT_TRUE(token);
+
+        boost::shared_ptr<TokenVendorClass> vendor =
+            boost::dynamic_pointer_cast<TokenVendorClass>(token);
+
+        ASSERT_TRUE(vendor);
+
+        EXPECT_EQ(vendor_id, vendor->getVendorId());
+        EXPECT_EQ(index, vendor->getDataIndex());
+        EXPECT_EQ(repr, vendor->getRepresentation());
+        EXPECT_EQ(field, vendor->getField());
+    }
+
+    Option::Universe universe_; ///< Universe (V4 or V6)
     bool parsed_; ///< Parsing status
+
 };
 
 // Test the error method without location
@@ -751,8 +932,7 @@ TEST_F(EvalContextTest, relay6OptionLimits) {
                      
     // next level must be a positive number
     checkError("relay6[-1].option[123].text == 'foo'",
-               "<string>:1.8-9: Nest level has invalid value in -1. "
-               "Allowed range: 0..31");
+               "<string>:1.8-9: Invalid value in -1. Allowed range: 0..255");
 }
 
 // Verify that relay6[13].option is not usable in v4
@@ -1168,4 +1348,95 @@ TEST_F(EvalContextTest, typeErrors) {
                "<string>:1.8-9: syntax error, unexpected or, expecting ==");
 }
 
+
+TEST_F(EvalContextTest, vendor4SpecificVendorExists) {
+    testVendor("vendor[4491].exists", Option::V4, 4491, TokenOption::EXISTS);
+}
+
+TEST_F(EvalContextTest, vendor6SpecificVendorExists) {
+    testVendor("vendor[4491].exists", Option::V6, 4491, TokenOption::EXISTS);
+}
+
+TEST_F(EvalContextTest, vendor4AnyVendorExists) {
+    testVendor("vendor[*].exists", Option::V4, 0, TokenOption::EXISTS);
+}
+
+TEST_F(EvalContextTest, vendor6AnyVendorExists) {
+    testVendor("vendor[*].exists", Option::V6, 0, TokenOption::EXISTS);
+}
+
+TEST_F(EvalContextTest, vendor4enterprise) {
+    testVendorEnterprise("vendor.enterprise == 0x1234", Option::V4);
+}
+
+TEST_F(EvalContextTest, vendor6enterprise) {
+    testVendorEnterprise("vendor.enterprise == 0x1234", Option::V6);
+}
+
+TEST_F(EvalContextTest, vendor4SuboptionExists) {
+    testVendor("vendor[4491].option[1].exists", Option::V4, 4491, 1, TokenOption::EXISTS);
+}
+
+TEST_F(EvalContextTest, vendor6SuboptionExists) {
+    testVendor("vendor[4491].option[1].exists", Option::V6, 4491, 1, TokenOption::EXISTS);
+}
+
+TEST_F(EvalContextTest, vendor4SuboptionHex) {
+    testVendor("vendor[4491].option[1].hex == 0x1234", Option::V4, 4491, 1,
+               TokenOption::HEXADECIMAL);
+}
+
+TEST_F(EvalContextTest, vendor6SuboptionHex) {
+    testVendor("vendor[4491].option[1].hex == 0x1234", Option::V6, 4491, 1,
+               TokenOption::HEXADECIMAL);
+}
+
+TEST_F(EvalContextTest, vendorClass4SpecificVendorExists) {
+    testVendorClass("vendor-class[4491].exists", Option::V4, 4491);
+}
+
+TEST_F(EvalContextTest, vendorClass6SpecificVendorExists) {
+    testVendorClass("vendor-class[4491].exists", Option::V6, 4491);
+}
+
+TEST_F(EvalContextTest, vendorClass4AnyVendorExists) {
+    testVendorClass("vendor-class[*].exists", Option::V4, 0);
+}
+
+TEST_F(EvalContextTest, vendorClass6AnyVendorExists) {
+    testVendorClass("vendor-class[*].exists", Option::V6, 0);
+}
+
+TEST_F(EvalContextTest, vendorClass4enterprise) {
+    testVendorClassEnterprise("vendor-class.enterprise == 0x1234", Option::V4);
+}
+
+TEST_F(EvalContextTest, vendorClass6enterprise) {
+    testVendorClassEnterprise("vendor-class.enterprise == 0x1234", Option::V6);
+}
+
+TEST_F(EvalContextTest, vendorClass4SpecificVendorData) {
+    testVendorClass("vendor-class[4491].data == 0x1234", Option::V4, 4491, 0);
+}
+
+TEST_F(EvalContextTest, vendorClass6SpecificVendorData) {
+    testVendorClass("vendor-class[4491].data == 0x1234", Option::V6, 4491, 0);
+}
+
+TEST_F(EvalContextTest, vendorClass4AnyVendorData) {
+    testVendorClass("vendor-class[*].data == 0x1234", Option::V4, 0, 0);
+}
+
+TEST_F(EvalContextTest, vendorClass6AnyVendorData) {
+    testVendorClass("vendor-class[*].data == 0x1234", Option::V6, 0, 0);
+}
+
+TEST_F(EvalContextTest, vendorClass4DataIndex) {
+    testVendorClass("vendor-class[4491].data[3] == 0x1234", Option::V4, 4491, 3);
+}
+
+TEST_F(EvalContextTest, vendorClass6DataIndex) {
+    testVendorClass("vendor-class[4491].data[3] == 0x1234", Option::V6, 4491, 3);
+}
+
 };

+ 905 - 1
src/lib/eval/tests/token_unittest.cc

@@ -12,6 +12,8 @@
 #include <dhcp/dhcp4.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/option_string.h>
+#include <dhcp/option_vendor.h>
+#include <dhcp/option_vendor_class.h>
 #include <log/logger_manager.h>
 #include <log/logger_name.h>
 #include <log/logger_support.h>
@@ -51,6 +53,10 @@ public:
 
         pkt4_->addOption(option_str4_);
         pkt6_->addOption(option_str6_);
+
+        // Change this to true if you need extra information about logging
+        // checks to be printed.
+        logCheckVerbose(false);
     }
 
     /// @brief Inserts RAI option with several suboptions
@@ -185,6 +191,19 @@ public:
         t_.reset();
     }
 
+    /// @brief Aux. function that stores integer values as 4 bytes string.
+    ///
+    /// @param value integer value to be stored
+    /// @return 4 bytes long string with encoded value.
+    string encode(uint32_t value) {
+        string tmp(4,0);
+        tmp[0] = value >> 24;
+        tmp[1] = value >> 16;
+        tmp[2] = value >> 8;
+        tmp[3] = value;
+        return (tmp);
+    }
+
     TokenPtr t_; ///< Just a convenience pointer
 
     ValueStack values_; ///< evaluated values will be stored here
@@ -195,6 +214,8 @@ public:
     OptionPtr option_str4_; ///< A string option for DHCPv4
     OptionPtr option_str6_; ///< A string option for DHCPv6
 
+    OptionVendorPtr vendor_; ///< Vendor option used during tests
+    OptionVendorClassPtr vendor_class_; ///< Vendor class option used during tests
 
     /// @brief Verify that the substring eval works properly
     ///
@@ -236,8 +257,233 @@ public:
         }
     }
 
-    /// @todo: Add more option types here
+    /// @brief Creates vendor-option with specified value and adds it to packet
+    ///
+    /// This method creates specified vendor option, removes any existing
+    /// vendor options and adds the new one to v4 or v6 packet.
+    ///
+    /// @param u universe (V4 or V6)
+    /// @param vendor_id specifies enterprise-id value.
+    void setVendorOption(Option::Universe u, uint32_t vendor_id) {
+        vendor_.reset(new OptionVendor(u, vendor_id));
+        switch (u) {
+        case Option::V4:
+            pkt4_->delOption(DHO_VIVSO_SUBOPTIONS);
+            pkt4_->addOption(vendor_);
+            break;
+        case Option::V6:
+            pkt6_->delOption(D6O_VENDOR_OPTS);
+            pkt6_->addOption(vendor_);
+            break;
+        }
+    }
+
+    /// @brief Creates vendor-class option with specified values and adds it to packet
+    ///
+    /// This method creates specified vendor-class option, removes any existing
+    /// vendor class options and adds the new one to v4 or v6 packet.
+    /// It also creates data tuples with greek alphabet names.
+    ///
+    /// @param u universe (V4 or V6)
+    /// @param vendor_id specifies enterprise-id value.
+    /// @param tuples_size number of data tuples to create.
+    void setVendorClassOption(Option::Universe u, uint32_t vendor_id,
+                              size_t tuples_size = 0) {
+        // Create the option first.
+        vendor_class_.reset(new OptionVendorClass(u, vendor_id));
+
+        // Now let's add specified number of data tuples
+        OpaqueDataTuple::LengthFieldType len = (u == Option::V4?OpaqueDataTuple::LENGTH_1_BYTE:
+                                                OpaqueDataTuple::LENGTH_2_BYTES);
+        const char * content[] = { "alpha", "beta", "delta", "gamma", "epsilon",
+                                 "zeta", "eta", "theta", "iota", "kappa" };
+        ASSERT_TRUE(tuples_size < sizeof(content));
+        for (int i = 0; i < tuples_size; i++) {
+            OpaqueDataTuple tuple(len);
+            tuple.assign(string(content[i]));
+            if (u == Option::V4 && i == 0) {
+                // vendor-clas for v4 has a pecurilar quirk. The first tuple is being
+                // added, even if there's no data at all.
+                vendor_class_->setTuple(0, tuple);
+            } else {
+                vendor_class_->addTuple(tuple);
+            }
+        }
+
+        switch (u) {
+        case Option::V4:
+            pkt4_->delOption(DHO_VIVCO_SUBOPTIONS);
+            pkt4_->addOption(vendor_class_);
+            break;
+        case Option::V6:
+            pkt6_->delOption(D6O_VENDOR_CLASS);
+            pkt6_->addOption(vendor_class_);
+            break;
+        }
+    }
+
+    /// @brief Auxiliary function that evaluates tokens and checks result
+    ///
+    /// Depending on the universe, either pkt4_ or pkt6_ are supposed to have
+    /// all the necessary values and options set. The result is checked
+    /// on the values_ stack.
+    ///
+    /// @param u universe (V4 or V6)
+    /// @param expected_result text representation of the expected outcome
+    void evaluate(Option::Universe u, std::string expected_result) {
+        switch (u) {
+        case Option::V4:
+            EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+            break;
+        case Option::V6:
+            EXPECT_NO_THROW(t_->evaluate(*pkt6_, values_));
+            break;
+        default:
+            ADD_FAILURE() << "Invalid universe specified.";
+        }
+        ASSERT_EQ(1, values_.size());
+        EXPECT_EQ(expected_result, values_.top());
+    }
+
+    /// @brief Tests if vendor token behaves properly.
+    ///
+    /// @param u universe (V4 or V6)
+    /// @param token_vendor_id enterprise-id used in the token
+    /// @param option_vendor_id enterprise-id used in option (0 means don't
+    ///        create the option)
+    /// @param expected_result text representation of the expected outcome
+    void testVendorExists(Option::Universe u, uint32_t token_vendor_id,
+                          uint32_t option_vendor_id, std::string expected_result) {
+        // Let's clear any old values, so we can run multiple cases in each test
+        clearStack();
+
+        // Create the token
+        ASSERT_NO_THROW(t_.reset(new TokenVendor(u, token_vendor_id,
+                                                 TokenOption::EXISTS)));
+
+        // If specified option is non-zero, create it.
+        if (option_vendor_id) {
+            setVendorOption(u, option_vendor_id);
+        }
+
+        evaluate(u, expected_result);
+    }
+
+    /// @brief Tests if vendor token properly returns enterprise-id.
+    ///
+    /// @param u universe (V4 or V6)
+    /// @param option_vendor_id enterprise-id used in option (0 means don't
+    ///        create the option)
+    /// @param expected_result text representation of the expected outcome
+    void testVendorEnterprise(Option::Universe u, uint32_t option_vendor_id,
+                              std::string expected_result) {
+        // Let's clear any old values, so we can run multiple cases in each test
+        clearStack();
+
+        ASSERT_NO_THROW(t_.reset(new TokenVendor(u, 0, TokenVendor::ENTERPRISE_ID)));
+        if (option_vendor_id) {
+            setVendorOption(u, option_vendor_id);
+        }
+
+        evaluate(u, expected_result);
+    }
 
+    /// @brief Tests if vendor class token properly returns enterprise-id.
+    ///
+    /// @param u universe (V4 or V6)
+    /// @param option_vendor_id enterprise-id used in option (0 means don't
+    ///        create the option)
+    /// @param expected_result text representation of the expected outcome
+    void testVendorClassEnterprise(Option::Universe u, uint32_t option_vendor_id,
+                                   std::string expected_result) {
+        // Let's clear any old values, so we can run multiple cases in each test
+        clearStack();
+
+        ASSERT_NO_THROW(t_.reset(new TokenVendorClass(u, 0, TokenVendor::ENTERPRISE_ID)));
+        if (option_vendor_id) {
+            setVendorClassOption(u, option_vendor_id);
+        }
+
+        evaluate(u, expected_result);
+    }
+
+    /// @brief Tests if vendor class token can report existence properly.
+    ///
+    /// @param u universe (V4 or V6)
+    /// @param token_vendor_id enterprise-id used in the token
+    /// @param option_vendor_id enterprise-id used in option (0 means don't
+    ///        create the option)
+    /// @param expected_result text representation of the expected outcome
+    void testVendorClassExists(Option::Universe u, uint32_t token_vendor_id,
+                               uint32_t option_vendor_id, std::string expected_result) {
+        // Let's clear any old values, so we can run multiple cases in each test
+        clearStack();
+
+        ASSERT_NO_THROW(t_.reset(new TokenVendorClass(u, token_vendor_id,
+                                                      TokenOption::EXISTS)));
+
+        if (option_vendor_id) {
+            setVendorClassOption(u, option_vendor_id);
+        }
+
+        evaluate(u, expected_result);
+    }
+
+    /// @brief Tests if vendor token can handle sub-options properly.
+    ///
+    /// @param u universe (V4 or V6)
+    /// @param token_vendor_id enterprise-id used in the token
+    /// @param token_option_code option code in the token
+    /// @param option_vendor_id enterprise-id used in option (0 means don't
+    ///        create the option)
+    /// @param option_code sub-option code (0 means don't create suboption)
+    /// @param repr representation (TokenOption::EXISTS or HEXADECIMAL)
+    /// @param expected_result text representation of the expected outcome
+    void testVendorSuboption(Option::Universe u,
+                             uint32_t token_vendor_id, uint16_t token_option_code,
+                             uint32_t option_vendor_id, uint16_t option_code,
+                             TokenOption::RepresentationType repr, std::string expected) {
+        // Let's clear any old values, so we can run multiple cases in each test
+        clearStack();
+
+        ASSERT_NO_THROW(t_.reset(new TokenVendor(u, token_vendor_id, repr,
+                                                 token_option_code)));
+        if (option_vendor_id) {
+            setVendorOption(u, option_vendor_id);
+            if (option_code) {
+                ASSERT_TRUE(vendor_);
+                OptionPtr subopt(new OptionString(u, option_code, "alpha"));
+                vendor_->addOption(subopt);
+            }
+        }
+
+        evaluate(u, expected);
+    }
+
+    /// @brief Tests if vendor class token can handle data chunks properly.
+    ///
+    /// @param u universe (V4 or V6)
+    /// @param token_vendor_id enterprise-id used in the token
+    /// @param token_index data index used in the token
+    /// @param option_vendor_id enterprise-id used in option (0 means don't
+    ///        create the option)
+    /// @param data_tuples number of data tuples in the option
+    /// @param expected_result text representation of the expected outcome
+    void testVendorClassData(Option::Universe u,
+                             uint32_t token_vendor_id, uint16_t token_index,
+                             uint32_t option_vendor_id, uint16_t data_tuples,
+                             std::string expected) {
+        // Let's clear any old values, so we can run multiple cases in each test
+        clearStack();
+
+        ASSERT_NO_THROW(t_.reset(new TokenVendorClass(u, token_vendor_id,
+                                                      TokenVendor::DATA, token_index)));
+        if (option_vendor_id) {
+            setVendorClassOption(u, option_vendor_id, data_tuples);
+        }
+
+        evaluate(u, expected);
+    }
 };
 
 // This tests the toBool() conversions
@@ -1817,4 +2063,662 @@ TEST_F(TokenTest, operatorOrTrue) {
     EXPECT_TRUE(checkFile());
 }
 
+// This test verifies if expression vendor[4491].exists works properly in DHCPv4.
+TEST_F(TokenTest, vendor4SpecificVendorExists) {
+    // Case 1: no option, should evaluate to false
+    testVendorExists(Option::V4, 4491, 0, "false");
+
+    // Case 2: option present, but uses different enterprise-id, should fail
+    testVendorExists(Option::V4, 4491, 1234, "false");
+
+    // Case 3: option present and has matchin enterprise-id, should succeed
+    testVendorExists(Option::V4, 4491, 4491, "true");
+
+    // Check if the logged messages are correct.
+    addString("EVAL_DEBUG_VENDOR_NO_OPTION Option with code 125 missing, "
+              "pushing result 'false'");
+    addString("EVAL_DEBUG_VENDOR_ENTERPRISE_ID_MISMATCH Was looking for 4491, "
+              "option had 1234, pushing result 'false'");
+    addString("EVAL_DEBUG_VENDOR_EXISTS Option with enterprise-id 4491 "
+              "found, pushing result 'true'");
+    EXPECT_TRUE(checkFile());
+}
+
+// This test verifies if expression vendor[4491].exists works properly in DHCPv6.
+TEST_F(TokenTest, vendor6SpecificVendorExists) {
+    // Case 1: no option, should evaluate to false
+    testVendorExists(Option::V6, 4491, 0, "false");
+
+    // Case 2: option present, but uses different enterprise-id, should fail
+    testVendorExists(Option::V6, 4491, 1234, "false");
+
+    // Case 3: option present and has matchin enterprise-id, should suceed
+    testVendorExists(Option::V6, 4491, 4491, "true");
+
+    // Check if the logged messages are correct.
+    addString("EVAL_DEBUG_VENDOR_NO_OPTION Option with code 17 missing, "
+              "pushing result 'false'");
+    addString("EVAL_DEBUG_VENDOR_ENTERPRISE_ID_MISMATCH Was looking for 4491, "
+              "option had 1234, pushing result 'false'");
+    addString("EVAL_DEBUG_VENDOR_EXISTS Option with enterprise-id 4491 "
+              "found, pushing result 'true'");
+    EXPECT_TRUE(checkFile());
+}
+
+/// Test if expression vendor[*].exists works properly for DHCPv4.
+TEST_F(TokenTest, vendor4AnyVendorExists) {
+    // Case 1: no option, should evaluate to false
+    testVendorExists(Option::V4, 0, 0, "false");
+
+    // Case 2: option present with vendor-id 1234, should succeed
+    testVendorExists(Option::V4, 0, 1234, "true");
+
+    // Case 3: option present with vendor-id 4491, should succeed
+    testVendorExists(Option::V4, 0, 4491, "true");
+
+    // Check if the logged messages are correct.
+    addString("EVAL_DEBUG_VENDOR_NO_OPTION Option with code 125 missing, "
+              "pushing result 'false'");
+    addString("EVAL_DEBUG_VENDOR_EXISTS Option with enterprise-id 1234 "
+              "found, pushing result 'true'");
+    addString("EVAL_DEBUG_VENDOR_EXISTS Option with enterprise-id 4491 "
+              "found, pushing result 'true'");
+    EXPECT_TRUE(checkFile());
+}
+
+// Test if expression vendor[*].exists works properly for DHCPv6.
+TEST_F(TokenTest, vendor6AnyVendorExists) {
+    // Case 1: no option, should evaluate to false
+    testVendorExists(Option::V6, 0, 0, "false");
+
+    // Case 2: option present with vendor-id 1234, should succeed
+    testVendorExists(Option::V6, 0, 1234, "true");
+
+    // Case 3: option present with vendor-id 4491, should succeed
+    testVendorExists(Option::V6, 0, 4491, "true");
+
+    // Check if the logged messages are correct.
+    addString("EVAL_DEBUG_VENDOR_NO_OPTION Option with code 17 missing, "
+              "pushing result 'false'");
+    addString("EVAL_DEBUG_VENDOR_EXISTS Option with enterprise-id 1234 "
+              "found, pushing result 'true'");
+    addString("EVAL_DEBUG_VENDOR_EXISTS Option with enterprise-id 4491 "
+              "found, pushing result 'true'");
+    EXPECT_TRUE(checkFile());
+}
+
+// Test if expression vendor[*].enterprise works properly for DHCPv4.
+TEST_F(TokenTest, vendor4enterprise) {
+    // Case 1: No option present, should return empty string
+    testVendorEnterprise(Option::V4, 0, "");
+
+    // Case 2: Option with vendor-id 1234, should return "1234"
+    testVendorEnterprise(Option::V4, 1234, encode(1234));
+
+    // Case 3: Option with vendor-id set to maximum value, should still
+    // be able to handle it
+    testVendorEnterprise(Option::V4, 4294967295, encode(4294967295));
+
+    // Check if the logged messages are correct.
+    addString("EVAL_DEBUG_VENDOR_NO_OPTION Option with code 125 missing, pushing"
+              " result ''");
+    addString("EVAL_DEBUG_VENDOR_ENTERPRISE_ID Pushing enterprise-id 1234 as "
+              "result 0x000004D2");
+    addString("EVAL_DEBUG_VENDOR_ENTERPRISE_ID Pushing enterprise-id 4294967295"
+              " as result 0xFFFFFFFF");
+    EXPECT_TRUE(checkFile());
+}
+
+// Test if expression vendor[*].enterprise works properly for DHCPv6.
+TEST_F(TokenTest, vendor6enterprise) {
+    // Case 1: No option present, should return empty string
+    testVendorEnterprise(Option::V6, 0, "");
+
+    // Case 2: Option with vendor-id 1234, should return "1234"
+    testVendorEnterprise(Option::V6, 1234, encode(1234));
+
+    // Case 3: Option with vendor-id set to maximum value, should still
+    // be able to handle it
+    testVendorEnterprise(Option::V6, 4294967295, encode(4294967295));
+
+    // Check if the logged messages are correct.
+    addString("EVAL_DEBUG_VENDOR_NO_OPTION Option with code 17 missing, pushing"
+              " result ''");
+    addString("EVAL_DEBUG_VENDOR_ENTERPRISE_ID Pushing enterprise-id 1234 as "
+              "result 0x000004D2");
+    addString("EVAL_DEBUG_VENDOR_ENTERPRISE_ID Pushing enterprise-id 4294967295 "
+              "as result 0xFFFFFFFF");
+    EXPECT_TRUE(checkFile());
+}
+
+// This one tests "vendor[4491].option[1].exists" expression. There are so many
+// wonderful ways in which this could fail: the option could not be there,
+// it could have different enterprise-id, may not have suboption 1. Or may
+// have the suboption with valid type, but enterprise may be different.
+TEST_F(TokenTest, vendor4SuboptionExists) {
+    // Case 1: expression vendor[4491].option[1].exists, no option present
+    testVendorSuboption(Option::V4, 4491, 1, 0, 0, TokenOption::EXISTS, "false");
+
+    // Case 2: expression vendor[4491].option[1].exists, option with vendor-id = 1234,
+    // no suboptions, expected result "false"
+    testVendorSuboption(Option::V4, 4491, 1, 1234, 0, TokenOption::EXISTS, "false");
+
+    // Case 3: expression vendor[4491].option[1].exists, option with vendor-id = 1234,
+    // suboption 1, expected result "false"
+    testVendorSuboption(Option::V4, 4491, 1, 1234, 1, TokenOption::EXISTS, "false");
+
+    // Case 4: expression vendor[4491].option[1].exists, option with vendor-id = 4491,
+    // suboption 2, expected result "false"
+    testVendorSuboption(Option::V4, 4491, 1, 4491, 2, TokenOption::EXISTS, "false");
+
+    // Case 5: expression vendor[4491].option[1].exists, option with vendor-id = 4491,
+    // suboption 1, expected result "true"
+    testVendorSuboption(Option::V4, 4491, 1, 4491, 1, TokenOption::EXISTS, "true");
+
+    // Check if the logged messages are correct.
+    addString("EVAL_DEBUG_VENDOR_NO_OPTION Option with code 125 missing, pushing "
+              "result 'false'");
+    addString("EVAL_DEBUG_VENDOR_ENTERPRISE_ID_MISMATCH Was looking for 4491, "
+              "option had 1234, pushing result 'false'");
+    addString("EVAL_DEBUG_VENDOR_ENTERPRISE_ID_MISMATCH Was looking for 4491, "
+              "option had 1234, pushing result 'false'");
+    addString("EVAL_DEBUG_OPTION Pushing option 1 with value 'false'");
+    addString("EVAL_DEBUG_OPTION Pushing option 1 with value 'true'");
+    EXPECT_TRUE(checkFile());
+}
+
+// This is similar to the previous one, but tests vendor[4491].option[1].exists
+// for DHCPv6.
+TEST_F(TokenTest, vendor6SuboptionExists) {
+    // Case 1: expression vendor[4491].option[1].exists, no option present
+    testVendorSuboption(Option::V6, 4491, 1, 0, 0, TokenOption::EXISTS, "false");
+
+    // Case 2: expression vendor[4491].option[1].exists, option with vendor-id = 1234,
+    // no suboptions, expected result "false"
+    testVendorSuboption(Option::V6, 4491, 1, 1234, 0, TokenOption::EXISTS, "false");
+
+    // Case 3: expression vendor[4491].option[1].exists, option with vendor-id = 1234,
+    // suboption 1, expected result "false"
+    testVendorSuboption(Option::V6, 4491, 1, 1234, 1, TokenOption::EXISTS, "false");
+
+    // Case 4: expression vendor[4491].option[1].exists, option with vendor-id = 4491,
+    // suboption 2, expected result "false"
+    testVendorSuboption(Option::V6, 4491, 1, 4491, 2, TokenOption::EXISTS, "false");
+
+    // Case 5: expression vendor[4491].option[1].exists, option with vendor-id = 4491,
+    // suboption 1, expected result "true"
+    testVendorSuboption(Option::V6, 4491, 1, 4491, 1, TokenOption::EXISTS, "true");
+
+    // Check if the logged messages are correct.
+    addString("EVAL_DEBUG_VENDOR_NO_OPTION Option with code 17 missing, pushing "
+              "result 'false'");
+    addString("EVAL_DEBUG_VENDOR_ENTERPRISE_ID_MISMATCH Was looking for 4491, "
+              "option had 1234, pushing result 'false'");
+    addString("EVAL_DEBUG_VENDOR_ENTERPRISE_ID_MISMATCH Was looking for 4491, "
+              "option had 1234, pushing result 'false'");
+    addString("EVAL_DEBUG_OPTION Pushing option 1 with value 'false'");
+    addString("EVAL_DEBUG_OPTION Pushing option 1 with value 'true'");
+    EXPECT_TRUE(checkFile());
+}
+
+// This test verifies if vendor[4491].option[1].hex expression properly returns
+// value of said sub-option or empty string if desired option is not present.
+// This test is for DHCPv4.
+TEST_F(TokenTest, vendor4SuboptionHex) {
+    // Case 1: no option present, should return empty string
+    testVendorSuboption(Option::V4, 4491, 1, 0, 0, TokenOption::HEXADECIMAL, "");
+
+    // Case 2: option with vendor-id = 1234, no suboptions, expected result ""
+    testVendorSuboption(Option::V4, 4491, 1, 1234, 0, TokenOption::HEXADECIMAL, "");
+
+    // Case 3: option with vendor-id = 1234, suboption 1, expected result ""
+    testVendorSuboption(Option::V4, 4491, 1, 1234, 1, TokenOption::HEXADECIMAL, "");
+
+    // Case 4: option with vendor-id = 4491, suboption 2, expected result ""
+    testVendorSuboption(Option::V4, 4491, 1, 4491, 2, TokenOption::HEXADECIMAL, "");
+
+    // Case 5: option with vendor-id = 4491, suboption 1, expected result content
+    // of the option
+    testVendorSuboption(Option::V4, 4491, 1, 4491, 1, TokenOption::HEXADECIMAL, "alpha");
+
+    // Check if the logged messages are correct.
+    addString("EVAL_DEBUG_VENDOR_NO_OPTION Option with code 125 missing, pushing "
+              "result ''");
+    addString("EVAL_DEBUG_VENDOR_ENTERPRISE_ID_MISMATCH Was looking for 4491, "
+              "option had 1234, pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_ENTERPRISE_ID_MISMATCH Was looking for 4491, "
+              "option had 1234, pushing result ''");
+    addString("EVAL_DEBUG_OPTION Pushing option 1 with value 0x");
+    addString("EVAL_DEBUG_OPTION Pushing option 1 with value 0x616C706861");
+    EXPECT_TRUE(checkFile());
+}
+
+// This test verifies if vendor[4491].option[1].hex expression properly returns
+// value of said sub-option or empty string if desired option is not present.
+// This test is for DHCPv4.
+TEST_F(TokenTest, vendor6SuboptionHex) {
+    // Case 1: no option present, should return empty string
+    testVendorSuboption(Option::V6, 4491, 1, 0, 0, TokenOption::HEXADECIMAL, "");
+
+    // Case 2: option with vendor-id = 1234, no suboptions, expected result ""
+    testVendorSuboption(Option::V6, 4491, 1, 1234, 0, TokenOption::HEXADECIMAL, "");
+
+    // Case 3: option with vendor-id = 1234, suboption 1, expected result ""
+    testVendorSuboption(Option::V6, 4491, 1, 1234, 1, TokenOption::HEXADECIMAL, "");
+
+    // Case 4: option with vendor-id = 4491, suboption 2, expected result ""
+    testVendorSuboption(Option::V6, 4491, 1, 4491, 2, TokenOption::HEXADECIMAL, "");
+
+    // Case 5: option with vendor-id = 4491, suboption 1, expected result content
+    // of the option
+    testVendorSuboption(Option::V6, 4491, 1, 4491, 1, TokenOption::HEXADECIMAL, "alpha");
+
+    // Check if the logged messages are correct.
+    addString("EVAL_DEBUG_VENDOR_NO_OPTION Option with code 17 missing, pushing "
+              "result ''");
+    addString("EVAL_DEBUG_VENDOR_ENTERPRISE_ID_MISMATCH Was looking for 4491, "
+              "option had 1234, pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_ENTERPRISE_ID_MISMATCH Was looking for 4491, "
+              "option had 1234, pushing result ''");
+    addString("EVAL_DEBUG_OPTION Pushing option 1 with value 0x");
+    addString("EVAL_DEBUG_OPTION Pushing option 1 with value 0x616C706861");
+    EXPECT_TRUE(checkFile());
+}
+
+// This test verifies that "vendor-class[4491].exists" expression can be used
+// in DHCPv4.
+TEST_F(TokenTest, vendorClass4SpecificVendorExists) {
+    // Case 1: no option present, should fail
+    testVendorClassExists(Option::V4, 4491, 0, "false");
+
+    // Case 2: option exists, but has different vendor-id (1234), should fail
+    testVendorClassExists(Option::V4, 4491, 1234, "false");
+
+    // Case 3: option exists and has matching vendor-id, should succeed
+    testVendorClassExists(Option::V4, 4491, 4491, "true");
+
+    // Check if the logged messages are correct.
+    addString("EVAL_DEBUG_VENDOR_CLASS_NO_OPTION Option with code 124 missing, "
+              "pushing result 'false'");
+    addString("EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID_MISMATCH Was looking for "
+              "4491, option had 1234, pushing result 'false'");
+    addString("EVAL_DEBUG_VENDOR_CLASS_EXISTS Option with enterprise-id 4491 "
+              "found, pushing result 'true'");
+    EXPECT_TRUE(checkFile());
+}
+
+// This test verifies that "vendor-class[4491].exists" expression can be used
+// in DHCPv6.
+TEST_F(TokenTest, vendorClass6SpecificVendorExists) {
+    // Case 1: no option present, should fail
+    testVendorClassExists(Option::V6, 4491, 0, "false");
+
+    // Case 2: option exists, but has different vendor-id (1234), should fail
+    testVendorClassExists(Option::V6, 4491, 1234, "false");
+
+    // Case 3: option exists and has matching vendor-id, should succeed
+    testVendorClassExists(Option::V6, 4491, 4491, "true");
+
+    // Check if the logged messages are correct.
+    addString("EVAL_DEBUG_VENDOR_CLASS_NO_OPTION Option with code 16 missing, pushing "
+              "result 'false'");
+    addString("EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID_MISMATCH Was looking for "
+              "4491, option had 1234, pushing result 'false'");
+    addString("EVAL_DEBUG_VENDOR_CLASS_EXISTS Option with enterprise-id 4491 "
+              "found, pushing result 'true'");
+    EXPECT_TRUE(checkFile());
+}
+
+// This test verifies that "vendor-class[*].exists" can be used in DHCPv4
+// and it matches a vendor class option with any vendor-id.
+TEST_F(TokenTest, vendorClass4AnyVendorExists) {
+    // Case 1: no option present, should fail
+    testVendorClassExists(Option::V4, 0, 0, "false");
+
+    // Case 2: option exists, should succeed, regardless of the vendor-id
+    testVendorClassExists(Option::V4, 0, 1234, "true");
+
+    // Case 3: option exists, should succeed, regardless of the vendor-id
+    testVendorClassExists(Option::V4, 0, 4491, "true");
+
+    // Check if the logged messages are correct.
+    addString("EVAL_DEBUG_VENDOR_CLASS_NO_OPTION Option with code 124 missing, "
+              "pushing result 'false'");
+    addString("EVAL_DEBUG_VENDOR_CLASS_EXISTS Option with enterprise-id 1234 "
+              "found, pushing result 'true'");
+    addString("EVAL_DEBUG_VENDOR_CLASS_EXISTS Option with enterprise-id 4491 "
+              "found, pushing result 'true'");
+    EXPECT_TRUE(checkFile());
+}
+
+// This test verifies that "vendor-class[*].exists" can be used in DHCPv6
+// and it matches a vendor class option with any vendor-id.
+TEST_F(TokenTest, vendorClass6AnyVendorExists) {
+    // Case 1: no option present, should fail
+    testVendorClassExists(Option::V6, 0, 0, "false");
+
+    // Case 2: option exists, should succeed, regardless of the vendor-id
+    testVendorClassExists(Option::V6, 0, 1234, "true");
+
+    // Case 3: option exists, should succeed, regardless of the vendor-id
+    testVendorClassExists(Option::V6, 0, 4491, "true");
+
+    // Check if the logged messages are correct.
+    addString("EVAL_DEBUG_VENDOR_CLASS_NO_OPTION Option with code 16 missing, pushing "
+              "result 'false'");
+    addString("EVAL_DEBUG_VENDOR_CLASS_EXISTS Option with enterprise-id 1234 "
+              "found, pushing result 'true'");
+    addString("EVAL_DEBUG_VENDOR_CLASS_EXISTS Option with enterprise-id 4491 "
+              "found, pushing result 'true'");
+    EXPECT_TRUE(checkFile());
+}
+
+// Test if expression "vendor-class.enterprise" works properly for DHCPv4.
+TEST_F(TokenTest, vendorClass4enterprise) {
+    // Case 1: No option present, should return empty string
+    testVendorClassEnterprise(Option::V4, 0, "");
+
+    // Case 2: Option with vendor-id 1234, should return "1234"
+    testVendorClassEnterprise(Option::V4, 1234, encode(1234));
+
+    // Case 3: Option with vendor-id set to maximum value, should still
+    // be able to handle it
+    testVendorClassEnterprise(Option::V4, 4294967295, encode(4294967295));
+
+    // Check if the logged messages are correct.
+    addString("EVAL_DEBUG_VENDOR_CLASS_NO_OPTION Option with code 124 missing, pushing "
+              "result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID Pushing enterprise-id "
+              "1234 as result 0x000004D2");
+    addString("EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID Pushing enterprise-id "
+              "4294967295 as result 0xFFFFFFFF");
+    EXPECT_TRUE(checkFile());
+}
+
+// Test if expression "vendor-class.enterprise" works properly for DHCPv6.
+TEST_F(TokenTest, vendorClass6enterprise) {
+    // Case 1: No option present, should return empty string
+    testVendorClassEnterprise(Option::V6, 0, "");
+
+    // Case 2: Option with vendor-id 1234, should return "1234"
+    testVendorClassEnterprise(Option::V6, 1234, encode(1234));
+
+    // Case 3: Option with vendor-id set to maximum value, should still
+    // be able to handle it.
+    testVendorClassEnterprise(Option::V6, 4294967295, encode(4294967295));
+
+    // Check if the logged messages are correct.
+    addString("EVAL_DEBUG_VENDOR_CLASS_NO_OPTION Option with code 16 missing, pushing "
+              "result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID Pushing enterprise-id "
+              "1234 as result 0x000004D2");
+    addString("EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID Pushing enterprise-id "
+              "4294967295 as result 0xFFFFFFFF");
+    EXPECT_TRUE(checkFile());
+}
+
+// Test that expression "vendor-class[4491].data" is able to retrieve content
+// of the first tuple of the vendor-class option in DHCPv4.
+TEST_F(TokenTest, vendorClass4SpecificVendorData) {
+    // Case 1: Expression looks for vendor-id 4491, data[0], there is no
+    // vendor-class option at all, expected result is empty string.
+    testVendorClassData(Option::V4, 4491, 0, 0, 0, "");
+
+    // Case 2: Expression looks for vendor-id 4491, data[0], there is
+    // vendor-class with vendor-id 1234 and no data, expected result is empty string.
+    testVendorClassData(Option::V4, 4491, 0, 1234, 0, "");
+
+    // Case 3: Expression looks for vendor-id 4491, data[0], there is
+    // vendor-class with vendor-id 4491 and no data, expected result is empty string.
+    // Note that vendor option in v4 always have at least one data chunk, even though
+    // it may be empty. The OptionVendor code was told to not create any special
+    // tuples, but it creates one empty on its own. So the code finds that one
+    // tuple and extracts its content (an empty string).
+    testVendorClassData(Option::V4, 4491, 0, 4491, 0, "");
+
+    // Case 4: Expression looks for vendor-id 4491, data[0], there is
+    // vendor-class with vendor-id 1234 and 1 data tuple, expected result is empty string
+    testVendorClassData(Option::V4, 4491, 0, 1234, 1, "");
+
+    // Case 5: Expression looks for vendor-id 4491, data[0], there is
+    // vendor-class with vendor-id 4491 and 1 data tuple, expected result is
+    // content of that data ("alpha")
+    testVendorClassData(Option::V4, 4491, 0, 4491, 1, "alpha");
+
+    // Check if the logged messages are correct.
+    addString("EVAL_DEBUG_VENDOR_CLASS_NO_OPTION Option with code 124 missing, "
+              "pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID_MISMATCH Was looking for "
+              "4491, option had 1234, pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_DATA Data 0 (out of 1 received) in vendor "
+              "class found, pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID_MISMATCH Was looking for "
+              "4491, option had 1234, pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_DATA Data 0 (out of 1 received) in vendor "
+              "class found, pushing result 'alpha'");
+    EXPECT_TRUE(checkFile());
+}
+
+// Test that expression "vendor-class[4491].data" is able to retrieve content
+// of the first tuple of the vendor-class option in DHCPv6.
+TEST_F(TokenTest, vendorClass6SpecificVendorData) {
+    // Case 1: Expression looks for vendor-id 4491, data[0], there is no
+    // vendor-class option at all, expected result is empty string.
+    testVendorClassData(Option::V6, 4491, 0, 0, 0, "");
+
+    // Case 2: Expression looks for vendor-id 4491, data[0], there is
+    // vendor-class with vendor-id 1234 and no data, expected result is empty string.
+    testVendorClassData(Option::V6, 4491, 0, 1234, 0, "");
+
+    // Case 3: Expression looks for vendor-id 4491, data[0], there is
+    // vendor-class with vendor-id 4491 and no data, expected result is empty string
+    testVendorClassData(Option::V6, 4491, 0, 4491, 0, "");
+
+    // Case 4: Expression looks for vendor-id 4491, data[0], there is
+    // vendor-class with vendor-id 1234 and 1 data tuple, expected result is empty string
+    testVendorClassData(Option::V6, 4491, 0, 1234, 1, "");
+
+    // Case 5: Expression looks for vendor-id 4491, data[0], there is
+    // vendor-class with vendor-id 4491 and 1 data tuple, expected result is
+    // content of that data ("alpha")
+    testVendorClassData(Option::V6, 4491, 0, 4491, 1, "alpha");
+
+    // Check if the logged messages are correct.
+    addString("EVAL_DEBUG_VENDOR_CLASS_NO_OPTION Option with code 16 missing, "
+              "pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID_MISMATCH Was looking for "
+              "4491, option had 1234, pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_DATA_NOT_FOUND Requested data index 0, "
+              "but option with enterprise-id 4491 has only 0 data tuple(s), "
+              "pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID_MISMATCH Was looking for "
+              "4491, option had 1234, pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_DATA Data 0 (out of 1 received) in vendor "
+              "class found, pushing result 'alpha'");
+    EXPECT_TRUE(checkFile());
+}
+
+// Test that expression "vendor-class[*].data" is able to retrieve content
+// of the first tuple of the vendor-class option in DHCPv4.
+TEST_F(TokenTest, vendorClass4AnyVendorData) {
+    // Case 1: Expression looks for any vendor-id (0), data[0], there is no
+    // vendor-class option at all, expected result is empty string.
+    testVendorClassData(Option::V4, 0, 0, 0, 0, "");
+
+    // Case 2: Expression looks for any vendor-id (0), data[0], there is
+    // vendor-class with vendor-id 1234 and no data (one empty tuple), expected
+    // result is empty string.
+    testVendorClassData(Option::V4, 0, 0, 1234, 0, "");
+
+    // Case 3: Expression looks for any vendor-id (0), data[0], there is
+    // vendor-class with vendor-id 4491 and no data (one empty tuple), expected
+    // result is empty string.
+    testVendorClassData(Option::V4, 0, 0, 4491, 0, "");
+
+    // Case 4: Expression looks for any vendor-id (0), data[0], there is
+    // vendor-class with vendor-id 1234 and 1 data tuple, expected result is
+    // content of that data ("alpha")
+    testVendorClassData(Option::V4, 0, 0, 1234, 1, "alpha");
+
+    // Case 5: Expression looks for any vendor-id (0), data[0], there is
+    // vendor-class with vendor-id 4491 and 1 data tuple, expected result is
+    // content of that data ("alpha")
+    testVendorClassData(Option::V4, 0, 0, 4491, 1, "alpha");
+
+    // Check if the logged messages are correct.
+    addString("EVAL_DEBUG_VENDOR_CLASS_NO_OPTION Option with code 124 missing, "
+              "pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_DATA Data 0 (out of 1 received) in "
+              "vendor class found, pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_DATA Data 0 (out of 1 received) in "
+              "vendor class found, pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_DATA Data 0 (out of 1 received) in "
+              "vendor class found, pushing result 'alpha'");
+    addString("EVAL_DEBUG_VENDOR_CLASS_DATA Data 0 (out of 1 received) in "
+              "vendor class found, pushing result 'alpha'");
+    EXPECT_TRUE(checkFile());
+}
+
+// Test that expression "vendor-class[*].data" is able to retrieve content
+// of the first tuple of the vendor-class option in DHCPv6.
+TEST_F(TokenTest, vendorClass6AnyVendorData) {
+    // Case 1: Expression looks for any vendor-id (0), data[0], there is no
+    // vendor-class option at all, expected result is empty string.
+    testVendorClassData(Option::V6, 0, 0, 0, 0, "");
+
+    // Case 2: Expression looks for any vendor-id (0), data[0], there is
+    // vendor-class with vendor-id 1234 and no data, expected result is empty string.
+    testVendorClassData(Option::V6, 0, 0, 1234, 0, "");
+
+    // Case 3: Expression looks for any vendor-id (0), data[0], there is
+    // vendor-class with vendor-id 4491 and no data, expected result is empty string
+    testVendorClassData(Option::V6, 0, 0, 4491, 0, "");
+
+    // Case 4: Expression looks for any vendor-id (0), data[0], there is
+    // vendor-class with vendor-id 1234 and 1 data tuple, expected result is
+    // content of that data ("alpha")
+    testVendorClassData(Option::V6, 0, 0, 1234, 1, "alpha");
+
+    // Case 5: Expression looks for any vendor-id (0), data[0], there is
+    // vendor-class with vendor-id 4491 and 1 data tuple, expected result is
+    // content of that data ("alpha")
+    testVendorClassData(Option::V6, 0, 0, 4491, 1, "alpha");
+
+    // Check if the logged messages are correct.
+    addString("EVAL_DEBUG_VENDOR_CLASS_NO_OPTION Option with code 16 missing, "
+              "pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_DATA_NOT_FOUND Requested data index 0, "
+              "but option with enterprise-id 1234 has only 0 data tuple(s), "
+              "pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_DATA_NOT_FOUND Requested data index 0, "
+              "but option with enterprise-id 4491 has only 0 data tuple(s), "
+              "pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_DATA Data 0 (out of 1 received) in vendor "
+              "class found, pushing result 'alpha'");
+    addString("EVAL_DEBUG_VENDOR_CLASS_DATA Data 0 (out of 1 received) in vendor "
+              "class found, pushing result 'alpha'");
+    EXPECT_TRUE(checkFile());
+}
+
+// This test verifies if expression vendor-class[4491].data[3] is able to access
+// the tuple specified by index. This is a DHCPv4 test.
+TEST_F(TokenTest, vendorClass4DataIndex) {
+    // Case 1: Expression looks for vendor-id 4491, data[3], there is no
+    // vendor-class option at all, expected result is empty string.
+    testVendorClassData(Option::V4, 4491, 3, 0, 0, "");
+
+    // Case 2: Expression looks for vendor-id 4491, data[3], there is
+    // vendor-class with vendor-id 1234 and no data, expected result is empty string.
+    testVendorClassData(Option::V4, 4491, 3, 1234, 0, "");
+
+    // Case 3: Expression looks for vendor-id 4491, data[3], there is
+    // vendor-class with vendor-id 4491 and no data, expected result is empty string
+    testVendorClassData(Option::V4, 4491, 3, 4491, 0, "");
+
+    // Case 4: Expression looks for vendor-id 4491, data[3], there is
+    // vendor-class with vendor-id 1234 and 1 data tuple, expected result is empty string.
+    testVendorClassData(Option::V4, 4491, 3, 1234, 1, "");
+
+    // Case 5: Expression looks for vendor-id 4491, data[3], there is
+    // vendor-class with vendor-id 4491, but has only 3 data tuples, expected
+    // result is empty string.
+    testVendorClassData(Option::V4, 4491, 3, 4491, 3, "");
+
+    // Case 6: Expression looks for vendor-id 4491, data[3], there is
+    // vendor-class with vendor-id 4491 and 5 data tuples, expected result is
+    // content of that tuple ("gamma")
+    testVendorClassData(Option::V4, 4491, 3, 4491, 5, "gamma");
+
+    // Case 6: Expression looks for vendor-id 4491, data[3], there is
+    // vendor-class with vendor-id 1234 and 5 data tuples, expected result is
+    // empty string, because vendor-id does not match.
+    testVendorClassData(Option::V4, 4491, 3, 1234, 5, "");
+
+    // Check if the logged messages are correct.
+    addString("EVAL_DEBUG_VENDOR_CLASS_NO_OPTION Option with code 124 missing, "
+              "pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID_MISMATCH Was looking for "
+              "4491, option had 1234, pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_DATA_NOT_FOUND Requested data index 3, "
+              "but option with enterprise-id 4491 has only 1 data tuple(s), "
+              "pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID_MISMATCH Was looking for "
+              "4491, option had 1234, pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_DATA_NOT_FOUND Requested data index 3, "
+              "but option with enterprise-id 4491 has only 3 data tuple(s), "
+              "pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_DATA Data 3 (out of 5 received) in vendor "
+              "class found, pushing result 'gamma'");
+    addString("EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID_MISMATCH Was looking for "
+              "4491, option had 1234, pushing result ''");
+    EXPECT_TRUE(checkFile());
+}
+
+// This test verifies if expression vendor-class[4491].data[3] is able to access
+// the tuple specified by index. This is a DHCPv6 test.
+TEST_F(TokenTest, vendorClass6DataIndex) {
+    // Case 1: Expression looks for vendor-id 4491, data[3], there is no
+    // vendor-class option at all, expected result is empty string.
+    testVendorClassData(Option::V6, 4491, 3, 0, 0, "");
+
+    // Case 2: Expression looks for vendor-id 4491, data[3], there is
+    // vendor-class with vendor-id 1234 and no data, expected result is empty string.
+    testVendorClassData(Option::V6, 4491, 3, 1234, 0, "");
+
+    // Case 3: Expression looks for vendor-id 4491, data[3], there is
+    // vendor-class with vendor-id 4491 and no data, expected result is empty string
+    testVendorClassData(Option::V6, 4491, 3, 4491, 0, "");
+
+    // Case 4: Expression looks for vendor-id 4491, data[3], there is
+    // vendor-class with vendor-id 1234 and 5 data tuples, expected result is empty string.
+    testVendorClassData(Option::V6, 4491, 3, 1234, 5, "");
+
+    // Case 5: Expression looks for vendor-id 4491, data[3], there is
+    // vendor-class with vendor-id 4491, but has only 3 data tuples, expected
+    // result is empty string.
+    testVendorClassData(Option::V6, 4491, 3, 4491, 3, "");
+
+    // Case 6: Expression looks for vendor-id 4491, data[3], there is
+    // vendor-class with vendor-id 4491 and 5 data tuples, expected result is
+    // content of that tuple ("gamma")
+    testVendorClassData(Option::V6, 4491, 3, 4491, 5, "gamma");
+
+    // Check if the logged messages are correct.
+    addString("EVAL_DEBUG_VENDOR_CLASS_NO_OPTION Option with code 16 missing, "
+              "pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID_MISMATCH Was looking for "
+              "4491, option had 1234, pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_DATA_NOT_FOUND Requested data index 3, "
+              "but option with enterprise-id 4491 has only 0 data tuple(s), "
+              "pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID_MISMATCH Was looking for "
+              "4491, option had 1234, pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_DATA_NOT_FOUND Requested data index 3, "
+              "but option with enterprise-id 4491 has only 3 data tuple(s), "
+              "pushing result ''");
+    addString("EVAL_DEBUG_VENDOR_CLASS_DATA Data 3 (out of 5 received) in vendor"
+              " class found, pushing result 'gamma'");
+    EXPECT_TRUE(checkFile());
+}
+
 };

+ 238 - 0
src/lib/eval/token.cc

@@ -12,6 +12,10 @@
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt6.h>
 #include <boost/lexical_cast.hpp>
+#include <dhcp/dhcp4.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/option_vendor.h>
+#include <dhcp/option_vendor_class.h>
 #include <cstring>
 #include <string>
 
@@ -137,6 +141,16 @@ TokenOption::evaluate(Pkt& pkt, ValueStack& values) {
     }
 }
 
+std::string
+TokenOption::pushFailure(ValueStack& values) {
+    std::string txt;
+    if (representation_type_ == EXISTS) {
+        txt = "false";
+    }
+    values.push(txt);
+    return (txt);
+}
+
 TokenRelay4Option::TokenRelay4Option(const uint16_t option_code,
                                      const RepresentationType& rep_type)
     :TokenOption(option_code, rep_type) {
@@ -641,3 +655,227 @@ TokenOr::evaluate(Pkt& /*pkt*/, ValueStack& values) {
         .arg('\'' + op2 + '\'')
         .arg('\'' + values.top() + '\'');
 }
+
+TokenVendor::TokenVendor(Option::Universe u, uint32_t vendor_id, RepresentationType repr,
+                         uint16_t option_code)
+    :TokenOption(option_code, repr), universe_(u), vendor_id_(vendor_id),
+     field_(option_code ? SUBOPTION : EXISTS)
+{
+}
+
+TokenVendor::TokenVendor(Option::Universe u, uint32_t vendor_id, FieldType field)
+    :TokenOption(0, TokenOption::HEXADECIMAL), universe_(u), vendor_id_(vendor_id),
+     field_(field)
+{
+    if (field_ == EXISTS) {
+        representation_type_ = TokenOption::EXISTS;
+    }
+}
+
+uint32_t TokenVendor::getVendorId() const {
+    return (vendor_id_);
+}
+
+TokenVendor::FieldType TokenVendor::getField() const {
+    return (field_);
+}
+
+void TokenVendor::evaluate(Pkt& pkt, ValueStack& values) {
+
+    // Get the option first.
+    uint16_t code = 0;
+    switch (universe_) {
+    case Option::V4:
+        code = DHO_VIVSO_SUBOPTIONS;
+        break;
+    case Option::V6:
+        code = D6O_VENDOR_OPTS;
+        break;
+    }
+
+    OptionPtr opt = pkt.getOption(code);
+    OptionVendorPtr vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
+    if (!vendor) {
+        // There's no vendor option, give up.
+        std::string txt = pushFailure(values);
+        LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_NO_OPTION)
+            .arg(code)
+            .arg(txt);
+        return;
+    }
+
+    if (vendor_id_ && (vendor_id_ != vendor->getVendorId())) {
+        // There is vendor option, but it has other vendor-id value
+        // than we're looking for. (0 means accept any vendor-id)
+        std::string txt = pushFailure(values);
+        LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_ENTERPRISE_ID_MISMATCH)
+            .arg(vendor_id_)
+            .arg(vendor->getVendorId())
+            .arg(txt);
+        return;
+    }
+
+    switch (field_) {
+    case ENTERPRISE_ID:
+    {
+        // Extract enterprise-id
+        string txt(sizeof(uint32_t), 0);
+        uint32_t value = htonl(vendor->getVendorId());
+        memcpy(&txt[0], &value, sizeof(uint32_t));
+        values.push(txt);
+        LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_ENTERPRISE_ID)
+            .arg(vendor->getVendorId())
+            .arg(util::encode::encodeHex(std::vector<uint8_t>(txt.begin(),
+                                                              txt.end())));
+        return;
+    }
+    case SUBOPTION:
+        /// This is vendor[X].option[Y].exists, let's try to
+        /// extract the option
+        TokenOption::evaluate(pkt, values);
+        return;
+    case EXISTS:
+        // We already passed all the checks: the option is there and has specified
+        // enterprise-id.
+        LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_EXISTS)
+            .arg(vendor->getVendorId())
+            .arg("true");
+        values.push("true");
+        return;
+    case DATA:
+        // This is for vendor-class option, we can skip it here.
+        isc_throw(EvalTypeError, "Field None is not valid for vendor-class");
+        return;
+    }
+}
+
+OptionPtr TokenVendor::getOption(Pkt& pkt) {
+   uint16_t code = 0;
+    switch (universe_) {
+    case Option::V4:
+        code = DHO_VIVSO_SUBOPTIONS;
+        break;
+    case Option::V6:
+        code = D6O_VENDOR_OPTS;
+        break;
+    }
+
+    OptionPtr opt = pkt.getOption(code);
+    if (!opt) {
+        // If vendor option is not found, return NULL
+        return (opt);
+    }
+
+    // If vendor option is found, try to return its
+    // encapsulated option.
+    return (opt->getOption(option_code_));
+}
+
+TokenVendorClass::TokenVendorClass(Option::Universe u, uint32_t vendor_id,
+                                   RepresentationType repr)
+    :TokenVendor(u, vendor_id, repr, 0), index_(0) {
+}
+
+TokenVendorClass::TokenVendorClass(Option::Universe u, uint32_t vendor_id,
+                                   FieldType field, uint16_t index)
+    :TokenVendor(u, vendor_id, TokenOption::HEXADECIMAL, 0), index_(index)
+{
+    field_ = field;
+}
+
+uint16_t TokenVendorClass::getDataIndex() const {
+    return (index_);
+}
+
+void TokenVendorClass::evaluate(Pkt& pkt, ValueStack& values) {
+
+    // Get the option first.
+    uint16_t code = 0;
+    switch (universe_) {
+    case Option::V4:
+        code = DHO_VIVCO_SUBOPTIONS;
+        break;
+    case Option::V6:
+        code = D6O_VENDOR_CLASS;
+        break;
+    }
+
+    OptionPtr opt = pkt.getOption(code);
+    OptionVendorClassPtr vendor = boost::dynamic_pointer_cast<OptionVendorClass>(opt);
+    if (!vendor) {
+        // There's no vendor class option, give up.
+        std::string txt = pushFailure(values);
+        LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_CLASS_NO_OPTION)
+            .arg(code)
+            .arg(txt);
+        return;
+    }
+
+    if (vendor_id_ && (vendor_id_ != vendor->getVendorId())) {
+        // There is vendor option, but it has other vendor-id value
+        // than we're looking for. (0 means accept any vendor-id)
+        std::string txt = pushFailure(values);
+        LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID_MISMATCH)
+            .arg(vendor_id_)
+            .arg(vendor->getVendorId())
+            .arg(txt);
+        return;
+    }
+
+    switch (field_) {
+    case ENTERPRISE_ID:
+    {
+        // Extract enterprise-id
+        string txt(sizeof(uint32_t), 0);
+        uint32_t value = htonl(vendor->getVendorId());
+        memcpy(&txt[0], &value, sizeof(uint32_t));
+        values.push(txt);
+        LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID)
+            .arg(vendor->getVendorId())
+            .arg(util::encode::encodeHex(std::vector<uint8_t>(txt.begin(),
+                                                              txt.end())));
+        return;
+    }
+    case SUBOPTION:
+        // Extract sub-options
+        isc_throw(EvalTypeError, "Field None is not valid for vendor-class");
+        return;
+    case EXISTS:
+        // We already passed all the checks: the option is there and has specified
+        // enterprise-id.
+        LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_CLASS_EXISTS)
+            .arg(vendor->getVendorId())
+            .arg("true");
+        values.push("true");
+        return;
+    case DATA:
+    {
+        size_t max = vendor->getTuplesNum();
+        if (index_ + 1 > max) {
+            // The index specified is out of bounds, e.g. there are only
+            // 2 tuples and index specified is 5.
+            LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_CLASS_DATA_NOT_FOUND)
+                .arg(index_)
+                .arg(vendor->getVendorId())
+                .arg(max)
+                .arg("");
+            values.push("");
+            return;
+        }
+
+        OpaqueDataTuple tuple = vendor->getTuple(index_);
+        OpaqueDataTuple::Buffer buf = tuple.getData();
+        string txt(buf.begin(), buf.end());
+
+        LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_CLASS_DATA)
+            .arg(index_)
+            .arg(max)
+            .arg(txt);
+
+        values.push(txt);
+        return;
+    }
+    default:
+        isc_throw(EvalTypeError, "Invalid field specified." << field_);
+    }
+}

+ 193 - 0
src/lib/eval/token.h

@@ -256,6 +256,14 @@ protected:
     /// @return option instance (or NULL if not found)
     virtual OptionPtr getOption(Pkt& pkt);
 
+    /// @brief Auxiliary method that puts string representing a failure
+    ///
+    /// Depending on the representation type, this is either "" or "false".
+    ///
+    /// @param values a string representing failure will be pushed here.
+    /// @return value pushed
+    virtual std::string pushFailure(ValueStack& values);
+
     uint16_t option_code_; ///< Code of the option to be extracted
     RepresentationType representation_type_; ///< Representation type.
 };
@@ -728,6 +736,191 @@ public:
     void evaluate(Pkt& pkt, ValueStack& values);
 };
 
+/// @brief Token that represents vendor options in DHCPv4 and DHCPv6.
+///
+/// It covers vendor independent vendor information option (125, DHCPv4)
+/// and vendor option (17, DHCPv6). Since both of those options may have
+/// suboptions, this class is derived from TokenOption and leverages its
+/// ability to operate on sub-options. It also adds additional capabilities.
+/// In particular, it allows retrieving enterprise-id.
+///
+/// It can represent the following expressions:
+/// vendor[4491].exists - if vendor option with enterprise-id = 4491 exists
+/// vendor[*].exists - if any vendor option exists
+/// vendor.enterprise - returns enterprise-id from vendor option
+/// vendor[4491].option[1].exists - check if suboption 1 exists for vendor 4491
+/// vendor[4491].option[1].hex - return content of suboption 1 for vendor 4491
+class TokenVendor : public TokenOption {
+public:
+
+    /// @brief Specifies a field of the vendor option
+    enum FieldType {
+        SUBOPTION,     ///< If this token fetches a suboption, not a field.
+        ENTERPRISE_ID, ///< enterprise-id field (vendor-info, vendor-class)
+        EXISTS,        ///< vendor[123].exists
+        DATA           ///< data chunk, used in derived vendor-class only
+    };
+
+    /// @brief Constructor used for accessing a field
+    ///
+    /// @param u universe (either V4 or V6)
+    /// @param vendor_id specifies enterprise-id (0 means any)
+    /// @param field specifies which field should be returned
+    TokenVendor(Option::Universe u, uint32_t vendor_id, FieldType field);
+
+
+    /// @brief Constructor used for accessing an option
+    ///
+    /// This constructor is used for accessing suboptions. In general
+    /// option_code is mandatory, except when repr is EXISTS. For
+    /// option_code = 0 and repr = EXISTS, the token will return true
+    /// if the whole option exists, not suboptions.
+    ///
+    /// @param u universe (either V4 or V6)
+    /// @param vendor_id specifies enterprise-id (0 means any)
+    /// @param repr representation type (hex or exists)
+    /// @param option_code sub-option code
+    TokenVendor(Option::Universe u, uint32_t vendor_id, RepresentationType repr,
+                uint16_t option_code = 0);
+
+    /// @brief Returns enterprise-id
+    ///
+    /// Used in tests only.
+    ///
+    /// @return enterprise-id
+    uint32_t getVendorId() const;
+
+    /// @brief Returns field.
+    ///
+    /// Used in tests only.
+    ///
+    /// @return field type.
+    FieldType getField() const;
+
+    /// @brief This is a method for evaluating a packet.
+    ///
+    /// Depending on the value of vendor_id, field type, representation and
+    /// option code, it will attempt to return specified characteristic of the
+    /// vendor option
+    ///
+    /// If vendor-id is specified, check only option with that particular
+    /// enterprise-id. If vendor-id is 0, check any vendor option, regardless
+    /// of its enterprise-id value.
+    ///
+    /// If FieldType is NONE, get specified suboption represented by option_code
+    /// and represent it as specified by repr.
+    ///
+    /// If FieldType is ENTERPRISE_ID, return value of the enterprise-id field
+    /// or "" if there's no vendor option.
+    ///
+    /// @throw EvalTypeError for any other FieldType values.
+    ///
+    /// The parameters passed are:
+    ///
+    /// @param pkt - vendor options will be searched for here.
+    /// @param values - the evaluated value will be pushed here.
+    virtual void evaluate(Pkt& pkt, ValueStack& values);
+
+protected:
+    /// @brief Attempts to get a suboption.
+    ///
+    /// This method overrides behavior of TokenOption method. It attempts to retrieve
+    /// the sub-option of the vendor option. Using derived method allows usage of
+    /// TokenOption routines.
+    ///
+    /// @param pkt vendor option will be searched here.
+    /// @return suboption of the vendor option (if exists)
+    virtual OptionPtr getOption(Pkt& pkt);
+
+    /// @brief Universe (V4 or V6)
+    ///
+    /// We need to remember it, because depending on the universe, the code needs
+    /// to retrieve either option 125 (DHCPv4) or 17 (DHCPv6).
+    Option::Universe universe_;
+
+    /// @brief Enterprise-id value
+    ///
+    /// Yeah, I know it technically should be called enterprise-id, but that's
+    /// too long and everyone calls it vendor-id.
+    uint32_t vendor_id_;
+
+    /// @brief Specifies which field should be accessed.
+    FieldType field_;
+};
+
+/// @brief Token that represents vendor class options in DHCPv4 and DHCPv6.
+///
+/// It covers vendor independent vendor information option (124, DHCPv4)
+/// and vendor option (16, DHCPv6). Contrary to vendor options, vendor class
+/// options don't have suboptions, but have data chunks (tuples) instead.
+/// Therefore they're not referenced by option codes, but by indexes.
+/// The first data chunk is data[0], the second is data[1] etc.
+///
+/// This class is derived from OptionVendor to take advantage of the
+/// enterprise handling field and field type.
+///
+/// It can represent the following expressions:
+/// vendor-class[4491].exists
+/// vendor-class[*].exists
+/// vendor-class[*].enterprise
+/// vendor-class[4491].data - content of the opaque-data of the first tuple
+/// vendor-class[4491].data[3] - content of the opaque-data of the 4th tuple
+class TokenVendorClass : public TokenVendor {
+public:
+
+    /// @brief This constructor is used to access fields.
+    ///
+    /// @param u universe (V4 or V6)
+    /// @param vendor_id value of enterprise-id field (0 means any)
+    /// @param repr representation type (EXISTS or HEX)
+    TokenVendorClass(Option::Universe u, uint32_t vendor_id, RepresentationType repr);
+
+    /// @brief This constructor is used to access data chunks.
+    ///
+    /// @param u universe (V4 or V6)
+    /// @param vendor_id value of enterprise-id field (0 means any)
+    /// @param field type of the field (usually DATA or ENTERPRISE)
+    /// @param index specifies which data chunk to retrieve
+    TokenVendorClass(Option::Universe u, uint32_t vendor_id, FieldType field,
+                     uint16_t index = 0);
+
+    /// @brief Returns data index.
+    ///
+    /// Used in testing.
+    /// @return data index (specifies which data chunk to retrieve)
+    uint16_t getDataIndex() const;
+
+protected:
+
+    /// @brief This is a method for evaluating a packet.
+    ///
+    /// Depending on the value of vendor_id, field type, representation and
+    /// option code, it will attempt to return specified characteristic of the
+    /// vendor option
+    ///
+    /// If vendor-id is specified, check only option with that particular
+    /// enterprise-id. If vendor-id is 0, check any vendor option, regardless
+    /// of its enterprise-id value.
+    ///
+    /// If FieldType is ENTERPRISE_ID, return value of the enterprise-id field
+    /// or "" if there's no vendor option.
+    ///
+    /// If FieldType is DATA, get specified data chunk represented by index_.
+    ///
+    /// If FieldType is EXISTS, return true if vendor-id matches.
+    ///
+    /// @throw EvalTypeError for any other FieldType values.
+    ///
+    /// The parameters passed are:
+    ///
+    /// @param pkt - vendor options will be searched for here.
+    /// @param values - the evaluated value will be pushed here.
+    void evaluate(Pkt& pkt, ValueStack& values);
+
+    /// @brief Data chunk index.
+    uint16_t index_;
+};
+
 }; // end of isc::dhcp namespace
 }; // end of isc namespace
 

+ 17 - 2
src/lib/testutils/log_utils.cc

@@ -5,12 +5,14 @@
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 #include <testutils/log_utils.h>
+#include <iostream>
 
 namespace isc {
 namespace dhcp {
 namespace test {
 
-LogContentTest::LogContentTest() {
+LogContentTest::LogContentTest()
+    :verbose_(false) {
     // Get rid of any old files
     remFile();
 
@@ -41,18 +43,31 @@ bool LogContentTest::checkFile() {
     int i = 0;
     bool found = true;
 
+    using namespace std;
+
     while (getline(file, line) && (i != exp_strings_.size())) {
         exp_line = exp_strings_[i];
+        if (verbose_) {
+            cout << "Read line  :" << line << endl;
+            cout << "Looking for:" << exp_line << endl;
+        }
         i++;
         if (string::npos == line.find(exp_line)) {
+            if (verbose_) {
+                cout << "Verdict    : not found" << endl;
+            }
             found = false;
         }
     }
 
     file.close();
 
-    if ((i != exp_strings_.size()) || (found == false))
+    if ((i != exp_strings_.size()) || (found == false)) {
+        if (verbose_) {
+            cout << "Final verdict: false" << endl;
+        }
         return (false);
+    }
 
     return (true);
 }

+ 17 - 0
src/lib/testutils/log_utils.h

@@ -63,6 +63,15 @@ public:
     /// @brief remove the test log file
     void remFile();
 
+    /// @brief Enables or disables verbose mode.
+    ///
+    /// See @ref verbose_ for details.
+    ///
+    /// @param talk_a_lot (true - as the name says, false - shut up)
+    void logCheckVerbose(bool talk_a_lot) {
+        verbose_ = talk_a_lot;
+    }
+
     /// @brief Add a string to the vector of expected strings
     ///
     /// @param new_string the string to add to the end of the vector
@@ -71,6 +80,14 @@ public:
 
     vector<string> exp_strings_;
     static const char* LOG_FILE;
+
+    /// @brief controls whether the checkFile() should print more details.
+    ///
+    /// If set to true, checkFile() will print each expected line, each
+    /// logged line and will print out a failure message if those two do
+    /// not match. Also, a final verdict is printed. Everything is printed
+    /// on stdout.
+    bool verbose_;
 };