Parcourir la source

[4272a] Ported previous attempt

Francis Dupont il y a 9 ans
Parent
commit
d480426005

+ 27 - 3
doc/guide/classify.xml

@@ -217,25 +217,49 @@
             <row>
               <entry>DHCPv6 Relay Options</entry>
               <entry>relay6[nest].option[code].hex</entry>
-              <!--  <entry>Value of the option</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>2001:DB8::1</entry>
               <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>2001:DB8::1</entry>
               <entry>The value of the link address field from the
               relay encapsulation "nest"</entry>
             </row>
             <row>
+              <entry>Interface name of packet</entry>
+              <entry>pkt.iface</entry>
+              <entry>eth0</entry>
+              <entry>The name of the incoming interface of a DHCP packet.</entry>
+            </row>
+            <row>
+              <entry>Source address of packet</entry>
+              <entry>pkt.src</entry>
+              <entry>10.1.2.3</entry>
+              <entry>The IP source address of a DHCP packet.</entry>
+            </row>
+            <row>
+              <entry>Destination address of packet</entry>
+              <entry>pkt.dst</entry>
+              <entry>10.1.2.3</entry>
+              <entry>The IP destination address of a DHCP packet.</entry>
+            </row>
+            <row>
+              <entry>Length of packet</entry>
+              <entry>pkt.len</entry>
+              <entry>0x00000100</entry>
+              <entry>The length of a DHCP packet (UDP header field) padded to 4 bytes.</entry>
+            </row>
+            <row>
               <entry>Hardware address in DHCPv4 packet</entry>
               <entry>pkt4.mac</entry>
               <entry>0x010203040506</entry>

+ 8 - 2
src/lib/eval/eval_messages.mes

@@ -60,16 +60,22 @@ the value stack.  Then are then combined via logical or and
 the result is pushed onto the value stack. The string is displayed
 in text.
 
+# For use with TokenPkt
+% EVAL_DEBUG_PKT Pushing PKT meta data %1 with value %2
+This debug message indicates that the given binary string representing
+the value of the requested meta data is being pushed onto the value stack.
+The string is displayed in hex at the exception of interface name.
+
 # For use with TokenPkt4
 % EVAL_DEBUG_PKT4 Pushing PKT4 field %1 with value %2
 This debug message indicates that the given binary string representing
-the value of the requested fied is being pushed onto the value stack.
+the value of the requested field is being pushed onto the value stack.
 The string is displayed in hex.
 
 # For use with TokenPkt6
 % EVAL_DEBUG_PKT6 Pushing PKT6 field %1 with value %2
 This debug message indicates that the given binary string representing
-the value of the requested fied is being pushed onto the value stack.
+the value of the requested field is being pushed onto the value stack.
 The string is displayed in hex.
 
 # For use with TokenRelay6Field

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

@@ -147,6 +147,11 @@ addr6 [0-9a-fA-F]*\:[0-9a-fA-F]*\:[0-9a-fA-F:.]*
 "text"      return isc::eval::EvalParser::make_TEXT(loc);
 "hex"       return isc::eval::EvalParser::make_HEX(loc);
 "exists"    return isc::eval::EvalParser::make_EXISTS(loc);
+"pkt"       return isc::eval::EvalParser::make_PKT(loc);
+"iface"     return isc::eval::EvalParser::make_IFACE(loc);
+"src"       return isc::eval::EvalParser::make_SRC(loc);
+"dst"       return isc::eval::EvalParser::make_DST(loc);
+"len"       return isc::eval::EvalParser::make_LEN(loc);
 "pkt4"      return isc::eval::EvalParser::make_PKT4(loc);
 "mac"       return isc::eval::EvalParser::make_CHADDR(loc);
 "hlen"      return isc::eval::EvalParser::make_HLEN(loc);

+ 51 - 4
src/lib/eval/parser.yy

@@ -52,6 +52,11 @@ using namespace isc::eval;
   TEXT "text"
   HEX "hex"
   EXISTS "exists"
