Commit 8e01dbe2 authored by Francis Dupont's avatar Francis Dupont
Browse files

[master] Finished merge of trac4231 (new boolean operators)

parents 0595242b 09233722
1079. [func] fdupont
Added Not, And and Or logical operators, parentheses around
logical expressions and option[code].exist logical predicate
(to check the presence of an empty option).
(Trac #4231, git xxx)
1078. [func] tomek
Client classification in DHCPv4 has been enhanced. It is now
possible to access relay sub-options using the expression
......
......@@ -164,6 +164,7 @@
<row><entry>Option Text</entry><entry>option[code].text</entry><entry>The value of the option with code "code" from the packet as text</entry></row>
-->
<row><entry>Option Hex</entry><entry>option[code].hex</entry><entry>The value of the option with code "code" from the packet as hex</entry></row>
<row><entry>Option Exist</entry><entry>option[code].exist</entry><entry>If the option with code "code" is present in the packet "true" else "false"</entry></row>
<row><entry>DHCPv4 Relay Agent
sub-option</entry><entry>relay[code].hex</entry><entry>The value of
sub-option with code "code" from the Relay Agent Information option
......@@ -188,6 +189,11 @@ sub-option with code "code" from the Relay Agent Information option
the option payload without the type code or length fields.
</para>
<para>
"option[code].exist" checks if an option with the given code is present
in the incoming packet. It can be used with empty options.
</para>
<para>
"relay[code].hex" attempts to extract the value of the sub-option
"code" from the option inserted as the Relay Agent Information
......@@ -214,12 +220,24 @@ sub-option with code "code" from the Relay Agent Information option
</thead>
<tbody>
<row><entry>Equal</entry> <entry>'foo' == 'bar'</entry><entry>Compare the two values and return "true" or "false"</entry></row>
<row><entry>Not</entry> <entry>not ('foo' == 'bar')</entry><entry>Logical negation</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>Substring</entry><entry>substring('foobar',0,3)</entry><entry>Return the requested substring</entry></row>
</tbody>
</tgroup>
</table>
</para>
<section>
<title>Logical operators</title>
The Not, And and Or logical operators are the common operators. Not
has the highest precedence, Or the lowest. And and Or are (left)
associative, parentheses around a logical expression can be used
to enforce a specific grouping, for instance in "A and (B or C)"
(without parentheses "A and B or C" means "(A and B) or C").
</section>
<section>
<title>Substring</title>
The substring operator "substring(value, start, length)" accepts both positive and
......@@ -376,7 +394,7 @@ sub-option with code "code" from the Relay Agent Information option
</para>
<para>
The following example shows restricting access to a DHCPv6 subnet. This
The following example shows restricting access to a DHCPv6 subnet. This
configuration will restrict use of the addresses 2001:db8:1::1 to
2001:db8:1::FFFF to members of the "Client_enterprise" class.
......
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
......@@ -19,14 +19,7 @@ bool evaluate(const Expression& expr, const Pkt& pkt) {
isc_throw(EvalBadStack, "Incorrect stack order. Expected exactly "
"1 value at the end of evaluatuion, got " << values.size());
}
if (values.top() == "false") {
return (false);
} else if (values.top() == "true") {
return (true);
} else {
isc_throw(EvalTypeError, "Incorrect evaluation type. Expected "
"\"false\" or \"true\", got \"" << values.top() << "\"");
}
return (Token::toBool(values.top()));
}
}; // end of isc::dhcp namespace
......
This diff is collapsed.
......@@ -126,9 +126,13 @@ blank [ \t]
"option" return isc::eval::EvalParser::make_OPTION(loc);
"text" return isc::eval::EvalParser::make_TEXT(loc);
"hex" return isc::eval::EvalParser::make_HEX(loc);
"exists" return isc::eval::EvalParser::make_EXISTS(loc);
"substring" return isc::eval::EvalParser::make_SUBSTRING(loc);
"relay4" return isc::eval::EvalParser::make_RELAY4(loc);
"all" return isc::eval::EvalParser::make_ALL(loc);
"not" return isc::eval::EvalParser::make_NOT(loc);
"and" return isc::eval::EvalParser::make_AND(loc);
"or" return isc::eval::EvalParser::make_OR(loc);
"." return isc::eval::EvalParser::make_DOT(loc);
"(" return isc::eval::EvalParser::make_LPAREN(loc);
")" return isc::eval::EvalParser::make_RPAREN(loc);
......@@ -151,7 +155,7 @@ EvalContext::scanStringBegin()
buffer = yy_scan_bytes(string_.c_str(), string_.size());
if (!buffer) {
fatal("cannot scan string");
// fatal() throws an exception so this can't be reached
// fatal() throws an exception so this can't be reached
}
}
......
// A Bison parser, made by GNU Bison 3.0.2.
// Generated 20160219
// A Bison parser, made by GNU Bison 3.0.4.
// Locations for Bison parsers in C++
......
This diff is collapsed.
// A Bison parser, made by GNU Bison 3.0.2.
// A Bison parser, made by GNU Bison 3.0.4.
// Skeleton interface for Bison LALR(1) parsers in C++
// Copyright (C) 2002-2013 Free Software Foundation, Inc.
// Copyright (C) 2002-2015 Free Software Foundation, Inc.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
......@@ -40,7 +40,7 @@
#ifndef YY_YY_PARSER_H_INCLUDED
# define YY_YY_PARSER_H_INCLUDED
// // "%code requires" blocks.
#line 16 "parser.yy" // lalr1.cc:372
#line 16 "parser.yy" // lalr1.cc:392
#include <string>
#include <eval/token.h>
......@@ -51,13 +51,14 @@
using namespace isc::dhcp;
using namespace isc::eval;
#line 55 "parser.h" // lalr1.cc:372
#line 55 "parser.h" // lalr1.cc:392
# include <cassert>
# include <vector>
# include <cstdlib> // std::abort
# include <iostream>
# include <stdexcept>
# include <string>
# include <vector>
# include "stack.hh"
# include "location.hh"
#include <typeinfo>
......@@ -125,9 +126,9 @@ using namespace isc::eval;
# define YYDEBUG 1
#endif
#line 13 "parser.yy" // lalr1.cc:372
#line 13 "parser.yy" // lalr1.cc:392
namespace isc { namespace eval {
#line 131 "parser.h" // lalr1.cc:372
#line 132 "parser.h" // lalr1.cc:392
......@@ -144,13 +145,13 @@ namespace isc { namespace eval {
/// Empty construction.
variant ()
: yytname_ (YY_NULLPTR)
: yytypeid_ (YY_NULLPTR)
{}
/// Construct and fill.
template <typename T>
variant (const T& t)
: yytname_ (typeid (T).name ())
: yytypeid_ (&typeid (T))
{
YYASSERT (sizeof (T) <= S);
new (yyas_<T> ()) T (t);
......@@ -159,7 +160,7 @@ namespace isc { namespace eval {
/// Destruction, allowed only if empty.
~variant ()
{
YYASSERT (!yytname_);
YYASSERT (!yytypeid_);
}
/// Instantiate an empty \a T in here.
......@@ -167,9 +168,9 @@ namespace isc { namespace eval {
T&
build ()
{
YYASSERT (!yytname_);
YYASSERT (!yytypeid_);
YYASSERT (sizeof (T) <= S);
yytname_ = typeid (T).name ();
yytypeid_ = & typeid (T);
return *new (yyas_<T> ()) T;
}
......@@ -178,9 +179,9 @@ namespace isc { namespace eval {
T&
build (const T& t)
{
YYASSERT (!yytname_);
YYASSERT (!yytypeid_);
YYASSERT (sizeof (T) <= S);
yytname_ = typeid (T).name ();
yytypeid_ = & typeid (T);
return *new (yyas_<T> ()) T (t);
}
......@@ -189,7 +190,7 @@ namespace isc { namespace eval {
T&
as ()
{
YYASSERT (yytname_ == typeid (T).name ());
YYASSERT (*yytypeid_ == typeid (T));
YYASSERT (sizeof (T) <= S);
return *yyas_<T> ();
}
......@@ -199,7 +200,7 @@ namespace isc { namespace eval {
const T&
as () const
{
YYASSERT (yytname_ == typeid (T).name ());
YYASSERT (*yytypeid_ == typeid (T));
YYASSERT (sizeof (T) <= S);
return *yyas_<T> ();
}
......@@ -216,8 +217,8 @@ namespace isc { namespace eval {
void
swap (self_type& other)
{
YYASSERT (yytname_);
YYASSERT (yytname_ == other.yytname_);
YYASSERT (yytypeid_);
YYASSERT (*yytypeid_ == *other.yytypeid_);
std::swap (as<T> (), other.as<T> ());
}
......@@ -247,7 +248,7 @@ namespace isc { namespace eval {
destroy ()
{
as<T> ().~T ();
yytname_ = YY_NULLPTR;
yytypeid_ = YY_NULLPTR;
}
private:
......@@ -282,7 +283,7 @@ namespace isc { namespace eval {
} yybuffer_;
/// Whether the content is built: if defined, the name of the stored type.
const char *yytname_;
const std::type_info *yytypeid_;
};
......@@ -332,30 +333,37 @@ namespace isc { namespace eval {
TOKEN_EQUAL = 258,
TOKEN_OPTION = 259,
TOKEN_SUBSTRING = 260,
TOKEN_TEXT = 261,
TOKEN_RELAY4 = 262,
TOKEN_HEX = 263,
TOKEN_ALL = 264,
TOKEN_DOT = 265,
TOKEN_COMA = 266,
TOKEN_LPAREN = 267,
TOKEN_RPAREN = 268,
TOKEN_LBRACKET = 269,
TOKEN_RBRACKET = 270,
TOKEN_STRING = 271,
TOKEN_INTEGER = 272,
TOKEN_HEXSTRING = 273,
TOKEN_OPTION_NAME = 274,
TOKEN_TOKEN = 275
TOKEN_NOT = 261,
TOKEN_AND = 262,
TOKEN_OR = 263,
TOKEN_TEXT = 264,
TOKEN_RELAY4 = 265,
TOKEN_HEX = 266,
TOKEN_EXISTS = 267,
TOKEN_ALL = 268,
TOKEN_DOT = 269,
TOKEN_COMA = 270,
TOKEN_LPAREN = 271,
TOKEN_RPAREN = 272,
TOKEN_LBRACKET = 273,
TOKEN_RBRACKET = 274,
TOKEN_STRING = 275,
TOKEN_INTEGER = 276,
TOKEN_HEXSTRING = 277,
TOKEN_OPTION_NAME = 278,
TOKEN_TOKEN = 279
};
};
/// (External) token type, as returned by yylex.
typedef token::yytokentype token_type;
/// Internal symbol number.
/// Symbol type: an internal symbol number.
typedef int symbol_number_type;
/// The symbol type number to denote an empty symbol.
enum { empty_symbol = -2 };
/// Internal symbol number for tokens (subsumed by symbol_number_type).
typedef unsigned char token_number_type;
......@@ -393,8 +401,15 @@ namespace isc { namespace eval {
const semantic_type& v,
const location_type& l);
/// Destroy the symbol.
~basic_symbol ();
/// Destroy contents, and record that is empty.
void clear ();
/// Whether empty.
bool empty () const;
/// Destructive move, \a s is emptied into this.
void move (basic_symbol& s);
......@@ -424,21 +439,23 @@ namespace isc { namespace eval {
/// Constructor from (external) token numbers.
by_type (kind_type t);
/// Record that this symbol is empty.
void clear ();
/// Steal the symbol type from \a that.
void move (by_type& that);
/// The (internal) type number (corresponding to \a type).
/// -1 when this symbol is empty.
/// \a empty when empty.
symbol_number_type type_get () const;
/// The token.
token_type token () const;
enum { empty = 0 };
/// The symbol type.
/// -1 when this symbol is empty.
token_number_type type;
/// \a empty_symbol when empty.
/// An int, not token_number_type, to be able to store empty_symbol.
int type;
};
/// "External" symbols: returned by the scanner.
......@@ -461,6 +478,18 @@ namespace isc { namespace eval {
symbol_type
make_SUBSTRING (const location_type& l);
static inline
symbol_type
make_NOT (const location_type& l);
static inline
symbol_type
make_AND (const location_type& l);
static inline
symbol_type
make_OR (const location_type& l);
static inline
symbol_type
make_TEXT (const location_type& l);
......@@ -473,6 +502,10 @@ namespace isc { namespace eval {
symbol_type
make_HEX (const location_type& l);
static inline
symbol_type
make_EXISTS (const location_type& l);
static inline
symbol_type
make_ALL (const location_type& l);
......@@ -562,9 +595,9 @@ namespace isc { namespace eval {
/// Generate an error message.
/// \param yystate the state where the error occurred.
/// \param yytoken the lookahead token type, or yyempty_.
/// \param yyla the lookahead token.
virtual std::string yysyntax_error_ (state_type yystate,
symbol_number_type yytoken) const;
const symbol_type& yyla) const;
/// Compute post-reduction state.
/// \param yystate the current state
......@@ -667,16 +700,21 @@ namespace isc { namespace eval {
/// Copy constructor.
by_state (const by_state& other);
/// Record that this symbol is empty.
void clear ();
/// Steal the symbol type from \a that.
void move (by_state& that);
/// The (internal) type number (corresponding to \a state).
/// "empty" when empty.
/// \a empty_symbol when empty.
symbol_number_type type_get () const;
enum { empty = 0 };
/// The state number used to denote an empty symbol.
enum { empty_state = -1 };
/// The state.
/// \a empty when empty.
state_type state;
};
......@@ -717,17 +755,16 @@ namespace isc { namespace eval {
/// Pop \a n symbols the three stacks.
void yypop_ (unsigned int n = 1);
// Constants.
/// Constants.
enum
{
yyeof_ = 0,
yylast_ = 30, ///< Last index in yytable_.
yylast_ = 57, ///< Last index in yytable_.
yynnts_ = 8, ///< Number of nonterminal symbols.
yyempty_ = -2,
yyfinal_ = 13, ///< Termination state number.
yyfinal_ = 17, ///< Termination state number.
yyterror_ = 1,
yyerrcode_ = 256,
yyntokens_ = 21 ///< Number of tokens.
yyntokens_ = 25 ///< Number of tokens.
};
......@@ -771,9 +808,9 @@ namespace isc { namespace eval {
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20
15, 16, 17, 18, 19, 20, 21, 22, 23, 24
};
const unsigned int user_token_number_max_ = 275;
const unsigned int user_token_number_max_ = 279;
const token_number_type undef_token_ = 2;
if (static_cast<int>(t) <= yyeof_)
......@@ -806,19 +843,19 @@ namespace isc { namespace eval {
{
switch (other.type_get ())
{
case 26: // option_repr_type
case 30: // option_repr_type
value.copy< TokenOption::RepresentationType > (other.value);
break;
case 16: // "constant string"
case 17: // "integer"
case 18: // "constant hexstring"
case 19: // "option name"
case 20: // TOKEN
case 20: // "constant string"
case 21: // "integer"
case 22: // "constant hexstring"
case 23: // "option name"
case 24: // TOKEN
value.copy< std::string > (other.value);
break;
case 25: // option_code
case 29: // option_code
value.copy< uint16_t > (other.value);
break;
......@@ -839,19 +876,19 @@ namespace isc { namespace eval {
(void) v;
switch (this->type_get ())
{
case 26: // option_repr_type
case 30: // option_repr_type
value.copy< TokenOption::RepresentationType > (v);
break;
case 16: // "constant string"
case 17: // "integer"
case 18: // "constant hexstring"
case 19: // "option name"
case 20: // TOKEN
case 20: // "constant string"
case 21: // "integer"
case 22: // "constant hexstring"
case 23: // "option name"
case 24: // TOKEN
value.copy< std::string > (v);
break;
case 25: // option_code
case 29: // option_code
value.copy< uint16_t > (v);
break;
......@@ -895,9 +932,19 @@ namespace isc { namespace eval {
template <typename Base>
inline
EvalParser::basic_symbol<Base>::~basic_symbol ()
{
clear ();
}
template <typename Base>
inline
void
EvalParser::basic_symbol<Base>::clear ()
{
// User destructor.
symbol_number_type yytype = this->type_get ();
basic_symbol<Base>& yysym = *this;
(void) yysym;
switch (yytype)
{
default:
......@@ -907,19 +954,19 @@ namespace isc { namespace eval {
// Type destructor.
switch (yytype)
{
case 26: // option_repr_type
case 30: // option_repr_type
value.template destroy< TokenOption::RepresentationType > ();
break;
case 16: // "constant string"
case 17: // "integer"
case 18: // "constant hexstring"
case 19: // "option name"
case 20: // TOKEN
case 20: // "constant string"
case 21: // "integer"
case 22: // "constant hexstring"
case 23: // "option name"
case 24: // TOKEN
value.template destroy< std::string > ();
break;
case 25: // option_code
case 29: // option_code
value.template destroy< uint16_t > ();
break;
......@@ -927,6 +974,15 @@ namespace isc { namespace eval {
break;
}
Base::clear ();
}
template <typename Base>
inline
bool
EvalParser::basic_symbol<Base>::empty () const
{
return Base::type_get () == empty_symbol;
}
template <typename Base>
......@@ -937,19 +993,19 @@ namespace isc { namespace eval {
super_type::move(s);
switch (this->type_get ())
{
case 26: // option_repr_type
case 30: // option_repr_type
value.move< TokenOption::RepresentationType > (s.value);
break;
case 16: // "constant string"
case 17: // "integer"
case 18: // "constant hexstring"
case 19: // "option name"
case 20: // TOKEN
case 20: // "constant string"
case 21: // "integer"
case 22: // "constant hexstring"
case 23: // "option name"
case 24: // TOKEN
value.move< std::string > (s.value);
break;
case 25: // option_code
case 29: // option_code
value.move< uint16_t > (s.value);
break;
......@@ -963,7 +1019,7 @@ namespace isc { namespace eval {
// by_type.
inline
EvalParser::by_type::by_type ()
: type (empty)
: type (empty_symbol)
{}
inline
......@@ -976,12 +1032,19 @@ namespace isc { namespace eval {
: type (yytranslate_ (t))
{}
inline
void
EvalParser::by_type::clear ()
{
type = empty_symbol;
}
inline
void
EvalParser::by_type::move (by_type& that)
{
type = that.type;
that.type = empty;
that.clear ();
}
inline
......@@ -1003,7 +1066,7 @@ namespace isc { namespace eval {
{
0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
275
275, 276, 277, 278, 279
};
return static_cast<token_type> (yytoken_number_[type]);
}
......@@ -1032,6 +1095,24 @@ namespace isc { namespace eval {
return symbol_type (token::TOKEN_SUBSTRING, l);
}