]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
added pdexclude unittests, joined unpackOptions[4/6] into one
authorAndrei Pavel <andrei.pavel@qualitance.com>
Wed, 6 Jul 2016 17:09:19 +0000 (20:09 +0300)
committerAndrei Pavel <andrei.pavel@qualitance.com>
Wed, 6 Jul 2016 17:09:19 +0000 (20:09 +0300)
13 files changed:
src/bin/dhcp4/dhcp4.dox
src/bin/dhcp6/dhcp6.dox
src/lib/dhcp/duid_factory.cc
src/lib/dhcp/libdhcp++.cc
src/lib/dhcp/libdhcp++.h
src/lib/dhcp/option.cc
src/lib/dhcp/option6_pdexclude.cc
src/lib/dhcp/option6_pdexclude.h
src/lib/dhcp/pkt4.cc
src/lib/dhcp/pkt6.cc
src/lib/dhcp/tests/Makefile.am
src/lib/dhcp/tests/libdhcp++_unittest.cc
src/lib/dhcp/tests/option6_pdexclude_unittest.cc [new file with mode: 0644]

index 1663d5da8c13f9175803eca6b345f86cc451e06d..6afd997a4b67c8184f52933e78acea42bbc4cc0d 100644 (file)
@@ -115,14 +115,14 @@ be stored in libdhcp++. Such option formats are stored in the
 of options which may be encapsulated by other options up to any level of
 encapsulation, but these functions are unaware of the option formats defined
 in the @ref isc::dhcp::CfgMgr because they belong to a different library.
-Therefore, the generic functions @ref isc::dhcp::LibDHCP::unpackOptions4 and
-@ref isc::dhcp::LibDHCP::unpackOptions6 are only useful to parse standard
+Therefore, the generic function @ref isc::dhcp::LibDHCP::unpackOptions is only
+useful to parse standard
 options whose definitions are provided in the libdhcp++. In order to overcome
 this problem a callback mechanism has been implemented in @c Option and @c Pkt4
 classes. By installing a callback function on an instance of @c Pkt4, the
 server may provide a custom implementation of the options parsing algorithm.
-This callback function will take precedence over the @c LibDHCP::unpackOptions4
-and @c LibDHCP::unpackOptions6 functions. With this approach, the callback is
+This callback function will take precedence over the @c LibDHCP::unpackOptions
+function. With this approach, the callback is
 implemented within the context of the server and it has access to all objects
 which define its configuration (including dynamically created option
 definitions).
index 559206348ec4f5a11a5689f7d607bc1c9cfb0c49..05c172cd47d863a4e238b74b1be0798e779076f5 100644 (file)
@@ -192,14 +192,14 @@ be stored in libdhcp++. Such option formats are stored in the
 of options which may be encapsulated by other options up to the any level of
 encapsulation but these functions are unaware of the option formats defined
 in the @ref isc::dhcp::CfgMgr because they belong to a different library.
-Therefore, the generic functions @ref isc::dhcp::LibDHCP::unpackOptions4 and
-@ref isc::dhcp::LibDHCP::unpackOptions6 are only useful to parse standard
+Therefore, the generic function @ref isc::dhcp::LibDHCP::unpackOptions is only
+useful to parse standard
 options whose definitions are provided in the libdhcp++. In order to overcome
 this problem a callback mechanism has been implemented in @c Option and @c Pkt6
 classes. By installing a callback function on the instance of the @c Pkt6 the
 server may provide a custom implementation of the options parsing algorithm.
-This callback function will take precedence over the @c LibDHCP::unpackOptions6
-and @c LibDHCP::unpackOptions4 functions. With this approach, the callback is
+This callback function will take precedence over the @c LibDHCP::unpackOptions
+ functions. With this approach, the callback is
 implemented within the context of the server and it has access to all objects
 which define its configuration (including dynamically created option
 definitions).
index e0420526224ff0283e964c742aec93017cd02e07..09c8dbc8acb10463657014d504ba65843afc7211 100644 (file)
@@ -28,7 +28,7 @@ const size_t DUID_TYPE_LEN = 2;
 /// @brief Minimal length of the MAC address.
 const size_t MIN_MAC_LEN = 6;
 
-/// @brief Length of the enterprise if field.
+/// @brief Length of the enterprise ID field.
 const size_t ENTERPRISE_ID_LEN = 4;
 
 /// @brief Default length of the variable length identifier in the DUID-EN.
index 3c71dc348fb1a228e85d3373a81f22db1662ae9a..555f5ff2618d15fd3e462c22f64ab0b7e9dfc926 100644 (file)
@@ -18,7 +18,6 @@
 #include <dhcp/std_option_defs.h>
 #include <dhcp/docsis3_option_defs.h>
 #include <exceptions/exceptions.h>
-#include <util/buffer.h>
 #include <dhcp/option_definition.h>
 
 #include <boost/lexical_cast.hpp>
@@ -133,7 +132,7 @@ LibDHCP::getVendorOptionDefs(const Option::Universe u, const uint32_t vendor_id)
             return getOptionDefs( Option::V6, ISC_V6_OPTION_SPACE );
         }
     }
-    return OptionDefContainerPtr();
+    isc_throw(isc::BadValue, "invalid universe " << u );
 }
 
 OptionDefinitionPtr
@@ -314,20 +313,21 @@ LibDHCP::optionFactory(Option::Universe u,
     return (it->second(u, type, buf));
 }
 