+  PKT "pkt"
+  IFACE "iface"
+  SRC "src"
+  DST "dst"
+  LEN "len"
   PKT4 "pkt4"
   CHADDR "mac"
   HLEN "hlen"
@@ -81,6 +86,7 @@ using namespace isc::eval;
 %type <TokenOption::RepresentationType> option_repr_type
 %type <TokenRelay6Field::FieldType> relay6_field
 %type <uint8_t> nest_level
+%type <TokenPkt::MetadataType> pkt_metadata
 %type <TokenPkt4::FieldType> pkt4_field
 %type <TokenPkt6::FieldType> pkt6_field
 
@@ -218,15 +224,38 @@ string_expr : STRING
                      }
                   }
 
+            | PKT "." pkt_metadata
+                  {
+                      TokenPtr pkt_metadata(new TokenPkt($3));
+                      ctx.expression.push_back(pkt_metadata);
+                  }
             | PKT4 "." pkt4_field
                   {
-                      TokenPtr pkt4_field(new TokenPkt4($3));
-                      ctx.expression.push_back(pkt4_field);
+                     switch (ctx.getUniverse()) {
+                     case Option::V4:
+                     {
+                         TokenPtr pkt4_field(new TokenPkt4($3));
+                         ctx.expression.push_back(pkt4_field);
+                         break;
+                     }
+                     case Option::V6:
+                         // For now we only use pkt4 in DHCPv4.
+                         error(@1, "pkt4 can only be used in DHCPv4.");
+                     }
                   }
             | PKT6 "." pkt6_field
                   {
-                      TokenPtr pkt6_field(new TokenPkt6($3));
-                      ctx.expression.push_back(pkt6_field);
+                     switch (ctx.getUniverse()) {
+                     case Option::V6:
+                     {
+                         TokenPtr pkt6_field(new TokenPkt6($3));
+                         ctx.expression.push_back(pkt6_field);
+                         break;
+                     }
+                     case Option::V4:
+                         // For now we only use pkt6 in DHCPv6.
+                         error(@1, "pkt6 can only be used in DHCPv6.");
+                     }
                   }
             | RELAY6 "[" nest_level "]" "." relay6_field
                   {
@@ -284,6 +313,24 @@ nest_level : INTEGER
                  // an option or field.
            ;
 
+pkt_metadata : IFACE
+                  {
+                      $$ = TokenPkt::IFACE;
+                  }
+             | SRC
+                  {
+                      $$ = TokenPkt::SRC;
+                  }
+             | DST
+                  {
+                      $$ = TokenPkt::DST;
+                  }
+             | LEN
+                  {
+                      $$ = TokenPkt::LEN;
+                  }
+             ;
+
 pkt4_field : CHADDR
                 {
                     $$ = TokenPkt4::CHADDR;

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

@@ -188,6 +188,51 @@ public:
         checkTokenRelay6Option(eval.expression.at(0), exp_level, exp_code, exp_repr);
     }
 
+    /// @brief check if the given token is a Pkt of specified type
+    /// @param token token to be checked
+    /// @param type expected type of the Pkt metadata
+    void checkTokenPkt(const TokenPtr& token, TokenPkt::MetadataType type) {
+        ASSERT_TRUE(token);
+        boost::shared_ptr<TokenPkt> pkt =
+            boost::dynamic_pointer_cast<TokenPkt>(token);
+        ASSERT_TRUE(pkt);
+
+        EXPECT_EQ(type, pkt->getType());
+    }
+
+    /// @brief Test that verifies access to the DHCP packet metadatas.
+    ///
+    /// This test attempts to parse the expression, will check if the number
+    /// of tokens is exactly as expected and then will try to verify if the
+    /// first token represents the expected metadata in DHCP packet.
+    ///
+    /// @param expr expression to be parsed
+    /// @param exp_type expected metadata type to be parsed
+    /// @param exp_tokens expected number of tokens
+    void testPktMetadata(std::string expr,
+                         TokenPkt::MetadataType 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 succeed and return a token.
+        EXPECT_TRUE(parsed_);
+
+        // There should be exactly the expected number of tokens.
+        ASSERT_EQ(exp_tokens, eval.expression.size());
+
+        // Check that the first token is TokenPkt instance and has correct type.
+        checkTokenPkt(eval.expression.at(0), exp_type);
+    }
+
     /// @brief checks if the given token is Pkt4 of specified type
     /// @param token token to be checked
     /// @param type expected type of the Pkt4 field
@@ -654,6 +699,26 @@ TEST_F(EvalContextTest, relay6OptionHex) {
                      2, 85, TokenOption::HEXADECIMAL, 3);
 }
 
