Parcourir la source

Merge branch 'trac4265' Access to options and constants from DHCP6 relay encapsulations

Shawn Routhier il y a 9 ans
Parent
commit
bb00d9d205

+ 5 - 0
ChangeLog

@@ -1,3 +1,8 @@
+1102.	[func]		sar
+	Added access to the peer address, link address and option
+	information added by relays in a DHCPv6 message.
+	(Trac $4269, git tbd)
+
 1101.	[bug]		stephen
 	Made DHCPSRV_MEMFILE_LFC_UNREGISTER_TIMER_FAILED a debug message as the
 	condition leading to it (trying to unregister a timer that is not

+ 33 - 1
doc/guide/classify.xml

@@ -170,6 +170,24 @@
 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>
           </tbody>
           </tgroup>
         </table>
@@ -211,11 +229,25 @@ sub-option with code "code" from the DHCPv4 Relay Agent Information option
       </para>
 
       <para>
-       "relay4" shares the same representation types than "option", for
+       "relay4" shares the same representation types as "option", for
        instance "relay4[code].exists" is supported.
       </para>
 
       <para>
+       "relay6[nest]" allows access to the encapsulations used by any DHCPv6
+       relays that forwarded the packet.  The "nest" level specifies the relay
+       from which to extract the information, with a value of 0 indicating
+       the relay closest to the DHCPv6 server.  If the requested encapsulation
+       doesn't exist an empty string "" is returned.  This expression is
+       allowed in DHCPv6 only.
+      </para>
+
+      <para>
+       "relay6[nest].option[code]" shares the same representation types as
+       "option", for instance "relay6[nest].option[code].exists" is supported.
+      </para>
+
+      <para>
         <table frame="all" id="classification-expressions-list">
           <title>List of Classification Expressions</title>
           <tgroup cols='3'>

+ 27 - 9
src/lib/dhcp/pkt6.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -112,22 +112,40 @@ OptionPtr Pkt6::getAnyRelayOption(uint16_t opt_type, RelaySearchOrder order) {
     return (OptionPtr());
 }
 
-OptionPtr Pkt6::getRelayOption(uint16_t opt_type, uint8_t relay_level) {
+OptionPtr Pkt6::getRelayOption(uint16_t opt_type, uint8_t relay_level) const {
     if (relay_level >= relay_info_.size()) {
         isc_throw(OutOfRange, "This message was relayed " << relay_info_.size() << " time(s)."
                   << " There is no info about " << relay_level + 1 << " relay.");
     }
 
-    for (OptionCollection::iterator it = relay_info_[relay_level].options_.begin();
-         it != relay_info_[relay_level].options_.end(); ++it) {
-        if ((*it).second->getType() == opt_type) {
-            return (it->second);
-        }
-    }
+    OptionCollection::const_iterator x = relay_info_[relay_level].options_.find(opt_type);
+    if (x != relay_info_[relay_level].options_.end()) {
+	return (*x).second;
+      }
 
     return (OptionPtr());
 }
 
+const isc::asiolink::IOAddress&
+Pkt6::getRelay6LinkAddress(uint8_t relay_level) const {
+    if (relay_level >= relay_info_.size()) {
+        isc_throw(OutOfRange, "This message was relayed " << relay_info_.size() << " time(s)."
+                  << " There is no info about " << relay_level + 1 << " relay.");
+    }
+
+    return (relay_info_[relay_level].linkaddr_);
+}
+
+const isc::asiolink::IOAddress&
+Pkt6::getRelay6PeerAddress(uint8_t relay_level) const {
+    if (relay_level >= relay_info_.size()) {
+        isc_throw(OutOfRange, "This message was relayed " << relay_info_.size() << " time(s)."
+                  << " There is no info about " << relay_level + 1 << " relay.");
+    }
+
+    return (relay_info_[relay_level].peeraddr_);
+}
+
 uint16_t Pkt6::getRelayOverhead(const RelayInfo& relay) const {
     uint16_t len = DHCPV6_RELAY_HDR_LEN // fixed header
         + Option::OPTION6_HDR_LEN; // header of the relay-msg option
@@ -429,7 +447,7 @@ Pkt6::unpackRelayMsg() {
 
 void
 Pkt6::addRelayInfo(const RelayInfo& relay) {
-    if (relay_info_.size() > 32) {
+    if (relay_info_.size() > HOP_COUNT_LIMIT) {
         isc_throw(BadValue, "Massage cannot be encapsulated more than 32 times");
     }
 

+ 37 - 4
src/lib/dhcp/pkt6.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -239,7 +239,7 @@ public:
     /// @param nesting_level see description above
     ///
     /// @return pointer to the option (or NULL if there is no such option)
-    OptionPtr getRelayOption(uint16_t option_code, uint8_t nesting_level);
+    OptionPtr getRelayOption(uint16_t option_code, uint8_t nesting_level) const;
 
     /// @brief Return first instance of a specified option
     ///
@@ -253,6 +253,39 @@ public:
     /// @return option pointer (or NULL if no option matches specified criteria)
     OptionPtr getAnyRelayOption(uint16_t option_code, RelaySearchOrder order);
 
+    /// @brief return the link address field from a relay option
+    ///
+    /// As with @c Pkt6::getRelayOption this returns information from the
+    /// specified relay scope.  The relay_level specifies which relay
+    /// scope is to be used.  0 is the outermost encapsulation (relay closest
+    /// to the server).  pkt->relay_info_.size() -1 is the innermost encapsulation
+    /// (relay closest to the client).
+    ///
+    /// @throw isc::OutOfRange if relay level has an invalid value.
+    ///
+    /// @param relay_level see description above
+    ///
+    /// @return pointer to the link address field
+    const isc::asiolink::IOAddress&
+    getRelay6LinkAddress(uint8_t relay_level) const;
+
+    /// @brief return the peer address field from a relay option
+    ///
+    /// As with @c Pkt6::getRelayOption this returns information from the
+    /// specified relay scope.  The relay_level specifies which relay
+    /// scope is to be used.  0 is the outermost encapsulation (relay closest
+    /// to the server).  pkt->relay_info_.size() -1 is the innermost encapsulation
+    /// (relay closest to the client).
+    ///
+    /// @throw isc::OutOfRange if relay level has an invalid value.
+    ///
+    /// @param relay_level see description above
+    ///
+    /// @return pointer to the peer address field
+    const isc::asiolink::IOAddress&
+    getRelay6PeerAddress(uint8_t relay_level) const;
+
+    ///
     /// @brief Returns all instances of specified type.
     ///
     /// Returns all instances of options of the specified type. DHCPv6 protocol
@@ -279,13 +312,13 @@ public:
     /// @param type DHCPv6 message type which name should be returned.
     ///
     /// @return Pointer to "const" string containing the message name. If
-    /// the message type is unknnown the "UNKNOWN" is returned. The caller
+    /// the message type is unknown the "UNKNOWN" is returned. The caller
     /// must not release the returned pointer.
     static const char* getName(const uint8_t type);
 
     /// @brief Returns name of the DHCPv6 message.
     ///
-    /// This method requires an object. There is also static version, which
+    /// This method requires an object. There is also a static version, which
     /// requires one parameter (type).
     ///
     /// @return Pointer to "const" string containing the message name. If

+ 12 - 2
src/lib/dhcp/tests/pkt6_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -763,7 +763,7 @@ TEST_F(Pkt6Test, relayPack) {
 
     Pkt6::RelayInfo relay1;
     relay1.msg_type_ = DHCPV6_RELAY_REPL;
-    relay1.hop_count_ = 17; // not very miningful, but useful for testing
+    relay1.hop_count_ = 17; // not very meaningful, but useful for testing
     relay1.linkaddr_ = IOAddress("2001:db8::1");
     relay1.peeraddr_ = IOAddress("fe80::abcd");
 
@@ -830,6 +830,16 @@ TEST_F(Pkt6Test, relayPack) {
     OptionBuffer data = opt->getData();
     ASSERT_EQ(data.size(), sizeof(relay_opt_data));
     EXPECT_EQ(0, memcmp(&data[0], relay_opt_data, sizeof(relay_opt_data)));
+
+    // As we have a nicely built relay packet we can check
+    // that the functions to get the peer and link addreses work
+    EXPECT_EQ("2001:db8::1", clone->getRelay6LinkAddress(0).toText());
+    EXPECT_EQ("fe80::abcd", clone->getRelay6PeerAddress(0).toText());
+
+    vector<uint8_t>binary = clone->getRelay6LinkAddress(0).toBytes();
+    uint8_t expected0[] = {0x20, 1, 0x0d, 0xb8, 0, 0, 0, 0,
+                           0, 0, 0, 0, 0, 0, 0, 1};
+    EXPECT_EQ(0, memcmp(expected0, &binary[0], 16));
 }
 
 

+ 25 - 1
src/lib/eval/eval_context.cc

@@ -1,9 +1,10 @@
-// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+#include <dhcp/dhcp6.h>
 #include <dhcp/option.h>
 #include <dhcp/option_definition.h>
 #include <dhcp/libdhcp++.h>
@@ -92,6 +93,29 @@ EvalContext::convertOptionName(const std::string& option_name,
     return (option_def->getCode());
 }
 
+uint8_t
+EvalContext::convertNestLevelNumber(const std::string& nest_level,
+                                    const isc::eval::location& loc)
+{
+    int n = 0;
+    try {
+        n  = boost::lexical_cast<int>(nest_level);
+    } catch (const boost::bad_lexical_cast &) {
+        error(loc, "Nest level has invalid value in " + nest_level);
+    }
+    if (option_universe_ == Option::V6) {
+        if (n < 0 || n >= HOP_COUNT_LIMIT) {
+            error(loc, "Nest level has invalid value in "
+                      + nest_level + ". Allowed range: 0..31");
+	}
+    } else {
+        error(loc, "Nest level invalid for DHCPv4 packets");
+    }
+
+    return (static_cast<uint8_t>(n));
+}
+
+
 void
 EvalContext::fatal (const std::string& what)
 {

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

@@ -82,12 +82,12 @@ public:
     /// This is for should not happen but fatal errors
     static void fatal(const std::string& what);
 
-    /// @brief Option code convertion
+    /// @brief Option code conversion
     ///
     /// @param option_code a string representing the integer code
     /// @param loc the location of the token
     /// @result the option code
-    /// @throw calls the syntax error function if the value is no in
+    /// @throw calls the syntax error function if the value is not in
     ///        the range 0..255 or 0..65535
     uint16_t convertOptionCode(const std::string& option_code,
                                const isc::eval::location& loc);
@@ -101,6 +101,16 @@ public:
     uint16_t convertOptionName(const std::string& option_name,
                                const isc::eval::location& loc);
 
+    /// @brief Nest level conversion
+    ///
+    /// @param nest_level a string representing the integer nesting level
+    /// @param loc the location of the token
+    /// @result the nesting level
+    /// @throw calls the syntax error function if the value is not in
+    ///        the range 0..31
+    uint8_t convertNestLevelNumber(const std::string& nest_level,
+                                    const isc::eval::location& loc);
+
     /// @brief Returns the universe (v4 or v6)
     ///
     /// @return universe

+ 177 - 145
src/lib/eval/lexer.cc

@@ -483,8 +483,8 @@ static void yy_fatal_error (yyconst char msg[]  );
 	(yy_c_buf_p) = yy_cp;
 
 /* %% [4.0] data tables for the DFA and the user's section 1 definitions go here */
-#define YY_NUM_RULES 27
-#define YY_END_OF_BUFFER 28
+#define YY_NUM_RULES 30
+#define YY_END_OF_BUFFER 31
 /* This struct is not used in this scanner,
    but its presence is necessary. */
 struct yy_trans_info
@@ -492,38 +492,43 @@ struct yy_trans_info
 	flex_int32_t yy_verify;
 	flex_int32_t yy_nxt;
 	};
-static yyconst flex_int16_t yy_acclist[138] =
+static yyconst flex_int16_t yy_acclist[162] =
     {   0,
-       28,   26,   27,    1,   26,   27,    2,   27,   26,   27,
-       21,   26,   27,   22,   26,   27,   25,   26,   27,   26,
-       27,   20,   26,   27,    5,   26,   27,    5,   26,   27,
-       26,   27,   26,   27,   26,   27,16390,   26,   27,16390,
-       23,   26,   27,   24,   26,   27,   26,   27,16390,   26,
-       27,16390,   26,   27,16390,   26,   27,16390,   26,   27,
-    16390,   26,   27,16390,   26,   27,16390,   26,   27,16390,
-       26,   27,16390,    1,    2,    3,    5,    5,    7,    8,
-    16390,16390, 8198,16390,16390,16390,16390,16390,16390,16390,
-       19,16390,16390,16390,16390,    4,    7,   15,16390,   18,
-
-    16390,16390,16390,   12,16390,   17,16390,16390,16390,16390,
-    16390,16390,16390,16390,16390,16390,   11,16390,16390,16390,
-    16390,16390,16390,   16,16390,   13,16390,    9,16390,   10,
-    16390,16390,    7,16390,16390,   14,16390
+       31,   29,   30,    1,   29,   30,    2,   30,   29,   30,
+       24,   29,   30,   25,   29,   30,   28,   29,   30,   29,
+       30,   23,   29,   30,    5,   29,   30,    5,   29,   30,
+       29,   30,   29,   30,   29,   30,16390,   29,   30,16390,
+       26,   29,   30,   27,   29,   30,   29,   30,16390,   29,
+       30,16390,   29,   30,16390,   29,   30,16390,   29,   30,
+    16390,   29,   30,16390,   29,   30,16390,   29,   30,16390,
+       29,   30,16390,   29,   30,16390,   29,   30,16390,    1,
+        2,    3,    5,    5,    7,    8,16390,16390, 8198,16390,
+    16390,16390,16390,16390,16390,16390,16390,   22,16390,16390,
+
+    16390,16390,16390,    4,    7,   18,16390,   21,16390,16390,
+    16390,   15,16390,16390,   20,16390,16390,16390,16390,16390,
+    16390,16390,16390,16390,16390,16390,16390,16390,   14,16390,
+    16390,16390,16390,16390,16390,16390,16390,   19,16390,   16,
+    16390,16390,    9,16390,16390,   10,16390,   11,16390,16390,
+        7,16390,16390,16390,   13,16390,   12,16390,16390,   17,
+    16390
     } ;
 
-static yyconst flex_int16_t yy_accept[97] =
+static yyconst flex_int16_t yy_accept[114] =
     {   0,
         1,    1,    1,    2,    4,    7,    9,   11,   14,   17,
        20,   22,   25,   28,   31,   33,   35,   38,   41,   44,
        47,   50,   53,   56,   59,   62,   65,   68,   71,   74,
-       75,   76,   76,   77,   78,   78,   79,   79,   79,   79,
-       79,   80,   81,   81,   81,   82,   83,   84,   85,   86,
-       87,   88,   89,   90,   91,   93,   94,   95,   96,   96,
-       97,   98,  100,  102,  103,  104,  106,  108,  109,  110,
-      111,  112,  112,  113,  114,  115,  116,  117,  119,  119,
-      120,  121,  122,  123,  124,  124,  126,  128,  130,  132,
-      133,  134,  135,  136,  138,  138
-
+       77,   80,   81,   82,   82,   83,   84,   84,   85,   85,
+       85,   85,   85,   86,   87,   87,   87,   88,   89,   90,
+       91,   92,   93,   94,   95,   96,   97,   98,  100,  101,
+      102,  103,  104,  104,  105,  106,  108,  110,  111,  112,
+      114,  115,  117,  118,  119,  120,  121,  122,  122,  123,
+      124,  125,  126,  127,  128,  129,  131,  131,  132,  133,
+      134,  135,  136,  137,  138,  138,  140,  142,  143,  145,
+
+      146,  148,  150,  151,  152,  153,  154,  155,  157,  159,
+      160,  162,  162
     } ;
 
 static yyconst YY_CHAR yy_ec[256] =
@@ -533,15 +538,15 @@ static yyconst YY_CHAR yy_ec[256] =
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    2,    1,    1,    1,    1,    1,    1,    4,    5,
         6,    1,    1,    7,    8,    9,    1,   10,   11,   11,
-       11,   12,   11,   11,   11,   11,   11,   13,    1,    1,
-       14,    1,    1,    1,   15,   15,   15,   15,   15,   15,
-       16,   16,   16,   16,   16,   16,   16,   16,   16,   16,
-       16,   16,   16,   16,   16,   16,   16,   17,   16,   16,
-       18,    1,   19,    1,   20,    1,   21,   22,   23,   24,
-
-       25,   15,   26,   27,   28,   16,   16,   29,   16,   30,
-       31,   32,   16,   33,   34,   35,   36,   16,   16,   37,
-       38,   16,    1,    1,    1,    1,    1,    1,    1,    1,
+       11,   12,   11,   13,   11,   11,   11,   14,    1,    1,
+       15,    1,    1,    1,   16,   16,   16,   16,   16,   16,
+       17,   17,   17,   17,   17,   17,   17,   17,   17,   17,
+       17,   17,   17,   17,   17,   17,   17,   18,   17,   17,
+       19,    1,   20,    1,   21,    1,   22,   23,   24,   25,
+
+       26,   16,   27,   28,   29,   17,   30,   31,   17,   32,
+       33,   34,   17,   35,   36,   37,   38,   17,   17,   39,
+       40,   17,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
@@ -558,116 +563,128 @@ static yyconst YY_CHAR yy_ec[256] =
         1,    1,    1,    1,    1
     } ;
 
-static yyconst YY_CHAR yy_meta[39] =
+static yyconst YY_CHAR yy_meta[41] =
     {   0,
         1,    1,    2,    1,    1,    1,    1,    1,    3,    4,
-        4,    4,    5,    1,    4,    1,    1,    1,    1,    1,
-        4,    4,    4,    4,    4,    1,    1,    1,    1,    1,
-        1,    1,    1,    1,    1,    1,    1,    1
+        4,    4,    4,    5,    1,    4,    1,    1,    1,    1,
+        1,    4,    4,    4,    4,    4,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1
     } ;
 
-static yyconst flex_uint16_t yy_base[101] =
+static yyconst flex_uint16_t yy_base[118] =
     {   0,
-        0,    0,  191,  192,  188,  186,  184,  192,  192,  192,
-       29,  192,   33,   30,  174,  172,   69,   98,  192,  192,
-       22,  154,  147,  158,  151,   27,  156,  144,  154,  176,
-      174,  172,  192,   51,   54,   32,  162,  161,    0,  160,
-        0,  192,  112,  114,    0,    0,  192,  143,  147,  140,
-      141,  131,  132,  131,    0,  136,  142,  126,   64,    0,
-        0,    0,    0,  139,  127,    0,    0,  132,  138,  124,
-      122,  114,  135,  120,  123,  115,  117,    0,  118,  106,
-       98,   88,  100,   54,  124,    0,    0,    0,    0,   55,
-      127,   48,   42,    0,  192,  139,  141,  143,   49,  146
-
+        0,    0,  220,  221,  217,  215,  213,  221,  221,  221,
+       31,  221,   36,   33,  202,  200,   74,  105,  221,  221,
+       24,  181,  174,  186,  182,  177,   29,  183,  182,  169,
+      180,  203,  201,  199,  221,   55,   68,   35,  188,  187,
+        0,  186,    0,  221,  120,  122,    0,    0,  221,  168,
+      173,  165,  167,  156,  162,  156,  155,    0,  165,  159,
+      166,  149,  123,    0,    0,    0,    0,  163,  150,    0,
+      155,    0,  155,  148,  160,  145,  143,  133,  157,  141,
+      155,  143,  153,  134,  136,    0,  138,  135,  125,  116,
+      107,  113,   57,  102,  142,    0,    0,  101,    0,   95,
+
+        0,    0,   64,  146,   54,   48,   41,    0,    0,   44,
+        0,  221,  159,  161,  163,   53,  166
     } ;
 
-static yyconst flex_int16_t yy_def[101] =
+static yyconst flex_int16_t yy_def[118] =
     {   0,
-       95,    1,   95,   95,   95,   95,   96,   95,   95,   95,
-       95,   95,   95,   13,   97,   95,   95,   17,   95,   95,
-       17,   17,   17,   18,   18,   18,   18,   18,   18,   95,
-       95,   96,   95,   95,   95,   13,   97,   98,   99,   97,
-      100,   95,   95,   18,   17,   18,   95,   18,   18,   18,
-       18,   18,   18,   18,   18,   18,   18,   18,   95,   99,
-      100,   18,   18,   18,   18,   18,   18,   18,   18,   18,
-       18,   95,   18,   18,   18,   18,   18,   18,   95,   18,
-       18,   18,   18,   18,   95,   18,   18,   18,   18,   18,
-       95,   18,   18,   18,    0,   95,   95,   95,   95,   95
-
+      112,    1,  112,  112,  112,  112,  113,  112,  112,  112,
+      112,  112,  112,   13,  114,  112,  112,   17,  112,  112,
+       17,   17,   17,   18,   18,   18,   18,   18,   18,   18,
+       18,  112,  112,  113,  112,  112,  112,   13,  114,  115,
+      116,  114,  117,  112,  112,   18,   17,   18,  112,   18,
+       18,   18,   18,   18,   18,   18,   18,   18,   18,   18,
+       18,   18,  112,  116,  117,   18,   18,   18,   18,   18,
+       18,   18,   18,   18,   18,   18,   18,  112,   18,   18,
+       18,   18,   18,   18,   18,   18,  112,   18,   18,   18,
+       18,   18,   18,   18,  112,   18,   18,   18,   18,   18,
+
+       18,   18,   18,  112,   18,   18,   18,   18,   18,   18,
+       18,    0,  112,  112,  112,  112,  112
     } ;
 
-static yyconst flex_uint16_t yy_nxt[231] =
+static yyconst flex_uint16_t yy_nxt[262] =
     {   0,
         4,    5,    6,    7,    8,    9,   10,   11,   12,   13,
-       14,   14,   15,   16,   17,   18,   18,   19,   20,    4,
-       21,   17,   22,   17,   23,   18,   24,   18,   18,   25,
-       26,   18,   27,   28,   29,   18,   18,   18,   34,   34,
-       34,   35,   36,   36,   36,   37,   95,   38,   95,   39,
-       48,   49,   60,   38,   38,   38,   38,   38,   54,   55,
-       34,   34,   34,   59,   59,   59,   95,   94,   95,   39,
-       43,   43,   72,   59,   59,   59,   44,   93,   45,   45,
-       45,   37,   92,   45,   46,   46,   90,   47,   44,   45,
-       45,   45,   45,   45,   46,   46,   46,   46,   46,   46,
-
-       46,   46,   46,   46,   46,   46,   46,   46,   46,   46,
-       95,   89,   46,   43,   43,   95,   95,   88,   46,   46,
-       46,   46,   46,   79,   79,   79,   85,   79,   79,   79,
-       47,   87,   95,   91,   91,   91,   91,   91,   91,   32,
-       86,   32,   32,   32,   40,   40,   38,   38,   61,   61,
-       61,   84,   83,   82,   81,   80,   78,   77,   76,   75,
-       74,   73,   71,   70,   69,   68,   67,   66,   65,   64,
-       63,   62,   41,   37,   41,   33,   31,   30,   58,   57,
-       56,   53,   52,   51,   50,   42,   41,   33,   31,   30,
-       95,    3,   95,   95,   95,   95,   95,   95,   95,   95,
-
-       95,   95,   95,   95,   95,   95,   95,   95,   95,   95,
-       95,   95,   95,   95,   95,   95,   95,   95,   95,   95,
-       95,   95,   95,   95,   95,   95,   95,   95,   95,   95
+       14,   14,   14,   15,   16,   17,   18,   18,   19,   20,
+        4,   21,   17,   22,   17,   23,   18,   24,   18,   18,
+       25,   26,   27,   28,   29,   30,   31,   18,   18,   18,
+       36,   36,   36,   36,   37,   38,   38,   38,   38,   39,
+      112,   40,  112,   41,   50,   51,   64,   40,   40,   40,
+       40,   40,   57,   58,   36,   36,   36,   36,  101,  102,
+      111,  112,  110,  112,   41,   45,   45,   63,   63,   63,
+       63,   46,  109,   47,   47,   47,   47,   39,  108,   47,
+       48,   48,  107,   49,   46,   47,   47,   47,   47,   47,
+
+       48,   48,   48,   48,   48,   48,   48,   48,   48,   48,
+       48,   48,   48,   48,   48,   48,   48,   48,  112,  106,
+       48,   45,   45,  112,  112,  105,   48,   48,   48,   48,
+       48,   78,   63,   63,   63,   63,  103,  100,   99,   49,
+       98,  112,   87,   87,   87,   87,   95,   87,   87,   87,
+       87,  104,  104,  104,  104,  104,  104,  104,  104,   34,
+       97,   34,   34,   34,   42,   42,   40,   40,   65,   65,
+       65,   96,   94,   93,   92,   91,   90,   89,   88,   86,
+       85,   84,   83,   82,   81,   80,   79,   77,   76,   75,
+       74,   73,   72,   71,   70,   69,   68,   67,   66,   43,
+
+       39,   43,   35,   33,   32,   62,   61,   60,   59,   56,
+       55,   54,   53,   52,   44,   43,   35,   33,   32,  112,
+        3,  112,  112,  112,  112,  112,  112,  112,  112,  112,
+      112,  112,  112,  112,  112,  112,  112,  112,  112,  112,
+      112,  112,  112,  112,  112,  112,  112,  112,  112,  112,
+      112,  112,  112,  112,  112,  112,  112,  112,  112,  112,
+      112
     } ;
 
-static yyconst flex_int16_t yy_chk[231] =
+static yyconst flex_int16_t yy_chk[262] =
     {   0,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
-        1,    1,    1,    1,    1,    1,    1,    1,   11,   11,
-       11,   13,   13,   13,   13,   13,   14,   13,   36,   13,
-       21,   21,   99,   13,   13,   13,   13,   13,   26,   26,
-       34,   34,   34,   35,   35,   35,   14,   93,   36,   13,
-       17,   17,   59,   59,   59,   59,   17,   92,   17,   17,
-       17,   17,   90,   17,   17,   17,   84,   17,   17,   17,
-       17,   17,   17,   17,   17,   17,   17,   17,   17,   17,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+       11,   11,   11,   11,   13,   13,   13,   13,   13,   13,
+       14,   13,   38,   13,   21,   21,  116,   13,   13,   13,
+       13,   13,   27,   27,   36,   36,   36,   36,   93,   93,
+      110,   14,  107,   38,   13,   17,   17,   37,   37,   37,
+       37,   17,  106,   17,   17,   17,   17,   17,  105,   17,
+       17,   17,  103,   17,   17,   17,   17,   17,   17,   17,
 
-       17,   17,   17,   17,   17,   17,   17,   18,   18,   18,
-       18,   83,   18,   43,   43,   44,   44,   82,   18,   18,
-       18,   18,   18,   72,   72,   72,   79,   79,   79,   79,
-       43,   81,   44,   85,   85,   85,   91,   91,   91,   96,
-       80,   96,   96,   96,   97,   97,   98,   98,  100,  100,
-      100,   77,   76,   75,   74,   73,   71,   70,   69,   68,
-       65,   64,   58,   57,   56,   54,   53,   52,   51,   50,
-       49,   48,   40,   38,   37,   32,   31,   30,   29,   28,
-       27,   25,   24,   23,   22,   16,   15,    7,    6,    5,
-        3,   95,   95,   95,   95,   95,   95,   95,   95,   95,
-
-       95,   95,   95,   95,   95,   95,   95,   95,   95,   95,
-       95,   95,   95,   95,   95,   95,   95,   95,   95,   95,
-       95,   95,   95,   95,   95,   95,   95,   95,   95,   95
+       17,   17,   17,   17,   17,   17,   17,   17,   17,   17,
+       17,   17,   17,   17,   18,   18,   18,   18,   18,  100,
+       18,   45,   45,   46,   46,   98,   18,   18,   18,   18,
+       18,   63,   63,   63,   63,   63,   94,   92,   91,   45,
+       90,   46,   78,   78,   78,   78,   87,   87,   87,   87,
+       87,   95,   95,   95,   95,  104,  104,  104,  104,  113,
+       89,  113,  113,  113,  114,  114,  115,  115,  117,  117,
+      117,   88,   85,   84,   83,   82,   81,   80,   79,   77,
+       76,   75,   74,   73,   71,   69,   68,   62,   61,   60,
+       59,   57,   56,   55,   54,   53,   52,   51,   50,   42,
+
+       40,   39,   34,   33,   32,   31,   30,   29,   28,   26,
+       25,   24,   23,   22,   16,   15,    7,    6,    5,    3,
+      112,  112,  112,  112,  112,  112,  112,  112,  112,  112,
+      112,  112,  112,  112,  112,  112,  112,  112,  112,  112,
+      112,  112,  112,  112,  112,  112,  112,  112,  112,  112,
+      112,  112,  112,  112,  112,  112,  112,  112,  112,  112,
+      112
     } ;
 
 /* Table of booleans, true if rule could match eol. */
-static yyconst flex_int32_t yy_rule_can_match_eol[28] =
+static yyconst flex_int32_t yy_rule_can_match_eol[31] =
     {   0,
 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-    0, 0, 0, 0, 0, 0, 0, 0,     };
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,     };
 
 extern int yy_flex_debug;
 int yy_flex_debug = 1;
 
-static yyconst flex_int16_t yy_rule_linenum[27] =
+static yyconst flex_int16_t yy_rule_linenum[30] =
     {   0,
        82,   86,   92,  102,  108,  122,  129,  143,  144,  145,
       146,  147,  148,  149,  150,  151,  152,  153,  154,  155,
-      156,  157,  158,  159,  160,  162
+      156,  157,  158,  159,  160,  161,  162,  163,  165
     } ;
 
 static yy_state_type *yy_state_buf=0, *yy_state_ptr=0;
@@ -746,7 +763,7 @@ static isc::eval::location loc;
 // by moving it ahead by yyleng bytes. yyleng specifies the length of the
 // currently matched token.
 #define YY_USER_ACTION  loc.columns(yyleng);
-#line 750 "lexer.cc"
+#line 767 "lexer.cc"
 
 #define INITIAL 0
 
@@ -1043,7 +1060,7 @@ YY_DECL
     loc.step();
 
 
-#line 1047 "lexer.cc"
+#line 1064 "lexer.cc"
 
 	while ( /*CONSTCOND*/1 )		/* loops until end-of-file is reached */
 		{
@@ -1071,14 +1088,14 @@ yy_match:
 			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 				{
 				yy_current_state = (int) yy_def[yy_current_state];
-				if ( yy_current_state >= 96 )
+				if ( yy_current_state >= 113 )
 					yy_c = yy_meta[(unsigned int) yy_c];
 				}
 			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
 			*(yy_state_ptr)++ = yy_current_state;
 			++yy_cp;
 			}
-		while ( yy_current_state != 95 );
+		while ( yy_current_state != 112 );
 
 yy_find_action:
 /* %% [10.0] code to find the action number goes here */
@@ -1141,13 +1158,13 @@ do_action:	/* This label is used only to access EOF actions. */
 			{
 			if ( yy_act == 0 )
 				fprintf( stderr, "--scanner backing up\n" );
-			else if ( yy_act < 27 )
+			else if ( yy_act < 30 )
 				fprintf( stderr, "--accepting rule at line %ld (\"%s\")\n",
 				         (long)yy_rule_linenum[yy_act], yytext );
-			else if ( yy_act == 27 )
+			else if ( yy_act == 30 )
 				fprintf( stderr, "--accepting default rule (\"%s\")\n",
 				         yytext );
-			else if ( yy_act == 28 )
+			else if ( yy_act == 31 )
 				fprintf( stderr, "--(end of buffer or a NUL)\n" );
 			else
 				fprintf( stderr, "--EOF (start condition %d)\n", YY_START );
@@ -1259,93 +1276,108 @@ return isc::eval::EvalParser::make_RELAY4(loc);
 case 11:
 YY_RULE_SETUP
 #line 146 "lexer.ll"
-return isc::eval::EvalParser::make_TEXT(loc);
+return isc::eval::EvalParser::make_RELAY6(loc);
 	YY_BREAK
 case 12:
 YY_RULE_SETUP
 #line 147 "lexer.ll"
-return isc::eval::EvalParser::make_HEX(loc);
+return isc::eval::EvalParser::make_PEERADDR(loc);
 	YY_BREAK
 case 13:
 YY_RULE_SETUP
 #line 148 "lexer.ll"
-return isc::eval::EvalParser::make_EXISTS(loc);
+return isc::eval::EvalParser::make_LINKADDR(loc);
 	YY_BREAK
 case 14:
 YY_RULE_SETUP
 #line 149 "lexer.ll"
-return isc::eval::EvalParser::make_SUBSTRING(loc);
+return isc::eval::EvalParser::make_TEXT(loc);
 	YY_BREAK
 case 15:
 YY_RULE_SETUP
 #line 150 "lexer.ll"
-return isc::eval::EvalParser::make_ALL(loc);
+return isc::eval::EvalParser::make_HEX(loc);
 	YY_BREAK
 case 16:
 YY_RULE_SETUP
 #line 151 "lexer.ll"
-return isc::eval::EvalParser::make_CONCAT(loc);
+return isc::eval::EvalParser::make_EXISTS(loc);
 	YY_BREAK
 case 17:
 YY_RULE_SETUP
 #line 152 "lexer.ll"
-return isc::eval::EvalParser::make_NOT(loc);
+return isc::eval::EvalParser::make_SUBSTRING(loc);
 	YY_BREAK
 case 18:
 YY_RULE_SETUP
 #line 153 "lexer.ll"
-return isc::eval::EvalParser::make_AND(loc);
+return isc::eval::EvalParser::make_ALL(loc);
 	YY_BREAK
 case 19:
 YY_RULE_SETUP
 #line 154 "lexer.ll"
-return isc::eval::EvalParser::make_OR(loc);
+return isc::eval::EvalParser::make_CONCAT(loc);
 	YY_BREAK
 case 20:
 YY_RULE_SETUP
 #line 155 "lexer.ll"
-return isc::eval::EvalParser::make_DOT(loc);
+return isc::eval::EvalParser::make_NOT(loc);
 	YY_BREAK
 case 21:
 YY_RULE_SETUP
 #line 156 "lexer.ll"
-return isc::eval::EvalParser::make_LPAREN(loc);
+return isc::eval::EvalParser::make_AND(loc);
 	YY_BREAK
 case 22:
 YY_RULE_SETUP
 #line 157 "lexer.ll"
-return isc::eval::EvalParser::make_RPAREN(loc);
+return isc::eval::EvalParser::make_OR(loc);
 	YY_BREAK
 case 23:
 YY_RULE_SETUP
 #line 158 "lexer.ll"
-return isc::eval::EvalParser::make_LBRACKET(loc);
+return isc::eval::EvalParser::make_DOT(loc);
 	YY_BREAK
 case 24:
 YY_RULE_SETUP
 #line 159 "lexer.ll"
-return isc::eval::EvalParser::make_RBRACKET(loc);
+return isc::eval::EvalParser::make_LPAREN(loc);
 	YY_BREAK
 case 25:
 YY_RULE_SETUP
 #line 160 "lexer.ll"
-return isc::eval::EvalParser::make_COMA(loc);
+return isc::eval::EvalParser::make_RPAREN(loc);
 	YY_BREAK
 case 26:
 YY_RULE_SETUP
+#line 161 "lexer.ll"
+return isc::eval::EvalParser::make_LBRACKET(loc);
+	YY_BREAK
+case 27:
+YY_RULE_SETUP
 #line 162 "lexer.ll"
+return isc::eval::EvalParser::make_RBRACKET(loc);
+	YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 163 "lexer.ll"
+return isc::eval::EvalParser::make_COMA(loc);
+	YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 165 "lexer.ll"
 driver.error (loc, "Invalid character: " + std::string(yytext));
 	YY_BREAK
 case YY_STATE_EOF(INITIAL):
-#line 163 "lexer.ll"
+#line 166 "lexer.ll"
 return isc::eval::EvalParser::make_END(loc);
 	YY_BREAK
-case 27:
+case 30:
 YY_RULE_SETUP
-#line 164 "lexer.ll"
+#line 167 "lexer.ll"
 ECHO;
 	YY_BREAK
-#line 1349 "lexer.cc"
+#line 1381 "lexer.cc"
 
 	case YY_END_OF_BUFFER:
 		{
@@ -1630,7 +1662,7 @@ static int yy_get_next_buffer (void)
 		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 			{
 			yy_current_state = (int) yy_def[yy_current_state];
-			if ( yy_current_state >= 96 )
+			if ( yy_current_state >= 113 )
 				yy_c = yy_meta[(unsigned int) yy_c];
 			}
 		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
@@ -1658,11 +1690,11 @@ static int yy_get_next_buffer (void)
 	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 		{
 		yy_current_state = (int) yy_def[yy_current_state];
-		if ( yy_current_state >= 96 )
+		if ( yy_current_state >= 113 )
 			yy_c = yy_meta[(unsigned int) yy_c];
 		}
 	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
-	yy_is_jam = (yy_current_state == 95);
+	yy_is_jam = (yy_current_state == 112);
 	if ( ! yy_is_jam )
 		*(yy_state_ptr)++ = yy_current_state;
 
@@ -2428,7 +2460,7 @@ void yyfree (void * ptr )
 
 /* %ok-for-header */
 
-#line 164 "lexer.ll"
+#line 167 "lexer.ll"
 
 
 

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

@@ -143,6 +143,9 @@ addr6 [0-9a-fA-F]*\:[0-9a-fA-F]*\:[0-9a-fA-F:.]*
 "=="        return isc::eval::EvalParser::make_EQUAL(loc);
 "option"    return isc::eval::EvalParser::make_OPTION(loc);
 "relay4"    return isc::eval::EvalParser::make_RELAY4(loc);
+"relay6"    return isc::eval::EvalParser::make_RELAY6(loc);
+"peeraddr"  return isc::eval::EvalParser::make_PEERADDR(loc);
+"linkaddr"  return isc::eval::EvalParser::make_LINKADDR(loc);
 "text"      return isc::eval::EvalParser::make_TEXT(loc);
 "hex"       return isc::eval::EvalParser::make_HEX(loc);
 "exists"    return isc::eval::EvalParser::make_EXISTS(loc);

+ 275 - 149
src/lib/eval/parser.cc

@@ -251,22 +251,30 @@ namespace isc { namespace eval {
   {
       switch (that.type_get ())
     {
-      case 31: // option_repr_type
+      case 34: // option_repr_type
         value.move< TokenOption::RepresentationType > (that.value);
         break;
 
-      case 21: // "constant string"
-      case 22: // "integer"
-      case 23: // "constant hexstring"
-      case 24: // "option name"
-      case 25: // "ip address"
+      case 37: // relay6_field
+        value.move< TokenRelay6Field::FieldType > (that.value);
+        break;
+
+      case 24: // "constant string"
+      case 25: // "integer"
+      case 26: // "constant hexstring"
+      case 27: // "option name"
+      case 28: // "ip address"
         value.move< std::string > (that.value);
         break;
 
-      case 30: // option_code
+      case 33: // option_code
         value.move< uint16_t > (that.value);
         break;
 
+      case 38: // nest_level
+        value.move< uint8_t > (that.value);
+        break;
+
       default:
         break;
     }
@@ -282,22 +290,30 @@ namespace isc { namespace eval {
     state = that.state;
       switch (that.type_get ())
     {
-      case 31: // option_repr_type
+      case 34: // option_repr_type
         value.copy< TokenOption::RepresentationType > (that.value);
         break;
 
-      case 21: // "constant string"
-      case 22: // "integer"
-      case 23: // "constant hexstring"
-      case 24: // "option name"
-      case 25: // "ip address"
+      case 37: // relay6_field
+        value.copy< TokenRelay6Field::FieldType > (that.value);
+        break;
+
+      case 24: // "constant string"
+      case 25: // "integer"
+      case 26: // "constant hexstring"
+      case 27: // "option name"
+      case 28: // "ip address"
         value.copy< std::string > (that.value);
         break;
 
-      case 30: // option_code
+      case 33: // option_code
         value.copy< uint16_t > (that.value);
         break;
 
+      case 38: // nest_level
+        value.copy< uint8_t > (that.value);
+        break;
+
       default:
         break;
     }
@@ -334,53 +350,67 @@ namespace isc { namespace eval {
         << yysym.location << ": ";
     switch (yytype)
     {
-            case 21: // "constant string"
+            case 24: // "constant string"
 
-#line 73 "parser.yy" // lalr1.cc:636
+#line 78 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 342 "parser.cc" // lalr1.cc:636
+#line 358 "parser.cc" // lalr1.cc:636
         break;
 
-      case 22: // "integer"
+      case 25: // "integer"
 
-#line 73 "parser.yy" // lalr1.cc:636
+#line 78 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 349 "parser.cc" // lalr1.cc:636
+#line 365 "parser.cc" // lalr1.cc:636
         break;
 
-      case 23: // "constant hexstring"
+      case 26: // "constant hexstring"
 
-#line 73 "parser.yy" // lalr1.cc:636
+#line 78 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 356 "parser.cc" // lalr1.cc:636
+#line 372 "parser.cc" // lalr1.cc:636
         break;
 
-      case 24: // "option name"
+      case 27: // "option name"
 
-#line 73 "parser.yy" // lalr1.cc:636
+#line 78 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 363 "parser.cc" // lalr1.cc:636
+#line 379 "parser.cc" // lalr1.cc:636
         break;
 
-      case 25: // "ip address"
+      case 28: // "ip address"
 
-#line 73 "parser.yy" // lalr1.cc:636
+#line 78 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 370 "parser.cc" // lalr1.cc:636
+#line 386 "parser.cc" // lalr1.cc:636
         break;
 
-      case 30: // option_code
+      case 33: // option_code
 
-#line 73 "parser.yy" // lalr1.cc:636
+#line 78 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< uint16_t > (); }
-#line 377 "parser.cc" // lalr1.cc:636
+#line 393 "parser.cc" // lalr1.cc:636
         break;
 
-      case 31: // option_repr_type
+      case 34: // option_repr_type
 
-#line 73 "parser.yy" // lalr1.cc:636
+#line 78 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< TokenOption::RepresentationType > (); }
-#line 384 "parser.cc" // lalr1.cc:636
+#line 400 "parser.cc" // lalr1.cc:636
+        break;
+
+      case 37: // relay6_field
+
+#line 78 "parser.yy" // lalr1.cc:636
+        { yyoutput << yysym.value.template as< TokenRelay6Field::FieldType > (); }
+#line 407 "parser.cc" // lalr1.cc:636
+        break;
+
+      case 38: // nest_level
+
+#line 78 "parser.yy" // lalr1.cc:636
+        { yyoutput << yysym.value.template as< uint8_t > (); }
+#line 414 "parser.cc" // lalr1.cc:636
         break;
 
 
@@ -580,22 +610,30 @@ namespace isc { namespace eval {
          when using variants.  */
         switch (yyr1_[yyn])
     {
-      case 31: // option_repr_type
+      case 34: // option_repr_type
         yylhs.value.build< TokenOption::RepresentationType > ();
         break;
 
-      case 21: // "constant string"
-      case 22: // "integer"
-      case 23: // "constant hexstring"
-      case 24: // "option name"
-      case 25: // "ip address"
+      case 37: // relay6_field
+        yylhs.value.build< TokenRelay6Field::FieldType > ();
+        break;
+
+      case 24: // "constant string"
+      case 25: // "integer"
+      case 26: // "constant hexstring"
+      case 27: // "option name"
+      case 28: // "ip address"
         yylhs.value.build< std::string > ();
         break;
 
-      case 30: // option_code
+      case 33: // option_code
         yylhs.value.build< uint16_t > ();
         break;
 
+      case 38: // nest_level
+        yylhs.value.build< uint8_t > ();
+        break;
+
       default:
         break;
     }
@@ -614,52 +652,52 @@ namespace isc { namespace eval {
           switch (yyn)
             {
   case 4:
-#line 87 "parser.yy" // lalr1.cc:859
+#line 92 "parser.yy" // lalr1.cc:859
     {
                     TokenPtr neg(new TokenNot());
                     ctx.expression.push_back(neg);
                 }
-#line 623 "parser.cc" // lalr1.cc:859
+#line 661 "parser.cc" // lalr1.cc:859
     break;
 
   case 5:
-#line 92 "parser.yy" // lalr1.cc:859
+#line 97 "parser.yy" // lalr1.cc:859
     {
                     TokenPtr neg(new TokenAnd());
                     ctx.expression.push_back(neg);
                 }
-#line 632 "parser.cc" // lalr1.cc:859
+#line 670 "parser.cc" // lalr1.cc:859
     break;
 
   case 6:
-#line 97 "parser.yy" // lalr1.cc:859
+#line 102 "parser.yy" // lalr1.cc:859
     {
                     TokenPtr neg(new TokenOr());
                     ctx.expression.push_back(neg);
                 }
-#line 641 "parser.cc" // lalr1.cc:859
+#line 679 "parser.cc" // lalr1.cc:859
     break;
 
   case 7:
-#line 102 "parser.yy" // lalr1.cc:859
+#line 107 "parser.yy" // lalr1.cc:859
     {
                     TokenPtr eq(new TokenEqual());
                     ctx.expression.push_back(eq);
                 }
-#line 650 "parser.cc" // lalr1.cc:859
+#line 688 "parser.cc" // lalr1.cc:859
     break;
 
   case 8:
-#line 107 "parser.yy" // lalr1.cc:859
+#line 112 "parser.yy" // lalr1.cc:859
     {
                     TokenPtr opt(new TokenOption(yystack_[3].value.as< uint16_t > (), TokenOption::EXISTS));
                     ctx.expression.push_back(opt);
                 }
-#line 659 "parser.cc" // lalr1.cc:859
+#line 697 "parser.cc" // lalr1.cc:859
     break;
 
   case 9:
-#line 112 "parser.yy" // lalr1.cc:859
+#line 117 "parser.yy" // lalr1.cc:859
     {
                    switch (ctx.getUniverse()) {
                    case Option::V4:
@@ -679,47 +717,65 @@ namespace isc { namespace eval {
                        error(yystack_[5].location, "relay4 can only be used in DHCPv4.");
                    }
                 }
-#line 683 "parser.cc" // lalr1.cc:859
+#line 721 "parser.cc" // lalr1.cc:859
     break;
 
   case 10:
-#line 134 "parser.yy" // lalr1.cc:859
+#line 137 "parser.yy" // lalr1.cc:859
+    {
+                    switch (ctx.getUniverse()) {
+                    case Option::V6:
+                    {
+                        TokenPtr opt(new TokenRelay6Option(yystack_[8].value.as< uint8_t > (), yystack_[3].value.as< uint16_t > (), TokenOption::EXISTS));
+                        ctx.expression.push_back(opt);
+                        break;
+                    }
+                    case Option::V4:
+                        // For now we only use relay6 in DHCPv6.
+                        error(yystack_[10].location, "relay6 can only be used in DHCPv6.");
+                    }
+                }
+#line 739 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 11:
+#line 153 "parser.yy" // lalr1.cc:859
     {
                       TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                       ctx.expression.push_back(str);
                   }
-#line 692 "parser.cc" // lalr1.cc:859
+#line 748 "parser.cc" // lalr1.cc:859
     break;
 
-  case 11:
-#line 139 "parser.yy" // lalr1.cc:859
+  case 12:
+#line 158 "parser.yy" // lalr1.cc:859
     {
                       TokenPtr hex(new TokenHexString(yystack_[0].value.as< std::string > ()));
                       ctx.expression.push_back(hex);
                   }
-#line 701 "parser.cc" // lalr1.cc:859
+#line 757 "parser.cc" // lalr1.cc:859
     break;
 
-  case 12:
-#line 144 "parser.yy" // lalr1.cc:859
+  case 13:
+#line 163 "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
+#line 766 "parser.cc" // lalr1.cc:859
     break;
 
-  case 13:
-#line 149 "parser.yy" // lalr1.cc:859
+  case 14:
+#line 168 "parser.yy" // lalr1.cc:859
     {
                       TokenPtr opt(new TokenOption(yystack_[3].value.as< uint16_t > (), yystack_[0].value.as< TokenOption::RepresentationType > ()));
                       ctx.expression.push_back(opt);
                   }
-#line 719 "parser.cc" // lalr1.cc:859
+#line 775 "parser.cc" // lalr1.cc:859
     break;
 
-  case 14:
-#line 154 "parser.yy" // lalr1.cc:859
+  case 15:
+#line 173 "parser.yy" // lalr1.cc:859
     {
                      switch (ctx.getUniverse()) {
                      case Option::V4:
@@ -739,88 +795,144 @@ namespace isc { namespace eval {
                          error(yystack_[5].location, "relay4 can only be used in DHCPv4.");
                      }
                   }
-#line 743 "parser.cc" // lalr1.cc:859
+#line 799 "parser.cc" // lalr1.cc:859
     break;
 
-  case 15:
-#line 174 "parser.yy" // lalr1.cc:859
+  case 16:
+#line 194 "parser.yy" // lalr1.cc:859
+    {
+                     switch (ctx.getUniverse()) {
+                     case Option::V6:
+                     {
+                         TokenPtr opt(new TokenRelay6Option(yystack_[8].value.as< uint8_t > (), yystack_[3].value.as< uint16_t > (), yystack_[0].value.as< TokenOption::RepresentationType > ()));
+                         ctx.expression.push_back(opt);
+                         break;
+                     }
+                     case Option::V4:
+                         // For now we only use relay6 in DHCPv6.
+                         error(yystack_[10].location, "relay6 can only be used in DHCPv6.");
+                     }
+                  }
+#line 817 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 17:
+#line 209 "parser.yy" // lalr1.cc:859
+    {
+                     switch (ctx.getUniverse()) {
+                     case Option::V6:
+                     {
+                         TokenPtr relay6field(new TokenRelay6Field(yystack_[3].value.as< uint8_t > (), yystack_[0].value.as< TokenRelay6Field::FieldType > ()));
+                         ctx.expression.push_back(relay6field);
+                         break;
+                     }
+                     case Option::V4:
+                         // For now we only use relay6 in DHCPv6.
+                         error(yystack_[5].location, "relay6 can only be used in DHCPv6.");
+                     }
+                  }
+#line 835 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 18:
+#line 225 "parser.yy" // lalr1.cc:859
     {
                       TokenPtr sub(new TokenSubstring());
                       ctx.expression.push_back(sub);
                   }
-#line 752 "parser.cc" // lalr1.cc:859
+#line 844 "parser.cc" // lalr1.cc:859
     break;
 
-  case 16:
-#line 179 "parser.yy" // lalr1.cc:859
+  case 19:
+#line 230 "parser.yy" // lalr1.cc:859
     {
                       TokenPtr conc(new TokenConcat());
                       ctx.expression.push_back(conc);
                   }
-#line 761 "parser.cc" // lalr1.cc:859
+#line 853 "parser.cc" // lalr1.cc:859
     break;
 
-  case 17:
-#line 186 "parser.yy" // lalr1.cc:859
+  case 20:
+#line 237 "parser.yy" // lalr1.cc:859
     {
                      yylhs.value.as< uint16_t > () = ctx.convertOptionCode(yystack_[0].value.as< std::string > (), yystack_[0].location);
                  }
-#line 769 "parser.cc" // lalr1.cc:859
+#line 861 "parser.cc" // lalr1.cc:859
     break;
 
-  case 18:
-#line 190 "parser.yy" // lalr1.cc:859
+  case 21:
+#line 241 "parser.yy" // lalr1.cc:859
     {
                      yylhs.value.as< uint16_t > () = ctx.convertOptionName(yystack_[0].value.as< std::string > (), yystack_[0].location);
                  }
-#line 777 "parser.cc" // lalr1.cc:859
+#line 869 "parser.cc" // lalr1.cc:859
     break;
 
-  case 19:
-#line 196 "parser.yy" // lalr1.cc:859
+  case 22:
+#line 247 "parser.yy" // lalr1.cc:859
     {
                           yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::TEXTUAL;
                       }
-#line 785 "parser.cc" // lalr1.cc:859
+#line 877 "parser.cc" // lalr1.cc:859
     break;
 
-  case 20:
-#line 200 "parser.yy" // lalr1.cc:859
+  case 23:
+#line 251 "parser.yy" // lalr1.cc:859
     {
                           yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::HEXADECIMAL;
                       }
-#line 793 "parser.cc" // lalr1.cc:859
+#line 885 "parser.cc" // lalr1.cc:859
     break;
 
-  case 21:
-#line 206 "parser.yy" // lalr1.cc:859
+  case 24:
+#line 257 "parser.yy" // lalr1.cc:859
     {
                      TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                      ctx.expression.push_back(str);
                  }
-#line 802 "parser.cc" // lalr1.cc:859
+#line 894 "parser.cc" // lalr1.cc:859
     break;
 
-  case 22:
-#line 213 "parser.yy" // lalr1.cc:859
+  case 25:
+#line 264 "parser.yy" // lalr1.cc:859
     {
                       TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                       ctx.expression.push_back(str);
                   }
-#line 811 "parser.cc" // lalr1.cc:859
+#line 903 "parser.cc" // lalr1.cc:859
     break;
 
-  case 23:
-#line 218 "parser.yy" // lalr1.cc:859
+  case 26:
+#line 269 "parser.yy" // lalr1.cc:859
     {
                      TokenPtr str(new TokenString("all"));
                      ctx.expression.push_back(str);
                  }
-#line 820 "parser.cc" // lalr1.cc:859
+#line 912 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 27:
+#line 275 "parser.yy" // lalr1.cc:859
+    { yylhs.value.as< TokenRelay6Field::FieldType > () = TokenRelay6Field::PEERADDR; }
+#line 918 "parser.cc" // lalr1.cc:859
     break;
 
+  case 28:
+#line 276 "parser.yy" // lalr1.cc:859
+    { yylhs.value.as< TokenRelay6Field::FieldType > () = TokenRelay6Field::LINKADDR; }
+#line 924 "parser.cc" // lalr1.cc:859
+    break;
 
-#line 824 "parser.cc" // lalr1.cc:859
+  case 29:
+#line 280 "parser.yy" // lalr1.cc:859
+    {
+		 yylhs.value.as< uint8_t > () = ctx.convertNestLevelNumber(yystack_[0].value.as< std::string > (), yystack_[0].location);
+                 }
+#line 932 "parser.cc" // lalr1.cc:859
+    break;
+
+
+#line 936 "parser.cc" // lalr1.cc:859
             default:
               break;
             }
@@ -1075,96 +1187,108 @@ namespace isc { namespace eval {
   }
 
 
-  const signed char EvalParser::yypact_ninf_ = -29;
+  const signed char EvalParser::yypact_ninf_ = -47;
 
   const signed char EvalParser::yytable_ninf_ = -1;
 
   const signed char
   EvalParser::yypact_[] =
   {
-      -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
+      11,    11,    11,    -4,    -1,     3,    21,    27,   -47,   -47,
+     -47,    49,    51,    28,    44,   -47,    -2,    -2,    19,    36,
+      36,   -47,    11,    11,    36,   -47,   -47,   -47,    46,    48,
+     -47,    57,    59,    60,    61,    43,    54,   -47,    71,   -47,
+      62,    63,    64,    -2,    -2,    19,    56,    36,    23,    35,
+      -5,    67,    68,    69,   -47,    65,    81,   -47,   -47,   -47,
+     -47,   -47,   -47,    72,   -47,   -47,   -47,    73,    74,    75,
+     -16,   -47,    -2,    53,    53,     6,   -47,   -47,    84,    77,
+      79,   -47,    78,    -2,    50,    80,   -47,   -47,    82,    53
   };
 
   const unsigned char
   EvalParser::yydefact_[] =
   {
-       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
+       0,     0,     0,     0,     0,     0,     0,     0,    11,    12,
+      13,     0,     2,     0,     0,     4,     0,     0,     0,     0,
+       0,     1,     0,     0,     0,     3,    20,    21,     0,     0,
+      29,     0,     0,     0,     0,     0,     0,     5,     6,     7,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,    24,     0,     0,    22,    23,     8,
+      14,     9,    15,     0,    27,    28,    17,     0,     0,     0,
+       0,    19,     0,     0,     0,     0,    26,    25,     0,     0,
+       0,    18,     0,     0,     0,     0,    10,    16,     0,     0
   };
 
   const signed char
   EvalParser::yypgoto_[] =
   {
-     -29,   -29,     9,   -17,   -10,   -28,   -29,   -29
+     -47,   -47,    10,   -18,   -17,   -46,   -47,   -47,   -47,    52
   };
 
   const signed char
   EvalParser::yydefgoto_[] =
   {
-      -1,    10,    11,    12,    26,    51,    46,    62
+      -1,    11,    12,    13,    28,    60,    55,    78,    66,    31
   };
 
   const unsigned char
   EvalParser::yytable_[] =
   {
-      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
+      29,    35,    36,    62,    63,    76,    39,    64,    65,    77,
+      16,    14,    15,    17,     1,    80,     2,    18,    64,    65,
+       3,     4,     5,    26,    19,    27,    51,    52,    62,    56,
+      20,     6,    37,    38,     7,     8,    24,     9,    87,    10,
+      57,    58,    59,    87,    30,    32,    33,    34,    25,    21,
+      22,    23,    57,    58,    61,    79,     6,    22,    23,     7,
+       8,    40,     9,    41,    10,    46,    85,    57,    58,    86,
+      57,    58,    42,    43,    44,    45,    47,    22,    48,    49,
+      50,    54,    67,    68,    69,    71,    72,    70,    81,    73,
+      74,    75,    82,    83,    84,    88,     0,    53,    89
   };
 
   const signed char
   EvalParser::yycheck_[] =
   {
-      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
+      17,    19,    20,    49,     9,    21,    24,    12,    13,    25,
+      14,     1,     2,    14,     3,     9,     5,    14,    12,    13,
+       9,    10,    11,    25,     3,    27,    43,    44,    74,    47,
+       3,    20,    22,    23,    23,    24,     8,    26,    84,    28,
+      17,    18,    19,    89,    25,     9,    10,    11,     4,     0,
+       6,     7,    17,    18,    19,    72,    20,     6,     7,    23,
+      24,    15,    26,    15,    28,    22,    83,    17,    18,    19,
+      17,    18,    15,    14,    14,    14,    22,     6,    16,    16,
+      16,    25,    15,    15,    15,     4,    14,    22,     4,    16,
+      16,    16,    15,    14,    16,    15,    -1,    45,    16
   };
 
   const unsigned char
   EvalParser::yystos_[] =
   {
-       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
+       0,     3,     5,     9,    10,    11,    20,    23,    24,    26,
+      28,    30,    31,    32,    31,    31,    14,    14,    14,     3,
+       3,     0,     6,     7,     8,     4,    25,    27,    33,    33,
+      25,    38,     9,    10,    11,    32,    32,    31,    31,    32,
+      15,    15,    15,    14,    14,    14,    22,    22,    16,    16,
+      16,    33,    33,    38,    25,    35,    32,    17,    18,    19,
+      34,    19,    34,     9,    12,    13,    37,    15,    15,    15,
+      22,     4,    14,    16,    16,    16,    21,    25,    36,    33,
+       9,     4,    15,    14,    16,    33,    19,    34,    15,    16
   };
 
   const unsigned char
   EvalParser::yyr1_[] =
   {
-       0,    26,    27,    28,    28,    28,    28,    28,    28,    28,
-      29,    29,    29,    29,    29,    29,    29,    30,    30,    31,
-      31,    32,    33,    33
+       0,    29,    30,    31,    31,    31,    31,    31,    31,    31,
+      31,    32,    32,    32,    32,    32,    32,    32,    32,    32,
+      33,    33,    34,    34,    35,    36,    36,    37,    37,    38
   };
 
   const unsigned char
   EvalParser::yyr2_[] =
   {
        0,     2,     1,     3,     2,     3,     3,     3,     6,     6,
-       1,     1,     1,     6,     6,     8,     6,     1,     1,     1,
-       1,     1,     1,     1
+      11,     1,     1,     1,     6,     6,    11,     6,     8,     6,
+       1,     1,     1,     1,     1,     1,     1,     1,     1,     1
   };
 
 
@@ -1175,21 +1299,23 @@ namespace isc { namespace eval {
   const EvalParser::yytname_[] =
   {
   "\"end of file\"", "error", "$undefined", "\"(\"", "\")\"", "\"not\"",
-  "\"and\"", "\"or\"", "\"==\"", "\"option\"", "\"relay4\"", "\"[\"",
-  "\"]\"", "\".\"", "\"text\"", "\"hex\"", "\"exists\"", "\"substring\"",
-  "\"all\"", "\",\"", "\"concat\"", "\"constant string\"", "\"integer\"",
+  "\"and\"", "\"or\"", "\"==\"", "\"option\"", "\"relay4\"", "\"relay6\"",
+  "\"peeraddr\"", "\"linkaddr\"", "\"[\"", "\"]\"", "\".\"", "\"text\"",
+  "\"hex\"", "\"exists\"", "\"substring\"", "\"all\"", "\",\"",
+  "\"concat\"", "\"constant string\"", "\"integer\"",
   "\"constant hexstring\"", "\"option name\"", "\"ip address\"", "$accept",
   "expression", "bool_expr", "string_expr", "option_code",
-  "option_repr_type", "start_expr", "length_expr", YY_NULLPTR
+  "option_repr_type", "start_expr", "length_expr", "relay6_field",
+  "nest_level", YY_NULLPTR
   };
 
 #if YYDEBUG
-  const unsigned char
+  const unsigned short int
   EvalParser::yyrline_[] =
   {
-       0,    82,    82,    85,    86,    91,    96,   101,   106,   111,
-     133,   138,   143,   148,   153,   173,   178,   185,   189,   195,
-     199,   205,   212,   217
+       0,    87,    87,    90,    91,    96,   101,   106,   111,   116,
+     136,   152,   157,   162,   167,   172,   193,   208,   224,   229,
+     236,   240,   246,   250,   256,   263,   268,   275,   276,   279
   };
 
   // Print the state stack on the debug stream.
@@ -1224,8 +1350,8 @@ namespace isc { namespace eval {
 
 #line 13 "parser.yy" // lalr1.cc:1167
 } } // isc::eval
-#line 1228 "parser.cc" // lalr1.cc:1167
-#line 224 "parser.yy" // lalr1.cc:1168
+#line 1354 "parser.cc" // lalr1.cc:1167
+#line 288 "parser.yy" // lalr1.cc:1168
 
 void
 isc::eval::EvalParser::error(const location_type& loc,

+ 143 - 54
src/lib/eval/parser.h

@@ -298,15 +298,21 @@ namespace isc { namespace eval {
       // option_repr_type
       char dummy1[sizeof(TokenOption::RepresentationType)];
 
+      // relay6_field
+      char dummy2[sizeof(TokenRelay6Field::FieldType)];
+
       // "constant string"
       // "integer"
       // "constant hexstring"
       // "option name"
       // "ip address"
-      char dummy2[sizeof(std::string)];
+      char dummy3[sizeof(std::string)];
 
       // option_code
-      char dummy3[sizeof(uint16_t)];
+      char dummy4[sizeof(uint16_t)];
+
+      // nest_level
+      char dummy5[sizeof(uint8_t)];
 };
 
     /// Symbol semantic values.
@@ -338,21 +344,24 @@ namespace isc { namespace eval {
         TOKEN_EQUAL = 263,
         TOKEN_OPTION = 264,
         TOKEN_RELAY4 = 265,
-        TOKEN_LBRACKET = 266,
-        TOKEN_RBRACKET = 267,
-        TOKEN_DOT = 268,
-        TOKEN_TEXT = 269,
-        TOKEN_HEX = 270,
-        TOKEN_EXISTS = 271,
-        TOKEN_SUBSTRING = 272,
-        TOKEN_ALL = 273,
-        TOKEN_COMA = 274,
-        TOKEN_CONCAT = 275,
-        TOKEN_STRING = 276,
-        TOKEN_INTEGER = 277,
-        TOKEN_HEXSTRING = 278,
-        TOKEN_OPTION_NAME = 279,
-        TOKEN_IP_ADDRESS = 280
+        TOKEN_RELAY6 = 266,
+        TOKEN_PEERADDR = 267,
+        TOKEN_LINKADDR = 268,
+        TOKEN_LBRACKET = 269,
+        TOKEN_RBRACKET = 270,
+        TOKEN_DOT = 271,
+        TOKEN_TEXT = 272,
+        TOKEN_HEX = 273,
+        TOKEN_EXISTS = 274,
+        TOKEN_SUBSTRING = 275,
+        TOKEN_ALL = 276,
+        TOKEN_COMA = 277,
+        TOKEN_CONCAT = 278,
+        TOKEN_STRING = 279,
+        TOKEN_INTEGER = 280,
+        TOKEN_HEXSTRING = 281,
+        TOKEN_OPTION_NAME = 282,
+        TOKEN_IP_ADDRESS = 283
       };
     };
 
@@ -392,10 +401,14 @@ 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 TokenRelay6Field::FieldType v, const location_type& l);
+
   basic_symbol (typename Base::kind_type t, const std::string v, const location_type& l);
 
   basic_symbol (typename Base::kind_type t, const uint16_t v, const location_type& l);
 
+  basic_symbol (typename Base::kind_type t, const uint8_t v, const location_type& l);
+
 
       /// Constructor for symbols with semantic value.
       basic_symbol (typename Base::kind_type t,
@@ -501,6 +514,18 @@ namespace isc { namespace eval {
 
     static inline
     symbol_type
+    make_RELAY6 (const location_type& l);
+
+    static inline
+    symbol_type
+    make_PEERADDR (const location_type& l);
+
+    static inline
+    symbol_type
+    make_LINKADDR (const location_type& l);
+
+    static inline
+    symbol_type
     make_LBRACKET (const location_type& l);
 
     static inline
@@ -665,7 +690,7 @@ namespace isc { namespace eval {
     static const char* const yytname_[];
 #if YYDEBUG
   // YYRLINE[YYN] -- Source line where rule number YYN was defined.
-  static const unsigned char yyrline_[];
+  static const unsigned short int yyrline_[];
     /// Report on the debug stream that the rule \a r is going to be reduced.
     virtual void yy_reduce_print_ (int r);
     /// Print the state stack on the debug stream.
@@ -764,12 +789,12 @@ namespace isc { namespace eval {
     enum
     {
       yyeof_ = 0,
-      yylast_ = 69,     ///< Last index in yytable_.
-      yynnts_ = 8,  ///< Number of nonterminal symbols.
-      yyfinal_ = 19, ///< Termination state number.
+      yylast_ = 98,     ///< Last index in yytable_.
+      yynnts_ = 10,  ///< Number of nonterminal symbols.
+      yyfinal_ = 21, ///< Termination state number.
       yyterror_ = 1,
       yyerrcode_ = 256,
-      yyntokens_ = 26  ///< Number of tokens.
+      yyntokens_ = 29  ///< Number of tokens.
     };
 
 
@@ -814,9 +839,9 @@ namespace isc { namespace eval {
        2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
        5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
       15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
-      25
+      25,    26,    27,    28
     };
-    const unsigned int user_token_number_max_ = 280;
+    const unsigned int user_token_number_max_ = 283;
     const token_number_type undef_token_ = 2;
 
     if (static_cast<int>(t) <= yyeof_)
@@ -849,22 +874,30 @@ namespace isc { namespace eval {
   {
       switch (other.type_get ())
     {
-      case 31: // option_repr_type
+      case 34: // option_repr_type
         value.copy< TokenOption::RepresentationType > (other.value);
         break;
 
-      case 21: // "constant string"
-      case 22: // "integer"
-      case 23: // "constant hexstring"
-      case 24: // "option name"
-      case 25: // "ip address"
+      case 37: // relay6_field
+        value.copy< TokenRelay6Field::FieldType > (other.value);
+        break;
+
+      case 24: // "constant string"
+      case 25: // "integer"
+      case 26: // "constant hexstring"
+      case 27: // "option name"
+      case 28: // "ip address"
         value.copy< std::string > (other.value);
         break;
 
-      case 30: // option_code
+      case 33: // option_code
         value.copy< uint16_t > (other.value);
         break;
 
+      case 38: // nest_level
+        value.copy< uint8_t > (other.value);
+        break;
+
       default:
         break;
     }
@@ -882,22 +915,30 @@ namespace isc { namespace eval {
     (void) v;
       switch (this->type_get ())
     {
-      case 31: // option_repr_type
+      case 34: // option_repr_type
         value.copy< TokenOption::RepresentationType > (v);
         break;
 
-      case 21: // "constant string"
-      case 22: // "integer"
-      case 23: // "constant hexstring"
-      case 24: // "option name"
-      case 25: // "ip address"
+      case 37: // relay6_field
+        value.copy< TokenRelay6Field::FieldType > (v);
+        break;
+
+      case 24: // "constant string"
+      case 25: // "integer"
+      case 26: // "constant hexstring"
+      case 27: // "option name"
+      case 28: // "ip address"
         value.copy< std::string > (v);
         break;
 
-      case 30: // option_code
+      case 33: // option_code
         value.copy< uint16_t > (v);
         break;
 
+      case 38: // nest_level
+        value.copy< uint8_t > (v);
+        break;
+
       default:
         break;
     }
@@ -921,6 +962,13 @@ namespace isc { namespace eval {
   {}
 
   template <typename Base>
+  EvalParser::basic_symbol<Base>::basic_symbol (typename Base::kind_type t, const TokenRelay6Field::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 std::string v, const location_type& l)
     : Base (t)
     , value (v)
@@ -934,6 +982,13 @@ namespace isc { namespace eval {
     , location (l)
   {}
 
+  template <typename Base>
+  EvalParser::basic_symbol<Base>::basic_symbol (typename Base::kind_type t, const uint8_t v, const location_type& l)
+    : Base (t)
+    , value (v)
+    , location (l)
+  {}
+
 
   template <typename Base>
   inline
@@ -960,22 +1015,30 @@ namespace isc { namespace eval {
     // Type destructor.
     switch (yytype)
     {
-      case 31: // option_repr_type
+      case 34: // option_repr_type
         value.template destroy< TokenOption::RepresentationType > ();
         break;
 
-      case 21: // "constant string"
-      case 22: // "integer"
-      case 23: // "constant hexstring"
-      case 24: // "option name"
-      case 25: // "ip address"
+      case 37: // relay6_field
+        value.template destroy< TokenRelay6Field::FieldType > ();
+        break;
+
+      case 24: // "constant string"
+      case 25: // "integer"
+      case 26: // "constant hexstring"
+      case 27: // "option name"
+      case 28: // "ip address"
         value.template destroy< std::string > ();
         break;
 
-      case 30: // option_code
+      case 33: // option_code
         value.template destroy< uint16_t > ();
         break;
 
+      case 38: // nest_level
+        value.template destroy< uint8_t > ();
+        break;
+
       default:
         break;
     }
@@ -999,22 +1062,30 @@ namespace isc { namespace eval {
     super_type::move(s);
       switch (this->type_get ())
     {
-      case 31: // option_repr_type
+      case 34: // option_repr_type
         value.move< TokenOption::RepresentationType > (s.value);
         break;
 
-      case 21: // "constant string"
-      case 22: // "integer"
-      case 23: // "constant hexstring"
-      case 24: // "option name"
-      case 25: // "ip address"
+      case 37: // relay6_field
+        value.move< TokenRelay6Field::FieldType > (s.value);
+        break;
+
+      case 24: // "constant string"
+      case 25: // "integer"
+      case 26: // "constant hexstring"
+      case 27: // "option name"
+      case 28: // "ip address"
         value.move< std::string > (s.value);
         break;
 
-      case 30: // option_code
+      case 33: // option_code
         value.move< uint16_t > (s.value);
         break;
 
+      case 38: // nest_level
+        value.move< uint8_t > (s.value);
+        break;
+
       default:
         break;
     }
@@ -1072,7 +1143,7 @@ namespace isc { namespace eval {
     {
        0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
      265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
-     275,   276,   277,   278,   279,   280
+     275,   276,   277,   278,   279,   280,   281,   282,   283
     };
     return static_cast<token_type> (yytoken_number_[type]);
   }
@@ -1132,6 +1203,24 @@ namespace isc { namespace eval {
   }
 
   EvalParser::symbol_type
+  EvalParser::make_RELAY6 (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_RELAY6, l);
+  }
+
+  EvalParser::symbol_type
+  EvalParser::make_PEERADDR (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_PEERADDR, l);
+  }
+
+  EvalParser::symbol_type
+  EvalParser::make_LINKADDR (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_LINKADDR, l);
+  }
+
+  EvalParser::symbol_type
   EvalParser::make_LBRACKET (const location_type& l)
   {
     return symbol_type (token::TOKEN_LBRACKET, l);
@@ -1224,7 +1313,7 @@ namespace isc { namespace eval {
 
 #line 13 "parser.yy" // lalr1.cc:377
 } } // isc::eval
-#line 1228 "parser.h" // lalr1.cc:377
+#line 1317 "parser.h" // lalr1.cc:377
 
 
 

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

@@ -45,6 +45,9 @@ using namespace isc::eval;
   EQUAL "=="
   OPTION "option"
   RELAY4 "relay4"
+  RELAY6 "relay6"
+  PEERADDR "peeraddr"
+  LINKADDR "linkaddr"
   LBRACKET "["
   RBRACKET "]"
   DOT "."
@@ -65,6 +68,8 @@ using namespace isc::eval;
 
 %type <uint16_t> option_code
 %type <TokenOption::RepresentationType> option_repr_type
+%type <TokenRelay6Field::FieldType> relay6_field
+%type <uint8_t> nest_level
 
 %left OR
 %left AND
@@ -128,6 +133,20 @@ bool_expr : "(" bool_expr ")"
                        error(@1, "relay4 can only be used in DHCPv4.");
                    }
                 }
+          | RELAY6 "[" nest_level "]" "." OPTION "[" option_code "]" "." EXISTS
+                {
+                    switch (ctx.getUniverse()) {
+                    case Option::V6:
+                    {
+                        TokenPtr opt(new TokenRelay6Option($3, $8, TokenOption::EXISTS));
+                        ctx.expression.push_back(opt);
+                        break;
+                    }
+                    case Option::V4:
+                        // For now we only use relay6 in DHCPv6.
+                        error(@1, "relay6 can only be used in DHCPv6.");
+                    }
+                }
           ;
 
 string_expr : STRING
@@ -170,6 +189,38 @@ string_expr : STRING
                          error(@1, "relay4 can only be used in DHCPv4.");
                      }
                   }
+
+            | RELAY6 "[" nest_level "]" "." OPTION "[" option_code "]" "." option_repr_type
+                  {
+                     switch (ctx.getUniverse()) {
+                     case Option::V6:
+                     {
+                         TokenPtr opt(new TokenRelay6Option($3, $8, $11));
+                         ctx.expression.push_back(opt);
+                         break;
+                     }
+                     case Option::V4:
+                         // For now we only use relay6 in DHCPv6.
+                         error(@1, "relay6 can only be used in DHCPv6.");
+                     }
+                  }
+
+            | RELAY6 "[" nest_level "]" "." relay6_field
+                  {
+                     switch (ctx.getUniverse()) {
+                     case Option::V6:
+                     {
+                         TokenPtr relay6field(new TokenRelay6Field($3, $6));
+                         ctx.expression.push_back(relay6field);
+                         break;
+                     }
+                     case Option::V4:
+                         // For now we only use relay6 in DHCPv6.
+                         error(@1, "relay6 can only be used in DHCPv6.");
+                     }
+                  }
+
+
             | SUBSTRING "(" string_expr "," start_expr "," length_expr ")"
                   {
                       TokenPtr sub(new TokenSubstring());
@@ -221,6 +272,19 @@ length_expr : INTEGER
                  }
             ;
 
+relay6_field : PEERADDR { $$ = TokenRelay6Field::PEERADDR; }
+             | LINKADDR { $$ = TokenRelay6Field::LINKADDR; }
+             ;
+
+nest_level : INTEGER
+                 {
+		 $$ = ctx.convertNestLevelNumber($1, @1);
+                 }
+                 // Eventually we may add strings to handle different
+                 // ways of choosing from which relay we want to extract
+                 // an option or field.
+           ;
+
 %%
 void
 isc::eval::EvalParser::error(const location_type& loc,

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

@@ -148,6 +148,114 @@ public:
         EXPECT_TRUE(conc);
     }
 
+    /// @brief checks if the given token is a TokenRelay6Option with
+    /// the correct nesting level, option code and representation.
+    /// @param token token to be checked
+    /// @param expected_level expected nesting level
+    /// @param expected_code expected option code
+    /// @param expected_repr expected representation (text, hex, exists)
+    void checkTokenRelay6Option(const TokenPtr& token,
+                                uint8_t expected_level,
+                                uint16_t expected_code,
+                                TokenOption::RepresentationType expected_repr) {
+        ASSERT_TRUE(token);
+        boost::shared_ptr<TokenRelay6Option> opt =
+            boost::dynamic_pointer_cast<TokenRelay6Option>(token);
+        ASSERT_TRUE(opt);
+
+        EXPECT_EQ(expected_level, opt->getNest());
+        EXPECT_EQ(expected_code, opt->getCode());
+        EXPECT_EQ(expected_repr, opt->getRepresentation());
+    }
+
+    /// @brief This tests attempts to parse the expression then checks
+    /// if the number of tokens is correct and the TokenRelay6Option
+    /// is as expected.
+    ///
+    /// @param expr expression to be parsed
+    /// @param exp_level expected level to be parsed
+    /// @param exp_code expected option code to be parsed
+    /// @param exp_repr expected representation to be parsed
+    /// @param exp_tokens expected number of tokens
+    void testRelay6Option(std::string expr,
+                         uint8_t exp_level,
+                         uint16_t exp_code,
+                         TokenOption::RepresentationType exp_repr,
+                         int exp_tokens) {
+        EvalContext eval(Option::V6);
+
+        // parse the expression
+        try {
+            parsed_ = eval.parseString(expr);
+        }
+        catch (const EvalParseError& ex) {
+            FAIL() <<"Exception thrown: " << ex.what();
+            return;
+        }
+
+        // Parsing should succed and return a token.
+        EXPECT_TRUE(parsed_);
+
+        // There should be the expected number of tokens.
+        ASSERT_EQ(exp_tokens, eval.expression.size());
+
+        // checkt that the first token is TokenRelay6Option and that
+        // is has the correct attributes
+        checkTokenRelay6Option(eval.expression.at(0), exp_level, exp_code, exp_repr);
+    }
+
+    /// @brief checks if the given token is a TokenRelay with the
+    /// correct nesting level and field type.
+    /// @param token token to be checked
+    /// @param expected_level expected nesting level
+    /// @param expected_code expected option code
+    /// @param expected_repr expected representation (text, hex, exists)
+    void checkTokenRelay6Field(const TokenPtr& token,
+                               uint8_t expected_level,
+                               TokenRelay6Field::FieldType expected_type) {
+        ASSERT_TRUE(token);
+        boost::shared_ptr<TokenRelay6Field> opt =
+            boost::dynamic_pointer_cast<TokenRelay6Field>(token);
+        ASSERT_TRUE(opt);
+
+        EXPECT_EQ(expected_level, opt->getNest());
+        EXPECT_EQ(expected_type, opt->getType());
+    }
+
+    /// @brief This tests attempts to parse the expression then checks
+    /// if the number of tokens is correct and the TokenRelay6Field is as
+    /// expected.
+    ///
+    /// @param expr expression to be parsed
+    /// @param exp_level expected level to be parsed
+    /// @param exp_type expected field type to be parsed
+    /// @param exp_tokens expected number of tokens
+    void testRelay6Field(std::string expr,
+                         uint8_t exp_level,
+                         TokenRelay6Field::FieldType exp_type,
+                         int exp_tokens) {
+        EvalContext eval(Option::V6);
+
+        // parse the expression
+        try {
+            parsed_ = eval.parseString(expr);
+        }
+        catch (const EvalParseError& ex) {
+            FAIL() <<"Exception thrown: " << ex.what();
+            return;
+        }
+
+        // Parsing should succed and return a token.
+        EXPECT_TRUE(parsed_);
+
+        // There should be the expected number of tokens.
+        ASSERT_EQ(exp_tokens, eval.expression.size());
+
+        // checkt that the first token is TokenRelay6Field and that
+        // is has the correct attributes
+        checkTokenRelay6Field(eval.expression.at(0), exp_level, exp_type);
+    }
+
     /// @brief checks if the given expression raises the expected message
     /// when it is parsed.
     void checkError(const string& expr, const string& msg) {
@@ -576,6 +684,43 @@ TEST_F(EvalContextTest, concat) {
     checkTokenConcat(tmp3);
 }
 
+// Test the parsing of a relay6 option
+TEST_F(EvalContextTest, relay6Option) {
+    EvalContext eval(Option::V6);
+
+    testRelay6Option("relay6[0].option[123].text == 'foo'",
+                     0, 123, TokenOption::TEXTUAL, 3);
+}
+
+// Test the parsing of existence for a relay6 option
+TEST_F(EvalContextTest, relay6OptionExists) {
+    EvalContext eval(Option::V6);
+
+    testRelay6Option("relay6[1].option[75].exists",
+                     1, 75, TokenOption::EXISTS, 1);
+}
+
+// Test the parsing of hex for a relay6 option
+TEST_F(EvalContextTest, relay6OptionHex) {
+    EvalContext eval(Option::V6);
+
+    testRelay6Option("relay6[2].option[85].hex == 'foo'",
+                     2, 85, TokenOption::HEXADECIMAL, 3);
+}
+
+// Tests if the linkaddr field in a Relay6 encapsulation can be accessed.
+TEST_F(EvalContextTest, relay6FieldLinkAddr) {
+    testRelay6Field("relay6[0].linkaddr == ::",
+                    0, TokenRelay6Field::LINKADDR, 3);
+}
+
+// Tests if the peeraddr field in a Relay6 encapsulation can be accessed.
+TEST_F(EvalContextTest, relay6FieldPeerAddr) {
+    testRelay6Field("relay6[1].peeraddr == ::",
+                    1, TokenRelay6Field::PEERADDR, 3);
+}
+
+//
 // Test some scanner error cases
 TEST_F(EvalContextTest, scanErrors) {
     checkError("'", "<string>:1.1: Invalid character: '");

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

@@ -62,6 +62,111 @@ public:
         pkt4_->addOption(rai);
     }
 
+    /// @brief Adds relay encapsulations with some suboptions
+    ///
+    /// This will add 2 relay encapsulations all will have
+    /// msg_type of RELAY_FORW
+    /// Relay 0 (closest to server) will have
+    /// linkaddr = peeraddr = 0, hop-count = 1
+    /// option 100 "hundred.zero", option 101 "hundredone.zero"
+    /// Relay 1 (closest to client) will have
+    /// linkaddr 1::1= peeraddr = 1::2, hop-count = 0
+    /// option 100 "hundred.one", option 102 "hundredtwo.one"
+    void addRelay6Encapsulations() {
+        // First relay
+        Pkt6::RelayInfo relay0;
+        relay0.msg_type_ = DHCPV6_RELAY_FORW;
+        relay0.hop_count_ = 1;
+        relay0.linkaddr_ = isc::asiolink::IOAddress("::");
+        relay0.peeraddr_ = isc::asiolink::IOAddress("::");
+        OptionPtr optRelay01(new OptionString(Option::V6, 100,
+                                              "hundred.zero"));
+        OptionPtr optRelay02(new OptionString(Option::V6, 101,
+                                              "hundredone.zero"));
+
+        relay0.options_.insert(make_pair(optRelay01->getType(), optRelay01));
+        relay0.options_.insert(make_pair(optRelay02->getType(), optRelay02));
+
+        pkt6_->addRelayInfo(relay0);
+        // Second relay
+        Pkt6::RelayInfo relay1;
+        relay1.msg_type_ = DHCPV6_RELAY_FORW;
+        relay1.hop_count_ = 0;
+        relay1.linkaddr_ = isc::asiolink::IOAddress("1::1");
+        relay1.peeraddr_ = isc::asiolink::IOAddress("1::2");
+        OptionPtr optRelay11(new OptionString(Option::V6, 100,
+                                              "hundred.one"));
+        OptionPtr optRelay12(new OptionString(Option::V6, 102,
+                                              "hundredtwo.one"));
+
+        relay1.options_.insert(make_pair(optRelay11->getType(), optRelay11));
+        relay1.options_.insert(make_pair(optRelay12->getType(), optRelay12));
+        pkt6_->addRelayInfo(relay1);
+    }
+
+    /// @brief Verify that the relay6 option evaluatiosn work properly
+    ///
+    /// Given the nesting level and option code extract the option
+    /// and compare it to the expected string.
+    ///
+    /// @param test_level The nesting level
+    /// @param test_code The code of the option to extract
+    /// @param result_addr The expected result of the address as a string
+    void verifyRelay6Option(const uint8_t test_level,
+                            const uint16_t test_code,
+                            const TokenOption::RepresentationType& test_rep,
+                            const std::string& result_string) {
+        // Create the token
+        ASSERT_NO_THROW(t_.reset(new TokenRelay6Option(test_level,
+                                                       test_code,
+                                                       test_rep)));
+
+        // We should be able to evaluate it
+        EXPECT_NO_THROW(t_->evaluate(*pkt6_, values_));
+
+        // We should have one value on the stack
+        ASSERT_EQ(1, values_.size());
+
+        // And it should match the expected result
+        // Invalid nesting levels result in a 0 length string
+        EXPECT_EQ(result_string, values_.top());
+
+        // Then we clear the stack
+        clearStack();
+    }
+
+    /// @brief Verify that the relay6 field evaluations work properly
+    ///
+    /// Given the nesting level, the field to extract and the expected
+    /// address create a token and evaluate it then compare the addresses
+    ///
+    /// @param test_level The nesting level
+    /// @param test_field The type of the field to extract
+    /// @param result_addr The expected result of the address as a string
+    void verifyRelay6Eval(const uint8_t test_level,
+                          const TokenRelay6Field::FieldType test_field,
+                          const int result_len,
+                          const uint8_t result_addr[]) {
+        // Create the token
+        ASSERT_NO_THROW(t_.reset(new TokenRelay6Field(test_level, test_field)));
+
+        // We should be able to evaluate it
+        EXPECT_NO_THROW(t_->evaluate(*pkt6_, values_));
+
+        // We should have one value on the stack
+        ASSERT_EQ(1, values_.size());
+
+        // And it should match the expected result
+        // Invalid nesting levels result in a 0 length string
+        EXPECT_EQ(result_len, values_.top().size());
+        if (result_len != 0) {
+            EXPECT_EQ(0, memcmp(result_addr, &values_.top()[0], result_len));
+        }
+
+        // Then we clear the stack
+        clearStack();
+    }
+
     /// @brief Convenience function. Removes token and values stacks.
     void clearStack() {
         while (!values_.empty()) {
@@ -1012,3 +1117,89 @@ TEST_F(TokenTest, concat) {
     ASSERT_EQ(1, values_.size());
     EXPECT_EQ("foobar", values_.top());
 }
+
+// This test checks if we can properly extract the link and peer
+// address fields from relay encapsulations.  Our packet has
+// two relay encapsulations.  We attempt to extract the two
+// fields from both of the encapsulations and compare them.
+// We also try to extract one of the fields from an encapsulation
+// that doesn't exist (level 2), this should result in an empty
+// string.
+TEST_F(TokenTest, relay6Field) {
+    // Values for the address results
+    uint8_t zeroaddr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
+                           0, 0, 0, 0, 0, 0, 0, 0 };
+    uint8_t linkaddr[] = { 0, 1, 0, 0, 0, 0, 0, 0,
+                           0, 0, 0, 0, 0, 0, 0, 1 };
+    uint8_t peeraddr[] = { 0, 1, 0, 0, 0, 0, 0, 0,
+                           0, 0, 0, 0, 0, 0, 0, 2 };
+
+    // We start by adding a set of relay encapsulations to the
+    // basic v6 packet.
+    addRelay6Encapsulations();
+
+    // Then we work our way through the set of choices
+    // Level 0 both link and peer address should be 0::0
+    verifyRelay6Eval(0, TokenRelay6Field::LINKADDR, 16, zeroaddr);
+    verifyRelay6Eval(0, TokenRelay6Field::PEERADDR, 16, zeroaddr);
+
+    // Level 1 link and peer should have different non-zero addresses
+    verifyRelay6Eval(1, TokenRelay6Field::LINKADDR, 16, linkaddr);
+    verifyRelay6Eval(1, TokenRelay6Field::PEERADDR, 16, peeraddr);
+
+    // Level 2 has no encapsulation so the address should be zero length
+    verifyRelay6Eval(2, TokenRelay6Field::LINKADDR, 0, zeroaddr);
+
+    // Lets check that the layout of the address returned by the
+    // token matches that of the TokenIpAddress
+    TokenPtr trelay;
+    TokenPtr taddr;
+    TokenPtr tequal;
+    ASSERT_NO_THROW(trelay.reset(new TokenRelay6Field(1, TokenRelay6Field::LINKADDR)));
+    ASSERT_NO_THROW(taddr.reset(new TokenIpAddress("1::1")));
+    ASSERT_NO_THROW(tequal.reset(new TokenEqual()));
+
+    EXPECT_NO_THROW(trelay->evaluate(*pkt6_, values_));
+    EXPECT_NO_THROW(taddr->evaluate(*pkt6_, values_));
+    EXPECT_NO_THROW(tequal->evaluate(*pkt6_, values_));
+
+    // We should have a single value on the stack and it should be "true"
+    ASSERT_EQ(1, values_.size());
+    EXPECT_EQ("true", values_.top());
+
+    // be tidy
+    clearStack();
+}
+
+// This test checks if we can properly extract an option
+// from relay encapsulations.  Our packet has two relay
+// encapsulations.  Both include a common option with the
+// original message (option 100) and both include their
+// own option (101 and 102).  We attempt to extract the
+// options and compare them to expected values.  We also
+// try to extract an option from an encapsulation
+// that doesn't exist (level 2), this should result in an empty
+// string.
+TEST_F(TokenTest, relay6Option) {
+    // We start by adding a set of relay encapsulations to the
+    // basic v6 packet.
+    addRelay6Encapsulations();
+
+    // Then we work our way through the set of choices
+    // Level 0 both options it has and the check that
+    // the checking for an option it doesn't have results
+    // in an empty string.
+    verifyRelay6Option(0, 100, TokenOption::TEXTUAL, "hundred.zero");
+    verifyRelay6Option(0, 100, TokenOption::EXISTS, "true");
+    verifyRelay6Option(0, 101, TokenOption::TEXTUAL, "hundredone.zero");
+    verifyRelay6Option(0, 102, TokenOption::TEXTUAL, "");
+    verifyRelay6Option(0, 102, TokenOption::EXISTS, "false");
+
+    // Level 1, again both options it has and the one for level 0
+    verifyRelay6Option(1, 100, TokenOption::TEXTUAL, "hundred.one");
+    verifyRelay6Option(1, 101, TokenOption::TEXTUAL, "");
+    verifyRelay6Option(1, 102, TokenOption::TEXTUAL, "hundredtwo.one");
+
+    // Level 2, no encapsulation so no options
+    verifyRelay6Option(2, 100, TokenOption::TEXTUAL, "");
+}

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

@@ -9,6 +9,7 @@
 #include <util/encode/hex.h>
 #include <asiolink/io_address.h>
 #include <boost/lexical_cast.hpp>
+#include <dhcp/pkt6.h>
 #include <cstring>
 #include <string>
 
@@ -295,3 +296,67 @@ TokenOr::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
         values.push("false");
     }
 }
+
+OptionPtr TokenRelay6Option::getOption(const Pkt& pkt) {
+
+    try {
+        // Check if it's a Pkt6.  If it's not the dynamic_cast will
+        // throw std::bad_cast.
+        const Pkt6& pkt6 = dynamic_cast<const Pkt6&>(pkt);
+
+        try {
+            // Now that we have the right type of packet we can
+            // get the option and return it.
+            return(pkt6.getRelayOption(option_code_, nest_level_));
+        }
+        catch (const isc::OutOfRange&) {
+            // The only exception we expect is OutOfRange if the nest
+            // level is out of range of the encapsulations, for example
+            // if nest_level_ is 4 and there are only 2 encapsulations.
+            // We return a NULL in that case.
+           return (OptionPtr());
+        }
+
+    } catch (const std::bad_cast&) {
+        isc_throw(EvalTypeError, "Specified packet is not Pkt6");
+    }
+
+}
+
+void
+TokenRelay6Field::evaluate(const Pkt& pkt, ValueStack& values) {
+
+    vector<uint8_t> binary;
+    try {
+        // Check if it's a Pkt6.  If it's not the dynamic_cast will
+        // throw std::bad_cast.
+        const Pkt6& pkt6 = dynamic_cast<const Pkt6&>(pkt);
+
+        try {
+        switch (type_) {
+            // Now that we have the right type of packet we can
+            // get the option and return it.
+            case LINKADDR:
+                binary = pkt6.getRelay6LinkAddress(nest_level_).toBytes();
+                break;
+            case PEERADDR:
+                binary = pkt6.getRelay6PeerAddress(nest_level_).toBytes();
+                break;
+            }
+        } catch (const isc::OutOfRange&) {
+            // The only exception we expect is OutOfRange if the nest
+            // level is invalid.  We push "" in that case.
+            values.push("");
+            return;
+        }
+    } catch (const std::bad_cast&) {
+        isc_throw(EvalTypeError, "Specified packet is not Pkt6");
+    }
+
+    string value;
+    value.resize(binary.size());
+    if (!binary.empty()) {
+        memmove(&value[0], &binary[0], binary.size());
+    }
+    values.push(value);
+}

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

@@ -467,6 +467,119 @@ public:
     void evaluate(const Pkt& pkt, ValueStack& values);
 };
 
+/// @brief Token that represents a value of an option within a DHCPv6 relay
+/// encapsulation
+///
+/// This represents a reference to a given option similar to TokenOption
+/// but from within the information from a relay.  In the expresssion
+/// relay6[nest-level].option[option-code], nest-level indicates which
+/// of the relays to examine and option-code which option to extract.
+///
+/// During the evaluation it tries to extract the value of the specified
+/// option from the requested relay block.  If the relay block doesn't
+/// exist or the option is not found an empty string ("") is returned
+/// (or "false" when the representation is EXISTS).
+///
+/// The nesting level can go from 0 (closest to the server) to 31
+class TokenRelay6Option : public TokenOption {
+public:
+    /// @brief Constructor that takes a nesting level and an option
+    /// code as paramaters.
+    ///
+    /// @param nest_level the nesting for which relay to examine.
+    /// @param option_code code of the option.
+    /// @param rep_type Token representation type.
+    TokenRelay6Option(const uint8_t nest_level, const uint16_t option_code,
+                      const RepresentationType& rep_type)
+        :TokenOption(option_code, rep_type), nest_level_(nest_level) {}
+
+    /// @brief Returns nest-level
+    ///
+    /// This method is used in testing to determine if the parser has
+    /// instantiated TokenRelay6Option with correct parameters.
+    ///
+    /// @return nest-level of the relay block this token expects to use
+    /// for extraction.
+    uint8_t getNest() const {
+        return (nest_level_);
+    }
+
+protected:
+    /// @brief Attempts to obtain specified option from the specified relay block
+    /// @param pkt DHCPv6 packet that hopefully contains the proper relay block
+    /// @return option instance if available
+    virtual OptionPtr getOption(const Pkt& pkt);
+
+    uint8_t nest_level_; ///< nesting level of the relay block to use
+};
+
+/// @breif Token that represents a value of a field within a DHCPv6 relay
+/// encapsulation
+///
+/// This represents a reference to a field with a given DHCPv6 relay encapsulation.
+/// In the expression relay6[nest-level].field-name, nest-level indicates which of
+/// the relays to examine and field-name which of the fields to extract.
+///
+/// During the evaluation it tries to extract the value of the specified
+/// field from the requested relay block.  If the relay block doesn't exist
+/// an empty string ("") is returned.  If the relay block does exist the field
+/// is always returned as a 16 byte IPv6 address.  As the relay may not have
+/// set the field it may be 0s.
+///
+/// The nesting level can go from 0 (closest to the server) to 31.
+class TokenRelay6Field : public Token {
+public:
+
+    /// @brief enum value that determines the field.
+    enum FieldType {
+        PEERADDR, ///< Peer address field (IPv6 address)
+        LINKADDR  ///< Link address field (IPv6 address)
+    };
+
+    /// @brief Constructor that takes a nesting level and field type
+    /// as parameters.
+    ///
+    /// @param nest_level the nesting level for which relay to examine.
+    /// @param type which field to extract.
+    TokenRelay6Field(const uint8_t nest_level, const FieldType type)
+      : nest_level_(nest_level), type_(type) {}
+
+    /// @brief Extracts the specified field from the requested relay
+    ///
+    /// Evaluation uses fields available in the packet.  It does not require
+    /// any values to be present on the stack.
+    ///
+    /// @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 nest-level
+    ///
+    /// This method is used in testing to determine if the parser has
+    /// instantiated TokenRelay6Field with correct parameters.
+    ///
+    /// @return nest-level of the relay block this token expects to use
+    /// for extraction.
+    uint8_t getNest() const {
+        return (nest_level_);
+    }
+
+    /// @brief Returns field type
+    ///
+    /// This method is used only in testing to determine if the parser has
+    /// instantiated TokenRelay6Field with correct parameters.
+    ///
+    /// @return type of the field.
+    FieldType getType() {
+        return (type_);
+    }
+
+protected:
+    /// @brief Specifies field of the DHCPv6 relay option to get
+    uint8_t nest_level_; ///< nesting level of the relay block to use
+    FieldType type_; ///< field to get
+};
+
 }; // end of isc::dhcp namespace
 }; // end of isc namespace