Browse Source

[master] Finish merge of trac4268a (DHCPv4 fields)

Francis Dupont 9 years ago
parent
commit
e95f32ebf7

+ 5 - 0
ChangeLog

@@ -1,3 +1,8 @@
+1107.	[func]		fdupont
+	Added support for extracting constant length fields from a DHCPv4
+	packet.
+	(Trac #4268, git xxx)
+
 1106.	[func]		sar
 1106.	[func]		sar
 	Added support for accessing DHCPv6 packet fields message type
 	Added support for accessing DHCPv6 packet fields message type
 	and transaction id in a classification expression.
 	and transaction id in a classification expression.

+ 139 - 46
doc/guide/classify.xml

@@ -152,56 +152,145 @@
           <thead>
           <thead>
             <row>
             <row>
               <entry>Name</entry>
               <entry>Name</entry>
-              <entry>Example</entry>
+              <entry>Example expression</entry>
+              <entry>Example value</entry>
               <entry>Description</entry>
               <entry>Description</entry>
             </row>
             </row>
           </thead>
           </thead>
+
           <tbody>
           <tbody>
-<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>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>String literal</entry>
+              <entry>'example'</entry>
+              <entry>'example'</entry>
+              <entry>A string</entry>
+            </row>
+            <row>
+              <entry>Hexadecimal string literal</entry>
+              <entry>0x5a7d</entry>
+              <entry>'Z}'</entry>
+              <entry>A hexadecimal string</entry>
+            </row>
+            <row>
+              <entry>IP address literal</entry>
+              <entry>10.0.0.1</entry>
+              <entry>0x0a000001</entry>
+              <entry>An IP address</entry>
+            </row>
+            <row>
+              <entry>Integer literal</entry>
+              <entry>123</entry>
+              <entry>'123'</entry>
+              <entry>An integer value</entry>
+            </row>
+            <row></row>
+            <row>
+              <entry>Binary content of the option</entry>
+              <entry>option[123].hex</entry>
+              <entry>'(content of the option)'</entry>
+              <entry>The value of the option with given code from the
+              packet as hex</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 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>relay4[code].hex</entry><entry>The value of
-sub-option with code "code" from the DHCPv4 Relay Agent Information option
-(option 82)</entry></row>
-<row>
-  <entry>DHCPv6 Relay Options</entry>
-  <entry>relay6[nest].option[code].hex</entry>
-<!--  <entry>Value of the option</entry> -->
-  <entry>The value of the option with code "code" from the relay encapsulation "nest"</entry>
-</row>
-<row>
-  <entry>DHCPv6 Relay Peer Address</entry>
-  <entry>relay6[nest].peeraddr</entry>
-<!-- <entry>2001:DB8::1</entry> -->n
-  <entry>The value of the peer address field from the relay encapsulation "nest"</entry>
-</row>
-<row>
-  <entry>DHCPv6 Relay Link Address</entry>
-  <entry>relay6[nest].linkaddr</entry>
-<!-- <entry>2001:DB8::1</entry> -->n
-  <entry>The value of the link address field from the relay encapsulation "nest"</entry>
-</row>
-<row>
-  <entry>Message Type in DHCPv6 packet</entry>
-  <entry>pkt6.msgtype</entry>
-<!--  <entry>1</entry>
--->
-  <entry>The value of the message type field in the DHCPv6 packet.</entry>
-</row>
-<row>
-  <entry>Transaction ID in DHCPv6 packet</entry>
-  <entry>pkt6.transid</entry>
-<!--  <entry>12345</entry>
+            <row>
+              <entry>Option Text</entry>
+              <entry>option[123].text</entry>
+              <entry>'foobar'</entry>
+              <entry>The value of the option with given code from the
+              packet as text</entry>
+            </row>
 -->
 -->
-  <entry>The value of the transaction id in the DHCPv6 packet.</entry>
-</row>
+            <row>
+              <entry>Option existence</entry>
+              <entry>option[123].exist</entry>
+              <entry>'true'</entry>
+              <entry>If the option with given code is present in the
+              packet "true" else "false"</entry>
+            </row>
+            <row>
+              <entry>DHCPv4 relay agent sub-option</entry>
+              <entry>relay4[123].hex</entry>
+              <entry>'(content of the RAI sub-option)'</entry>
+              <entry>The value of sub-option with given code from the
+              DHCPv4 Relay Agent Information option (option 82)</entry>
+            </row>
+            <row>
+              <entry>DHCPv6 Relay Options</entry>
+              <entry>relay6[nest].option[code].hex</entry>
+              <!--  <entry>Value of the option</entry> -->
+              <entry>The value of the option with code "code" from the
+              relay encapsulation "nest"</entry>
+            </row>
+            <row>
+              <entry>DHCPv6 Relay Peer Address</entry>
+              <entry>relay6[nest].peeraddr</entry>
+              <!-- <entry>2001:DB8::1</entry> -->n
+              <entry>The value of the peer address field from the
+              relay encapsulation "nest"</entry>
+            </row>
+            <row>
+              <entry>DHCPv6 Relay Link Address</entry>
+              <entry>relay6[nest].linkaddr</entry>
+              <!-- <entry>2001:DB8::1</entry> -->n
+              <entry>The value of the link address field from the
+              relay encapsulation "nest"</entry>
+            </row>
+            <row>
+              <entry>Hardware address in DHCPv4 packet</entry>
+              <entry>pkt4.mac</entry>
+              <entry>0x010203040506</entry>
+              <entry>The value of the chaddr field of the DHCPv4 packet, hlen (0 to 16) bytes</entry>
+            </row>
+            <row>
+              <entry>Hardware length in DHCPv4 packet</entry>
+              <entry>pkt4.hlen</entry>
+              <entry>0x00000006</entry>
+              <entry>The value of the hlen field of the DHCPv4 packet padded to 4 bytes</entry>
+            </row>
+            <row>
+              <entry>Hardware type in DHCPv4 packet</entry>
+              <entry>pkt4.htype</entry>
+              <entry>0x0000007b</entry>
+              <entry>The value of the htype field of the DHCPv4 packet padded to 4 bytes</entry>
+            </row>
+            <row>
+              <entry>ciaddr field in DHCPv4 packet</entry>
+              <entry>pkt4.ciaddr</entry>
+              <entry>192.0.2.1</entry>
+              <entry>The value of the ciaddr field of the DHCPv4 packet (IPv4 address, 4 bytes)</entry>
+            </row>
+            <row>
+              <entry>giaddr field in DHCPv4 packet</entry>
+              <entry>pkt4.giaddr</entry>
+              <entry>192.0.2.1</entry>
+              <entry>The value of the giaddr field of the DHCPv4 packet (IPv4 address, 4 bytes)</entry>
+            </row>
+            <row>
+              <entry>yiaddr field in DHCPv4 packet</entry>
+              <entry>pkt4.yiaddr</entry>
+              <entry>192.0.2.1</entry>
+              <entry>The value of the yiaddr field of the DHCPv4 packet (IPv4 address, 4 bytes)</entry>
+            </row>
+            <row>
+              <entry>siaddr field in DHCPv4 packet</entry>
+              <entry>pkt4.siaddr</entry>
+              <entry>192.0.2.1</entry>
+              <entry>The value of the siaddr field of the DHCPv4 packet (IPv4 address, 4 bytes)</entry>
+            </row>
+            <row>
+              <entry>Message Type in DHCPv6 packet</entry>
+              <entry>pkt6.msgtype</entry>
+              <!--  <entry>1</entry> -->
+              <entry>The value of the message type field in the DHCPv6
+              packet.</entry>
+            </row>
+            <row>
+              <entry>Transaction ID in DHCPv6 packet</entry>
+              <entry>pkt6.transid</entry>
+              <!--  <entry>12345</entry> -->
+              <entry>The value of the transaction id in the DHCPv6
+              packet.</entry>
+            </row>
           </tbody>
           </tbody>
           </tgroup>
           </tgroup>
         </table>
         </table>
@@ -221,14 +310,14 @@ sub-option with code "code" from the DHCPv4 Relay Agent Information option
       </para>
       </para>
 
 
       <para>
       <para>
-      "option[code].hex" extracts the value of the option with the given code
+      "option[code].hex" extracts the value of the option with the code "code"
       from the incoming packet. If the packet doesn't contain the option, it
       from the incoming packet. If the packet doesn't contain the option, it
       returns the empty string. The string is presented as a byte string of
       returns the empty string. The string is presented as a byte string of
       the option payload without the type code or length fields.
       the option payload without the type code or length fields.
       </para>
       </para>
 
 
       <para>
       <para>
-      "option[code].exist" checks if an option with the given code is present
+      "option[code].exist" 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>
 
 
@@ -262,6 +351,10 @@ sub-option with code "code" from the DHCPv4 Relay Agent Information option
       </para>
       </para>
 
 
       <para>
       <para>
+        Expressions starting with pkt4 can be used only in DHCPv4.
+      </para>
+
+      <para>
        "pkt6" refers to information from the client request.  To access any
        "pkt6" refers to information from the client request.  To access any
        information from an intermediate relay use "relay6".  "pkt6.msgtype"
        information from an intermediate relay use "relay6".  "pkt6.msgtype"
        and "pkt6.transid" output a 4 byte binary string for the message type
        and "pkt6.transid" output a 4 byte binary string for the message type

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


+ 8 - 0
src/lib/eval/lexer.ll

@@ -149,6 +149,14 @@ addr6 [0-9a-fA-F]*\:[0-9a-fA-F]*\:[0-9a-fA-F:.]*
 "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);
+"pkt4"      return isc::eval::EvalParser::make_PKT4(loc);
+"mac"       return isc::eval::EvalParser::make_CHADDR(loc);
+"hlen"      return isc::eval::EvalParser::make_HLEN(loc);
+"htype"     return isc::eval::EvalParser::make_HTYPE(loc);
+"ciaddr"    return isc::eval::EvalParser::make_CIADDR(loc);
+"giaddr"    return isc::eval::EvalParser::make_GIADDR(loc);
+"yiaddr"    return isc::eval::EvalParser::make_YIADDR(loc);
+"siaddr"    return isc::eval::EvalParser::make_SIADDR(loc);
 "substring" return isc::eval::EvalParser::make_SUBSTRING(loc);
 "substring" return isc::eval::EvalParser::make_SUBSTRING(loc);
 "all"       return isc::eval::EvalParser::make_ALL(loc);
 "all"       return isc::eval::EvalParser::make_ALL(loc);
 "concat"    return isc::eval::EvalParser::make_CONCAT(loc);
 "concat"    return isc::eval::EvalParser::make_CONCAT(loc);

+ 306 - 212
src/lib/eval/parser.cc

@@ -251,31 +251,35 @@ namespace isc { namespace eval {
   {
   {
       switch (that.type_get ())
       switch (that.type_get ())
     {
     {
-      case 37: // option_repr_type
+      case 45: // option_repr_type
         value.move< TokenOption::RepresentationType > (that.value);
         value.move< TokenOption::RepresentationType > (that.value);
         break;
         break;
 
 
-      case 42: // pkt6_field
+      case 46: // pkt4_field
+        value.move< TokenPkt4::FieldType > (that.value);
+        break;
+
+      case 51: // pkt6_field
         value.move< TokenPkt6::FieldType > (that.value);
         value.move< TokenPkt6::FieldType > (that.value);
         break;
         break;
 
 
-      case 40: // relay6_field
+      case 49: // relay6_field
         value.move< TokenRelay6Field::FieldType > (that.value);
         value.move< TokenRelay6Field::FieldType > (that.value);
         break;
         break;
 
 
-      case 27: // "constant string"
-      case 28: // "integer"
-      case 29: // "constant hexstring"
-      case 30: // "option name"
-      case 31: // "ip address"
+      case 35: // "constant string"
+      case 36: // "integer"
+      case 37: // "constant hexstring"
+      case 38: // "option name"
+      case 39: // "ip address"
         value.move< std::string > (that.value);
         value.move< std::string > (that.value);
         break;
         break;
 
 
-      case 36: // option_code
+      case 44: // option_code
         value.move< uint16_t > (that.value);
         value.move< uint16_t > (that.value);
         break;
         break;
 
 
-      case 41: // nest_level
+      case 50: // nest_level
         value.move< uint8_t > (that.value);
         value.move< uint8_t > (that.value);
         break;
         break;
 
 
@@ -294,31 +298,35 @@ namespace isc { namespace eval {
     state = that.state;
     state = that.state;
       switch (that.type_get ())
       switch (that.type_get ())
     {
     {
-      case 37: // option_repr_type
+      case 45: // option_repr_type
         value.copy< TokenOption::RepresentationType > (that.value);
         value.copy< TokenOption::RepresentationType > (that.value);
         break;
         break;
 
 
-      case 42: // pkt6_field
+      case 46: // pkt4_field
+        value.copy< TokenPkt4::FieldType > (that.value);
+        break;
+
+      case 51: // pkt6_field
         value.copy< TokenPkt6::FieldType > (that.value);
         value.copy< TokenPkt6::FieldType > (that.value);
         break;
         break;
 
 
-      case 40: // relay6_field
+      case 49: // relay6_field
         value.copy< TokenRelay6Field::FieldType > (that.value);
         value.copy< TokenRelay6Field::FieldType > (that.value);
         break;
         break;
 
 
-      case 27: // "constant string"
-      case 28: // "integer"
-      case 29: // "constant hexstring"
-      case 30: // "option name"
-      case 31: // "ip address"
+      case 35: // "constant string"
+      case 36: // "integer"
+      case 37: // "constant hexstring"
+      case 38: // "option name"
+      case 39: // "ip address"
         value.copy< std::string > (that.value);
         value.copy< std::string > (that.value);
         break;
         break;
 
 
-      case 36: // option_code
+      case 44: // option_code
         value.copy< uint16_t > (that.value);
         value.copy< uint16_t > (that.value);
         break;
         break;
 
 
-      case 41: // nest_level
+      case 50: // nest_level
         value.copy< uint8_t > (that.value);
         value.copy< uint8_t > (that.value);
         break;
         break;
 
 
@@ -358,74 +366,81 @@ namespace isc { namespace eval {
         << yysym.location << ": ";
         << yysym.location << ": ";
     switch (yytype)
     switch (yytype)
     {
     {
-            case 27: // "constant string"
+            case 35: // "constant string"
 
 
-#line 82 "parser.yy" // lalr1.cc:636
+#line 91 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 366 "parser.cc" // lalr1.cc:636
+#line 374 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
-      case 28: // "integer"
+      case 36: // "integer"
 
 
-#line 82 "parser.yy" // lalr1.cc:636
+#line 91 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 373 "parser.cc" // lalr1.cc:636
+#line 381 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
-      case 29: // "constant hexstring"
+      case 37: // "constant hexstring"
 
 
-#line 82 "parser.yy" // lalr1.cc:636
+#line 91 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 380 "parser.cc" // lalr1.cc:636
+#line 388 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
-      case 30: // "option name"
+      case 38: // "option name"
 
 
-#line 82 "parser.yy" // lalr1.cc:636
+#line 91 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 387 "parser.cc" // lalr1.cc:636
+#line 395 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
-      case 31: // "ip address"
+      case 39: // "ip address"
 
 
-#line 82 "parser.yy" // lalr1.cc:636
+#line 91 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 394 "parser.cc" // lalr1.cc:636
+#line 402 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
-      case 36: // option_code
+      case 44: // option_code
 
 
-#line 82 "parser.yy" // lalr1.cc:636
+#line 91 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< uint16_t > (); }
         { yyoutput << yysym.value.template as< uint16_t > (); }
-#line 401 "parser.cc" // lalr1.cc:636
+#line 409 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
-      case 37: // option_repr_type
+      case 45: // option_repr_type
 
 
-#line 82 "parser.yy" // lalr1.cc:636
+#line 91 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< TokenOption::RepresentationType > (); }
         { yyoutput << yysym.value.template as< TokenOption::RepresentationType > (); }
-#line 408 "parser.cc" // lalr1.cc:636
+#line 416 "parser.cc" // lalr1.cc:636
+        break;
+
+      case 46: // pkt4_field
+
+#line 91 "parser.yy" // lalr1.cc:636
+        { yyoutput << yysym.value.template as< TokenPkt4::FieldType > (); }
+#line 423 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
-      case 40: // relay6_field
+      case 49: // relay6_field
 
 
-#line 82 "parser.yy" // lalr1.cc:636
+#line 91 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< TokenRelay6Field::FieldType > (); }
         { yyoutput << yysym.value.template as< TokenRelay6Field::FieldType > (); }
-#line 415 "parser.cc" // lalr1.cc:636
+#line 430 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
-      case 41: // nest_level
+      case 50: // nest_level
 
 
-#line 82 "parser.yy" // lalr1.cc:636
+#line 91 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< uint8_t > (); }
         { yyoutput << yysym.value.template as< uint8_t > (); }
-#line 422 "parser.cc" // lalr1.cc:636
+#line 437 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
-      case 42: // pkt6_field
+      case 51: // pkt6_field
 
 
-#line 82 "parser.yy" // lalr1.cc:636
+#line 91 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< TokenPkt6::FieldType > (); }
         { yyoutput << yysym.value.template as< TokenPkt6::FieldType > (); }
-#line 429 "parser.cc" // lalr1.cc:636
+#line 444 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
 
 
@@ -625,31 +640,35 @@ namespace isc { namespace eval {
          when using variants.  */
          when using variants.  */
         switch (yyr1_[yyn])
         switch (yyr1_[yyn])
     {
     {
-      case 37: // option_repr_type
+      case 45: // option_repr_type
         yylhs.value.build< TokenOption::RepresentationType > ();
         yylhs.value.build< TokenOption::RepresentationType > ();
         break;
         break;
 
 
-      case 42: // pkt6_field
+      case 46: // pkt4_field
+        yylhs.value.build< TokenPkt4::FieldType > ();
+        break;
+
+      case 51: // pkt6_field
         yylhs.value.build< TokenPkt6::FieldType > ();
         yylhs.value.build< TokenPkt6::FieldType > ();
         break;
         break;
 
 
-      case 40: // relay6_field
+      case 49: // relay6_field
         yylhs.value.build< TokenRelay6Field::FieldType > ();
         yylhs.value.build< TokenRelay6Field::FieldType > ();
         break;
         break;
 
 
-      case 27: // "constant string"
-      case 28: // "integer"
-      case 29: // "constant hexstring"
-      case 30: // "option name"
-      case 31: // "ip address"
+      case 35: // "constant string"
+      case 36: // "integer"
+      case 37: // "constant hexstring"
+      case 38: // "option name"
+      case 39: // "ip address"
         yylhs.value.build< std::string > ();
         yylhs.value.build< std::string > ();
         break;
         break;
 
 
-      case 36: // option_code
+      case 44: // option_code
         yylhs.value.build< uint16_t > ();
         yylhs.value.build< uint16_t > ();
         break;
         break;
 
 
-      case 41: // nest_level
+      case 50: // nest_level
         yylhs.value.build< uint8_t > ();
         yylhs.value.build< uint8_t > ();
         break;
         break;
 
 
@@ -671,52 +690,52 @@ namespace isc { namespace eval {
           switch (yyn)
           switch (yyn)
             {
             {
   case 4:
   case 4:
-#line 96 "parser.yy" // lalr1.cc:859
+#line 105 "parser.yy" // lalr1.cc:859
     {
     {
                     TokenPtr neg(new TokenNot());
                     TokenPtr neg(new TokenNot());
                     ctx.expression.push_back(neg);
                     ctx.expression.push_back(neg);
                 }
                 }
-#line 680 "parser.cc" // lalr1.cc:859
+#line 699 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 5:
   case 5:
-#line 101 "parser.yy" // lalr1.cc:859
+#line 110 "parser.yy" // lalr1.cc:859
     {
     {
                     TokenPtr neg(new TokenAnd());
                     TokenPtr neg(new TokenAnd());
                     ctx.expression.push_back(neg);
                     ctx.expression.push_back(neg);
                 }
                 }
-#line 689 "parser.cc" // lalr1.cc:859
+#line 708 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 6:
   case 6:
-#line 106 "parser.yy" // lalr1.cc:859
+#line 115 "parser.yy" // lalr1.cc:859
     {
     {
                     TokenPtr neg(new TokenOr());
                     TokenPtr neg(new TokenOr());
                     ctx.expression.push_back(neg);
                     ctx.expression.push_back(neg);
                 }
                 }
-#line 698 "parser.cc" // lalr1.cc:859
+#line 717 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 7:
   case 7:
-#line 111 "parser.yy" // lalr1.cc:859
+#line 120 "parser.yy" // lalr1.cc:859
     {
     {
                     TokenPtr eq(new TokenEqual());
                     TokenPtr eq(new TokenEqual());
                     ctx.expression.push_back(eq);
                     ctx.expression.push_back(eq);
                 }
                 }
-#line 707 "parser.cc" // lalr1.cc:859
+#line 726 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 8:
   case 8:
-#line 116 "parser.yy" // lalr1.cc:859
+#line 125 "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 716 "parser.cc" // lalr1.cc:859
+#line 735 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 9:
   case 9:
-#line 121 "parser.yy" // lalr1.cc:859
+#line 130 "parser.yy" // lalr1.cc:859
     {
     {
                    switch (ctx.getUniverse()) {
                    switch (ctx.getUniverse()) {
                    case Option::V4:
                    case Option::V4:
@@ -736,11 +755,11 @@ 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 740 "parser.cc" // lalr1.cc:859
+#line 759 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 10:
   case 10:
-#line 141 "parser.yy" // lalr1.cc:859
+#line 150 "parser.yy" // lalr1.cc:859
     {
     {
                     switch (ctx.getUniverse()) {
                     switch (ctx.getUniverse()) {
                     case Option::V6:
                     case Option::V6:
@@ -754,47 +773,47 @@ namespace isc { namespace eval {
                         error(yystack_[10].location, "relay6 can only be used in DHCPv6.");
                         error(yystack_[10].location, "relay6 can only be used in DHCPv6.");
                     }
                     }
                 }
                 }
-#line 758 "parser.cc" // lalr1.cc:859
+#line 777 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 11:
   case 11:
-#line 157 "parser.yy" // lalr1.cc:859
+#line 166 "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 767 "parser.cc" // lalr1.cc:859
+#line 786 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 12:
   case 12:
-#line 162 "parser.yy" // lalr1.cc:859
+#line 171 "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 776 "parser.cc" // lalr1.cc:859
+#line 795 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 13:
   case 13:
-#line 167 "parser.yy" // lalr1.cc:859
+#line 176 "parser.yy" // lalr1.cc:859
     {
     {
                       TokenPtr ip(new TokenIpAddress(yystack_[0].value.as< std::string > ()));
                       TokenPtr ip(new TokenIpAddress(yystack_[0].value.as< std::string > ()));
                       ctx.expression.push_back(ip);
                       ctx.expression.push_back(ip);
                   }
                   }
-#line 785 "parser.cc" // lalr1.cc:859
+#line 804 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 14:
   case 14:
-#line 172 "parser.yy" // lalr1.cc:859
+#line 181 "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 794 "parser.cc" // lalr1.cc:859
+#line 813 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 15:
   case 15:
-#line 177 "parser.yy" // lalr1.cc:859
+#line 186 "parser.yy" // lalr1.cc:859
     {
     {
                      switch (ctx.getUniverse()) {
                      switch (ctx.getUniverse()) {
                      case Option::V4:
                      case Option::V4:
@@ -814,11 +833,11 @@ 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 818 "parser.cc" // lalr1.cc:859
+#line 837 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 16:
   case 16:
-#line 198 "parser.yy" // lalr1.cc:859
+#line 207 "parser.yy" // lalr1.cc:859
     {
     {
                      switch (ctx.getUniverse()) {
                      switch (ctx.getUniverse()) {
                      case Option::V6:
                      case Option::V6:
@@ -832,11 +851,11 @@ namespace isc { namespace eval {
                          error(yystack_[10].location, "relay6 can only be used in DHCPv6.");
                          error(yystack_[10].location, "relay6 can only be used in DHCPv6.");
                      }
                      }
                   }
                   }
-#line 836 "parser.cc" // lalr1.cc:859
+#line 855 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 17:
   case 17:
-#line 213 "parser.yy" // lalr1.cc:859
+#line 222 "parser.yy" // lalr1.cc:859
     {
     {
                      switch (ctx.getUniverse()) {
                      switch (ctx.getUniverse()) {
                      case Option::V6:
                      case Option::V6:
@@ -850,129 +869,194 @@ namespace isc { namespace eval {
                          error(yystack_[5].location, "relay6 can only be used in DHCPv6.");
                          error(yystack_[5].location, "relay6 can only be used in DHCPv6.");
                      }
                      }
                   }
                   }
-#line 854 "parser.cc" // lalr1.cc:859
+#line 873 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 18:
   case 18:
-#line 229 "parser.yy" // lalr1.cc:859
+#line 237 "parser.yy" // lalr1.cc:859
     {
     {
-                      TokenPtr sub(new TokenSubstring());
-                      ctx.expression.push_back(sub);
+                      TokenPtr pkt4_field(new TokenPkt4(yystack_[0].value.as< TokenPkt4::FieldType > ()));
+                      ctx.expression.push_back(pkt4_field);
                   }
                   }
-#line 863 "parser.cc" // lalr1.cc:859
+#line 882 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 19:
   case 19:
-#line 234 "parser.yy" // lalr1.cc:859
+#line 242 "parser.yy" // lalr1.cc:859
     {
     {
-                      TokenPtr conc(new TokenConcat());
-                      ctx.expression.push_back(conc);
+                      TokenPtr pkt6_field(new TokenPkt6(yystack_[0].value.as< TokenPkt6::FieldType > ()));
+                      ctx.expression.push_back(pkt6_field);
                   }
                   }
-#line 872 "parser.cc" // lalr1.cc:859
+#line 891 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 20:
   case 20:
-#line 239 "parser.yy" // lalr1.cc:859
+#line 247 "parser.yy" // lalr1.cc:859
     {
     {
-                      TokenPtr pkt6_field(new TokenPkt6(yystack_[0].value.as< TokenPkt6::FieldType > ()));
-                      ctx.expression.push_back(pkt6_field);
+                      TokenPtr sub(new TokenSubstring());
+                      ctx.expression.push_back(sub);
                   }
                   }
-#line 881 "parser.cc" // lalr1.cc:859
+#line 900 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 21:
   case 21:
-#line 246 "parser.yy" // lalr1.cc:859
+#line 252 "parser.yy" // lalr1.cc:859
+    {
+                      TokenPtr conc(new TokenConcat());
+                      ctx.expression.push_back(conc);
+                  }
+#line 909 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 22:
+#line 259 "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 889 "parser.cc" // lalr1.cc:859
+#line 917 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 22:
-#line 250 "parser.yy" // lalr1.cc:859
+  case 23:
+#line 263 "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 897 "parser.cc" // lalr1.cc:859
+#line 925 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 23:
-#line 256 "parser.yy" // lalr1.cc:859
+  case 24:
+#line 269 "parser.yy" // lalr1.cc:859
     {
     {
                           yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::TEXTUAL;
                           yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::TEXTUAL;
                       }
                       }
-#line 905 "parser.cc" // lalr1.cc:859
+#line 933 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 24:
-#line 260 "parser.yy" // lalr1.cc:859
+  case 25:
+#line 273 "parser.yy" // lalr1.cc:859
     {
     {
                           yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::HEXADECIMAL;
                           yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::HEXADECIMAL;
                       }
                       }
-#line 913 "parser.cc" // lalr1.cc:859
+#line 941 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 25:
-#line 266 "parser.yy" // lalr1.cc:859
+  case 26:
+#line 279 "parser.yy" // lalr1.cc:859
+    {
+                    yylhs.value.as< TokenPkt4::FieldType > () = TokenPkt4::CHADDR;
+                }
+#line 949 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 27:
+#line 283 "parser.yy" // lalr1.cc:859
+    {
+                    yylhs.value.as< TokenPkt4::FieldType > () = TokenPkt4::HLEN;
+                }
+#line 957 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 28:
+#line 287 "parser.yy" // lalr1.cc:859
+    {
+                    yylhs.value.as< TokenPkt4::FieldType > () = TokenPkt4::HTYPE;
+                }
+#line 965 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 29:
+#line 291 "parser.yy" // lalr1.cc:859
+    {
+                    yylhs.value.as< TokenPkt4::FieldType > () = TokenPkt4::CIADDR;
+                }
+#line 973 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 30:
+#line 295 "parser.yy" // lalr1.cc:859
+    {
+                    yylhs.value.as< TokenPkt4::FieldType > () = TokenPkt4::GIADDR;
+                }
+#line 981 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 31:
+#line 299 "parser.yy" // lalr1.cc:859
+    {
+                    yylhs.value.as< TokenPkt4::FieldType > () = TokenPkt4::YIADDR;
+                }
+#line 989 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 32:
+#line 303 "parser.yy" // lalr1.cc:859
+    {
+                    yylhs.value.as< TokenPkt4::FieldType > () = TokenPkt4::SIADDR;
+                }
+#line 997 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 33:
+#line 309 "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 922 "parser.cc" // lalr1.cc:859
+#line 1006 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 26:
-#line 273 "parser.yy" // lalr1.cc:859
+  case 34:
+#line 316 "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 931 "parser.cc" // lalr1.cc:859
+#line 1015 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 27:
-#line 278 "parser.yy" // lalr1.cc:859
+  case 35:
+#line 321 "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 940 "parser.cc" // lalr1.cc:859
+#line 1024 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 28:
-#line 284 "parser.yy" // lalr1.cc:859
+  case 36:
+#line 327 "parser.yy" // lalr1.cc:859
     { yylhs.value.as< TokenRelay6Field::FieldType > () = TokenRelay6Field::PEERADDR; }
     { yylhs.value.as< TokenRelay6Field::FieldType > () = TokenRelay6Field::PEERADDR; }
-#line 946 "parser.cc" // lalr1.cc:859
+#line 1030 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 29:
-#line 285 "parser.yy" // lalr1.cc:859
+  case 37:
+#line 328 "parser.yy" // lalr1.cc:859
     { yylhs.value.as< TokenRelay6Field::FieldType > () = TokenRelay6Field::LINKADDR; }
     { yylhs.value.as< TokenRelay6Field::FieldType > () = TokenRelay6Field::LINKADDR; }
-#line 952 "parser.cc" // lalr1.cc:859
+#line 1036 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 30:
-#line 289 "parser.yy" // lalr1.cc:859
+  case 38:
+#line 332 "parser.yy" // lalr1.cc:859
     {
     {
 		 yylhs.value.as< uint8_t > () = ctx.convertNestLevelNumber(yystack_[0].value.as< std::string > (), yystack_[0].location);
 		 yylhs.value.as< uint8_t > () = ctx.convertNestLevelNumber(yystack_[0].value.as< std::string > (), yystack_[0].location);
                  }
                  }
-#line 960 "parser.cc" // lalr1.cc:859
+#line 1044 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 31:
-#line 297 "parser.yy" // lalr1.cc:859
+  case 39:
+#line 340 "parser.yy" // lalr1.cc:859
     { yylhs.value.as< TokenPkt6::FieldType > () = TokenPkt6::MSGTYPE; }
     { yylhs.value.as< TokenPkt6::FieldType > () = TokenPkt6::MSGTYPE; }
-#line 966 "parser.cc" // lalr1.cc:859
+#line 1050 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 32:
-#line 298 "parser.yy" // lalr1.cc:859
+  case 40:
+#line 341 "parser.yy" // lalr1.cc:859
     { yylhs.value.as< TokenPkt6::FieldType > () = TokenPkt6::TRANSID; }
     { yylhs.value.as< TokenPkt6::FieldType > () = TokenPkt6::TRANSID; }
-#line 972 "parser.cc" // lalr1.cc:859
+#line 1056 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
 
 
-#line 976 "parser.cc" // lalr1.cc:859
+#line 1060 "parser.cc" // lalr1.cc:859
             default:
             default:
               break;
               break;
             }
             }
@@ -1227,117 +1311,124 @@ namespace isc { namespace eval {
   }
   }
 
 
 
 
-  const signed char EvalParser::yypact_ninf_ = -54;
+  const signed char EvalParser::yypact_ninf_ = -62;
 
 
   const signed char EvalParser::yytable_ninf_ = -1;
   const signed char EvalParser::yytable_ninf_ = -1;
 
 
   const signed char
   const signed char
   EvalParser::yypact_[] =
   EvalParser::yypact_[] =
   {
   {
-      29,    29,    29,   -12,    -4,     7,    21,    30,    32,   -54,
-     -54,   -54,    13,    10,    46,    16,   -54,    -3,    -3,     9,
-      52,    52,    42,   -54,    29,    29,    52,   -54,   -54,   -54,
-      40,    54,   -54,    56,    43,    59,    60,    55,    58,   -54,
-     -54,   -54,   -54,    72,   -54,    66,    68,    69,    -3,    -3,
-       9,    61,    52,    25,    28,    -1,    71,    73,    75,   -54,
-      65,    87,   -54,   -54,   -54,   -54,   -54,   -54,    78,   -54,
-     -54,   -54,    77,    79,    80,   -14,   -54,    -3,    33,    33,
-       6,   -54,   -54,    90,    82,    84,   -54,    83,    -3,    47,
-      85,   -54,   -54,    86,    33
+      14,    14,    14,     6,    17,    18,    21,    41,    44,    48,
+     -62,   -62,   -62,    72,    20,    66,    29,   -62,    12,    12,
+      16,    36,    45,    45,   -24,   -62,    14,    14,    45,   -62,
+     -62,   -62,    60,    63,   -62,    73,   -62,   -62,   -62,   -62,
+     -62,   -62,   -62,   -62,    67,    69,    75,    61,    62,   -62,
+     -62,   -62,   -62,    84,   -62,    77,    78,    79,    12,    12,
+      16,    64,    45,    -3,    52,    -1,    81,    82,    83,   -62,
+      71,    95,   -62,   -62,   -62,   -62,   -62,   -62,    88,   -62,
+     -62,   -62,    87,    89,    90,   -23,   -62,    12,    49,    49,
+       9,   -62,   -62,   100,    92,    94,   -62,    93,    12,    68,
+      96,   -62,   -62,    97,    49
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yydefact_[] =
   EvalParser::yydefact_[] =
   {
   {
-       0,     0,     0,     0,     0,     0,     0,     0,     0,    11,
-      12,    13,     0,     2,     0,     0,     4,     0,     0,     0,
-       0,     0,     0,     1,     0,     0,     0,     3,    21,    22,
-       0,     0,    30,     0,     0,     0,     0,     0,     0,    31,
-      32,    20,     5,     6,     7,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,     0,     0,     0,     0,     0,    25,
-       0,     0,    23,    24,     8,    14,     9,    15,     0,    28,
-      29,    17,     0,     0,     0,     0,    19,     0,     0,     0,
-       0,    27,    26,     0,     0,     0,    18,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+      11,    12,    13,     0,     2,     0,     0,     4,     0,     0,
+       0,     0,     0,     0,     0,     1,     0,     0,     0,     3,
+      22,    23,     0,     0,    38,     0,    26,    27,    28,    29,
+      30,    31,    32,    18,     0,     0,     0,     0,     0,    39,
+      40,    19,     5,     6,     7,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,    33,
+       0,     0,    24,    25,     8,    14,     9,    15,     0,    36,
+      37,    17,     0,     0,     0,     0,    21,     0,     0,     0,
+       0,    35,    34,     0,     0,     0,    20,     0,     0,     0,
        0,    10,    16,     0,     0
        0,    10,    16,     0,     0
   };
   };
 
 
   const signed char
   const signed char
   EvalParser::yypgoto_[] =
   EvalParser::yypgoto_[] =
   {
   {
-     -54,   -54,     4,   -17,   -18,   -53,   -54,   -54,   -54,    51,
-     -54
+     -62,   -62,     3,   -21,   -19,   -61,   -62,   -62,   -62,   -62,
+      50,   -62
   };
   };
 
 
   const signed char
   const signed char
   EvalParser::yydefgoto_[] =
   EvalParser::yydefgoto_[] =
   {
   {
-      -1,    12,    13,    14,    30,    65,    60,    83,    71,    33,
-      41
+      -1,    13,    14,    15,    32,    75,    43,    70,    93,    81,
+      35,    51
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yytable_[] =
   EvalParser::yytable_[] =
   {
   {
-      31,    67,    17,    37,    38,    15,    16,    81,    68,    44,
-      18,    69,    70,    23,    82,    85,    24,    25,    69,    70,
-      27,    19,    24,    25,    20,    28,    67,    29,    42,    43,
-      56,    57,     1,    21,     2,    61,    92,    32,     3,     4,
-       5,    92,    62,    63,    64,    62,    63,    66,    22,     6,
-      62,    63,     7,     8,    26,    45,     9,    48,    10,    84,
-      11,    34,    35,    36,    62,    63,    91,    39,    40,    46,
-      90,    47,     6,    49,    50,     7,     8,    51,    24,     9,
-      52,    10,    53,    11,    54,    55,    72,    75,    73,    59,
-      74,    76,    77,    78,    86,    79,    80,    87,    88,    89,
-      93,    58,    94
+      33,    47,    48,    77,    16,    17,    91,    54,    78,    49,
+      50,    79,    80,    92,    72,    73,    74,     1,    95,     2,
+      18,    79,    80,     3,     4,     5,    26,    27,    77,    52,
+      53,    19,    20,    29,     6,    26,    27,    21,   102,    66,
+      67,    71,     7,   102,    22,     8,     9,    23,    30,    10,
+      31,    11,    34,    12,    44,    45,    46,    36,    37,    38,
+      39,    40,    41,    42,    24,     6,    72,    73,    94,    72,
+      73,    76,    25,     7,    28,    55,     8,     9,    56,   100,
+      10,    58,    11,    59,    12,    72,    73,   101,    57,    60,
+      26,    61,    62,    63,    64,    65,    82,    83,    84,    86,
+      69,    85,    87,    88,    96,    89,    90,    97,    98,    99,
+      68,   103,     0,   104
   };
   };
 
 
-  const unsigned char
+  const signed char
   EvalParser::yycheck_[] =
   EvalParser::yycheck_[] =
   {
   {
-      18,    54,    14,    20,    21,     1,     2,    21,     9,    26,
-      14,    12,    13,     0,    28,     9,     6,     7,    12,    13,
-       4,    14,     6,     7,     3,    28,    79,    30,    24,    25,
-      48,    49,     3,     3,     5,    52,    89,    28,     9,    10,
-      11,    94,    17,    18,    19,    17,    18,    19,    16,    20,
-      17,    18,    23,    24,     8,    15,    27,    14,    29,    77,
-      31,     9,    10,    11,    17,    18,    19,    25,    26,    15,
-      88,    15,    20,    14,    14,    23,    24,    22,     6,    27,
-      22,    29,    16,    31,    16,    16,    15,    22,    15,    28,
-      15,     4,    14,    16,     4,    16,    16,    15,    14,    16,
-      15,    50,    16
+      19,    22,    23,    64,     1,     2,    29,    28,     9,    33,
+      34,    12,    13,    36,    17,    18,    19,     3,     9,     5,
+      14,    12,    13,     9,    10,    11,     6,     7,    89,    26,
+      27,    14,    14,     4,    20,     6,     7,    16,    99,    58,
+      59,    62,    28,   104,     3,    31,    32,     3,    36,    35,
+      38,    37,    36,    39,     9,    10,    11,    21,    22,    23,
+      24,    25,    26,    27,    16,    20,    17,    18,    87,    17,
+      18,    19,     0,    28,     8,    15,    31,    32,    15,    98,
+      35,    14,    37,    14,    39,    17,    18,    19,    15,    14,
+       6,    30,    30,    16,    16,    16,    15,    15,    15,     4,
+      36,    30,    14,    16,     4,    16,    16,    15,    14,    16,
+      60,    15,    -1,    16
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yystos_[] =
   EvalParser::yystos_[] =
   {
   {
-       0,     3,     5,     9,    10,    11,    20,    23,    24,    27,
-      29,    31,    33,    34,    35,    34,    34,    14,    14,    14,
-       3,     3,    16,     0,     6,     7,     8,     4,    28,    30,
-      36,    36,    28,    41,     9,    10,    11,    35,    35,    25,
-      26,    42,    34,    34,    35,    15,    15,    15,    14,    14,
-      14,    22,    22,    16,    16,    16,    36,    36,    41,    28,
-      38,    35,    17,    18,    19,    37,    19,    37,     9,    12,
-      13,    40,    15,    15,    15,    22,     4,    14,    16,    16,
-      16,    21,    28,    39,    36,     9,     4,    15,    14,    16,
-      36,    19,    37,    15,    16
+       0,     3,     5,     9,    10,    11,    20,    28,    31,    32,
+      35,    37,    39,    41,    42,    43,    42,    42,    14,    14,
+      14,    16,     3,     3,    16,     0,     6,     7,     8,     4,
+      36,    38,    44,    44,    36,    50,    21,    22,    23,    24,
+      25,    26,    27,    46,     9,    10,    11,    43,    43,    33,
+      34,    51,    42,    42,    43,    15,    15,    15,    14,    14,
+      14,    30,    30,    16,    16,    16,    44,    44,    50,    36,
+      47,    43,    17,    18,    19,    45,    19,    45,     9,    12,
+      13,    49,    15,    15,    15,    30,     4,    14,    16,    16,
+      16,    29,    36,    48,    44,     9,     4,    15,    14,    16,
+      44,    19,    45,    15,    16
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yyr1_[] =
   EvalParser::yyr1_[] =
   {
   {
-       0,    32,    33,    34,    34,    34,    34,    34,    34,    34,
-      34,    35,    35,    35,    35,    35,    35,    35,    35,    35,
-      35,    36,    36,    37,    37,    38,    39,    39,    40,    40,
-      41,    42,    42
+       0,    40,    41,    42,    42,    42,    42,    42,    42,    42,
+      42,    43,    43,    43,    43,    43,    43,    43,    43,    43,
+      43,    43,    44,    44,    45,    45,    46,    46,    46,    46,
+      46,    46,    46,    47,    48,    48,    49,    49,    50,    51,
+      51
   };
   };
 
 
   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,
-      11,     1,     1,     1,     6,     6,    11,     6,     8,     6,
-       3,     1,     1,     1,     1,     1,     1,     1,     1,     1,
-       1,     1,     1
+      11,     1,     1,     1,     6,     6,    11,     6,     3,     3,
+       8,     6,     1,     1,     1,     1,     1,     1,     1,     1,
+       1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
+       1
   };
   };
 
 
 
 
@@ -1350,22 +1441,25 @@ namespace isc { namespace eval {
   "\"end of file\"", "error", "$undefined", "\"(\"", "\")\"", "\"not\"",
   "\"end of file\"", "error", "$undefined", "\"(\"", "\")\"", "\"not\"",
   "\"and\"", "\"or\"", "\"==\"", "\"option\"", "\"relay4\"", "\"relay6\"",
   "\"and\"", "\"or\"", "\"==\"", "\"option\"", "\"relay4\"", "\"relay6\"",
   "\"peeraddr\"", "\"linkaddr\"", "\"[\"", "\"]\"", "\".\"", "\"text\"",
   "\"peeraddr\"", "\"linkaddr\"", "\"[\"", "\"]\"", "\".\"", "\"text\"",
-  "\"hex\"", "\"exists\"", "\"substring\"", "\"all\"", "\",\"",
-  "\"concat\"", "\"pkt6\"", "\"msgtype\"", "\"transid\"",
-  "\"constant string\"", "\"integer\"", "\"constant hexstring\"",
-  "\"option name\"", "\"ip address\"", "$accept", "expression",
-  "bool_expr", "string_expr", "option_code", "option_repr_type",
-  "start_expr", "length_expr", "relay6_field", "nest_level", "pkt6_field", YY_NULLPTR
+  "\"hex\"", "\"exists\"", "\"pkt4\"", "\"mac\"", "\"hlen\"", "\"htype\"",
+  "\"ciaddr\"", "\"giaddr\"", "\"yiaddr\"", "\"siaddr\"", "\"substring\"",
+  "\"all\"", "\",\"", "\"concat\"", "\"pkt6\"", "\"msgtype\"",
+  "\"transid\"", "\"constant string\"", "\"integer\"",
+  "\"constant hexstring\"", "\"option name\"", "\"ip address\"", "$accept",
+  "expression", "bool_expr", "string_expr", "option_code",
+  "option_repr_type", "pkt4_field", "start_expr", "length_expr",
+  "relay6_field", "nest_level", "pkt6_field", YY_NULLPTR
   };
   };
 
 
 #if YYDEBUG
 #if YYDEBUG
   const unsigned short int
   const unsigned short int
   EvalParser::yyrline_[] =
   EvalParser::yyrline_[] =
   {
   {
-       0,    91,    91,    94,    95,   100,   105,   110,   115,   120,
-     140,   156,   161,   166,   171,   176,   197,   212,   228,   233,
-     238,   245,   249,   255,   259,   265,   272,   277,   284,   285,
-     288,   297,   298
+       0,   100,   100,   103,   104,   109,   114,   119,   124,   129,
+     149,   165,   170,   175,   180,   185,   206,   221,   236,   241,
+     246,   251,   258,   262,   268,   272,   278,   282,   286,   290,
+     294,   298,   302,   308,   315,   320,   327,   328,   331,   340,
+     341
   };
   };
 
 
   // Print the state stack on the debug stream.
   // Print the state stack on the debug stream.
@@ -1400,8 +1494,8 @@ namespace isc { namespace eval {
 
 
 #line 13 "parser.yy" // lalr1.cc:1167
 #line 13 "parser.yy" // lalr1.cc:1167
 } } // isc::eval
 } } // isc::eval
-#line 1404 "parser.cc" // lalr1.cc:1167
-#line 301 "parser.yy" // lalr1.cc:1168
+#line 1498 "parser.cc" // lalr1.cc:1167
+#line 344 "parser.yy" // lalr1.cc:1168
 
 
 void
 void
 isc::eval::EvalParser::error(const location_type& loc,
 isc::eval::EvalParser::error(const location_type& loc,

+ 188 - 71
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
 
 
 
 
 
 
@@ -298,24 +298,27 @@ namespace isc { namespace eval {
       // option_repr_type
       // option_repr_type
       char dummy1[sizeof(TokenOption::RepresentationType)];
       char dummy1[sizeof(TokenOption::RepresentationType)];
 
 
+      // pkt4_field
+      char dummy2[sizeof(TokenPkt4::FieldType)];
+
       // pkt6_field
       // pkt6_field
-      char dummy2[sizeof(TokenPkt6::FieldType)];
+      char dummy3[sizeof(TokenPkt6::FieldType)];
 
 
       // relay6_field
       // relay6_field
-      char dummy3[sizeof(TokenRelay6Field::FieldType)];
+      char dummy4[sizeof(TokenRelay6Field::FieldType)];
 
 
       // "constant string"
       // "constant string"
       // "integer"
       // "integer"
       // "constant hexstring"
       // "constant hexstring"
       // "option name"
       // "option name"
       // "ip address"
       // "ip address"
-      char dummy4[sizeof(std::string)];
+      char dummy5[sizeof(std::string)];
 
 
       // option_code
       // option_code
-      char dummy5[sizeof(uint16_t)];
+      char dummy6[sizeof(uint16_t)];
 
 
       // nest_level
       // nest_level
-      char dummy6[sizeof(uint8_t)];
+      char dummy7[sizeof(uint8_t)];
 };
 };
 
 
     /// Symbol semantic values.
     /// Symbol semantic values.
@@ -356,18 +359,26 @@ namespace isc { namespace eval {
         TOKEN_TEXT = 272,
         TOKEN_TEXT = 272,
         TOKEN_HEX = 273,
         TOKEN_HEX = 273,
         TOKEN_EXISTS = 274,
         TOKEN_EXISTS = 274,
-        TOKEN_SUBSTRING = 275,
-        TOKEN_ALL = 276,
-        TOKEN_COMA = 277,
-        TOKEN_CONCAT = 278,
-        TOKEN_PKT6 = 279,
-        TOKEN_MSGTYPE = 280,
-        TOKEN_TRANSID = 281,
-        TOKEN_STRING = 282,
-        TOKEN_INTEGER = 283,
-        TOKEN_HEXSTRING = 284,
-        TOKEN_OPTION_NAME = 285,
-        TOKEN_IP_ADDRESS = 286
+        TOKEN_PKT4 = 275,
+        TOKEN_CHADDR = 276,
+        TOKEN_HLEN = 277,
+        TOKEN_HTYPE = 278,
+        TOKEN_CIADDR = 279,
+        TOKEN_GIADDR = 280,
+        TOKEN_YIADDR = 281,
+        TOKEN_SIADDR = 282,
+        TOKEN_SUBSTRING = 283,
+        TOKEN_ALL = 284,
+        TOKEN_COMA = 285,
+        TOKEN_CONCAT = 286,
+        TOKEN_PKT6 = 287,
+        TOKEN_MSGTYPE = 288,
+        TOKEN_TRANSID = 289,
+        TOKEN_STRING = 290,
+        TOKEN_INTEGER = 291,
+        TOKEN_HEXSTRING = 292,
+        TOKEN_OPTION_NAME = 293,
+        TOKEN_IP_ADDRESS = 294
       };
       };
     };
     };
 
 
@@ -407,6 +418,8 @@ namespace isc { namespace eval {
 
 
   basic_symbol (typename Base::kind_type t, const TokenOption::RepresentationType v, const location_type& l);
   basic_symbol (typename Base::kind_type t, const TokenOption::RepresentationType v, const location_type& l);
 
 
+  basic_symbol (typename Base::kind_type t, const TokenPkt4::FieldType v, const location_type& l);
+
   basic_symbol (typename Base::kind_type t, const TokenPkt6::FieldType v, const location_type& l);
   basic_symbol (typename Base::kind_type t, const TokenPkt6::FieldType v, const location_type& l);
 
 
   basic_symbol (typename Base::kind_type t, const TokenRelay6Field::FieldType v, const location_type& l);
   basic_symbol (typename Base::kind_type t, const TokenRelay6Field::FieldType v, const location_type& l);
@@ -558,6 +571,38 @@ namespace isc { namespace eval {
 
 
     static inline
     static inline
     symbol_type
     symbol_type
+    make_PKT4 (const location_type& l);
+
+    static inline
+    symbol_type
+    make_CHADDR (const location_type& l);
+
+    static inline
+    symbol_type
+    make_HLEN (const location_type& l);
+
+    static inline
+    symbol_type
+    make_HTYPE (const location_type& l);
+
+    static inline
+    symbol_type
+    make_CIADDR (const location_type& l);
+
+    static inline
+    symbol_type
+    make_GIADDR (const location_type& l);
+
+    static inline
+    symbol_type
+    make_YIADDR (const location_type& l);
+
+    static inline
+    symbol_type
+    make_SIADDR (const location_type& l);
+
+    static inline
+    symbol_type
     make_SUBSTRING (const location_type& l);
     make_SUBSTRING (const location_type& l);
 
 
     static inline
     static inline
@@ -689,7 +734,7 @@ namespace isc { namespace eval {
   // 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 unsigned char yycheck_[];
+  static const signed char 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.
@@ -809,12 +854,12 @@ namespace isc { namespace eval {
     enum
     enum
     {
     {
       yyeof_ = 0,
       yyeof_ = 0,
-      yylast_ = 102,     ///< Last index in yytable_.
-      yynnts_ = 11,  ///< Number of nonterminal symbols.
-      yyfinal_ = 23, ///< Termination state number.
+      yylast_ = 113,     ///< Last index in yytable_.
+      yynnts_ = 12,  ///< Number of nonterminal symbols.
+      yyfinal_ = 25, ///< Termination state number.
       yyterror_ = 1,
       yyterror_ = 1,
       yyerrcode_ = 256,
       yyerrcode_ = 256,
-      yyntokens_ = 32  ///< Number of tokens.
+      yyntokens_ = 40  ///< Number of tokens.
     };
     };
 
 
 
 
@@ -859,9 +904,10 @@ namespace isc { namespace eval {
        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,    26,    27,    28,    29,    30,    31
+      25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
+      35,    36,    37,    38,    39
     };
     };
-    const unsigned int user_token_number_max_ = 286;
+    const unsigned int user_token_number_max_ = 294;
     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_)
@@ -894,31 +940,35 @@ namespace isc { namespace eval {
   {
   {
       switch (other.type_get ())
       switch (other.type_get ())
     {
     {
-      case 37: // option_repr_type
+      case 45: // option_repr_type
         value.copy< TokenOption::RepresentationType > (other.value);
         value.copy< TokenOption::RepresentationType > (other.value);
         break;
         break;
 
 
-      case 42: // pkt6_field
+      case 46: // pkt4_field
+        value.copy< TokenPkt4::FieldType > (other.value);
+        break;
+
+      case 51: // pkt6_field
         value.copy< TokenPkt6::FieldType > (other.value);
         value.copy< TokenPkt6::FieldType > (other.value);
         break;
         break;
 
 
-      case 40: // relay6_field
+      case 49: // relay6_field
         value.copy< TokenRelay6Field::FieldType > (other.value);
         value.copy< TokenRelay6Field::FieldType > (other.value);
         break;
         break;
 
 
-      case 27: // "constant string"
-      case 28: // "integer"
-      case 29: // "constant hexstring"
-      case 30: // "option name"
-      case 31: // "ip address"
+      case 35: // "constant string"
+      case 36: // "integer"
+      case 37: // "constant hexstring"
+      case 38: // "option name"
+      case 39: // "ip address"
         value.copy< std::string > (other.value);
         value.copy< std::string > (other.value);
         break;
         break;
 
 
-      case 36: // option_code
+      case 44: // option_code
         value.copy< uint16_t > (other.value);
         value.copy< uint16_t > (other.value);
         break;
         break;
 
 
-      case 41: // nest_level
+      case 50: // nest_level
         value.copy< uint8_t > (other.value);
         value.copy< uint8_t > (other.value);
         break;
         break;
 
 
@@ -939,31 +989,35 @@ namespace isc { namespace eval {
     (void) v;
     (void) v;
       switch (this->type_get ())
       switch (this->type_get ())
     {
     {
-      case 37: // option_repr_type
+      case 45: // option_repr_type
         value.copy< TokenOption::RepresentationType > (v);
         value.copy< TokenOption::RepresentationType > (v);
         break;
         break;
 
 
-      case 42: // pkt6_field
+      case 46: // pkt4_field
+        value.copy< TokenPkt4::FieldType > (v);
+        break;
+
+      case 51: // pkt6_field
         value.copy< TokenPkt6::FieldType > (v);
         value.copy< TokenPkt6::FieldType > (v);
         break;
         break;
 
 
-      case 40: // relay6_field
+      case 49: // relay6_field
         value.copy< TokenRelay6Field::FieldType > (v);
         value.copy< TokenRelay6Field::FieldType > (v);
         break;
         break;
 
 
-      case 27: // "constant string"
-      case 28: // "integer"
-      case 29: // "constant hexstring"
-      case 30: // "option name"
-      case 31: // "ip address"
+      case 35: // "constant string"
+      case 36: // "integer"
+      case 37: // "constant hexstring"
+      case 38: // "option name"
+      case 39: // "ip address"
         value.copy< std::string > (v);
         value.copy< std::string > (v);
         break;
         break;
 
 
-      case 36: // option_code
+      case 44: // option_code
         value.copy< uint16_t > (v);
         value.copy< uint16_t > (v);
         break;
         break;
 
 
-      case 41: // nest_level
+      case 50: // nest_level
         value.copy< uint8_t > (v);
         value.copy< uint8_t > (v);
         break;
         break;
 
 
@@ -990,6 +1044,13 @@ namespace isc { namespace eval {
   {}
   {}
 
 
   template <typename Base>
   template <typename Base>
+  EvalParser::basic_symbol<Base>::basic_symbol (typename Base::kind_type t, const TokenPkt4::FieldType v, const location_type& l)
+    : Base (t)
+    , value (v)
+    , location (l)
+  {}
+
+  template <typename Base>
   EvalParser::basic_symbol<Base>::basic_symbol (typename Base::kind_type t, const TokenPkt6::FieldType v, const location_type& l)
   EvalParser::basic_symbol<Base>::basic_symbol (typename Base::kind_type t, const TokenPkt6::FieldType v, const location_type& l)
     : Base (t)
     : Base (t)
     , value (v)
     , value (v)
@@ -1050,31 +1111,35 @@ namespace isc { namespace eval {
     // Type destructor.
     // Type destructor.
     switch (yytype)
     switch (yytype)
     {
     {
-      case 37: // option_repr_type
+      case 45: // option_repr_type
         value.template destroy< TokenOption::RepresentationType > ();
         value.template destroy< TokenOption::RepresentationType > ();
         break;
         break;
 
 
-      case 42: // pkt6_field
+      case 46: // pkt4_field
+        value.template destroy< TokenPkt4::FieldType > ();
+        break;
+
+      case 51: // pkt6_field
         value.template destroy< TokenPkt6::FieldType > ();
         value.template destroy< TokenPkt6::FieldType > ();
         break;
         break;
 
 
-      case 40: // relay6_field
+      case 49: // relay6_field
         value.template destroy< TokenRelay6Field::FieldType > ();
         value.template destroy< TokenRelay6Field::FieldType > ();
         break;
         break;
 
 
-      case 27: // "constant string"
-      case 28: // "integer"
-      case 29: // "constant hexstring"
-      case 30: // "option name"
-      case 31: // "ip address"
+      case 35: // "constant string"
+      case 36: // "integer"
+      case 37: // "constant hexstring"
+      case 38: // "option name"
+      case 39: // "ip address"
         value.template destroy< std::string > ();
         value.template destroy< std::string > ();
         break;
         break;
 
 
-      case 36: // option_code
+      case 44: // option_code
         value.template destroy< uint16_t > ();
         value.template destroy< uint16_t > ();
         break;
         break;
 
 
-      case 41: // nest_level
+      case 50: // nest_level
         value.template destroy< uint8_t > ();
         value.template destroy< uint8_t > ();
         break;
         break;
 
 
@@ -1101,31 +1166,35 @@ namespace isc { namespace eval {
     super_type::move(s);
     super_type::move(s);
       switch (this->type_get ())
       switch (this->type_get ())
     {
     {
-      case 37: // option_repr_type
+      case 45: // option_repr_type
         value.move< TokenOption::RepresentationType > (s.value);
         value.move< TokenOption::RepresentationType > (s.value);
         break;
         break;
 
 
-      case 42: // pkt6_field
+      case 46: // pkt4_field
+        value.move< TokenPkt4::FieldType > (s.value);
+        break;
+
+      case 51: // pkt6_field
         value.move< TokenPkt6::FieldType > (s.value);
         value.move< TokenPkt6::FieldType > (s.value);
         break;
         break;
 
 
-      case 40: // relay6_field
+      case 49: // relay6_field
         value.move< TokenRelay6Field::FieldType > (s.value);
         value.move< TokenRelay6Field::FieldType > (s.value);
         break;
         break;
 
 
-      case 27: // "constant string"
-      case 28: // "integer"
-      case 29: // "constant hexstring"
-      case 30: // "option name"
-      case 31: // "ip address"
+      case 35: // "constant string"
+      case 36: // "integer"
+      case 37: // "constant hexstring"
+      case 38: // "option name"
+      case 39: // "ip address"
         value.move< std::string > (s.value);
         value.move< std::string > (s.value);
         break;
         break;
 
 
-      case 36: // option_code
+      case 44: // option_code
         value.move< uint16_t > (s.value);
         value.move< uint16_t > (s.value);
         break;
         break;
 
 
-      case 41: // nest_level
+      case 50: // nest_level
         value.move< uint8_t > (s.value);
         value.move< uint8_t > (s.value);
         break;
         break;
 
 
@@ -1187,7 +1256,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,   280,   281,   282,   283,   284,
      275,   276,   277,   278,   279,   280,   281,   282,   283,   284,
-     285,   286
+     285,   286,   287,   288,   289,   290,   291,   292,   293,   294
     };
     };
     return static_cast<token_type> (yytoken_number_[type]);
     return static_cast<token_type> (yytoken_number_[type]);
   }
   }
@@ -1301,6 +1370,54 @@ namespace isc { namespace eval {
   }
   }
 
 
   EvalParser::symbol_type
   EvalParser::symbol_type
+  EvalParser::make_PKT4 (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_PKT4, l);
+  }
+
+  EvalParser::symbol_type
+  EvalParser::make_CHADDR (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_CHADDR, l);
+  }
+
+  EvalParser::symbol_type
+  EvalParser::make_HLEN (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_HLEN, l);
+  }
+
+  EvalParser::symbol_type
+  EvalParser::make_HTYPE (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_HTYPE, l);
+  }
+
+  EvalParser::symbol_type
+  EvalParser::make_CIADDR (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_CIADDR, l);
+  }
+
+  EvalParser::symbol_type
+  EvalParser::make_GIADDR (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_GIADDR, l);
+  }
+
+  EvalParser::symbol_type
+  EvalParser::make_YIADDR (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_YIADDR, l);
+  }
+
+  EvalParser::symbol_type
+  EvalParser::make_SIADDR (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_SIADDR, l);
+  }
+
+  EvalParser::symbol_type
   EvalParser::make_SUBSTRING (const location_type& l)
   EvalParser::make_SUBSTRING (const location_type& l)
   {
   {
     return symbol_type (token::TOKEN_SUBSTRING, l);
     return symbol_type (token::TOKEN_SUBSTRING, l);
@@ -1373,9 +1490,9 @@ namespace isc { namespace eval {
   }
   }
 
 
 
 
-#line 13 "parser.yy" // lalr1.cc:377
+#line 13 "parser.yy" // lalr1.cc:392
 } } // isc::eval
 } } // isc::eval
-#line 1379 "parser.h" // lalr1.cc:377
+#line 1496 "parser.h" // lalr1.cc:392
 
 
 
 
 
 

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

@@ -54,6 +54,14 @@ using namespace isc::eval;
   TEXT "text"
   TEXT "text"
   HEX "hex"
   HEX "hex"
   EXISTS "exists"
   EXISTS "exists"
+  PKT4 "pkt4"
+  CHADDR "mac"
+  HLEN "hlen"
+  HTYPE "htype"
+  CIADDR "ciaddr"
+  GIADDR "giaddr"
+  YIADDR "yiaddr"
+  SIADDR "siaddr"
   SUBSTRING "substring"
   SUBSTRING "substring"
   ALL "all"
   ALL "all"
   COMA ","
   COMA ","
@@ -73,6 +81,7 @@ using namespace isc::eval;
 %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
+%type <TokenPkt4::FieldType> pkt4_field
 %type <TokenPkt6::FieldType> pkt6_field
 %type <TokenPkt6::FieldType> pkt6_field
 
 
 %left OR
 %left OR
@@ -224,7 +233,16 @@ string_expr : STRING
                      }
                      }
                   }
                   }
 
 
-
+            | PKT4 "." pkt4_field
+                  {
+                      TokenPtr pkt4_field(new TokenPkt4($3));
+                      ctx.expression.push_back(pkt4_field);
+                  }
+            | PKT6 "." pkt6_field
+                  {
+                      TokenPtr pkt6_field(new TokenPkt6($3));
+                      ctx.expression.push_back(pkt6_field);
+                  }
             | SUBSTRING "(" string_expr "," start_expr "," length_expr ")"
             | SUBSTRING "(" string_expr "," start_expr "," length_expr ")"
                   {
                   {
                       TokenPtr sub(new TokenSubstring());
                       TokenPtr sub(new TokenSubstring());
@@ -235,11 +253,6 @@ string_expr : STRING
                       TokenPtr conc(new TokenConcat());
                       TokenPtr conc(new TokenConcat());
                       ctx.expression.push_back(conc);
                       ctx.expression.push_back(conc);
                   }
                   }
-            | PKT6 "." pkt6_field
-                  {
-                      TokenPtr pkt6_field(new TokenPkt6($3));
-                      ctx.expression.push_back(pkt6_field);
-                  }
             ;
             ;
 
 
 option_code : INTEGER
 option_code : INTEGER
@@ -262,6 +275,36 @@ option_repr_type : TEXT
                       }
                       }
                  ;
                  ;
 
 
+pkt4_field : CHADDR
+                {
+                    $$ = TokenPkt4::CHADDR;
+                }
+           | HLEN
+                {
+                    $$ = TokenPkt4::HLEN;
+                }
+           | HTYPE
+                {
+                    $$ = TokenPkt4::HTYPE;
+                }
+           | CIADDR
+                {
+                    $$ = TokenPkt4::CIADDR;
+                }
+           | GIADDR
+                {
+                    $$ = TokenPkt4::GIADDR;
+                }
+           | YIADDR
+                {
+                    $$ = TokenPkt4::YIADDR;
+                }
+           | SIADDR
+                {
+                    $$ = TokenPkt4::SIADDR;
+                }
+           ;
+
 start_expr : INTEGER
 start_expr : INTEGER
                  {
                  {
                      TokenPtr str(new TokenString($1));
                      TokenPtr str(new TokenString($1));

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

@@ -132,6 +132,51 @@ public:
         }
         }
     }
     }
 
 
+    /// @brief checks if the given token is Pkt4 of specified type
+    /// @param token token to be checked
+    /// @param type expected type of the Pkt4 field
+    void checkTokenPkt4(const TokenPtr& token, TokenPkt4::FieldType type) {
+        ASSERT_TRUE(token);
+        boost::shared_ptr<TokenPkt4> pkt =
+            boost::dynamic_pointer_cast<TokenPkt4>(token);
+        ASSERT_TRUE(pkt);
+
+        EXPECT_EQ(type, pkt->getType());
+    }
+
+    /// @brief Test that verifies access to the DHCPv4 packet fields.
+    ///
+    /// This test attempts to parse the expression, will check if the number
+    /// of tokens is exactly as expected and then will try to verify if the
+    /// first token represents the expected field in DHCPv4 packet.
+    ///
+    /// @param expr expression to be parsed
+    /// @param exp_type expected field type to be parsed
+    /// @param exp_tokens expected number of tokens
+    void testPkt4Field(std::string expr,
+                       TokenPkt4::FieldType exp_type,
+                       int exp_tokens) {
+        EvalContext eval(Option::V4);
+
+        // Parse the expression.
+        try {
+            parsed_ = eval.parseString(expr);
+        }
+        catch (const EvalParseError& ex) {
+            FAIL() << "Exception thrown: " << ex.what();
+            return;
+        }
+
+        // Parsing should succeed and return a token.
+        EXPECT_TRUE(parsed_);
+
+        // There should be exactly the expected number of tokens.
+        ASSERT_EQ(exp_tokens, eval.expression.size());
+
+        // Check that the first token is TokenPkt4 instance and has correct type.
+        checkTokenPkt4(eval.expression.at(0), exp_type);
+    }
+
     /// @brief checks if the given token is a substring operator
     /// @brief checks if the given token is a substring operator
     void checkTokenSubstring(const TokenPtr& token) {
     void checkTokenSubstring(const TokenPtr& token) {
         ASSERT_TRUE(token);
         ASSERT_TRUE(token);
@@ -585,6 +630,41 @@ TEST_F(EvalContextTest, relay4Error) {
                "<string>:1.1-6: relay4 can only be used in DHCPv4.");
                "<string>:1.1-6: relay4 can only be used in DHCPv4.");
 }
 }
 
 
+// Tests whether chaddr field in DHCPv4 can be accessed.
+TEST_F(EvalContextTest, pkt4FieldChaddr) {
+    testPkt4Field("pkt4.mac == 0x000102030405", TokenPkt4::CHADDR, 3);
+}
+
+// Tests whether hlen field in DHCPv4 can be accessed.
+TEST_F(EvalContextTest, pkt4FieldHlen) {
+    testPkt4Field("pkt4.hlen == 0x6", TokenPkt4::HLEN, 3);
+}
+
+// Tests whether htype field in DHCPv4 can be accessed.
+TEST_F(EvalContextTest, pkt4FieldHtype) {
+    testPkt4Field("pkt4.htype == 0x1", TokenPkt4::HTYPE, 3);
+}
+
+// Tests whether ciaddr field in DHCPv4 can be accessed.
+TEST_F(EvalContextTest, pkt4FieldCiaddr) {
+    testPkt4Field("pkt4.ciaddr == 192.0.2.1", TokenPkt4::CIADDR, 3);
+}
+
+// Tests whether giaddr field in DHCPv4 can be accessed.
+TEST_F(EvalContextTest, pkt4FieldGiaddr) {
+    testPkt4Field("pkt4.giaddr == 192.0.2.1", TokenPkt4::GIADDR, 3);
+}
+
+// Tests whether yiaddr field in DHCPv4 can be accessed.
+TEST_F(EvalContextTest, pkt4FieldYiaddr) {
+    testPkt4Field("pkt4.yiaddr == 192.0.2.1", TokenPkt4::YIADDR, 3);
+}
+
+// Tests whether siaddr field in DHCPv4 can be accessed.
+TEST_F(EvalContextTest, pkt4FieldSiaddr) {
+    testPkt4Field("pkt4.siaddr == 192.0.2.1", TokenPkt4::SIADDR, 3);
+}
+
 // Tests whether message type field in DHCPv6 can be accessed.
 // Tests whether message type field in DHCPv6 can be accessed.
 TEST_F(EvalContextTest, pkt6FieldMsgtype) {
 TEST_F(EvalContextTest, pkt6FieldMsgtype) {
     testPkt6Field("pkt6.msgtype == '1'", TokenPkt6::MSGTYPE, 3);
     testPkt6Field("pkt6.msgtype == '1'", TokenPkt6::MSGTYPE, 3);
@@ -790,6 +870,7 @@ TEST_F(EvalContextTest, scanErrors) {
     checkError("foo", "<string>:1.1: Invalid character: f");
     checkError("foo", "<string>:1.1: Invalid character: f");
     checkError(" bar", "<string>:1.2: Invalid character: b");
     checkError(" bar", "<string>:1.2: Invalid character: b");
     checkError("relay[12].hex == 'foo'", "<string>:1.1: Invalid character: r");
     checkError("relay[12].hex == 'foo'", "<string>:1.1: Invalid character: r");
+    checkError("pkt4.ziaddr", "<string>:1.6: Invalid character: z");
 }
 }
 
 
 // Tests some scanner/parser error cases
 // Tests some scanner/parser error cases

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

@@ -20,6 +20,7 @@
 
 
 using namespace std;
 using namespace std;
 using namespace isc::dhcp;
 using namespace isc::dhcp;
+using namespace isc::asiolink;
 
 
 namespace {
 namespace {
 
 
@@ -708,6 +709,86 @@ TEST_F(TokenTest, relay4RAIOnly) {
     EXPECT_EQ("false", values_.top());
     EXPECT_EQ("false", values_.top());
 }
 }
 
 
+// Verifies if the DHCPv4 packet fields can be extracted.
+TEST_F(TokenTest, pkt4Fields) {
+    pkt4_->setGiaddr(IOAddress("192.0.2.1"));
+    pkt4_->setCiaddr(IOAddress("192.0.2.2"));
+    pkt4_->setYiaddr(IOAddress("192.0.2.3"));
+    pkt4_->setSiaddr(IOAddress("192.0.2.4"));
+
+    // We're setting hardware address to uncommon (7 bytes rather than 6 and
+    // hardware type 123) HW address. We'll use it in hlen and htype checks.
+    HWAddrPtr hw(new HWAddr(HWAddr::fromText("01:02:03:04:05:06:07", 123)));
+    pkt4_->setHWAddr(hw);
+
+    // Check hardware address field.
+    ASSERT_NO_THROW(t_.reset(new TokenPkt4(TokenPkt4::CHADDR)));
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+    ASSERT_EQ(1, values_.size());
+    uint8_t expected_hw[] = { 1, 2, 3, 4, 5, 6, 7 };
+    ASSERT_EQ(7, values_.top().size());
+    EXPECT_EQ(0, memcmp(expected_hw, &values_.top()[0], 7));
+
+    // Check hlen value field.
+    clearStack();
+    ASSERT_NO_THROW(t_.reset(new TokenPkt4(TokenPkt4::HLEN)));
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+    ASSERT_EQ(1, values_.size());
+    ASSERT_EQ(4, values_.top().size());
+    uint32_t expected_hlen = htonl(7);
+    EXPECT_EQ(0, memcmp(&expected_hlen, &values_.top()[0], 4));
+
+    // Check htype value.
+    clearStack();
+    ASSERT_NO_THROW(t_.reset(new TokenPkt4(TokenPkt4::HTYPE)));
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+    ASSERT_EQ(1, values_.size());
+    ASSERT_EQ(4, values_.top().size());
+    uint32_t expected_htype = htonl(123);
+    EXPECT_EQ(0, memcmp(&expected_htype, &values_.top()[0], 4));
+
+    // Check giaddr value.
+    clearStack();
+    ASSERT_NO_THROW(t_.reset(new TokenPkt4(TokenPkt4::GIADDR)));
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+    ASSERT_EQ(1, values_.size());
+    uint8_t expected_addr[] = { 192, 0, 2, 1 };
+    ASSERT_EQ(4, values_.top().size());
+    EXPECT_EQ(0, memcmp(expected_addr, &values_.top()[0], 4));
+
+    // Check ciaddr value.
+    clearStack();
+    ASSERT_NO_THROW(t_.reset(new TokenPkt4(TokenPkt4::CIADDR)));
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+    ASSERT_EQ(1, values_.size());
+    expected_addr[3] = 2;
+    ASSERT_EQ(4, values_.top().size());
+    EXPECT_EQ(0, memcmp(expected_addr, &values_.top()[0], 4));
+
+    // Check yiaddr value.
+    clearStack();
+    ASSERT_NO_THROW(t_.reset(new TokenPkt4(TokenPkt4::YIADDR)));
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+    ASSERT_EQ(1, values_.size());
+    expected_addr[3] = 3;
+    ASSERT_EQ(4, values_.top().size());
+    EXPECT_EQ(0, memcmp(expected_addr, &values_.top()[0], 4));
+
+    // Check siaddr value.
+    clearStack();
+    ASSERT_NO_THROW(t_.reset(new TokenPkt4(TokenPkt4::SIADDR)));
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+    ASSERT_EQ(1, values_.size());
+    expected_addr[3] = 4;
+    ASSERT_EQ(4, values_.top().size());
+    EXPECT_EQ(0, memcmp(expected_addr, &values_.top()[0], 4));
+
+    // Check a DHCPv6 packet throws.
+    clearStack();
+    ASSERT_NO_THROW(t_.reset(new TokenPkt4(TokenPkt4::HLEN)));
+    EXPECT_THROW(t_->evaluate(*pkt6_, values_), EvalTypeError);
+}
+
 // This test checks if a token representing an == operator is able to
 // This test checks if a token representing an == operator is able to
 // compare two values (with incorrectly built stack).
 // compare two values (with incorrectly built stack).
 TEST_F(TokenTest, optionEqualInvalid) {
 TEST_F(TokenTest, optionEqualInvalid) {

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

@@ -8,6 +8,7 @@
 #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 <asiolink/io_address.h>
+#include <dhcp/pkt4.h>
 #include <boost/lexical_cast.hpp>
 #include <boost/lexical_cast.hpp>
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt6.h>
 #include <cstring>
 #include <cstring>
@@ -125,6 +126,77 @@ OptionPtr TokenRelay4Option::getOption(const Pkt& pkt) {
 }
 }
 
 
 void
 void
+TokenPkt4::evaluate(const Pkt& pkt, ValueStack& values) {
+
+    vector<uint8_t> binary;
+    try {
+        // Check if it's a Pkt4. If it's not, the dynamic_cast will throw
+        // std::bad_cast (failed dynamic_cast returns NULL for pointers and
+        // throws for references).
+        const Pkt4& pkt4 = dynamic_cast<const Pkt4&>(pkt);
+
+        switch (type_) {
+        case CHADDR: {
+            HWAddrPtr hwaddr = pkt4.getHWAddr();
+            if (!hwaddr) {
+                // This should never happen. Every Pkt4 should always have
+                // a hardware address.
+                isc_throw(EvalTypeError,
+                          "Packet does not have hardware address");
+            }
+            binary = hwaddr->hwaddr_;
+            break;
+        }
+        case GIADDR:
+            binary = pkt4.getGiaddr().toBytes();
+            break;
+
+        case CIADDR:
+            binary = pkt4.getCiaddr().toBytes();
+            break;
+
+        case YIADDR:
+            binary = pkt4.getYiaddr().toBytes();
+            break;
+
+        case SIADDR:
+            binary = pkt4.getSiaddr().toBytes();
+            break;
+
+        case HLEN:
+            // Pad the uint8_t field to 4 bytes.
+            binary.push_back(0);
+            binary.push_back(0);
+            binary.push_back(0);
+            binary.push_back(pkt4.getHlen());
+            break;
+
+        case HTYPE:
+            // Pad the uint8_t field to 4 bytes.
+            binary.push_back(0);
+            binary.push_back(0);
+            binary.push_back(0);
+            binary.push_back(pkt4.getHtype());
+            break;
+
+        default:
+            isc_throw(EvalTypeError, "Bad field specified: "
+                      << static_cast<int>(type_) );
+        }
+
+    } catch (const std::bad_cast&) {
+        isc_throw(EvalTypeError, "Specified packet is not a Pkt4");
+    }
+
+    string value;
+    value.resize(binary.size());
+    if (!binary.empty()) {
+        memmove(&value[0], &binary[0], binary.size());
+    }
+    values.push(value);
+}
+
+void
 TokenEqual::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
 TokenEqual::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
 
 
     if (values.size() < 2) {
     if (values.size() < 2) {

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

@@ -288,6 +288,61 @@ protected:
     virtual OptionPtr getOption(const Pkt& pkt);
     virtual OptionPtr getOption(const Pkt& pkt);
 };
 };
 
 
+/// @brief Token that represents fields of a DHCPv4 packet.
+///
+/// For example in the expression pkt4.chaddr == 0x0102030405
+/// this token represents the pkt4.chaddr expression.
+///
+/// Currently supported fields are:
+/// - chaddr (client hardware address, hlen [0..16] octets)
+/// - giaddr (relay agent IP address, 4 octets)
+/// - ciaddr (client IP address, 4 octets)
+/// - yiaddr ('your' (client) IP address, 4 octets)
+/// - siaddr (next server IP address, 4 octets)
+/// - hlen   (hardware address length, padded to 4 octets)
+/// - htype  (hardware address type, padded to 4 octets)
+class TokenPkt4 : public Token {
+public:
+
+    /// @brief enum value that determines the field.
+    enum FieldType {
+        CHADDR, ///< chaddr field (up to 16 bytes link-layer address)
+        GIADDR, ///< giaddr (IPv4 address)
+        CIADDR, ///< ciaddr (IPv4 address)
+        YIADDR, ///< yiaddr (IPv4 address)
+        SIADDR, ///< siaddr (IPv4 address)
+        HLEN,   ///< hlen (hardware address length)
+        HTYPE   ///< htype (hardware address type)
+    };
+
+    /// @brief Constructor (does nothing)
+    TokenPkt4(const FieldType type)
+        : type_(type) {}
+
+    /// @brief Gets a value from the specified packet.
+    ///
+    /// Evaluation uses fields available in the packet. It does not require
+    /// any values to be present on the stack.
+    ///
+    /// @throw EvalTypeError when called for DHCPv6 packet
+    ///
+    /// @param pkt - fields will be extracted from here
+    /// @param values - stack of values (1 result will be pushed)
+    void evaluate(const Pkt& pkt, ValueStack& values);
+
+    /// @brief Returns field type
+    ///
+    /// This method is used only in tests.
+    /// @return type of the field.
+    FieldType getType() {
+        return (type_);
+    }
+
+private:
+    /// @brief Specifies field of the DHCPv4 packet
+    FieldType type_;
+};
+
 /// @brief Token that represents equality operator (compares two other tokens)
 /// @brief Token that represents equality operator (compares two other tokens)
 ///
 ///
 /// For example in the expression option[vendor-class].text == "MSFT"
 /// For example in the expression option[vendor-class].text == "MSFT"