+// Tests whether iface metadata in DHCP can be accessed.
+TEST_F(EvalContextTest, pktMetadataIface) {
+    testPktMetadata("pkt.iface == 'eth0'", TokenPkt::IFACE, 3);
+}
+
+// Tests whether src metadata in DHCP can be accessed.
+TEST_F(EvalContextTest, pktMetadataSrc) {
+    testPktMetadata("pkt.src == fe80::1", TokenPkt::SRC, 3);
+}
+
+// Tests whether dst metadata in DHCP can be accessed.
+TEST_F(EvalContextTest, pktMetadataDst) {
+    testPktMetadata("pkt.dst == fe80::2", TokenPkt::DST, 3);
+}
+
+// Tests whether len metadata in DHCP can be accessed.
+TEST_F(EvalContextTest, pktMetadataLen) {
+    testPktMetadata("pkt.len == 0x00000100", TokenPkt::LEN, 3);
+}
+
 // Tests whether chaddr field in DHCPv4 can be accessed.
 TEST_F(EvalContextTest, pkt4FieldChaddr) {
     testPkt4Field("pkt4.mac == 0x000102030405", TokenPkt4::CHADDR, 3);

+ 88 - 1
src/lib/eval/tests/token_unittest.cc

@@ -39,7 +39,7 @@ namespace {
 class TokenTest : public LogContentTest {
 public:
 
-    /// @brief Initializes Pkt4,Pkt6 and options that can be useful for
+    /// @brief Initializes Pkt4, Pkt6 and options that can be useful for
     ///        evaluation tests.
     TokenTest() {
         pkt4_.reset(new Pkt4(DHCPDISCOVER, 12345));
@@ -885,6 +885,93 @@ TEST_F(TokenTest, relay6Option) {
     EXPECT_TRUE(checkFile());
 }
 
+// Verifies that DHCPv4 packet metadata can be extracted.
+TEST_F(TokenTest, pkt4MetaData) {
+    pkt4_->setIface("eth0");
+    pkt4_->setLocalAddr(IOAddress("10.0.0.1"));
+    pkt4_->setRemoteAddr(IOAddress("10.0.0.2"));
+
+    // Check interface (expect eth0)
+    ASSERT_NO_THROW(t_.reset(new TokenPkt(TokenPkt::IFACE)));
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+    ASSERT_EQ(1, values_.size());
+    ASSERT_EQ("eth0", values_.top());
+
+    // Check source (expect 10.0.0.2)
+    clearStack();
+    ASSERT_NO_THROW(t_.reset(new TokenPkt(TokenPkt::SRC)));
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+    ASSERT_EQ(1, values_.size());
+    ASSERT_EQ(4, values_.top().size());
+    EXPECT_EQ(10, values_.top()[0]);
+    EXPECT_EQ(0, values_.top()[1]);
+    EXPECT_EQ(0, values_.top()[2]);
+    EXPECT_EQ(2, values_.top()[3]);
+
+    // Check destination (expect 10.0.0.1)
+    clearStack();
+    ASSERT_NO_THROW(t_.reset(new TokenPkt(TokenPkt::DST)));
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+    ASSERT_EQ(1, values_.size());
+    vector<uint8_t> a1 = IOAddress("10.0.0.1").toBytes();
+    ASSERT_EQ(a1.size(), values_.top().size());
+    EXPECT_EQ(0, memcmp(&a1[0], &values_.top()[0], a1.size()));
+
+    // Check length (expect 249)
+    clearStack();
+    ASSERT_NO_THROW(t_.reset(new TokenPkt(TokenPkt::LEN)));
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+    ASSERT_EQ(1, values_.size());
+    uint32_t length = htonl(static_cast<uint32_t>(pkt4_->len()));
+    ASSERT_EQ(4, values_.top().size());
+    EXPECT_EQ(0, memcmp(&length, &values_.top()[0], 4));
+}
+
+// Verifies that DHCPv6 packet metadata can be extracted.
+TEST_F(TokenTest, pkt6MetaData) {
+    pkt6_->setIface("eth0");
+    pkt6_->setLocalAddr(IOAddress("ff02::1:2"));
+    pkt6_->setRemoteAddr(IOAddress("fe80::1234"));
+
+    // Check interface (expect eth0)
+    ASSERT_NO_THROW(t_.reset(new TokenPkt(TokenPkt::IFACE)));
+    EXPECT_NO_THROW(t_->evaluate(*pkt6_, values_));
+    ASSERT_EQ(1, values_.size());
+    ASSERT_EQ("eth0", values_.top());
+
+    // Check source (expect fe80::1234)
+    clearStack();
+    ASSERT_NO_THROW(t_.reset(new TokenPkt(TokenPkt::SRC)));
+    EXPECT_NO_THROW(t_->evaluate(*pkt6_, values_));
+    ASSERT_EQ(1, values_.size());
+    ASSERT_EQ(16, values_.top().size());
+    EXPECT_EQ(0xfe, static_cast<uint8_t>(values_.top()[0]));
+    EXPECT_EQ(0x80, static_cast<uint8_t>(values_.top()[1]));
+    for (unsigned i = 2; i < 14; ++i) {
+        EXPECT_EQ(0, values_.top()[i]);
+    }
+    EXPECT_EQ(0x12, values_.top()[14]);
+    EXPECT_EQ(0x34, values_.top()[15]);
+
+    // Check destination (expect ff02::1:2)
+    clearStack();
+    ASSERT_NO_THROW(t_.reset(new TokenPkt(TokenPkt::DST)));
+    EXPECT_NO_THROW(t_->evaluate(*pkt6_, values_));
+    ASSERT_EQ(1, values_.size());
+    vector<uint8_t> ma = IOAddress("ff02::1:2").toBytes();
+    ASSERT_EQ(ma.size(), values_.top().size());
+    EXPECT_EQ(0, memcmp(&ma[0], &values_.top()[0], ma.size()));
+
+    // Check length (expect 16)
+    clearStack();
+    ASSERT_NO_THROW(t_.reset(new TokenPkt(TokenPkt::LEN)));
+    EXPECT_NO_THROW(t_->evaluate(*pkt6_, values_));
+    ASSERT_EQ(1, values_.size());
+    uint32_t length = htonl(static_cast<uint32_t>(pkt6_->len()));
+    ASSERT_EQ(4, values_.top().size());
+    EXPECT_EQ(0, memcmp(&length, &values_.top()[0], 4));
+}
+
 // Verifies if the DHCPv4 packet fields can be extracted.
 TEST_F(TokenTest, pkt4Fields) {
     pkt4_->setGiaddr(IOAddress("192.0.2.1"));

+ 87 - 28
src/lib/eval/token.cc

@@ -17,6 +17,19 @@
 using namespace isc::dhcp;
 using namespace std;
 
+namespace {
+
+/// @brief encode in hexadecimal
+///
+/// @param value the value to encode
+/// @return 0x followed by the value encoded in hexa
+inline string toHex(string value) {
+    vector<uint8_t> bin(value.begin(), value.end());
+    return ("0x" + isc::util::encode::encodeHex(bin));
+}
+
+}; // end of anonymous namespace
+
 void
 TokenString::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
     // Literals only push, nothing to pop
@@ -62,8 +75,7 @@ TokenHexString::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
 
     // Log what we pushed
     LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_HEXSTRING)
-        .arg("0x" + util::encode::encodeHex(std::vector<uint8_t>(value_.begin(),
-								 value_.end())));
+        .arg(toHex(value_));
 }
 
 TokenIpAddress::TokenIpAddress(const string& addr) : value_("") {
@@ -88,8 +100,7 @@ TokenIpAddress::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
 
     // Log what we pushed
     LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_IPADDRESS)
-        .arg("0x" + util::encode::encodeHex(std::vector<uint8_t>(value_.begin(),
-								 value_.end())));
+        .arg(toHex(value_));
 }
 
 OptionPtr
@@ -127,8 +138,7 @@ TokenOption::evaluate(const Pkt& pkt, ValueStack& values) {
     if (representation_type_ == HEXADECIMAL) {
         LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_OPTION)
             .arg(option_code_)
-            .arg("0x" + util::encode::encodeHex(std::vector<uint8_t>(opt_str.begin(),
-                                                                     opt_str.end())));
+            .arg(toHex(opt_str));
     } else {
         LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_OPTION)
             .arg(option_code_)
@@ -180,6 +190,66 @@ OptionPtr TokenRelay6Option::getOption(const Pkt& pkt) {
 }
 
 void
+TokenPkt::evaluate(const Pkt& pkt, ValueStack& values) {
+
+    string value;
+    vector<uint8_t> binary;
+    string type_str;
+    uint32_t len;
+    bool is_binary = true;
+    switch (type_) {
+    case IFACE:
+        is_binary = false;
+        value = pkt.getIface();
+        type_str = "iface";
+        break;
+    case SRC:
+        binary = pkt.getRemoteAddr().toBytes();
+        type_str = "src";
+        break;
+    case DST:
+        binary = pkt.getLocalAddr().toBytes();
+        type_str = "dst";
+        break;
+    case LEN:
+        // len() returns a size_t but in fact it can't be very large
+        // (with UDP transport it fits in 16 bits)
+        // the len() method is not const because of DHCPv6 relays.
+        // We assume here it has no bad side effects...
+        len = static_cast<uint32_t>(const_cast<Pkt&>(pkt).len());
+        binary.push_back(len >> 24);
+        binary.push_back((len >> 16) & 0xFF);
+        binary.push_back((len >> 8) & 0xFF);
+        binary.push_back(len & 0xFF);
+        type_str = "len";
+        break;
+
+    default:
+        isc_throw(EvalTypeError, "Bad meta data specified: "
+                  << static_cast<int>(type_) );
+    }
+
+    if (is_binary) {
+        value.resize(binary.size());
+        if (!binary.empty()) {
+            memmove(&value[0], &binary[0], binary.size());
+        }
+    }
+    values.push(value);
+
+    // Log what we pushed
+    if (is_binary) {
+        LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_PKT)
+            .arg(type_str)
+            .arg(toHex(value));
+    } else {
+        LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_PKT)
+            .arg(type_str)
+            .arg(value);
+    }
+}
+
+void
 TokenPkt4::evaluate(const Pkt& pkt, ValueStack& values) {
 
     vector<uint8_t> binary;
@@ -260,8 +330,7 @@ TokenPkt4::evaluate(const Pkt& pkt, ValueStack& values) {
     // Log what we pushed
     LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_PKT4)
         .arg(type_str)
-        .arg("0x" + util::encode::encodeHex(std::vector<uint8_t>(value.begin(),
-                                                                 value.end())));
+        .arg(toHex(value));
 }
 
 void
