2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-29 13:07:50 +00:00

[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
This commit is contained in:
Tomek Mrugalski 2016-08-18 23:40:27 +02:00
commit ef676368f9
21 changed files with 2952 additions and 709 deletions

View File

@ -202,7 +202,7 @@
--> -->
<row> <row>
<entry>Option existence</entry> <entry>Option existence</entry>
<entry>option[123].exist</entry> <entry>option[123].exists</entry>
<entry>'true'</entry> <entry>'true'</entry>
<entry>If the option with given code is present in the <entry>If the option with given code is present in the
packet "true" else "false"</entry> packet "true" else "false"</entry>
@ -315,6 +315,88 @@
<entry>The value of the transaction id in the DHCPv6 <entry>The value of the transaction id in the DHCPv6
packet.</entry> packet.</entry>
</row> </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> </tbody>
</tgroup> </tgroup>
</table> </table>
@ -341,7 +423,7 @@
</para> </para>
<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. in the incoming packet. It can be used with empty options.
</para> </para>
@ -387,6 +469,33 @@
"0x00000001" or simply 1 as in "pkt6.msgtype == 1". "0x00000001" or simply 1 as in "pkt6.msgtype == 1".
</para> </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> <para>
<table frame="all" id="classification-expressions-list"> <table frame="all" id="classification-expressions-list">
<title>List of Classification Expressions</title> <title>List of Classification Expressions</title>
@ -407,7 +516,8 @@
<row><entry>And</entry> <entry>('foo' == 'bar') and ('bar' == 'foo')</entry><entry>Logical and</entry></row> <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>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>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> </tbody>
</tgroup> </tgroup>
</table> </table>

View File

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

View File

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

View File

@ -85,6 +85,11 @@ parser: lexer.cc location.hh position.hh stack.hh parser.cc parser.h
@echo "Flex/bison files regenerated" @echo "Flex/bison files regenerated"
# --- Flex/Bison stuff below -------------------------------------------------- # --- 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 location.hh position.hh stack.hh parser.cc parser.h: parser.yy
$(YACC) --defines=parser.h -o parser.cc parser.yy $(YACC) --defines=parser.h -o parser.cc parser.yy

View File

@ -13,6 +13,7 @@
#include <exceptions/exceptions.h> #include <exceptions/exceptions.h>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <fstream> #include <fstream>
#include <limits>
EvalContext::EvalContext(const Option::Universe& option_universe) EvalContext::EvalContext(const Option::Universe& option_universe)
: trace_scanning_(false), trace_parsing_(false), : trace_scanning_(false), trace_parsing_(false),
@ -97,14 +98,9 @@ uint8_t
EvalContext::convertNestLevelNumber(const std::string& nest_level, EvalContext::convertNestLevelNumber(const std::string& nest_level,
const isc::eval::location& loc) const isc::eval::location& loc)
{ {
int n = 0; uint8_t n = convertUint8(nest_level, loc);
try {
n = boost::lexical_cast<int>(nest_level);
} catch (const boost::bad_lexical_cast &) {
error(loc, "Nest level has invalid value in " + nest_level);
}
if (option_universe_ == Option::V6) { 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 " error(loc, "Nest level has invalid value in "
+ nest_level + ". Allowed range: 0..31"); + 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"); 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)); 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 void
EvalContext::fatal (const std::string& what) EvalContext::fatal (const std::string& what)

View File

@ -96,16 +96,34 @@ public:
/// ///
/// @param option_name the option name /// @param option_name the option name
/// @param loc the location of the token /// @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 /// @throw calls the syntax error function if the name cannot be resolved
uint16_t convertOptionName(const std::string& option_name, uint16_t convertOptionName(const std::string& option_name,
const isc::eval::location& loc); 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 /// @brief Nest level conversion
/// ///
/// @param nest_level a string representing the integer nesting level /// @param nest_level a string representing the integer nesting level
/// @param loc the location of the token /// @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 /// @throw calls the syntax error function if the value is not in
/// the range 0..31 /// the range 0..31
uint8_t convertNestLevelNumber(const std::string& nest_level, uint8_t convertNestLevelNumber(const std::string& nest_level,

View File

@ -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 length and string are still popped from the stack and the result is
still pushed. The strings are displayed in hex. 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 % EVAL_RESULT Expression %1 evaluated to %2
This debug message indicates that the expression has been evaluated This debug message indicates that the expression has been evaluated
to said value. This message is mostly useful during debugging of the to said value. This message is mostly useful during debugging of the

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -67,13 +67,18 @@ using namespace isc::eval;
GIADDR "giaddr" GIADDR "giaddr"
YIADDR "yiaddr" YIADDR "yiaddr"
SIADDR "siaddr" SIADDR "siaddr"
PKT6 "pkt6"
MSGTYPE "msgtype"
TRANSID "transid"
SUBSTRING "substring" SUBSTRING "substring"
ALL "all" ALL "all"
COMA "," COMA ","
CONCAT "concat" 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" %token <std::string> STRING "constant string"
@ -83,6 +88,7 @@ using namespace isc::eval;
%token <std::string> IP_ADDRESS "ip address" %token <std::string> IP_ADDRESS "ip address"
%type <uint16_t> option_code %type <uint16_t> option_code
%type <uint32_t> enterprise_id
%type <TokenOption::RepresentationType> option_repr_type %type <TokenOption::RepresentationType> option_repr_type
%type <TokenRelay6Field::FieldType> relay6_field %type <TokenRelay6Field::FieldType> relay6_field
%type <uint8_t> nest_level %type <uint8_t> nest_level
@ -166,6 +172,34 @@ bool_expr : "(" bool_expr ")"
error(@1, "relay6 can only be used in DHCPv6."); 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 string_expr : STRING
@ -282,6 +316,60 @@ string_expr : STRING
TokenPtr conc(new TokenConcat()); TokenPtr conc(new TokenConcat());
ctx.expression.push_back(conc); 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 option_code : INTEGER
@ -331,6 +419,16 @@ pkt_metadata : IFACE
} }
; ;
enterprise_id : INTEGER
{
$$ = ctx.convertUint32($1, @1);
}
| "*"
{
$$ = 0;
}
;
pkt4_field : CHADDR pkt4_field : CHADDR
{ {
$$ = TokenPkt4::CHADDR; $$ = TokenPkt4::CHADDR;
@ -382,17 +480,17 @@ relay6_field : PEERADDR
; ;
start_expr : INTEGER start_expr : INTEGER
{
TokenPtr str(new TokenString($1));
ctx.expression.push_back(str);
}
;
length_expr : INTEGER
{ {
TokenPtr str(new TokenString($1)); TokenPtr str(new TokenString($1));
ctx.expression.push_back(str); ctx.expression.push_back(str);
} }
;
length_expr : INTEGER
{
TokenPtr str(new TokenString($1));
ctx.expression.push_back(str);
}
| ALL | ALL
{ {
TokenPtr str(new TokenString("all")); TokenPtr str(new TokenString("all"));

View File

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

View File

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

View File

@ -417,8 +417,189 @@ public:
universe_ = universe; 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 bool parsed_; ///< Parsing status
}; };
// Test the error method without location // Test the error method without location
@ -751,8 +932,7 @@ TEST_F(EvalContextTest, relay6OptionLimits) {
// next level must be a positive number // next level must be a positive number
checkError("relay6[-1].option[123].text == 'foo'", checkError("relay6[-1].option[123].text == 'foo'",
"<string>:1.8-9: Nest level has invalid value in -1. " "<string>:1.8-9: Invalid value in -1. Allowed range: 0..255");
"Allowed range: 0..31");
} }
// Verify that relay6[13].option is not usable in v4 // 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 =="); "<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);
}
}; };

View File

@ -12,6 +12,8 @@
#include <dhcp/dhcp4.h> #include <dhcp/dhcp4.h>
#include <dhcp/dhcp6.h> #include <dhcp/dhcp6.h>
#include <dhcp/option_string.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_manager.h>
#include <log/logger_name.h> #include <log/logger_name.h>
#include <log/logger_support.h> #include <log/logger_support.h>
@ -51,6 +53,10 @@ public:
pkt4_->addOption(option_str4_); pkt4_->addOption(option_str4_);
pkt6_->addOption(option_str6_); 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 /// @brief Inserts RAI option with several suboptions
@ -185,6 +191,19 @@ public:
t_.reset(); 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 TokenPtr t_; ///< Just a convenience pointer
ValueStack values_; ///< evaluated values will be stored here ValueStack values_; ///< evaluated values will be stored here
@ -195,6 +214,8 @@ public:
OptionPtr option_str4_; ///< A string option for DHCPv4 OptionPtr option_str4_; ///< A string option for DHCPv4
OptionPtr option_str6_; ///< A string option for DHCPv6 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 /// @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 // This tests the toBool() conversions
@ -1817,4 +2063,662 @@ TEST_F(TokenTest, operatorOrTrue) {
EXPECT_TRUE(checkFile()); 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());
}
}; };

