Browse Source

[master] Finished merge of trac4232a (IP address literal)

Francis Dupont 9 years ago
parent
commit
7917d94e55

+ 5 - 0
ChangeLog

@@ -1,3 +1,8 @@
+1090.	[func]		fdupont
+	Added support for IP address (IPv4 and IPv6) literals in
+	classification expressions.
+	(Trac #4232, git xxx)
+
 1089.	[func]		fdupont
 1089.	[func]		fdupont
 	Added relay4[X].exists method in classifications that checks
 	Added relay4[X].exists method in classifications that checks
 	whether a sub-option is present in theDHCPv4 RAI (Relay Agent
 	whether a sub-option is present in theDHCPv4 RAI (Relay Agent

+ 6 - 0
doc/guide/classify.xml

@@ -159,6 +159,7 @@
           <tbody>
           <tbody>
 <row><entry>String</entry><entry>'example'</entry><entry>A string</entry></row>
 <row><entry>String</entry><entry>'example'</entry><entry>A string</entry></row>
 <row><entry>Hex String</entry><entry>0XABCD</entry><entry>A hexadecimal string</entry></row>
 <row><entry>Hex String</entry><entry>0XABCD</entry><entry>A hexadecimal string</entry></row>
+<row><entry>IP Address</entry><entry>10.0.0.1</entry><entry>An IP address</entry></row>
 <row><entry>Integer</entry><entry>123</entry><entry>An integer value</entry></row>
 <row><entry>Integer</entry><entry>123</entry><entry>An integer value</entry></row>
 <!-- Text option not fully defined yet, leave it out
 <!-- Text option not fully defined yet, leave it out
 <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 Text</entry><entry>option[code].text</entry><entry>The value of the option with code "code" from the packet as text</entry></row>
@@ -178,6 +179,11 @@ sub-option with code "code" from the DHCPv4 Relay Agent Information option
       </para>
       </para>
 
 
       <para>
       <para>
+      IP addresses are converted into strings of length 4 or 16. IPv4, IPv6,
+      and IPv4 embedded IPv6 (e.g., IPv4 mapped IPv6) addresses are supported.
+      </para>
+
+      <para>
       Integers in the expression are converted to strings
       Integers in the expression are converted to strings
       when the expression is read into Kea.
       when the expression is read into Kea.
       </para>
       </para>

File diff suppressed because it is too large
+ 273 - 297
src/lib/eval/lexer.cc


+ 19 - 1
src/lib/eval/lexer.ll

@@ -11,6 +11,7 @@
 #include <string>
 #include <string>
 #include <eval/eval_context.h>
 #include <eval/eval_context.h>
 #include <eval/parser.h>
 #include <eval/parser.h>
+#include <asiolink/io_address.h>
 #include <boost/lexical_cast.hpp>
 #include <boost/lexical_cast.hpp>
 
 
 // Work around an incompatibility in flex (at least versions
 // Work around an incompatibility in flex (at least versions
@@ -56,10 +57,13 @@ static isc::eval::location loc;
 %option yylineno
 %option yylineno
 
 
 /* These are not token expressions yet, just convenience expressions that
 /* These are not token expressions yet, just convenience expressions that
-   can be used during actual token definitions. */
+   can be used during actual token definitions. Note some can match
+   incorrect inputs (e.g., IP addresses) which must be checked. */
 int   \-?[0-9]+
 int   \-?[0-9]+
 hex   [0-9a-fA-F]+
 hex   [0-9a-fA-F]+
 blank [ \t]
 blank [ \t]
+addr4 [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+
+addr6 [0-9a-fA-F]*\:[0-9a-fA-F]*\:[0-9a-fA-F:.]*
 
 
 %{
 %{
 // This code run each time a pattern is matched. It updates the location
 // This code run each time a pattern is matched. It updates the location
@@ -122,6 +126,20 @@ blank [ \t]
     return isc::eval::EvalParser::make_OPTION_NAME(yytext, loc);
     return isc::eval::EvalParser::make_OPTION_NAME(yytext, loc);
 }
 }
 
 
+{addr4}|{addr6} {
+    // IPv4 or IPv6 address
+    std::string tmp(yytext);
+
+    // Some incorrect addresses can match so we have to check.
+    try {
+        isc::asiolink::IOAddress ip(tmp);
+    } catch (...) {
+        driver.error(loc, "Failed to convert " + tmp + " to an IP address.");
+    }
+
+    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);

+ 4 - 4
src/lib/eval/location.hh

@@ -40,9 +40,9 @@
 
 
 # include "position.hh"
 # include "position.hh"
 
 
-#line 13 "parser.yy" // location.cc:296
+#line 13 "parser.yy" // location.cc:337
 namespace isc { namespace eval {
 namespace isc { namespace eval {
-#line 46 "location.hh" // location.cc:296
+#line 46 "location.hh" // location.cc:337
   /// Abstract a location.
   /// Abstract a location.
   class location
   class location
   {
   {
@@ -186,7 +186,7 @@ namespace isc { namespace eval {
     return ostr;
     return ostr;
   }
   }
 
 
-#line 13 "parser.yy" // location.cc:296
+#line 13 "parser.yy" // location.cc:337
 } } // isc::eval
 } } // isc::eval
-#line 192 "location.hh" // location.cc:296
+#line 192 "location.hh" // location.cc:337
 #endif // !YY_YY_LOCATION_HH_INCLUDED
 #endif // !YY_YY_LOCATION_HH_INCLUDED

+ 138 - 121
src/lib/eval/parser.cc

@@ -251,7 +251,7 @@ namespace isc { namespace eval {
   {
   {
       switch (that.type_get ())
       switch (that.type_get ())
     {
     {
-      case 30: // option_repr_type
+      case 31: // option_repr_type
         value.move< TokenOption::RepresentationType > (that.value);
         value.move< TokenOption::RepresentationType > (that.value);
         break;
         break;
 
 
@@ -259,10 +259,11 @@ namespace isc { namespace eval {
       case 22: // "integer"
       case 22: // "integer"
       case 23: // "constant hexstring"
       case 23: // "constant hexstring"
       case 24: // "option name"
       case 24: // "option name"
+      case 25: // "ip address"
         value.move< std::string > (that.value);
         value.move< std::string > (that.value);
         break;
         break;
 
 
-      case 29: // option_code
+      case 30: // option_code
         value.move< uint16_t > (that.value);
         value.move< uint16_t > (that.value);
         break;
         break;
 
 
@@ -281,7 +282,7 @@ namespace isc { namespace eval {
     state = that.state;
     state = that.state;
       switch (that.type_get ())
       switch (that.type_get ())
     {
     {
-      case 30: // option_repr_type
+      case 31: // option_repr_type
         value.copy< TokenOption::RepresentationType > (that.value);
         value.copy< TokenOption::RepresentationType > (that.value);
         break;
         break;
 
 
@@ -289,10 +290,11 @@ namespace isc { namespace eval {
       case 22: // "integer"
       case 22: // "integer"
       case 23: // "constant hexstring"
       case 23: // "constant hexstring"
       case 24: // "option name"
       case 24: // "option name"
+      case 25: // "ip address"
         value.copy< std::string > (that.value);
         value.copy< std::string > (that.value);
         break;
         break;
 
 
-      case 29: // option_code
+      case 30: // option_code
         value.copy< uint16_t > (that.value);
         value.copy< uint16_t > (that.value);
         break;
         break;
 
 
@@ -334,44 +336,51 @@ namespace isc { namespace eval {
     {
     {
             case 21: // "constant string"
             case 21: // "constant string"
 
 
-#line 72 "parser.yy" // lalr1.cc:636
+#line 73 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 340 "parser.cc" // lalr1.cc:636
+#line 342 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
       case 22: // "integer"
       case 22: // "integer"
 
 
-#line 72 "parser.yy" // lalr1.cc:636
+#line 73 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 347 "parser.cc" // lalr1.cc:636
+#line 349 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
       case 23: // "constant hexstring"
       case 23: // "constant hexstring"
 
 
-#line 72 "parser.yy" // lalr1.cc:636
+#line 73 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 354 "parser.cc" // lalr1.cc:636
+#line 356 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
       case 24: // "option name"
       case 24: // "option name"
 
 
-#line 72 "parser.yy" // lalr1.cc:636
+#line 73 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 361 "parser.cc" // lalr1.cc:636
+#line 363 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
-      case 29: // option_code
+      case 25: // "ip address"
 
 
-#line 72 "parser.yy" // lalr1.cc:636
+#line 73 "parser.yy" // lalr1.cc:636
+        { yyoutput << yysym.value.template as< std::string > (); }
+#line 370 "parser.cc" // lalr1.cc:636
+        break;
+
+      case 30: // option_code
+
+#line 73 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< uint16_t > (); }
         { yyoutput << yysym.value.template as< uint16_t > (); }
-#line 368 "parser.cc" // lalr1.cc:636
+#line 377 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
-      case 30: // option_repr_type
+      case 31: // option_repr_type
 
 
-#line 72 "parser.yy" // lalr1.cc:636
+#line 73 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< TokenOption::RepresentationType > (); }
         { yyoutput << yysym.value.template as< TokenOption::RepresentationType > (); }
-#line 375 "parser.cc" // lalr1.cc:636
+#line 384 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
 
 
@@ -571,7 +580,7 @@ namespace isc { namespace eval {
          when using variants.  */
          when using variants.  */
         switch (yyr1_[yyn])
         switch (yyr1_[yyn])
     {
     {
-      case 30: // option_repr_type
+      case 31: // option_repr_type
         yylhs.value.build< TokenOption::RepresentationType > ();
         yylhs.value.build< TokenOption::RepresentationType > ();
         break;
         break;
 
 
@@ -579,10 +588,11 @@ namespace isc { namespace eval {
       case 22: // "integer"
       case 22: // "integer"
       case 23: // "constant hexstring"
       case 23: // "constant hexstring"
       case 24: // "option name"
       case 24: // "option name"
+      case 25: // "ip address"
         yylhs.value.build< std::string > ();
         yylhs.value.build< std::string > ();
         break;
         break;
 
 
-      case 29: // option_code
+      case 30: // option_code
         yylhs.value.build< uint16_t > ();
         yylhs.value.build< uint16_t > ();
         break;
         break;
 
 
@@ -604,52 +614,52 @@ namespace isc { namespace eval {
           switch (yyn)
           switch (yyn)
             {
             {
   case 4:
   case 4:
-#line 86 "parser.yy" // lalr1.cc:859
+#line 87 "parser.yy" // lalr1.cc:859
     {
     {
                     TokenPtr neg(new TokenNot());
                     TokenPtr neg(new TokenNot());
                     ctx.expression.push_back(neg);
                     ctx.expression.push_back(neg);
                 }
                 }
-#line 613 "parser.cc" // lalr1.cc:859
+#line 623 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 5:
   case 5:
-#line 91 "parser.yy" // lalr1.cc:859
+#line 92 "parser.yy" // lalr1.cc:859
     {
     {
                     TokenPtr neg(new TokenAnd());
                     TokenPtr neg(new TokenAnd());
                     ctx.expression.push_back(neg);
                     ctx.expression.push_back(neg);
                 }
                 }
-#line 622 "parser.cc" // lalr1.cc:859
+#line 632 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 6:
   case 6:
-#line 96 "parser.yy" // lalr1.cc:859
+#line 97 "parser.yy" // lalr1.cc:859
     {
     {
                     TokenPtr neg(new TokenOr());
                     TokenPtr neg(new TokenOr());
                     ctx.expression.push_back(neg);
                     ctx.expression.push_back(neg);
                 }
                 }
-#line 631 "parser.cc" // lalr1.cc:859
+#line 641 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 7:
   case 7:
-#line 101 "parser.yy" // lalr1.cc:859
+#line 102 "parser.yy" // lalr1.cc:859
     {
     {
                     TokenPtr eq(new TokenEqual());
                     TokenPtr eq(new TokenEqual());
                     ctx.expression.push_back(eq);
                     ctx.expression.push_back(eq);
                 }
                 }
-#line 640 "parser.cc" // lalr1.cc:859
+#line 650 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 8:
   case 8:
-#line 106 "parser.yy" // lalr1.cc:859
+#line 107 "parser.yy" // lalr1.cc:859
     {
     {
                     TokenPtr opt(new TokenOption(yystack_[3].value.as< uint16_t > (), TokenOption::EXISTS));
                     TokenPtr opt(new TokenOption(yystack_[3].value.as< uint16_t > (), TokenOption::EXISTS));
                     ctx.expression.push_back(opt);
                     ctx.expression.push_back(opt);
                 }
                 }
-#line 649 "parser.cc" // lalr1.cc:859
+#line 659 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 9:
   case 9:
-#line 111 "parser.yy" // lalr1.cc:859
+#line 112 "parser.yy" // lalr1.cc:859
     {
     {
                    switch (ctx.getUniverse()) {
                    switch (ctx.getUniverse()) {
                    case Option::V4:
                    case Option::V4:
@@ -669,38 +679,47 @@ namespace isc { namespace eval {
                        error(yystack_[5].location, "relay4 can only be used in DHCPv4.");
                        error(yystack_[5].location, "relay4 can only be used in DHCPv4.");
                    }
                    }
                 }
                 }
-#line 673 "parser.cc" // lalr1.cc:859
+#line 683 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 10:
   case 10:
-#line 133 "parser.yy" // lalr1.cc:859
+#line 134 "parser.yy" // lalr1.cc:859
     {
     {
                       TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                       TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                       ctx.expression.push_back(str);
                       ctx.expression.push_back(str);
                   }
                   }
-#line 682 "parser.cc" // lalr1.cc:859
+#line 692 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 11:
   case 11:
-#line 138 "parser.yy" // lalr1.cc:859
+#line 139 "parser.yy" // lalr1.cc:859
     {
     {
                       TokenPtr hex(new TokenHexString(yystack_[0].value.as< std::string > ()));
                       TokenPtr hex(new TokenHexString(yystack_[0].value.as< std::string > ()));
                       ctx.expression.push_back(hex);
                       ctx.expression.push_back(hex);
                   }
                   }
-#line 691 "parser.cc" // lalr1.cc:859
+#line 701 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 12:
   case 12:
-#line 143 "parser.yy" // lalr1.cc:859
+#line 144 "parser.yy" // lalr1.cc:859
+    {
+                      TokenPtr ip(new TokenIpAddress(yystack_[0].value.as< std::string > ()));
+                      ctx.expression.push_back(ip);
+                  }
+#line 710 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 13:
+#line 149 "parser.yy" // lalr1.cc:859
     {
     {
                       TokenPtr opt(new TokenOption(yystack_[3].value.as< uint16_t > (), yystack_[0].value.as< TokenOption::RepresentationType > ()));
                       TokenPtr opt(new TokenOption(yystack_[3].value.as< uint16_t > (), yystack_[0].value.as< TokenOption::RepresentationType > ()));
                       ctx.expression.push_back(opt);
                       ctx.expression.push_back(opt);
                   }
                   }
-#line 700 "parser.cc" // lalr1.cc:859
+#line 719 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 13:
-#line 148 "parser.yy" // lalr1.cc:859
+  case 14:
+#line 154 "parser.yy" // lalr1.cc:859
     {
     {
                      switch (ctx.getUniverse()) {
                      switch (ctx.getUniverse()) {
                      case Option::V4:
                      case Option::V4:
@@ -720,88 +739,88 @@ namespace isc { namespace eval {
                          error(yystack_[5].location, "relay4 can only be used in DHCPv4.");
                          error(yystack_[5].location, "relay4 can only be used in DHCPv4.");
                      }
                      }
                   }
                   }
-#line 724 "parser.cc" // lalr1.cc:859
+#line 743 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 14:
-#line 168 "parser.yy" // lalr1.cc:859
+  case 15:
+#line 174 "parser.yy" // lalr1.cc:859
     {
     {
                       TokenPtr sub(new TokenSubstring());
                       TokenPtr sub(new TokenSubstring());
                       ctx.expression.push_back(sub);
                       ctx.expression.push_back(sub);
                   }
                   }
-#line 733 "parser.cc" // lalr1.cc:859
+#line 752 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 15:
-#line 173 "parser.yy" // lalr1.cc:859
+  case 16:
+#line 179 "parser.yy" // lalr1.cc:859
     {
     {
                       TokenPtr conc(new TokenConcat());
                       TokenPtr conc(new TokenConcat());
                       ctx.expression.push_back(conc);
                       ctx.expression.push_back(conc);
                   }
                   }
-#line 742 "parser.cc" // lalr1.cc:859
+#line 761 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 16:
-#line 180 "parser.yy" // lalr1.cc:859
+  case 17:
+#line 186 "parser.yy" // lalr1.cc:859
     {
     {
                      yylhs.value.as< uint16_t > () = ctx.convertOptionCode(yystack_[0].value.as< std::string > (), yystack_[0].location);
                      yylhs.value.as< uint16_t > () = ctx.convertOptionCode(yystack_[0].value.as< std::string > (), yystack_[0].location);
                  }
                  }
-#line 750 "parser.cc" // lalr1.cc:859
+#line 769 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 17:
-#line 184 "parser.yy" // lalr1.cc:859
+  case 18:
+#line 190 "parser.yy" // lalr1.cc:859
     {
     {
                      yylhs.value.as< uint16_t > () = ctx.convertOptionName(yystack_[0].value.as< std::string > (), yystack_[0].location);
                      yylhs.value.as< uint16_t > () = ctx.convertOptionName(yystack_[0].value.as< std::string > (), yystack_[0].location);
                  }
                  }
-#line 758 "parser.cc" // lalr1.cc:859
+#line 777 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 18:
-#line 190 "parser.yy" // lalr1.cc:859
+  case 19:
+#line 196 "parser.yy" // lalr1.cc:859
     {
     {
                           yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::TEXTUAL;
                           yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::TEXTUAL;
                       }
                       }
-#line 766 "parser.cc" // lalr1.cc:859
+#line 785 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 19:
-#line 194 "parser.yy" // lalr1.cc:859
+  case 20:
+#line 200 "parser.yy" // lalr1.cc:859
     {
     {
                           yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::HEXADECIMAL;
                           yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::HEXADECIMAL;
                       }
                       }
-#line 774 "parser.cc" // lalr1.cc:859
+#line 793 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 20:
-#line 200 "parser.yy" // lalr1.cc:859
+  case 21:
+#line 206 "parser.yy" // lalr1.cc:859
     {
     {
                      TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                      TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                      ctx.expression.push_back(str);
                      ctx.expression.push_back(str);
                  }
                  }
-#line 783 "parser.cc" // lalr1.cc:859
+#line 802 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 21:
-#line 207 "parser.yy" // lalr1.cc:859
+  case 22:
+#line 213 "parser.yy" // lalr1.cc:859
     {
     {
                       TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                       TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                       ctx.expression.push_back(str);
                       ctx.expression.push_back(str);
                   }
                   }
-#line 792 "parser.cc" // lalr1.cc:859
+#line 811 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 22:
-#line 212 "parser.yy" // lalr1.cc:859
+  case 23:
+#line 218 "parser.yy" // lalr1.cc:859
     {
     {
                      TokenPtr str(new TokenString("all"));
                      TokenPtr str(new TokenString("all"));
                      ctx.expression.push_back(str);
                      ctx.expression.push_back(str);
                  }
                  }
-#line 801 "parser.cc" // lalr1.cc:859
+#line 820 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
 
 
-#line 805 "parser.cc" // lalr1.cc:859
+#line 824 "parser.cc" // lalr1.cc:859
             default:
             default:
               break;
               break;
             }
             }
@@ -1063,91 +1082,89 @@ namespace isc { namespace eval {
   const signed char
   const signed char
   EvalParser::yypact_[] =
   EvalParser::yypact_[] =
   {
   {
-      -1,    -1,    -1,     1,    10,    28,    36,   -29,   -29,    32,
-       0,    41,    29,   -29,    -7,    -7,    17,    17,   -29,    -1,
-      -1,    17,   -29,   -29,   -29,    38,    39,    42,    43,    37,
-      40,   -29,    46,   -29,    44,    45,    -7,    -7,    47,    17,
-      27,    30,    48,    49,   -29,    51,    58,   -29,   -29,   -29,
-     -29,   -29,   -29,    50,    52,    -4,   -29,    33,    33,   -29,
-     -29,    60,   -29
+      -1,    -1,    -1,    24,    27,    18,    37,   -29,   -29,   -29,
+      50,     6,    43,    11,   -29,    10,    10,    16,    16,   -29,
+      -1,    -1,    16,   -29,   -29,   -29,    40,    41,    44,    45,
+      35,    38,   -29,    52,   -29,    46,    47,    10,    10,    39,
+      16,    28,    31,    51,    53,   -29,    48,    58,   -29,   -29,
+     -29,   -29,   -29,   -29,    55,    56,   -15,   -29,    34,    34,
+     -29,   -29,    60,   -29
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yydefact_[] =
   EvalParser::yydefact_[] =
   {
   {
-       0,     0,     0,     0,     0,     0,     0,    10,    11,     0,
-       2,     0,     0,     4,     0,     0,     0,     0,     1,     0,
-       0,     0,     3,    16,    17,     0,     0,     0,     0,     0,
-       0,     5,     6,     7,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,    20,     0,     0,    18,    19,     8,
-      12,     9,    13,     0,     0,     0,    15,     0,     0,    22,
-      21,     0,    14
+       0,     0,     0,     0,     0,     0,     0,    10,    11,    12,
+       0,     2,     0,     0,     4,     0,     0,     0,     0,     1,
+       0,     0,     0,     3,    17,    18,     0,     0,     0,     0,
+       0,     0,     5,     6,     7,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,    21,     0,     0,    19,    20,
+       8,    13,     9,    14,     0,     0,     0,    16,     0,     0,
+      23,    22,     0,    15
   };
   };
 
 
   const signed char
   const signed char
   EvalParser::yypgoto_[] =
   EvalParser::yypgoto_[] =
   {
   {
-     -29,   -29,     9,   -16,   -12,   -28,   -29,   -29
+     -29,   -29,     9,   -17,   -10,   -28,   -29,   -29
   };
   };
 
 
   const signed char
   const signed char
   EvalParser::yydefgoto_[] =
   EvalParser::yydefgoto_[] =
   {
   {
-      -1,     9,    10,    11,    25,    50,    45,    61
+      -1,    10,    11,    12,    26,    51,    46,    62
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yytable_[] =
   EvalParser::yytable_[] =
   {
   {
-      29,    30,     1,    26,     2,    33,    19,    20,     3,     4,
-      12,    13,    14,    52,    59,    23,     5,    24,    60,     6,
-       7,    15,     8,    46,    42,    43,    27,    28,    31,    32,
-      52,    16,    18,    22,     5,    19,    20,     6,     7,    17,
-       8,    47,    48,    49,    47,    48,    51,    47,    48,    21,
-      34,    35,    19,    36,    37,     0,    38,    40,    41,    39,
-      53,    54,    56,    57,    62,    58,     0,     0,     0,    44,
-      55
+      30,    31,     1,    60,     2,    34,    27,    61,     3,     4,
+      13,    14,    20,    21,    53,    23,     5,    20,    21,     6,
+       7,    17,     8,    47,     9,    28,    29,    43,    44,    32,
+      33,    53,    24,     5,    25,    15,     6,     7,    16,     8,
+      18,     9,    48,    49,    50,    48,    49,    52,    48,    49,
+      19,    22,    35,    36,    39,    37,    38,    40,    20,    41,
+      42,    45,    57,    54,    63,    55,     0,    56,    58,    59
   };
   };
 
 
   const signed char
   const signed char
   EvalParser::yycheck_[] =
   EvalParser::yycheck_[] =
   {
   {
-      16,    17,     3,    15,     5,    21,     6,     7,     9,    10,
-       1,     2,    11,    41,    18,    22,    17,    24,    22,    20,
-      21,    11,    23,    39,    36,    37,     9,    10,    19,    20,
-      58,     3,     0,     4,    17,     6,     7,    20,    21,     3,
-      23,    14,    15,    16,    14,    15,    16,    14,    15,     8,
-      12,    12,     6,    11,    11,    -1,    19,    13,    13,    19,
-      12,    12,     4,    13,     4,    13,    -1,    -1,    -1,    22,
-      19
+      17,    18,     3,    18,     5,    22,    16,    22,     9,    10,
+       1,     2,     6,     7,    42,     4,    17,     6,     7,    20,
+      21,     3,    23,    40,    25,     9,    10,    37,    38,    20,
+      21,    59,    22,    17,    24,    11,    20,    21,    11,    23,
+       3,    25,    14,    15,    16,    14,    15,    16,    14,    15,
+       0,     8,    12,    12,    19,    11,    11,    19,     6,    13,
+      13,    22,     4,    12,     4,    12,    -1,    19,    13,    13
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yystos_[] =
   EvalParser::yystos_[] =
   {
   {
-       0,     3,     5,     9,    10,    17,    20,    21,    23,    26,
-      27,    28,    27,    27,    11,    11,     3,     3,     0,     6,
-       7,     8,     4,    22,    24,    29,    29,     9,    10,    28,
-      28,    27,    27,    28,    12,    12,    11,    11,    19,    19,
-      13,    13,    29,    29,    22,    31,    28,    14,    15,    16,
-      30,    16,    30,    12,    12,    19,     4,    13,    13,    18,
-      22,    32,     4
+       0,     3,     5,     9,    10,    17,    20,    21,    23,    25,
+      27,    28,    29,    28,    28,    11,    11,     3,     3,     0,
+       6,     7,     8,     4,    22,    24,    30,    30,     9,    10,
+      29,    29,    28,    28,    29,    12,    12,    11,    11,    19,
+      19,    13,    13,    30,    30,    22,    32,    29,    14,    15,
+      16,    31,    16,    31,    12,    12,    19,     4,    13,    13,
+      18,    22,    33,     4
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yyr1_[] =
   EvalParser::yyr1_[] =
   {
   {
-       0,    25,    26,    27,    27,    27,    27,    27,    27,    27,
-      28,    28,    28,    28,    28,    28,    29,    29,    30,    30,
-      31,    32,    32
+       0,    26,    27,    28,    28,    28,    28,    28,    28,    28,
+      29,    29,    29,    29,    29,    29,    29,    30,    30,    31,
+      31,    32,    33,    33
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yyr2_[] =
   EvalParser::yyr2_[] =
   {
   {
        0,     2,     1,     3,     2,     3,     3,     3,     6,     6,
        0,     2,     1,     3,     2,     3,     3,     3,     6,     6,
-       1,     1,     6,     6,     8,     6,     1,     1,     1,     1,
-       1,     1,     1
+       1,     1,     1,     6,     6,     8,     6,     1,     1,     1,
+       1,     1,     1,     1
   };
   };
 
 
 
 
@@ -1161,18 +1178,18 @@ namespace isc { namespace eval {
   "\"and\"", "\"or\"", "\"==\"", "\"option\"", "\"relay4\"", "\"[\"",
   "\"and\"", "\"or\"", "\"==\"", "\"option\"", "\"relay4\"", "\"[\"",
   "\"]\"", "\".\"", "\"text\"", "\"hex\"", "\"exists\"", "\"substring\"",
   "\"]\"", "\".\"", "\"text\"", "\"hex\"", "\"exists\"", "\"substring\"",
   "\"all\"", "\",\"", "\"concat\"", "\"constant string\"", "\"integer\"",
   "\"all\"", "\",\"", "\"concat\"", "\"constant string\"", "\"integer\"",
-  "\"constant hexstring\"", "\"option name\"", "$accept", "expression",
-  "bool_expr", "string_expr", "option_code", "option_repr_type",
-  "start_expr", "length_expr", YY_NULLPTR
+  "\"constant hexstring\"", "\"option name\"", "\"ip address\"", "$accept",
+  "expression", "bool_expr", "string_expr", "option_code",
+  "option_repr_type", "start_expr", "length_expr", YY_NULLPTR
   };
   };
 
 
 #if YYDEBUG
 #if YYDEBUG
   const unsigned char
   const unsigned char
   EvalParser::yyrline_[] =
   EvalParser::yyrline_[] =
   {
   {
-       0,    81,    81,    84,    85,    90,    95,   100,   105,   110,
-     132,   137,   142,   147,   167,   172,   179,   183,   189,   193,
-     199,   206,   211
+       0,    82,    82,    85,    86,    91,    96,   101,   106,   111,
+     133,   138,   143,   148,   153,   173,   178,   185,   189,   195,
+     199,   205,   212,   217
   };
   };
 
 
   // Print the state stack on the debug stream.
   // Print the state stack on the debug stream.
@@ -1207,8 +1224,8 @@ namespace isc { namespace eval {
 
 
 #line 13 "parser.yy" // lalr1.cc:1167
 #line 13 "parser.yy" // lalr1.cc:1167
 } } // isc::eval
 } } // isc::eval
-#line 1211 "parser.cc" // lalr1.cc:1167
-#line 218 "parser.yy" // lalr1.cc:1168
+#line 1228 "parser.cc" // lalr1.cc:1167
+#line 224 "parser.yy" // lalr1.cc:1168
 
 
 void
 void
 isc::eval::EvalParser::error(const location_type& loc,
 isc::eval::EvalParser::error(const location_type& loc,

+ 38 - 21
src/lib/eval/parser.h

@@ -40,7 +40,7 @@
 #ifndef YY_YY_PARSER_H_INCLUDED
 #ifndef YY_YY_PARSER_H_INCLUDED
 # define YY_YY_PARSER_H_INCLUDED
 # define YY_YY_PARSER_H_INCLUDED
 // //                    "%code requires" blocks.
 // //                    "%code requires" blocks.
-#line 16 "parser.yy" // lalr1.cc:377
+#line 16 "parser.yy" // lalr1.cc:392
 
 
 #include <string>
 #include <string>
 #include <eval/token.h>
 #include <eval/token.h>
@@ -51,7 +51,7 @@
 using namespace isc::dhcp;
 using namespace isc::dhcp;
 using namespace isc::eval;
 using namespace isc::eval;
 
 
-#line 55 "parser.h" // lalr1.cc:377
+#line 55 "parser.h" // lalr1.cc:392
 
 
 # include <cassert>
 # include <cassert>
 # include <cstdlib> // std::abort
 # include <cstdlib> // std::abort
@@ -126,9 +126,9 @@ using namespace isc::eval;
 # define YYDEBUG 1
 # define YYDEBUG 1
 #endif
 #endif
 
 
-#line 13 "parser.yy" // lalr1.cc:377
+#line 13 "parser.yy" // lalr1.cc:392
 namespace isc { namespace eval {
 namespace isc { namespace eval {
-#line 132 "parser.h" // lalr1.cc:377
+#line 132 "parser.h" // lalr1.cc:392
 
 
 
 
 
 
@@ -302,6 +302,7 @@ namespace isc { namespace eval {
       // "integer"
       // "integer"
       // "constant hexstring"
       // "constant hexstring"
       // "option name"
       // "option name"
+      // "ip address"
       char dummy2[sizeof(std::string)];
       char dummy2[sizeof(std::string)];
 
 
       // option_code
       // option_code
@@ -350,7 +351,8 @@ namespace isc { namespace eval {
         TOKEN_STRING = 276,
         TOKEN_STRING = 276,
         TOKEN_INTEGER = 277,
         TOKEN_INTEGER = 277,
         TOKEN_HEXSTRING = 278,
         TOKEN_HEXSTRING = 278,
-        TOKEN_OPTION_NAME = 279
+        TOKEN_OPTION_NAME = 279,
+        TOKEN_IP_ADDRESS = 280
       };
       };
     };
     };
 
 
@@ -553,6 +555,10 @@ namespace isc { namespace eval {
     symbol_type
     symbol_type
     make_OPTION_NAME (const std::string& v, const location_type& l);
     make_OPTION_NAME (const std::string& v, const location_type& l);
 
 
+    static inline
+    symbol_type
+    make_IP_ADDRESS (const std::string& v, const location_type& l);
+
 
 
     /// Build a parser object.
     /// Build a parser object.
     EvalParser (EvalContext& ctx_yyarg);
     EvalParser (EvalContext& ctx_yyarg);
@@ -758,12 +764,12 @@ namespace isc { namespace eval {
     enum
     enum
     {
     {
       yyeof_ = 0,
       yyeof_ = 0,
-      yylast_ = 70,     ///< Last index in yytable_.
+      yylast_ = 69,     ///< Last index in yytable_.
       yynnts_ = 8,  ///< Number of nonterminal symbols.
       yynnts_ = 8,  ///< Number of nonterminal symbols.
-      yyfinal_ = 18, ///< Termination state number.
+      yyfinal_ = 19, ///< Termination state number.
       yyterror_ = 1,
       yyterror_ = 1,
       yyerrcode_ = 256,
       yyerrcode_ = 256,
-      yyntokens_ = 25  ///< Number of tokens.
+      yyntokens_ = 26  ///< Number of tokens.
     };
     };
 
 
 
 
@@ -807,9 +813,10 @@ namespace isc { namespace eval {
        2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
        2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
        2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
        2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
        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
     };
     };
-    const unsigned int user_token_number_max_ = 279;
+    const unsigned int user_token_number_max_ = 280;
     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_)
@@ -842,7 +849,7 @@ namespace isc { namespace eval {
   {
   {
       switch (other.type_get ())
       switch (other.type_get ())
     {
     {
-      case 30: // option_repr_type
+      case 31: // option_repr_type
         value.copy< TokenOption::RepresentationType > (other.value);
         value.copy< TokenOption::RepresentationType > (other.value);
         break;
         break;
 
 
@@ -850,10 +857,11 @@ namespace isc { namespace eval {
       case 22: // "integer"
       case 22: // "integer"
       case 23: // "constant hexstring"
       case 23: // "constant hexstring"
       case 24: // "option name"
       case 24: // "option name"
+      case 25: // "ip address"
         value.copy< std::string > (other.value);
         value.copy< std::string > (other.value);
         break;
         break;
 
 
-      case 29: // option_code
+      case 30: // option_code
         value.copy< uint16_t > (other.value);
         value.copy< uint16_t > (other.value);
         break;
         break;
 
 
@@ -874,7 +882,7 @@ namespace isc { namespace eval {
     (void) v;
     (void) v;
       switch (this->type_get ())
       switch (this->type_get ())
     {
     {
-      case 30: // option_repr_type
+      case 31: // option_repr_type
         value.copy< TokenOption::RepresentationType > (v);
         value.copy< TokenOption::RepresentationType > (v);
         break;
         break;
 
 
@@ -882,10 +890,11 @@ namespace isc { namespace eval {
       case 22: // "integer"
       case 22: // "integer"
       case 23: // "constant hexstring"
       case 23: // "constant hexstring"
       case 24: // "option name"
       case 24: // "option name"
+      case 25: // "ip address"
         value.copy< std::string > (v);
         value.copy< std::string > (v);
         break;
         break;
 
 
-      case 29: // option_code
+      case 30: // option_code
         value.copy< uint16_t > (v);
         value.copy< uint16_t > (v);
         break;
         break;
 
 
@@ -951,7 +960,7 @@ namespace isc { namespace eval {
     // Type destructor.
     // Type destructor.
     switch (yytype)
     switch (yytype)
     {
     {
-      case 30: // option_repr_type
+      case 31: // option_repr_type
         value.template destroy< TokenOption::RepresentationType > ();
         value.template destroy< TokenOption::RepresentationType > ();
         break;
         break;
 
 
@@ -959,10 +968,11 @@ namespace isc { namespace eval {
       case 22: // "integer"
       case 22: // "integer"
       case 23: // "constant hexstring"
       case 23: // "constant hexstring"
       case 24: // "option name"
       case 24: // "option name"
+      case 25: // "ip address"
         value.template destroy< std::string > ();
         value.template destroy< std::string > ();
         break;
         break;
 
 
-      case 29: // option_code
+      case 30: // option_code
         value.template destroy< uint16_t > ();
         value.template destroy< uint16_t > ();
         break;
         break;
 
 
@@ -989,7 +999,7 @@ namespace isc { namespace eval {
     super_type::move(s);
     super_type::move(s);
       switch (this->type_get ())
       switch (this->type_get ())
     {
     {
-      case 30: // option_repr_type
+      case 31: // option_repr_type
         value.move< TokenOption::RepresentationType > (s.value);
         value.move< TokenOption::RepresentationType > (s.value);
         break;
         break;
 
 
@@ -997,10 +1007,11 @@ namespace isc { namespace eval {
       case 22: // "integer"
       case 22: // "integer"
       case 23: // "constant hexstring"
       case 23: // "constant hexstring"
       case 24: // "option name"
       case 24: // "option name"
+      case 25: // "ip address"
         value.move< std::string > (s.value);
         value.move< std::string > (s.value);
         break;
         break;
 
 
-      case 29: // option_code
+      case 30: // option_code
         value.move< uint16_t > (s.value);
         value.move< uint16_t > (s.value);
         break;
         break;
 
 
@@ -1061,7 +1072,7 @@ namespace isc { namespace eval {
     {
     {
        0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
        0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
      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
+     275,   276,   277,   278,   279,   280
     };
     };
     return static_cast<token_type> (yytoken_number_[type]);
     return static_cast<token_type> (yytoken_number_[type]);
   }
   }
@@ -1204,10 +1215,16 @@ namespace isc { namespace eval {
     return symbol_type (token::TOKEN_OPTION_NAME, v, l);
     return symbol_type (token::TOKEN_OPTION_NAME, v, l);
   }
   }
 
 
+  EvalParser::symbol_type
+  EvalParser::make_IP_ADDRESS (const std::string& v, const location_type& l)
+  {
+    return symbol_type (token::TOKEN_IP_ADDRESS, v, l);
+  }
+
 
 
-#line 13 "parser.yy" // lalr1.cc:377
+#line 13 "parser.yy" // lalr1.cc:392
 } } // isc::eval
 } } // isc::eval
-#line 1211 "parser.h" // lalr1.cc:377
+#line 1228 "parser.h" // lalr1.cc:392
 
 
 
 
 
 

+ 6 - 0
src/lib/eval/parser.yy

@@ -61,6 +61,7 @@ using namespace isc::eval;
 %token <std::string> INTEGER "integer"
 %token <std::string> INTEGER "integer"
 %token <std::string> HEXSTRING "constant hexstring"
 %token <std::string> HEXSTRING "constant hexstring"
 %token <std::string> OPTION_NAME "option name"
 %token <std::string> OPTION_NAME "option name"
+%token <std::string> IP_ADDRESS "ip address"
 
 
 %type <uint16_t> option_code
 %type <uint16_t> option_code
 %type <TokenOption::RepresentationType> option_repr_type
 %type <TokenOption::RepresentationType> option_repr_type
@@ -139,6 +140,11 @@ string_expr : STRING
                       TokenPtr hex(new TokenHexString($1));
                       TokenPtr hex(new TokenHexString($1));
                       ctx.expression.push_back(hex);
                       ctx.expression.push_back(hex);
                   }
                   }
+            | IP_ADDRESS
+                  {
+                      TokenPtr ip(new TokenIpAddress($1));
+                      ctx.expression.push_back(ip);
+                  }
             | OPTION "[" option_code "]" "." option_repr_type
             | OPTION "[" option_code "]" "." option_repr_type
                   {
                   {
                       TokenPtr opt(new TokenOption($3, $6));
                       TokenPtr opt(new TokenOption($3, $6));

+ 4 - 4
src/lib/eval/position.hh

@@ -50,9 +50,9 @@
 #  endif
 #  endif
 # endif
 # endif
 
 
-#line 13 "parser.yy" // location.cc:296
+#line 13 "parser.yy" // location.cc:337
 namespace isc { namespace eval {
 namespace isc { namespace eval {
-#line 56 "position.hh" // location.cc:296
+#line 56 "position.hh" // location.cc:337
   /// Abstract a position.
   /// Abstract a position.
   class position
   class position
   {
   {
@@ -174,7 +174,7 @@ namespace isc { namespace eval {
     return ostr << pos.line << '.' << pos.column;
     return ostr << pos.line << '.' << pos.column;
   }
   }
 
 
-#line 13 "parser.yy" // location.cc:296
+#line 13 "parser.yy" // location.cc:337
 } } // isc::eval
 } } // isc::eval
-#line 180 "position.hh" // location.cc:296
+#line 180 "position.hh" // location.cc:337
 #endif // !YY_YY_POSITION_HH_INCLUDED
 #endif // !YY_YY_POSITION_HH_INCLUDED

+ 4 - 4
src/lib/eval/stack.hh

@@ -40,9 +40,9 @@
 
 
 # include <vector>
 # include <vector>
 
 
-#line 13 "parser.yy" // stack.hh:132
+#line 13 "parser.yy" // stack.hh:151
 namespace isc { namespace eval {
 namespace isc { namespace eval {
-#line 46 "stack.hh" // stack.hh:132
+#line 46 "stack.hh" // stack.hh:151
   template <class T, class S = std::vector<T> >
   template <class T, class S = std::vector<T> >
   class stack
   class stack
   {
   {
@@ -150,8 +150,8 @@ namespace isc { namespace eval {
     unsigned int range_;
     unsigned int range_;
   };
   };
 
 
-#line 13 "parser.yy" // stack.hh:132
+#line 13 "parser.yy" // stack.hh:151
 } } // isc::eval
 } } // isc::eval
-#line 156 "stack.hh" // stack.hh:132
+#line 156 "stack.hh" // stack.hh:151
 
 
 #endif // !YY_YY_STACK_HH_INCLUDED
 #endif // !YY_YY_STACK_HH_INCLUDED

+ 103 - 0
src/lib/eval/tests/context_unittest.cc

@@ -10,12 +10,14 @@
 #include <eval/token.h>
 #include <eval/token.h>
 #include <dhcp/option.h>
 #include <dhcp/option.h>
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt4.h>
+#include <asiolink/io_address.h>
 
 
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
 using namespace std;
 using namespace std;
+using namespace isc::asiolink;
 using namespace isc::dhcp;
 using namespace isc::dhcp;
 
 
 namespace {
 namespace {
@@ -63,6 +65,29 @@ public:
         EXPECT_EQ(expected, values.top());
         EXPECT_EQ(expected, values.top());
     }
     }
 
 
+    /// @brief checks if the given token is an IP address with the expected value
+    void checkTokenIpAddress(const TokenPtr& token,
+                             const std::string& expected) {
+        ASSERT_TRUE(token);
+        boost::shared_ptr<TokenIpAddress> ipaddr =
+            boost::dynamic_pointer_cast<TokenIpAddress>(token);
+        ASSERT_TRUE(ipaddr);
+
+        Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 12345));
+        ValueStack values;
+
+        EXPECT_NO_THROW(token->evaluate(*pkt4, values));
+
+        ASSERT_EQ(1, values.size());
+        string value = values.top();
+
+        boost::scoped_ptr<IOAddress> exp_ip;
+        ASSERT_NO_THROW(exp_ip.reset(new IOAddress(expected)));
+        vector<uint8_t> exp_addr = exp_ip->toBytes();
+        ASSERT_EQ(exp_addr.size(), value.size());
+        EXPECT_EQ(0, memcmp(&exp_addr[0], &value[0], value.size()));
+    }
+
     /// @brief checks if the given token is an equal operator
     /// @brief checks if the given token is an equal operator
     void checkTokenEq(const TokenPtr& token) {
     void checkTokenEq(const TokenPtr& token) {
         ASSERT_TRUE(token);
         ASSERT_TRUE(token);
@@ -215,6 +240,77 @@ TEST_F(EvalContextTest, oddHexstring) {
     checkTokenHexString(tmp, "\a");
     checkTokenHexString(tmp, "\a");
 }
 }
 
 
+// Test the parsing of an IPv4 address
+TEST_F(EvalContextTest, ipaddress4) {
+    EvalContext eval(Option::V6);
+
+    EXPECT_NO_THROW(parsed_ = eval.parseString("10.0.0.1 == 'foo'"));
+    EXPECT_TRUE(parsed_);
+
+    ASSERT_EQ(3, eval.expression.size());
+
+    TokenPtr tmp = eval.expression.at(0);
+
+    checkTokenIpAddress(tmp, "10.0.0.1");
+}
+
+// Test the parsing of an IPv6 address
+TEST_F(EvalContextTest, ipaddress6) {
+    EvalContext eval(Option::V6);
+
+    EXPECT_NO_THROW(parsed_ = eval.parseString("2001:db8::1 == 'foo'"));
+    EXPECT_TRUE(parsed_);
+
+    ASSERT_EQ(3, eval.expression.size());
+
+    TokenPtr tmp = eval.expression.at(0);
+
+    checkTokenIpAddress(tmp, "2001:db8::1");
+}
+
+// Test the parsing of an IPv4 compatible IPv6 address
+TEST_F(EvalContextTest, ipaddress46) {
+    EvalContext eval(Option::V6);
+
+    EXPECT_NO_THROW(parsed_ = eval.parseString("::10.0.0.1 == 'foo'"));
+    EXPECT_TRUE(parsed_);
+
+    ASSERT_EQ(3, eval.expression.size());
+
+    TokenPtr tmp = eval.expression.at(0);
+
+    checkTokenIpAddress(tmp, "::10.0.0.1");
+}
+
+// Test the parsing of the unspecified IPv6 address
+TEST_F(EvalContextTest, ipaddress6unspec) {
+    EvalContext eval(Option::V6);
+
+    EXPECT_NO_THROW(parsed_ = eval.parseString(":: == 'foo'"));
+    EXPECT_TRUE(parsed_);
+
+    ASSERT_EQ(3, eval.expression.size());
+
+    TokenPtr tmp = eval.expression.at(0);
+
+    checkTokenIpAddress(tmp, "::");
+}
+
+// Test the parsing of an IPv6 prefix
+TEST_F(EvalContextTest, ipaddress6prefix) {
+    EvalContext eval(Option::V6);
+
+    EXPECT_NO_THROW(parsed_ = eval.parseString("2001:db8:: == 'foo'"));
+    EXPECT_TRUE(parsed_);
+
+    ASSERT_EQ(3, eval.expression.size());
+
+    TokenPtr tmp = eval.expression.at(0);
+
+    checkTokenIpAddress(tmp, "2001:db8::");
+}
+
+
 // Test the parsing of an equal expression
 // Test the parsing of an equal expression
 TEST_F(EvalContextTest, equal) {
 TEST_F(EvalContextTest, equal) {
     EvalContext eval(Option::V4);
     EvalContext eval(Option::V4);
@@ -486,6 +582,7 @@ TEST_F(EvalContextTest, scanErrors) {
     checkError("'\''", "<string>:1.3: Invalid character: '");
     checkError("'\''", "<string>:1.3: Invalid character: '");
     checkError("'\n'", "<string>:1.1: Invalid character: '");
     checkError("'\n'", "<string>:1.1: Invalid character: '");
     checkError("0x123h", "<string>:1.6: Invalid character: h");
     checkError("0x123h", "<string>:1.6: Invalid character: h");
+    checkError(":1", "<string>:1.1: Invalid character: :");
     checkError("=", "<string>:1.1: Invalid character: =");
     checkError("=", "<string>:1.1: Invalid character: =");
     checkError("subtring", "<string>:1.1: Invalid character: s");
     checkError("subtring", "<string>:1.1: Invalid character: s");
     checkError("foo", "<string>:1.1: Invalid character: f");
     checkError("foo", "<string>:1.1: Invalid character: f");
@@ -500,6 +597,12 @@ TEST_F(EvalContextTest, scanParseErrors) {
     checkError("0x", "<string>:1.1: syntax error, unexpected integer");
     checkError("0x", "<string>:1.1: syntax error, unexpected integer");
     checkError("0abc",
     checkError("0abc",
                "<string>:1.1: syntax error, unexpected integer");
                "<string>:1.1: syntax error, unexpected integer");
+    checkError("10.0.1", "<string>:1.1-2: syntax error, unexpected integer");
+    checkError("10.256.0.1",
+               "<string>:1.1-10: Failed to convert 10.256.0.1 to "
+               "an IP address.");
+    checkError(":::",
+               "<string>:1.1-3: Failed to convert ::: to an IP address.");
     checkError("===", "<string>:1.1-2: syntax error, unexpected ==");
     checkError("===", "<string>:1.1-2: syntax error, unexpected ==");
     checkError("option[-1].text",
     checkError("option[-1].text",
                "<string>:1.8-9: Option code has invalid "
                "<string>:1.8-9: Option code has invalid "

+ 45 - 0
src/lib/eval/tests/token_unittest.cc

@@ -283,6 +283,51 @@ TEST_F(TokenTest, hexstring6) {
     EXPECT_EQ("", values_.top());
     EXPECT_EQ("", values_.top());
 }
 }
 
 
+// This test checks that a TokenIpAddress, representing an IP address as
+// a constant string, can be used in Pkt4/Pkt6 evaluation.
+// (The actual packet is not used)
+TEST_F(TokenTest, ipaddress) {
+    TokenPtr bad4;
+    TokenPtr bad6;
+    TokenPtr ip4;
+    TokenPtr ip6;
+
+    // Bad IP addresses
+    ASSERT_NO_THROW(bad4.reset(new TokenIpAddress("10.0.0.0.1")));
+    ASSERT_NO_THROW(bad6.reset(new TokenIpAddress(":::")));
+
+    // IP addresses
+    ASSERT_NO_THROW(ip4.reset(new TokenIpAddress("10.0.0.1")));
+    ASSERT_NO_THROW(ip6.reset(new TokenIpAddress("2001:db8::1")));
+
+    // Make sure that tokens can be evaluated without exceptions.
+    ASSERT_NO_THROW(ip4->evaluate(*pkt4_, values_));
+    ASSERT_NO_THROW(ip6->evaluate(*pkt6_, values_));
+    ASSERT_NO_THROW(bad4->evaluate(*pkt4_, values_));
+    ASSERT_NO_THROW(bad6->evaluate(*pkt6_, values_));
+
+    // Check that the evaluation put its value on the values stack.
+    ASSERT_EQ(4, values_.size());
+
+    // Check bad addresses (they pushed '' on the value stack)
+    EXPECT_EQ(0, values_.top().size());
+    values_.pop();
+    EXPECT_EQ(0, values_.top().size());
+    values_.pop();
+
+    // Check IPv6 address
+    uint8_t expected6[] = { 0x20, 1, 0xd, 0xb8, 0, 0, 0, 0,
+                            0, 0, 0, 0, 0, 0, 0, 1 };
+    EXPECT_EQ(16, values_.top().size());
+    EXPECT_EQ(0, memcmp(expected6, &values_.top()[0], 16));
+    values_.pop();
+
+    // Check IPv4 address
+    uint8_t expected4[] = { 10, 0, 0, 1 };
+    EXPECT_EQ(4, values_.top().size());
+    EXPECT_EQ(0, memcmp(expected4, &values_.top()[0], 4));
+}
+
 // This test checks if a token representing an option value is able to extract
 // This test checks if a token representing an option value is able to extract
 // the option from an IPv4 packet and properly store the option's value.
 // the option from an IPv4 packet and properly store the option's value.
 TEST_F(TokenTest, optionString4) {
 TEST_F(TokenTest, optionString4) {

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

@@ -7,6 +7,7 @@
 #include <eval/token.h>
 #include <eval/token.h>
 #include <eval/eval_log.h>
 #include <eval/eval_log.h>
 #include <util/encode/hex.h>
 #include <util/encode/hex.h>
+#include <asiolink/io_address.h>
 #include <boost/lexical_cast.hpp>
 #include <boost/lexical_cast.hpp>
 #include <cstring>
 #include <cstring>
 #include <string>
 #include <string>
@@ -54,6 +55,27 @@ TokenHexString::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
     values.push(value_);
     values.push(value_);
 }
 }
 
 
+TokenIpAddress::TokenIpAddress(const string& addr) : value_("") {
+    // Transform IP address into binary format
+    vector<uint8_t> binary;
+    try {
+        asiolink::IOAddress ip(addr);
+        binary = ip.toBytes();
+    } catch (...) {
+        return;
+    }
+
+    // Convert to a string (note that binary.size() is 4 or 16, so not 0)
+    value_.resize(binary.size());
+    memmove(&value_[0], &binary[0], binary.size());
+}
+
+void
+TokenIpAddress::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
+    // Literals only push, nothing to pop
+    values.push(value_);
+}
+
 OptionPtr
 OptionPtr
 TokenOption::getOption(const Pkt& pkt) {
 TokenOption::getOption(const Pkt& pkt) {
     return (pkt.getOption(option_code_));
     return (pkt.getOption(option_code_));

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

@@ -155,6 +155,29 @@ protected:
     std::string value_; ///< Constant value
     std::string value_; ///< Constant value
 };
 };
 
 
+/// @brief Token representing an IP address as a constant string
+///
+/// This token holds the value of an IP address as a constant string,
+/// for instance 10.0.0.1 is 0x10000001
+class TokenIpAddress : public Token {
+public:
+    /// Value is set during token construction.
+    ///
+    /// @param addr IP address to be represented as a constant string
+    TokenIpAddress(const std::string& addr);
+
+    /// @brief Token evaluation (puts value of the constant string on
+    /// the stack after decoding)
+    ///
+    /// @param pkt (ignored)
+    /// @param values (represented IP address will be pushed here)
+    void evaluate(const Pkt& pkt, ValueStack& values);
+
+protected:
+    ///< Constant value (empty string if the IP address cannot be converted)
+    std::string value_;
+};
+
 /// @brief Token that represents a value of an option
 /// @brief Token that represents a value of an option
 ///
 ///
 /// This represents a reference to a given option, e.g. in the expression
 /// This represents a reference to a given option, e.g. in the expression