@@ -312,8 +381,7 @@ TokenPkt6::evaluate(const Pkt& pkt, ValueStack& values) {
     // Log what we pushed
     LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_PKT6)
         .arg(type_str)
-        .arg("0x" + util::encode::encodeHex(std::vector<uint8_t>(value.begin(),
-                                                                 value.end())));
+        .arg(toHex(value));
 }
 
 void
@@ -365,8 +433,7 @@ TokenRelay6Field::evaluate(const Pkt& pkt, ValueStack& values) {
     LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_RELAY6)
         .arg(type_str)
         .arg(unsigned(nest_level_))
-        .arg("0x" + util::encode::encodeHex(std::vector<uint8_t>(value.begin(),
-                                                                 value.end())));
+        .arg(toHex(value));
 }
 
 void
@@ -389,10 +456,8 @@ TokenEqual::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
 
     // Log what we popped and pushed
     LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_EQUAL)
-        .arg("0x" + util::encode::encodeHex(std::vector<uint8_t>(op1.begin(),
-                                                                 op1.end())))
-        .arg("0x" + util::encode::encodeHex(std::vector<uint8_t>(op2.begin(),
-                                                                 op2.end())))
+        .arg(toHex(op1))
+        .arg(toHex(op2))
         .arg('\'' + values.top() + '\'');
 }
 