-
-size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
-                               const std::string& option_space,
-                               isc::dhcp::OptionCollection& options,
-                               size_t* relay_msg_offset /* = 0 */,
-                               size_t* relay_msg_len /* = 0 */) {
-    size_t offset = 0;
-    size_t length = buf.size();
-    size_t last_offset = 0;
+size_t LibDHCP::unpackOptions(const Option::Universe& universe,
+                              const OptionBuffer& buf,
+                              const std::string& option_space,
+                              isc::dhcp::OptionCollection& options,
+                              size_t* relay_msg_offset /* = NULL */,
+                              size_t* relay_msg_len /* = NULL */) {
+    // Sanity check
+    if (universe != Option::V4 || universe != Option::V6) {
+        return 0U; // no options parsed
+    }
 
     // Get the list of standard option definitions.
     OptionDefContainer option_defs;
     const OptionDefContainerPtr option_defs_ptr
-        = LibDHCP::getOptionDefs(Option::V6, option_space);
+        = LibDHCP::getOptionDefs(universe, option_space);
     OptionDefContainer runtime_option_defs;
     const OptionDefContainerPtr runtime_option_defs_ptr
         = LibDHCP::getRuntimeOptionDefs(option_space);
@@ -344,61 +344,81 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
     const OptionDefContainerTypeIndex& runtime_idx = runtime_option_defs.get<1>();
 
     // The buffer being read comprises a set of options, each starting with
-    // a two-byte type code and a two-byte length field.
-    while (offset < length) {
-        // Save the current offset for backtracking
-        last_offset = offset;
-
-        // Check if there is room for another option
-        if (offset + 4 > length) {
-            // Still something but smaller than an option
-            return (last_offset);
-        }
-
+    // a type code and a length field, each having one byte (DHCPv4) or two
+    // bytes (DHCPv6).
+    size_t offset = 0U;
+    size_t element_size = universe == Option::V4 ? sizeof(uint8_t) : sizeof(uint16_t);
+    size_t step = 2U * element_size;
+    size_t length = buf.size();
+    uint16_t opt_type = 0U;
+    uint16_t opt_len = 0U;
+    for (offset = 0U; offset < length; offset += step + opt_len) {
         // Parse the option header
-        uint16_t opt_type = isc::util::readUint16(&buf[offset], 2);
-        offset += 2;
-
-        uint16_t opt_len = isc::util::readUint16(&buf[offset], 2);
-        offset += 2;
-
-        if (offset + opt_len > length) {
-            // We peeked at the option header of the next option, but
-            // discovered that it would end up beyond buffer end, so
-            // the option is truncated. Hence we can't parse
-            // it. Therefore we revert back by those bytes (as if
-            // we never parsed them).
-            //
-            // @note it is the responsibility of the caller to throw
-            // an exception on partial parsing
-            return (last_offset);
-        }
-
-        if (opt_type == D6O_RELAY_MSG && relay_msg_offset && relay_msg_len) {
-            // remember offset of the beginning of the relay-msg option
-            *relay_msg_offset = offset;
-            *relay_msg_len = opt_len;
-
-            // do not create that relay-msg option
-            offset += opt_len;
-            continue;
-        }
-
-        if (opt_type == D6O_VENDOR_OPTS) {
-            if (offset + 4 > length) {
-                // Truncated vendor-option. We expect at least
-                // 4 bytes for the enterprise-id field. Let's roll back
-                // option code + option length (4 bytes) and return.
-                return (last_offset);
+        if (universe == Option::V4) {
+            opt_type = buf[offset];
+            if (opt_type == DHO_PAD) {
+                // DHO_PAD is just a padding after DHO_END. Let's continue
+                // parsing in case we receive a message without DHO_END.
+                continue;
+            } else if (opt_type == DHO_END) {
+                // Just return. Don't need to add DHO_END option.
+                break;
+            } else if (offset + element_size > length) {
+                // We peeked at the option header of the next option, but
+                // discovered that it would end up beyond buffer end, so
+                // the option is truncated. Hence we can't parse
+                // it. Therefore we revert back (as if we never parsed it).
+                //
+                // @note it is the responsibility of the caller to throw
+                // an exception on partial parsing
+                return offset;
             }
+            opt_len = buf[offset + element_size];
+        } else if (universe == Option::V6) {
+            if (offset + 2 * element_size > length) {
+                // We peeked at the option header of the next option, but
+                // discovered that it would end up beyond buffer end, so
+                // the option is truncated. Hence we can't parse
+                // it. Therefore we revert back (as if we never parsed it).
+                //
+                // @note it is the responsibility of the caller to throw
+                // an exception on partial parsing
+                return offset;
+            }
+            opt_type = isc::util::readUint16(&buf[offset], element_size);
+            opt_len = isc::util::readUint16(&buf[offset + element_size], element_size);
+            if (offset + 2 * element_size + opt_len > length) {
+                // We peeked at the option header of the next option, but
+                // discovered that it would end up beyond buffer end, so
+                // the option is truncated. Hence we can't parse
+                // it. Therefore we revert back (as if we never parsed it).
+                //
+                // @note it is the responsibility of the caller to throw
+                // an exception on partial parsing
+                return offset;
+            }
+            if (opt_type == D6O_RELAY_MSG && relay_msg_offset && relay_msg_len) {
+                // remember offset of the beginning of the relay-msg option
+                *relay_msg_offset = offset + 2 * element_size;
+                *relay_msg_len = opt_len;
 
-            // Parse this as vendor option
-            OptionPtr vendor_opt(new OptionVendor(Option::V6, buf.begin() + offset,
-                                                  buf.begin() + offset + opt_len));
-            options.insert(std::make_pair(opt_type, vendor_opt));
+                // do not create that relay-msg option
+                continue;
+            }
+            if (opt_type == D6O_VENDOR_OPTS) {
+                if (offset + 2 * element_size + 4 > length) {
+                    // Truncated vendor-option. We expect at least
+                    // 4 bytes for the enterprise-id field. Let's roll back
+                    // option code + option length (4 bytes) and return.
+                    return offset;
+                }
 
-            offset += opt_len;
-            continue;
+                // Parse this as vendor option
+                OptionBufferConstIter begin = buf.begin() + offset;
+                OptionPtr vendor_opt(new OptionVendor(Option::V6, begin, begin + opt_len));
+                options.insert(std::make_pair(opt_type, vendor_opt));
+                continue;
+            }
         }
 
         // Get all definitions with the particular option code. Note
@@ -408,8 +428,9 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
         // we report an error.
         OptionDefContainerTypeRange range;
         // Number of option definitions returned.
-        size_t num_defs = 0;
-        if (option_space == DHCP6_OPTION_SPACE) {
+        size_t num_defs = 0U;
+        if (option_space == DHCP4_OPTION_SPACE ||
+                option_space == DHCP6_OPTION_SPACE) {
             range = idx.equal_range(opt_type);
             num_defs = distance(range.first, range.second);
         }
@@ -417,13 +438,14 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
         // Standard option definitions do not include the definition for
         // our option or we're searching for non-standard option. Try to
         // find the definition among runtime option definitions.
-        if (num_defs == 0) {
+        if (num_defs == 0U) {
             range = runtime_idx.equal_range(opt_type);
             num_defs = distance(range.first, range.second);
         }
 
         OptionPtr opt;
-        if (num_defs > 1) {
+        OptionBufferConstIter begin = buf.begin() + offset;
+        if (num_defs > 1U) {
             // Multiple options of the same code are not supported right now!
             isc_throw(isc::Unexpected, "Internal error: multiple option"
                       " definitions for option type " << opt_type <<
@@ -431,151 +453,25 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
                       " multiple option definitions for the same option code."
                       " This will be supported once support for option spaces"
                       " is implemented");
-        } else if (num_defs == 0) {
+        } else if (num_defs == 0U) {
             // @todo Don't crash if definition does not exist because
             // only a few option definitions are initialized right
             // now. In the future we will initialize definitions for
             // all options and we will remove this elseif. For now,
             // return generic option.
-            opt = OptionPtr(new Option(Option::V6, opt_type,
-                                       buf.begin() + offset,
-                                       buf.begin() + offset + opt_len));
-        } else {
-            // The option definition has been found. Use it to create
-            // the option instance from the provided buffer chunk.
-            const OptionDefinitionPtr& def = *(range.first);
-            assert(def);
-            opt = def->optionFactory(Option::V6, opt_type,
-                                     buf.begin() + offset,
-                                     buf.begin() + offset + opt_len);
-        }
-        // add option to options
-        options.insert(std::make_pair(opt_type, opt));
-        offset += opt_len;
-    }
-
-    last_offset = offset;
-    return (last_offset);
-}
-
-size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
-                               const std::string& option_space,
-                               isc::dhcp::OptionCollection& options) {
-    size_t offset = 0;
-    size_t last_offset = 0;
-
-    // Get the list of standard option definitions.
-    OptionDefContainer option_defs;
-    const OptionDefContainerPtr option_defs_ptr
-        = LibDHCP::getOptionDefs(Option::V4, option_space);
-    OptionDefContainer runtime_option_defs;
-    const OptionDefContainerPtr runtime_option_defs_ptr
-        = LibDHCP::getRuntimeOptionDefs(option_space);
-    if (option_defs_ptr) {
-        option_defs = *option_defs_ptr;
-    }
-    if (runtime_option_defs_ptr) {
-        runtime_option_defs = *runtime_option_defs_ptr;
-    }
-
-    // Get the search indexes #1. It allows to search for option definitions
-    // using option code.
-    const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
-    const OptionDefContainerTypeIndex& runtime_idx = runtime_option_defs.get<1>();
-
-    // The buffer being read comprises a set of options, each starting with
-    // a one-byte type code and a one-byte length field.
-    while (offset < buf.size()) {
-        // Save the current offset for backtracking
-        last_offset = offset;
-
-        // Get the option type
-        uint8_t opt_type = buf[offset++];
-
-        // DHO_END is a special, one octet long option
-        if (opt_type == DHO_END) {
-            // just return. Don't need to add DHO_END option
-            // Don't return offset because it makes this condition
-            // and partial parsing impossible to recognize.
-            return (last_offset);
-        }
-
-        // DHO_PAD is just a padding after DHO_END. Let's continue parsing
-        // in case we receive a message without DHO_END.
-        if (opt_type == DHO_PAD)
-            continue;
-
-        if (offset + 1 > buf.size()) {
-            // We peeked at the option header of the next option, but
-            // discovered that it would end up beyond buffer end, so
-            // the option is truncated. Hence we can't parse
-            // it. Therefore we revert back (as if we never parsed it).
-            //
-            // @note it is the responsibility of the caller to throw
-            // an exception on partial parsing
-            return (last_offset);
-        }
-
-        uint8_t opt_len =  buf[offset++];
-        if (offset + opt_len > buf.size()) {
-            // We peeked at the option header of the next option, but
-            // discovered that it would end up beyond buffer end, so
-            // the option is truncated. Hence we can't parse
-            // it. Therefore we revert back (as if we never parsed it).
-            return (last_offset);
-        }
-
-        // Get all definitions with the particular option code. Note
-        // that option code is non-unique within this container
-        // however at this point we expect to get one option
-        // definition with the particular code. If more are returned
-        // we report an error.
-        OptionDefContainerTypeRange range;
-        // Number of option definitions returned.
-        size_t num_defs = 0;
-        if (option_space == DHCP4_OPTION_SPACE) {
-            range = idx.equal_range(opt_type);
-            num_defs = distance(range.first, range.second);
-        }
-
-        // Standard option definitions do not include the definition for
-        // our option or we're searching for non-standard option. Try to
-        // find the definition among runtime option definitions.
-        if (num_defs == 0) {
-            range = runtime_idx.equal_range(opt_type);
-            num_defs = distance(range.first, range.second);
-        }
-
-        OptionPtr opt;
-        if (num_defs > 1) {
-            // Multiple options of the same code are not supported right now!
-            isc_throw(isc::Unexpected, "Internal error: multiple option"
-                      " definitions for option type " <<
-                      static_cast<int>(opt_type) <<
-                      " returned. Currently it is not supported to initialize"
-                      " multiple option definitions for the same option code."
-                      " This will be supported once support for option spaces"
-                      " is implemented");
-        } else if (num_defs == 0) {
-            opt = OptionPtr(new Option(Option::V4, opt_type,
-                                       buf.begin() + offset,
-                                       buf.begin() + offset + opt_len));
-            opt->setEncapsulatedSpace(DHCP4_OPTION_SPACE);
+            opt = OptionPtr(new Option(universe, opt_type, begin, begin + opt_len));
+            // opt->setEncapsulatedSpace(DHCP4_OPTION_SPACE);
         } else {
             // The option definition has been found. Use it to create
             // the option instance from the provided buffer chunk.
             const OptionDefinitionPtr& def = *(range.first);
             assert(def);
-            opt = def->optionFactory(Option::V4, opt_type,
-                                     buf.begin() + offset,
-                                     buf.begin() + offset + opt_len);
+            opt = def->optionFactory(universe, opt_type, begin, begin + opt_len);
         }
 
         options.insert(std::make_pair(opt_type, opt));
-        offset += opt_len;
     }
-    last_offset = offset;
-    return (last_offset);
+    return offset;
 }
 
 size_t LibDHCP::unpackVendorOptions6(const uint32_t vendor_id,
@@ -611,13 +507,14 @@ size_t LibDHCP::unpackVendorOptions6(const uint32_t vendor_id,
         offset += 2;
 
         if (offset + opt_len > length) {
-            isc_throw(OutOfRange, "Vendor option parse failed. Tried to parse "
-                          << offset + opt_len << " bytes from " << length
-                          << "-byte long buffer.");
+            isc_throw(OutOfRange, "Vendor option parse failed. Tried to parse " <<
+                          offset + opt_len << " bytes from " << length <<
+                          "-byte long buffer.");
         }
 
         OptionPtr opt;
         opt.reset();
+        OptionBufferConstIter begin = buf.begin() + offset;
 
         // If there is a definition for such a vendor option...
         if (idx) {
@@ -646,9 +543,7 @@ size_t LibDHCP::unpackVendorOptions6(const uint32_t vendor_id,
                 // the option instance from the provided buffer chunk.
                 const OptionDefinitionPtr& def = *(range.first);
                 assert(def);
-                opt = def->optionFactory(Option::V6, opt_type,
-                                         buf.begin() + offset,
-                                         buf.begin() + offset + opt_len);
+                opt = def->optionFactory(Option::V6, opt_type, begin, begin + opt_len);
             }
         }
 
@@ -658,9 +553,7 @@ size_t LibDHCP::unpackVendorOptions6(const uint32_t vendor_id,
         //    not defined
 
         if (!opt) {
-            opt = OptionPtr(new Option(Option::V6, opt_type,
-                                       buf.begin() + offset,
-                                       buf.begin() + offset + opt_len));
+            opt = OptionPtr(new Option(Option::V6, opt_type, begin, begin + opt_len));
         }
 
         // add option to options
@@ -676,6 +569,7 @@ size_t LibDHCP::unpackVendorOptions6(const uint32_t vendor_id,
 size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffer& buf,
                                      isc::dhcp::OptionCollection& options) {
     size_t offset = 0;
+    size_t length = buf.size();
 
     // Get the list of stdandard option definitions.
     const OptionDefContainerPtr option_defs_ptr =
@@ -689,13 +583,13 @@ size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffe
 
     // The buffer being read comprises a set of options, each starting with
     // a one-byte type code and a one-byte length field.
-    while (offset < buf.size()) {
+    while (offset < length) {
         // Note that Vendor-Specific info option (RFC3925) has a
         // different option format than Vendor-Spec info for
         // DHCPv6. (there's additional layer of data-length)
         uint8_t data_len = buf[offset++];
 
-        if (offset + data_len > buf.size()) {
+        if (offset + data_len > length) {
             // The option is truncated.
             isc_throw(OutOfRange, "Attempt to parse truncated vendor option");
         }
@@ -719,13 +613,14 @@ size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffe
 
             uint8_t opt_len =  buf[offset++];
             if (offset + opt_len > offset_end) {
-                isc_throw(OutOfRange, "Option parse failed. Tried to parse "
-                          << offset + opt_len << " bytes from " << buf.size()
-                          << "-byte long buffer.");
+                isc_throw(OutOfRange, "Option parse failed. Tried to parse " <<
+                          offset + opt_len << " bytes from " << length <<
+                          "-byte long buffer.");
             }
 
             OptionPtr opt;
             opt.reset();
+            OptionBufferConstIter begin = buf.begin() + offset;
 
             if (idx) {
                 // Get all definitions with the particular option
@@ -754,16 +649,12 @@ size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffe
                     // the option instance from the provided buffer chunk.
                     const OptionDefinitionPtr& def = *(range.first);
                     assert(def);
-                    opt = def->optionFactory(Option::V4, opt_type,
-                                             buf.begin() + offset,
-                                             buf.begin() + offset + opt_len);
+                    opt = def->optionFactory(Option::V4, opt_type, begin, begin + opt_len);
                 }
             }
 
             if (!opt) {
-                opt = OptionPtr(new Option(Option::V4, opt_type,
-                                           buf.begin() + offset,
-                                           buf.begin() + offset + opt_len));
+                opt = OptionPtr(new Option(Option::V4, opt_type, begin, begin + opt_len));
             }
 
             options.insert(std::make_pair(opt_type, opt));
index d85b846e02307f19b850e91e9bacc4161dcc0fb4..6b606f6cddafe10c3b7815ca4e92a458798b2415 100644 (file)
@@ -200,7 +200,7 @@ public:
     static void packOptions6(isc::util::OutputBuffer& buf,
                              const isc::dhcp::OptionCollection& options);
 
-    /// @brief Parses provided buffer as DHCPv6 options and creates
+    /// @brief Parses provided buffer as DHCPV4 or DHCPv6 options and creates
     /// Option objects.
     ///
     /// Parses provided buffer and stores created Option objects in
@@ -229,30 +229,12 @@ public:
     /// than once, and it calls option building routines which can throw.
     /// Partial parsing does not throw: it is the responsibility of the
     /// caller to handle this condition.
-    static size_t unpackOptions6(const OptionBuffer& buf,
-                                 const std::string& option_space,
-                                 isc::dhcp::OptionCollection& options,
-                                 size_t* relay_msg_offset = 0,
-                                 size_t* relay_msg_len = 0);
-
-    /// @brief Parses provided buffer as DHCPv4 options and creates
-    /// Option objects.
-    ///
-    /// Parses provided buffer and stores created Option objects
-    /// in options container.
-    ///
-    /// @param buf Buffer to be parsed.
-    /// @param option_space A name of the option space which holds definitions
-    ///        to be used to parse options in the packets.
-    /// @param options Reference to option container. Options will be
-    ///        put here.
-    /// @return offset to the first byte after the last successfully
-    /// parsed option or the offset of the DHO_END option type.
-    ///
-    /// The unpackOptions6 note applies too.
-    static size_t unpackOptions4(const OptionBuffer& buf,
-                                 const std::string& option_space,
-                                 isc::dhcp::OptionCollection& options);
+    static size_t unpackOptions(const Option::Universe& universe,
+                                const OptionBuffer& buf,
+                                const std::string& option_space,
+                                isc::dhcp::OptionCollection& options,
+                                size_t* relay_msg_offset = NULL,
+                                size_t* relay_msg_len = NULL);
 
     /// Registers factory method that produces options of specific option types.
     ///
index b3f87869808d6e238899fe877156bd15d709a95d..f3041872f14bfda9a2b23d2ff1c8f18b691c9261 100644 (file)
@@ -131,10 +131,10 @@ void
 Option::unpackOptions(const OptionBuffer& buf) {
     switch (universe_) {
     case V4:
-        LibDHCP::unpackOptions4(buf, getEncapsulatedSpace(), options_);
+        LibDHCP::unpackOptions(Option::V4, buf, getEncapsulatedSpace(), options_);
         return;
     case V6:
-        LibDHCP::unpackOptions6(buf, getEncapsulatedSpace(), options_);
+        LibDHCP::unpackOptions(Option::V6, buf, getEncapsulatedSpace(), options_);
         return;
     default:
         isc_throw(isc::BadValue, "Invalid universe type " << universe_);
index 62779bf9e423e3947c96ef9001d3be7c2edf94e9..5805f677fd9ad4d8fecb3326aae2cf42b259d641 100644 (file)
@@ -1,27 +1,17 @@
-// Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
 //
 // Author: Cristian Secareanu <cristian.secareanu@qualitance.com>
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//           http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// 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 <config.h>
 
 #include <asiolink/io_address.h>
 #include <dhcp/dhcp6.h>
-#include <dhcp/libdhcp++.h>
 #include <dhcp/option6_pdexclude.h>
 #include <exceptions/exceptions.h>
-#include <util/io_utilities.h>
 
 #include <boost/dynamic_bitset.hpp>
 
@@ -39,44 +29,39 @@ using namespace isc::util;
 namespace isc {
 namespace dhcp {
 
-Option6PDExclude::Option6PDExclude(const isc::asiolink::IOAddress& addr,
-                                   uint8_t prefix_len,
-                                   const isc::asiolink::IOAddress& prefix_excluded,
-                                   uint8_t prefix_excluded_len
-                                   )
-    :Option(V6, D6O_PD_EXCLUDE)
-    ,addr_(addr), prefix_len_(prefix_len)
-    ,prefix_excluded_(prefix_excluded)
-    ,prefix_excluded_len_(prefix_excluded_len) {
-
+Option6PDExclude::Option6PDExclude(
+        const isc::asiolink::IOAddress& delegated_address,
+        uint8_t delegated_prefix_length,
+        const isc::asiolink::IOAddress& excluded_address,
+        uint8_t excluded_prefix_length) :
+    Option(V6, D6O_PD_EXCLUDE), delegated_address_(delegated_address),
+    delegated_prefix_length_(delegated_prefix_length),
+    excluded_address_(excluded_address),
+    excluded_prefix_length_(excluded_prefix_length) {
 }
 
-void
-Option6PDExclude::pack(isc::util::OutputBuffer& buf) {
+void Option6PDExclude::pack(isc::util::OutputBuffer& buf) {
     // Header = option code and length.
     packHeader(buf);
 
-    uint8_t excludedPrefLenBytes = excludedPrefixLenBytes();
+    buf.writeData(&excluded_prefix_length_, sizeof(excluded_prefix_length_));
 
-    buf.writeData(&prefix_excluded_len_, sizeof(prefix_excluded_len_));
+    std::vector<uint8_t> excluded_address_bytes = excluded_address_.toBytes();
+    boost::dynamic_bitset<uint8_t> bits(excluded_address_bytes.rbegin(), excluded_address_bytes.rend());
+    bits = bits << delegated_prefix_length_;
 
-    std::vector<uint8_t> addrV6 = prefix_excluded_.toBytes();
-    boost::dynamic_bitset<uint8_t> bits(addrV6.rbegin(), addrV6.rend());
-    bits = bits << prefix_len_;
-
-    for (int i = 0; i < excludedPrefLenBytes; i++) {
+    uint8_t subtractedPrefixesOctetLength = getSubtractedPrefixesOctetLength();
+    for (uint8_t i = 0U; i < subtractedPrefixesOctetLength; i++) {
         boost::dynamic_bitset<uint8_t> tmp = bits >> 120;
 
         uint8_t val = static_cast<uint8_t>(tmp.to_ulong());
 
-        //Zero padded bits follow when prefix_excluded_len_ is not divided exactly by 8
-        if (i == excludedPrefLenBytes - 1) {
-            uint8_t excluded_prefix_bits_no = prefix_excluded_len_ - prefix_len_;
-
-            uint8_t unusedBits = 0xFF;
-            unusedBits <<= (8 - (excluded_prefix_bits_no % 8)) % 8;
-
-            val = val & unusedBits;
+        //Zero padded bits follow when excluded_prefix_length_ is not divided exactly by 8
+        if (i == subtractedPrefixesOctetLength - 1U) {
+            uint8_t subtractedPrefixesBitLength = excluded_prefix_length_ -
+                    delegated_prefix_length_;
+            uint8_t zeroPaddingBitLength = (8 - (subtractedPrefixesBitLength % 8)) % 8;
+            val <<= zeroPaddingBitLength;
         }
         bits = bits << 8;
         buf.writeData(&val, sizeof(val));
@@ -84,28 +69,26 @@ Option6PDExclude::pack(isc::util::OutputBuffer& buf) {
 }
 
 void Option6PDExclude::unpack(OptionBufferConstIter begin,
-                              OptionBufferConstIter end) {
-    prefix_len_ = 0;
-    prefix_excluded_len_ = *begin;
+        OptionBufferConstIter end) {
+    delegated_prefix_length_ = 0;
+    excluded_prefix_length_ = *begin;
     begin += sizeof(uint8_t);
-    addr_ = IOAddress::IPV6_ZERO_ADDRESS();
-    prefix_excluded_ = IOAddress::IPV6_ZERO_ADDRESS();
+    delegated_address_ = IOAddress::IPV6_ZERO_ADDRESS();
+    excluded_address_ = IOAddress::IPV6_ZERO_ADDRESS();
     begin = end;
 }
 
-uint16_t
-Option6PDExclude::len()
-{
-    return getHeaderLen() + sizeof (prefix_excluded_len_) +
-            excludedPrefixLenBytes();
+uint16_t Option6PDExclude::len() {
+    return getHeaderLen() + sizeof(excluded_prefix_length_)
+            + getSubtractedPrefixesOctetLength();
 }
 
-uint8_t
-Option6PDExclude::excludedPrefixLenBytes()
-{
-    uint8_t excludedPrefLenBits = prefix_excluded_len_ - prefix_len_ - 1;
-    uint8_t excludedPrefLenBytes = (excludedPrefLenBits / 8) + 1;
-    return excludedPrefLenBytes;
+uint8_t Option6PDExclude::getSubtractedPrefixesOctetLength() {
+    // Promote what is less than 8 bits to 1 octet.
+    uint8_t subtractedPrefixesBitLength = excluded_prefix_length_
+            - delegated_prefix_length_ - 1;
+    uint8_t subtractedPrefixesOctetLength = (subtractedPrefixesBitLength / 8) + 1;
+    return subtractedPrefixesOctetLength;
 }
 
 } // end of namespace isc::dhcp
index fcb65d7a6c9ece844602ab57e2a54d891f32ecca..b736da3cd8985e4f25cd8eccefcce81c8749cc1d 100644 (file)
@@ -1,22 +1,16 @@
-// Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
 //
 // Author: Cristian Secareanu <cristian.secareanu@qualitance.com>
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//           http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// 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/.
 
 #ifndef OPTION6_PDEXCLUDE_H
 #define OPTION6_PDEXCLUDE_H
 
+#include <dhcp/option.h>
+
 #include <asiolink/io_address.h>
 #include <boost/shared_ptr.hpp>
 
@@ -31,10 +25,10 @@ class Option6PDExclude: public Option {
 
 public:
 
-    Option6PDExclude(const isc::asiolink::IOAddress& addr,
-                     uint8_t prefix_len,
-                     const isc::asiolink::IOAddress& prefix_excluded,
-                     uint8_t prefix_excluded_len);
+    Option6PDExclude(const isc::asiolink::IOAddress& delegated_address,
+            uint8_t delegated_prefix_length,
+            const isc::asiolink::IOAddress& excluded_address,
+            uint8_t excluded_prefix_length);
 
     /// @brief Writes option in wire-format to a buffer.
     ///
@@ -51,24 +45,61 @@ public:
     ///
     /// @param begin iterator to first byte of option data
     /// @param end iterator to end of option data (first byte after option end)
-    virtual void unpack(OptionBufferConstIter begin,
-                        OptionBufferConstIter end);
+    virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end);
 
-    /// Returns length of the complete option (data length + DHCPv6
+    /// @brief Returns length of the complete option (data length + DHCPv6
     /// option header)
     ///
     /// @return length of the option
     virtual uint16_t len();
 
-protected:
-    uint8_t excludedPrefixLenBytes();
+    /// @brief Returns the address of the delegated address space.
+    ///
+    /// @return address of delegated address space
+    isc::asiolink::IOAddress getDelegatedAddress() const {
+        return delegated_address_;
+    }
+
+    /// @brief Returns the prefix length of the delegated address space.
+    ///
+    /// @return prefix length of delegated address space
+    uint8_t getDelegatedPrefixLength() const {
+        return delegated_prefix_length_;
+    }
+
+    /// @brief Returns the address of the excluded address space.
+    ///
+    /// @return address of excluded address space
+    isc::asiolink::IOAddress getExcludedAddress() const {
+        return excluded_address_;
+    }
+
+    /// @brief Returns the prefix length of the excluded address space.
+    ///
+    /// @return prefix length of excluded address space
+    uint8_t getExcludedPrefixLength() const {
+        return excluded_prefix_length_;
+    }
 
 protected:
-    isc::asiolink::IOAddress addr_;
-    uint8_t prefix_len_;
+    /// @brief Returns the prefix length of the excluded prefix.
+    ///
+    /// @return prefix length of excluded prefix
+    uint8_t getSubtractedPrefixesOctetLength();
+
+    /// @brief The address and prefix length identifying the delegated IPV6
+    /// prefix.
+    /// {
+    isc::asiolink::IOAddress delegated_address_;
+    uint8_t delegated_prefix_length_;
+    /// }
 
-    isc::asiolink::IOAddress prefix_excluded_;
-    uint8_t prefix_excluded_len_;
+    /// @brief The address and prefix length identifying the excluded IPV6
+    /// prefix.
+    /// {
+    isc::asiolink::IOAddress excluded_address_;
+    uint8_t excluded_prefix_length_;
+    /// }
 };
 
 /// @brief Pointer to the @c Option6PDExclude object.
index e6c79075ab68cb0f9ebf94139edd957449a4c579..a1c46c78086571ae9b703d43c043169e234908b4 100644 (file)
@@ -203,7 +203,8 @@ Pkt4::unpack() {
     // a vector as an input.
     buffer_in.readVector(opts_buffer, opts_len);
 
-    size_t offset = LibDHCP::unpackOptions4(opts_buffer, DHCP4_OPTION_SPACE, options_);
+    size_t offset = LibDHCP::unpackOptions(Option::V4, opts_buffer,
+        DHCP4_OPTION_SPACE, options_);
 
     // If offset is not equal to the size and there is no DHO_END,
     // then something is wrong here. We either parsed past input
index 6c1f5c8dcc8280fa945b5188a9798a589901c7fe..dfeddc3bb5c22a54dd034bdcdc2c8739bf0669d1 100644 (file)
@@ -336,7 +336,8 @@ Pkt6::unpackMsg(OptionBuffer::const_iterator begin,
 
     // If custom option parsing function has been set, use this function
     // to parse options. Otherwise, use standard function from libdhcp.
-    size_t offset = LibDHCP::unpackOptions6(opt_buffer, DHCP6_OPTION_SPACE, options_);
+    size_t offset = LibDHCP::unpackOptions(Option::V6, opt_buffer,
+                                           DHCP6_OPTION_SPACE, options_);
 
     // If offset is not equal to the size, then something is wrong here. We
     // either parsed past input buffer (bug in our code) or we haven't parsed
@@ -386,8 +387,8 @@ Pkt6::unpackRelayMsg() {
 
         // If custom option parsing function has been set, use this function
         // to parse options. Otherwise, use standard function from libdhcp.
-        LibDHCP::unpackOptions6(opt_buffer, DHCP6_OPTION_SPACE, relay.options_,
-                                &relay_msg_offset, &relay_msg_len);
+        LibDHCP::unpackOptions(Option::V6, opt_buffer, DHCP6_OPTION_SPACE,
+                               relay.options_, &relay_msg_offset, &relay_msg_len);
 
         /// @todo: check that each option appears at most once
         //relay.interface_id_ = options->getOption(D6O_INTERFACE_ID);
index 05928bd7e61212fd2d6d976ed53378de2a1730eb..9abd0f31f6e8654d698ebd2f88bed6ad4a0c48a1 100644 (file)
@@ -60,6 +60,7 @@ libdhcp___unittests_SOURCES += option6_client_fqdn_unittest.cc
 libdhcp___unittests_SOURCES += option6_ia_unittest.cc
 libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc
 libdhcp___unittests_SOURCES += option6_iaprefix_unittest.cc
+libdhcp___unittests_SOURCES += option6_pdexclude_unittest.cc
 libdhcp___unittests_SOURCES += option6_status_code_unittest.cc
 libdhcp___unittests_SOURCES += option_int_unittest.cc
 libdhcp___unittests_SOURCES += option_int_array_unittest.cc
index 9bd5d1b8739c498b9b383a6fe588dff5ddcce30e..1dd3d8f93acb17c2a4e50449c94885e75918f4a6 100644 (file)
@@ -419,8 +419,9 @@ TEST_F(LibDhcpTest, unpackOptions6) {
     memcpy(&buf[0], v6packed, sizeof(v6packed));
 
     EXPECT_NO_THROW ({
-            LibDHCP::unpackOptions6(OptionBuffer(buf.begin(), buf.begin() + sizeof(v6packed)),
-                                    DHCP6_OPTION_SPACE, options);
+        OptionBufferIter begin = buf.begin();
+        LibDHCP::unpackOptions(Option::V6, OptionBuffer(begin,
+            begin + sizeof(v6packed)), DHCP6_OPTION_SPACE, options);
     });
 
     EXPECT_EQ(options.size(), 6); // there should be 5 options
@@ -543,8 +544,8 @@ TEST_F(LibDhcpTest, unpackEmptyOption6) {
 
     // Parse options.
     OptionCollection options;
-    ASSERT_NO_THROW(LibDHCP::unpackOptions6(buf, DHCP6_OPTION_SPACE,
-                                            options));
+    ASSERT_NO_THROW(LibDHCP::unpackOptions(Option::V6, buf, DHCP6_OPTION_SPACE,
+                                           options));
 
     // There should be one option.
     ASSERT_EQ(1, options.size());
@@ -600,7 +601,8 @@ TEST_F(LibDhcpTest, unpackSubOptions6) {
 
     // Parse options.
     OptionCollection options;
-    ASSERT_NO_THROW(LibDHCP::unpackOptions6(buf, "space-foobar", options, 0, 0));
+    ASSERT_NO_THROW(LibDHCP::unpackOptions(Option::V6, buf, "space-foobar",
+                                           options, 0, 0));
 
     // There should be one top level option.
     ASSERT_EQ(1, options.size());
@@ -766,7 +768,8 @@ TEST_F(LibDhcpTest, unpackOptions4) {
     isc::dhcp::OptionCollection options; // list of options
 
     ASSERT_NO_THROW(
-        LibDHCP::unpackOptions4(v4packed, DHCP4_OPTION_SPACE, options);
+        LibDHCP::unpackOptions(Option::V4, v4packed, DHCP4_OPTION_SPACE,
+                               options);
     );
 
     isc::dhcp::OptionCollection::const_iterator x = options.find(12);
@@ -857,9 +860,9 @@ TEST_F(LibDhcpTest, unpackOptions4) {
     ASSERT_TRUE(rai);
     // RAI should have 3 sub-options: Circuit ID, Agent Remote ID, Vendor
     // Specific Information option. Note that by parsing these suboptions we
-    // are checking that unpackOptions4 differentiates between standard option
-    // space called "dhcp4" and other option spaces. These sub-options do not
-    // belong to standard option space and should be parsed using different
+    // are checking that unpackOptions differentiates between standard option
+    // space called "dhcp4" or "dhcp6" and other option spaces. These sub-options
+    // do not belong to standard option space and should be parsed using different
     // option definitions.
     // @todo Currently, definitions for option space "dhcp-agent-options-space"
     // are not defined. Therefore all suboptions will be represented here by
@@ -926,8 +929,8 @@ TEST_F(LibDhcpTest, unpackEmptyOption4) {
 
     // Parse options.
     OptionCollection options;
-    ASSERT_NO_THROW(LibDHCP::unpackOptions4(buf, DHCP4_OPTION_SPACE,
-                                            options));
+    ASSERT_NO_THROW(LibDHCP::unpackOptions(Option::V4, buf, DHCP4_OPTION_SPACE,
+                                           options));
 
     // There should be one option.
     ASSERT_EQ(1, options.size());
@@ -986,7 +989,8 @@ TEST_F(LibDhcpTest, unpackSubOptions4) {
 
     // Parse options.
     OptionCollection options;
-    ASSERT_NO_THROW(LibDHCP::unpackOptions4(buf, "space-foobar", options));
+    ASSERT_NO_THROW(LibDHCP::unpackOptions(Option::V4, buf, "space-foobar",
+                                           options));
 
     // There should be one top level option.
     ASSERT_EQ(1, options.size());
@@ -1698,8 +1702,8 @@ TEST_F(LibDhcpTest, vendorClass6) {
     isc::util::encode::decodeHex(vendor_class_hex, bin);
 
     ASSERT_NO_THROW ({
-            LibDHCP::unpackOptions6(bin, DHCP6_OPTION_SPACE, options);
-        });
+        LibDHCP::unpackOptions(Option::V4, bin, DHCP6_OPTION_SPACE, options);
+    });
 
     EXPECT_EQ(options.size(), 1); // There should be 1 option.
 
diff --git a/src/lib/dhcp/tests/option6_pdexclude_unittest.cc b/src/lib/dhcp/tests/option6_pdexclude_unittest.cc
new file mode 100644 (file)
index 0000000..b8464c0
--- /dev/null
@@ -0,0 +1,61 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Author: Andrei Pavel <andrei.pavel@qualitance.com>
+//
+// 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 <config.h>
+
+#include <dhcp/option6_pdexclude.h>
+#include <asiolink/io_address.h>
+#include <dhcpsrv/pool.h>
+#include <gtest/gtest.h>
+
+using namespace isc;
+using namespace isc::dhcp;
+using namespace asiolink;
+
+namespace {
+
+const IOAddress empty("::");
+const IOAddress beef("2001:db8:dead:beef::"); // /48 prefix length
+const IOAddress cafe("2001:db8:dead:cafe::"); // /48 prefix length
+const IOAddress beef01("2001:db8:dead:beef::01"); // /56 prefix length
+
+// Description
+TEST(Option6PDExcludeTest, testName) {
+    Option6PDExclude option = Option6PDExclude(beef, 56, beef01, 60);
+
+    OptionBuffer data(option.getData());
+
+    util::OutputBuffer buf(128);
+    option.pack(buf);
+
+    Option6PDExclude unpackedOption(empty, 0, empty, 0);
+
+    unpackedOption.unpack(data.begin(), data.end());
+
+    EXPECT_EQ(option.getDelegatedAddress(),
+            unpackedOption.getDelegatedAddress());
+    EXPECT_EQ(option.getDelegatedPrefixLength(),
+            unpackedOption.getDelegatedPrefixLength());
+    EXPECT_EQ(option.getExcludedAddress(), unpackedOption.getExcludedAddress());
+    EXPECT_EQ(option.getExcludedPrefixLength(),
+            unpackedOption.getExcludedPrefixLength());
+}
+
+TEST(Option6PDExcludeTest, pool) {
+    //Pool6Ptr pool6Ptr = Pool6Ptr(new Pool6(Lease::TYPE_PD, beef, cafe));
+    //ASSERT_TRUE(pool6Ptr);
+    //ASSERT_GT(pool6Ptr->getPrefixExcludedLength(), 0);
+    /*
+     OptionPtr opt(
+     new Option6PDExclude((*l)->addr_, (*l)->prefixlen_,
+     pool->getPrefixExcluded(),
+     pool->getPrefixExcludedLength()));
+     */
+}
+
+} // anonymous namespace