View File

@ -12,6 +12,10 @@
#include <dhcp/pkt4.h> #include <dhcp/pkt4.h>
#include <dhcp/pkt6.h> #include <dhcp/pkt6.h>
#include <boost/lexical_cast.hpp> #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 <cstring>
#include <string> #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, TokenRelay4Option::TokenRelay4Option(const uint16_t option_code,
const RepresentationType& rep_type) const RepresentationType& rep_type)
:TokenOption(option_code, rep_type) { :TokenOption(option_code, rep_type) {
@ -641,3 +655,227 @@ TokenOr::evaluate(Pkt& /*pkt*/, ValueStack& values) {
.arg('\'' + op2 + '\'') .arg('\'' + op2 + '\'')
.arg('\'' + values.top() + '\''); .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_);
}
}

View File

@ -256,6 +256,14 @@ protected:
/// @return option instance (or NULL if not found) /// @return option instance (or NULL if not found)
virtual OptionPtr getOption(Pkt& pkt); 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 uint16_t option_code_; ///< Code of the option to be extracted
RepresentationType representation_type_; ///< Representation type. RepresentationType representation_type_; ///< Representation type.
}; };
@ -728,6 +736,191 @@ public:
void evaluate(Pkt& pkt, ValueStack& values); 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::dhcp namespace
}; // end of isc namespace }; // end of isc namespace

View File

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

View File

@ -63,6 +63,15 @@ public:
/// @brief remove the test log file /// @brief remove the test log file
void remFile(); 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 /// @brief Add a string to the vector of expected strings
/// ///
/// @param new_string the string to add to the end of the vector /// @param new_string the string to add to the end of the vector
@ -71,6 +80,14 @@ public:
vector<string> exp_strings_; vector<string> exp_strings_;
static const char* LOG_FILE; 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_;
}; };