@@ -459,8 +524,7 @@ TokenSubstring::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
         LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_SUBSTRING_RANGE)
             .arg(len_str)
             .arg(start_str)
-            .arg("0x" + util::encode::encodeHex(std::vector<uint8_t>(string_str.begin(),
-                                                                     string_str.end())))
+            .arg(toHex(string_str))
             .arg("0x");
         return;
     }
@@ -489,10 +553,8 @@ TokenSubstring::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
     LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_SUBSTRING)
         .arg(len_str)
         .arg(start_str)
-        .arg("0x" + util::encode::encodeHex(std::vector<uint8_t>(string_str.begin(),
-                                                                 string_str.end())))
-        .arg("0x" + util::encode::encodeHex(std::vector<uint8_t>(values.top().begin(),
-                                                                 values.top().end())));
+        .arg(toHex(string_str))
+        .arg(toHex(values.top()));
 }
 
 void
@@ -513,12 +575,9 @@ TokenConcat::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
 
     // Log what we popped and pushed
     LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_CONCAT)
-        .arg("0x" + util::encode::encodeHex(std::vector<uint8_t>(op1.begin(),
-                                                                 op1.end())))
-        .arg("0x" + util::encode::encodeHex(std::vector<uint8_t>(op2.begin(),
-                                                                 op2.end())))
-        .arg("0x" + util::encode::encodeHex(std::vector<uint8_t>(values.top().begin(),
-                                                                 values.top().end())));
+        .arg(toHex(op1))
+        .arg(toHex(op2))
+        .arg(toHex(values.top()));
 }
 
 void

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

@@ -334,6 +334,53 @@ protected:
     uint8_t nest_level_; ///< nesting level of the relay block to use
 };
 
+/// @brief Token that represents meta data of a DHCP packet.
+///
+/// For example in the expression pkt.iface == 'eth0'
+/// this token represents the pkt.iface expression.
+///
+/// Currently supported meta datas are:
+/// - iface (incoming/outgoinginterface name)
+/// - src   (source IP address, 4 or 16 octets)
+/// - dst   (destination IP address, 4 octets)
+/// - len   (length field in the UDP header, padded to 4 octets)
+class TokenPkt : public Token {
+public:
+
+    /// @brief enum value that determines the field.
+    enum MetadataType {
+        IFACE, ///< interface name (string)
+        SRC,   ///< source (IP address)
+        DST,   ///< destination (IP address)
+        LEN    ///< length (4 octets)
+    };
+
+    /// @brief Constructor (does nothing)
+    TokenPkt(const MetadataType type)
+        : type_(type) {}
+
+    /// @brief Gets a value from the specified packet.
+    ///
+    /// Evaluation uses metadatas available in the packet. It does not
+    /// require any values to be present on the stack.
+    ///
+    /// @param pkt - metadatas will be extracted from here
+    /// @param values - stack of values (1 result will be pushed)
+    void evaluate(const Pkt& pkt, ValueStack& values);
+
+    /// @brief Returns metadata type
+    ///
+    /// This method is used only in tests.
+    /// @return type of the metadata.
+    MetadataType getType() {
+        return (type_);
+    }
+
+private:
+    /// @brief Specifies metadata of the DHCP packet
+    MetadataType type_;
+};
+
 /// @brief Token that represents fields of a DHCPv4 packet.
 ///
 /// For example in the expression pkt4.chaddr == 0x0102030405