BIND 10 trac998, updated. ae21ebb0f609f8a2aa8ffc3d4b84c465111ec2c3 [trac998] All checks working

BIND 10 source code commits bind10-changes at lists.isc.org
Mon Jun 13 20:16:57 UTC 2011


The branch, trac998 has been updated
       via  ae21ebb0f609f8a2aa8ffc3d4b84c465111ec2c3 (commit)
       via  7cf66b7e44e389205ae4344764fbf136550854ce (commit)
       via  0c3b69c6e170bee7dd775090af2bdd1cae900080 (commit)
      from  f5edd310465966137f0cd4e2109d90f7e5d5965f (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit ae21ebb0f609f8a2aa8ffc3d4b84c465111ec2c3
Author: Stephen Morris <stephen at isc.org>
Date:   Mon Jun 13 21:16:33 2011 +0100

    [trac998] All checks working

commit 7cf66b7e44e389205ae4344764fbf136550854ce
Author: Stephen Morris <stephen at isc.org>
Date:   Mon Jun 13 20:51:45 2011 +0100

    [trac998] Just the undefined abstract methods to worry about

commit 0c3b69c6e170bee7dd775090af2bdd1cae900080
Author: Stephen Morris <stephen at isc.org>
Date:   Mon Jun 13 17:05:50 2011 +0100

    [trac998] IPv6 checks working

-----------------------------------------------------------------------

Summary of changes:
 src/lib/acl/ip_check.h                 |  380 ++++++++++++++++++++--
 src/lib/acl/tests/ip_check_unittest.cc |  577 ++++++++++++++++++++++++++++----
 2 files changed, 878 insertions(+), 79 deletions(-)

-----------------------------------------------------------------------
diff --git a/src/lib/acl/ip_check.h b/src/lib/acl/ip_check.h
index a598b83..951376f 100644
--- a/src/lib/acl/ip_check.h
+++ b/src/lib/acl/ip_check.h
@@ -15,10 +15,14 @@
 #ifndef __IP_CHECK_H
 #define __IP_CHECK_H
 
-#include <boost/lexical_cast.hpp>
+#include <cassert>
+#include <iterator>
 #include <utility>
 #include <vector>
 
+#include <boost/lexical_cast.hpp>
+#include <boost/scoped_ptr.hpp>
+
 #include <stdint.h>
 #include <arpa/inet.h>
 #include <netinet/in.h>
@@ -41,7 +45,7 @@ namespace acl {
 ///
 /// The function is templated on the data type of the mask.
 ///
-/// \param masksize Size of the mask.  This must be between 1 and 8*sizeof(T).
+/// \param masksize Size of the mask.  This must be between 0 and 8*sizeof(T).
 ///        An out of range exception is thrown if this is not the case.
 ///
 /// \return Value with the most significant "masksize" bits set.
@@ -69,9 +73,16 @@ T createNetmask(size_t masksize) {
         // the expression below will overflow.
 
         return (~((1 << (8 * sizeof(T) - masksize)) - 1));
+
+    } else if (masksize == 0) {
+
+        // Simplifies logic elsewhere we we are allowed to cope with a mask
+        // size of 0.
+
+        return (0);
     }
 
-    isc_throw(isc::OutOfRange, "mask size must be between 1 and " <<
+    isc_throw(isc::OutOfRange, "mask size must be between 0 and " <<
                                8 * sizeof(T));
 }
 
@@ -87,16 +98,16 @@ T createNetmask(size_t masksize) {
 ///
 /// \param addrmask Address and/or address/mask.  The string should be passed
 ///                 without leading or trailing spaces.
-/// \param defmask  Default value of the mask size, used if no mask is given.
-/// \param maxmask  Maximum valid value of the mask size.
+/// \param maxmask  Default value of the mask size, used if no mask is given.
+///                 It is also the maximum permitted value of the mask.
 ///
 /// \return Pair of (string, uint32) holding the address string and the mask
 ///         size value.
 
 std::pair<std::string, uint32_t>
-splitIpAddress(const std::string& addrmask, uint32_t defmask, uint32_t maxmask){
+splitIpAddress(const std::string& addrmask, uint32_t maxmask) {
 
-    uint32_t masksize = defmask;
+    uint32_t masksize = maxmask;
 
     // See if a mask size was given
     std::vector<std::string> components = isc::util::str::tokens(addrmask, "/");
@@ -127,6 +138,29 @@ splitIpAddress(const std::string& addrmask, uint32_t defmask, uint32_t maxmask){
 }
 
 
+/// \brief IP Base
+///
+/// Base class for both the Ipv4 and Ipv4 check classes.  The only thing this
+/// supplies over and above the Check class is a clone() method, to return
+/// a pointer to a newly-allocated copy of itself.
+
+template <typename Context>
+class IpBaseCheck : public Check<Context> {
+public:
+    /// \brief Clone Object
+    ///
+    /// Implemented by derived classes, this causes a class to create a copy
+    /// of itself and return a pointer to the object.
+    ///
+    /// \return Pointer to the cloned object.  It is the caller's responsibility
+    ///         to delete this object.
+    virtual IpBaseCheck* clone() const {
+        return (0);
+    }
+};
+
+
+
 /// \brief IPV4 Check
 ///
 /// This class performs a match between an IPv4 address specified in an ACL
@@ -136,7 +170,7 @@ splitIpAddress(const std::string& addrmask, uint32_t defmask, uint32_t maxmask){
 /// \param Context Structure holding address to be matched.
 
 template <typename Context>
-class Ipv4Check : public Check<Context> {
+class Ipv4Check : public IpBaseCheck<Context> {
 public:
     /// \brief IPV4 Constructor
     ///
@@ -158,8 +192,6 @@ public:
         setNetmask();
     }
 
-
-
     /// \brief String Constructor
     ///
     /// Constructs an IPv4 Check object from a network address and size of mask
@@ -176,7 +208,7 @@ public:
     {
         // Split the address into address part and mask.
         std::pair<std::string, uint32_t> result =
-            splitIpAddress(address, 8 * sizeof(uint32_t), 8 * sizeof(uint32_t));
+            splitIpAddress(address, 8 * sizeof(uint32_t));
 
         // Try to convert the address.
         int status = inet_pton(AF_INET, result.first.c_str(), &address_);
@@ -190,13 +222,12 @@ public:
         setNetmask();
     }
 
-
+    // Default copy constructor and assignment operator are correct for this
+    // class.
 
     /// \brief Destructor
     virtual ~Ipv4Check() {}
 
-
-
     /// \brief The check itself
     ///
     /// Matches the passed argument to the condition stored here.  Different
@@ -204,9 +235,7 @@ public:
     /// link will fail if used for a type for which no match is provided.
     ///
     /// \param context Information to be matched
-    virtual bool matches(const Context& context) const = 0;
-
-
+    virtual bool matches(const Context& context) const;
 
     /// \brief Estimated cost
     ///
@@ -216,8 +245,6 @@ public:
         return (1);             // Single check on a 32-bit word
     }
 
-
-
     ///@{
     /// Access methods - mainly for testing
 
@@ -299,9 +326,322 @@ public:
 
     uint32_t    address_;   ///< IPv4 address
     size_t      masksize_;  ///< Mask size passed to constructor
-    bool        inverse_;   ///< test for equality or inequality
+    bool        inverse_;   ///< Test for equality or inequality
     uint32_t    netmask_;   ///< Network mask applied to match
+};
+
+
+
+// Used for an assertion check
+namespace {
+bool isNonZero(uint8_t i) {
+    return (i != 0);
+}
+} // Anonymous namespace
+
+/// \brief IPV6 Check
+///
+/// This class performs a match between an IPv6 address specified in an ACL
+/// (IP address, network mask and a flag indicating whether the check should
+/// be for a match or for a non-match) and a given IP address.
+///
+/// \param Context Structure holding address to be matched.
+
+template <typename Context>
+class Ipv6Check : public IpBaseCheck<Context> {
+public:
+
+    // Size of array to old IPV6 address.
+    static const size_t IPV6_SIZE = sizeof(struct in6_addr);
+
+    /// \brief Default Constructor
+    Ipv6Check() :
+        address_(IPV6_SIZE, 0), masksize_(0),
+        inverse_(false), netmask_(IPV6_SIZE, 0)
+    {}
+
+    /// \brief IPV6 Constructor
+    ///
+    /// Constructs an IPv6 Check object from a network address given as a
+    /// 16-byte array in network-byte order.
+    ///
+    /// \param address IP address to check for (as an address in network-byte
+    ///        order).
+    /// \param mask The network mask specified as an integer between 1 and
+    ///        128 This determines the number of bits in the mask to check.
+    ///        An exception will be thrown if the number is not within these
+    ///        bounds.
+    /// \param inverse If false (the default), matches() returns true if the
+    ///        condition matches.  If true, matches() returns true if the
+    ///        condition does not match.
+    Ipv6Check(const uint8_t* address, size_t masksize = 8 * IPV6_SIZE,
+              bool inverse = false) :
+        address_(address, address + IPV6_SIZE), masksize_(masksize),
+        inverse_(inverse), netmask_(IPV6_SIZE, 0)
+    {
+        setNetmask();
+    }
+
+    /// \brief String Constructor
+    ///
+    /// Constructs an IPv6 Check object from a network address and size of mask
+    /// given as a string of the form "a.b.c.d/n", where the "/n" part is
+    /// optional.
+    ///
+    /// \param address IP address and netmask in the form "<v6-address>/n"
+    ///        (where the "/n" part is optional).
+    /// \param inverse If false (the default), matches() returns true if the
+    ///        condition matches.  If true, matches() returns true if the
+    ///        condition does not match.
+    Ipv6Check(const std::string& address, bool inverse = false) :
+        address_(IPV6_SIZE, 0), masksize_(8 * IPV6_SIZE),
+        inverse_(inverse), netmask_(IPV6_SIZE, 0)
+    {
+        // Split the address into address part and mask.
+        std::pair<std::string, uint32_t> result =
+            splitIpAddress(address, 8 * IPV6_SIZE);
+
+        // Try to convert the address.
+        int status = inet_pton(AF_INET6, result.first.c_str(), &address_[0]);
+        if (status == 0) {
+            isc_throw(isc::InvalidParameter, "address/masksize of " <<
+                      address << " is not valid IPV6 address");
+        }
+
+        // All done, so finish initialization.
+        masksize_ = result.second;
+        setNetmask();
+    }
+
+    /// \brief Copy constructor
+    Ipv6Check(const Ipv6Check& other) : IpBaseCheck<Context>(other),
+        address_(other.address_.begin(), other.address_.end()),
+        masksize_(other.masksize_), inverse_(other.inverse_),
+        netmask_(other.netmask_.begin(), other.netmask_.end())
+    {}
+
+    /// \brief Assignment operator
+    Ipv6Check& operator=(const Ipv6Check& other) {
+        if (this != &other) {
+            // Copy parent
+            IpBaseCheck<Context>::operator=(other);
+
+            // Copy this object
+            address_.clear();
+            std::copy(other.address_.begin(), other.address_.end(),
+                      std::back_inserter(address_));
+            masksize_ = other.masksize_;
+            inverse_ = other.inverse_;
+            netmask_.clear();
+            std::copy(other.netmask_.begin(), other.netmask_.end(),
+                      std::back_inserter(netmask_));
+        }
+        return (*this);
+    }
+
+    /// \brief Destructor
+    virtual ~Ipv6Check() {}
+
+    /// \brief The check itself
+    ///
+    /// Matches the passed argument to the condition stored here.  Different
+    /// specialisations are provided for different argument types, so the
+    /// link will fail if used for a type for which no match is provided.
+    ///
+    /// \param context Information to be matched
+    virtual bool matches(const Context& context) const;
+
+    /// \brief Estimated cost
+    ///
+    /// Assume that the cost of the match is linear and depends on the number
+    /// of comparison operations.
+    virtual unsigned cost() const {
+        return (IPV6_SIZE);             // Up to 16 checks
+    }
+
+    ///@{
+    /// Access methods - mainly for testing
+
+    /// \return Stored IP address
+    std::vector<uint8_t> getAddress() const {
+        return (address_);
+    }
+
+    /// \return Network mask applied to match
+    std::vector<uint8_t> getNetmask() const {
+        return (netmask_);
+    }
+
+    /// \return Mask size given to constructor
+    size_t getMasksize() const {
+        return (masksize_);
+    }
+
+    /// \return Setting of inverse flag
+    bool getInverse() {
+        return (inverse_);
+    }
+    ///@}
+
+    /// \brief Set Network Mask
+    ///
+    /// Sets up the network mask from the mask size.  This involves setting
+    /// mask in each byte of the network mask.
+    void setNetmask() {
+
+        // Validate that the mask is valid.
+        if ((masksize_ >= 1) && (masksize_ <=  8 * IPV6_SIZE)) {
+
+            // Loop, setting the bits in the set of mask bytes until all the
+            // specified bits have been used up.  The netmask array was
+            // initialized to zero in the constructor.
+            assert(std::find_if(netmask_.begin(), netmask_.end(), isNonZero) ==
+                   netmask_.end());
+
+            size_t bits_left = masksize_;   // Bits remaining to set
+            int i = -1;                     // To allow pre-incrementing
+            while (bits_left > 0) {
+                if (bits_left >= 8) {
+                    netmask_[++i] = ~0;  // All bits set
+                    bits_left -= 8;
+
+                } else if (bits_left > 0) {
+                    netmask_[++i] = createNetmask<uint8_t>(bits_left);
+                    bits_left = 0;
+
+                }
+            }
+        } else {
+            isc_throw(isc::OutOfRange,
+                      "mask size of " << masksize_ << " is invalid " <<
+                      "for the data type which is " << sizeof(uint32_t) <<
+                      " bytes long");
+        }
+    }
+
+    /// \brief Comparison
+    ///
+    /// This is the actual comparison function that checks the IP address passed
+    /// to this class with the matching information in the class itself.  It is
+    /// expected to be called from matches().
+    ///
+    /// \param testaddr Address (in network byte order) to test against the
+    ///                 check condition in the class.  This is expected to
+    ///                 be IPV6_SIZE bytes long.
+    ///
+    /// \return true if the address matches, false if it does not.
+    virtual bool compare(const uint8_t* testaddr) const {
+
+        // To check that the address given matches the stored network address
+        // and netmask, we check the simple condition that:
+        //
+        //     address_given & netmask_ == stored_address & netmask_
+        //
+        // The result is checked for all bytes for which there are bits set in
+        // the network mask.  We stop at the first non-match (or when we run
+        // out of bits in the network mask). (Note that the network mask
+        // represents a contiguous set of bits.  As such, as soon as we find
+        // a netmask byte of zeroes, we have run part the part of the address
+        // where we need to match.
+
+        bool match = true;
+        for (int i = 0; match && (i < IPV6_SIZE) && (netmask_[i] != 0); ++i){
+             match = ((testaddr[i] & netmask_[i]) ==
+                      (address_[i] & netmask_[i]));
+        }
+
+        // As with the V4 check, return the XOR with the inverse flag.
+        return (match != inverse_);
+    }
+
+    // Member variables
+
+    std::vector<uint8_t>    address_;   ///< IPv6 address
+    size_t                  masksize_;  ///< Mask size passed to constructor
+    bool                    inverse_;   ///< Test for equality or inequality
+    std::vector<uint8_t>    netmask_;   ///< Network mask applied to match
+
+};
+
+
+/// \brief Generic IP Address Check Object
+///
+/// This class performs a match between an IP address specified in an ACL
+/// (IP address (either V4 of V6), network mask and a flag indicating whether
+/// the check should be for a match or for a non-match) and a given IP address.
+///
+/// \param Context Structure holding address to be matched.
+
+template <typename Context>
+class IpCheck : public IpBaseCheck<Context> {
+public:
+
+    /// \brief String Constructor
+    ///
+    /// Constructs an IP Check object from a network address and size of mask
+    /// given as a string of the form "<address>/n", where the "/n" part is
+    /// optional.
+    ///
+    /// \param address IP address and netmask in the form "<address>/n"
+    ///        (where the "/n" part is optional).
+    /// \param inverse If false (the default), matches() returns true if the
+    ///        condition matches.  If true, matches() returns true if the
+    ///        condition does not match.
+    IpCheck(const std::string& address, bool inverse = false) {
+        try {
+            check_.reset(new Ipv4Check<Context>(address, inverse));
+            isv4_ = true;
+        } catch (isc::Exception&) {
+            check_.reset(new Ipv6Check<Context>(address, inverse));
+            isv4_ = false;
+        }
+    }
+
+    /// \brief Copy constructor
+    IpCheck(const IpCheck<Context>& other) : IpBaseCheck<Context>(other),
+                                             check_(other.check_->clone())
+    {}
+
+    /// \brief Assignment operator
+    IpCheck& operator=(const IpCheck& other) {
+        if (this != &other) {
+            IpBaseCheck<Context>::operator=(other);
+            check_.reset(other.check_->clone());
+        }
+    }
+
+    /// \brief Clone
+    IpBaseCheck<Context>* clone() const {
+        return (new IpCheck<Context>(*this));
+    }
+
+    /// \brief Destructor
+    virtual ~IpCheck() {}
+
+    /// \brief The check itself
+    ///
+    /// Matches the passed argument to the condition stored here.  Different
+    /// specialisations are provided for different argument types, so the
+    /// link will fail if used for a type for which no match is provided.
+    ///
+    /// \param context Information to be matched
+    virtual bool matches(const Context& context) const {
+        return (check_->matches(context));
+    }
+
+    /// \brief Estimated cost
+    ///
+    /// Assume that the cost of the match is linear and depends on the number
+    /// of comparison operations.
+    virtual unsigned cost() const {
+        return (check_->cost());
+    }
+
+private:
+    // Member variables
 
+    bool                                      isv4_;  ///< true if V4 address
+    boost::scoped_ptr<IpBaseCheck<Context> >  check_; ///< Check object
 };
 
 } // namespace acl
diff --git a/src/lib/acl/tests/ip_check_unittest.cc b/src/lib/acl/tests/ip_check_unittest.cc
index 9ff02e1..27d399c 100644
--- a/src/lib/acl/tests/ip_check_unittest.cc
+++ b/src/lib/acl/tests/ip_check_unittest.cc
@@ -15,83 +15,159 @@
 
 #include <gtest/gtest.h>
 #include <acl/ip_check.h>
+#include <boost/scoped_ptr.hpp>
 
 using namespace isc::acl;
+using namespace std;
+
+// Provide specializations for a simple joint struct holding both an
+// IPV4 address and an IPV6 address
+
+typedef struct {
+    bool        isv4;
+    uint32_t    v4addr;
+    uint8_t     v6addr[16];
+} GeneralAddress;
+
+namespace isc  {
+namespace acl {
+template <>
+bool Ipv4Check<GeneralAddress>::matches(const GeneralAddress& addr) const {
+    return (addr.isv4 ? compare(addr.v4addr) : false);
+}
+
+template <>
+bool Ipv6Check<GeneralAddress>::matches(const GeneralAddress& addr) const {
+    return (addr.isv4 ? false : compare(addr.v6addr));
+}
+} // namespace acl
+} // namespace isc
+
+/// *** Free Function Tests ***
+
+TEST(IpFunctionCheck, CreateNetmask) {
+
+    // 8-bit tests: invalid arguments should throw.
+    EXPECT_THROW(createNetmask<uint8_t>(9), isc::OutOfRange);
+
+    // Check on all possible 8-bit values.  Use a signed type to generate a
+    // variable with the most significant bits set, as right-shifting it is
+    // guaranteed to introduce additional bits.
+    int8_t expected8 = 0x80;
+    for (size_t i = 1; i <= 8; ++i, expected8 >>= 1) {
+        EXPECT_EQ(static_cast<uint8_t>(expected8), createNetmask<uint8_t>(i));
+    }
+    EXPECT_EQ(0, createNetmask<uint8_t>(0));
+
+    // Do the same for 32 bits.
+    EXPECT_THROW(createNetmask<int32_t>(33), isc::OutOfRange);
+
+    // Check on all possible 8-bit values
+    int32_t expected32 = 0x80000000;
+    for (size_t i = 1; i <= 32; ++i, expected32 >>= 1) {
+        EXPECT_EQ(static_cast<uint32_t>(expected32),
+                  createNetmask<uint32_t>(i));
+    }
+    EXPECT_EQ(0, createNetmask<uint32_t>(0));
+}
+
+TEST(IpFunctionCheck, SplitIp) {
+    pair<string, uint32_t> result;
+
+    result = splitIpAddress("192.0.2.1/24", 32);
+    EXPECT_EQ(string("192.0.2.1"), result.first);
+    EXPECT_EQ(24, result.second);
 
-// Declare a derived class to allow the abstract function to be declared
-// as a concrete one.
+    result = splitIpAddress("192.0.2.1/32", 32);
+    EXPECT_EQ(string("192.0.2.1"), result.first);
+    EXPECT_EQ(32, result.second);
 
-class DerivedV4Check : public Ipv4Check<uint32_t> {
+    result = splitIpAddress("2001:db8::/128", 128);
+    EXPECT_EQ(string("2001:db8::"), result.first);
+    EXPECT_EQ(128, result.second);
+
+    EXPECT_THROW(splitIpAddress("2001:db8::/129", 128), isc::OutOfRange);
+    EXPECT_THROW(splitIpAddress("2001:db8::/xxxx", 32), isc::InvalidParameter);
+    EXPECT_THROW(splitIpAddress("2001:db8::/32/s", 32), isc::InvalidParameter);
+}
+
+// *** IPV4 Tests ***
+
+// Declare a derived class to allow a definition of the match() method to be
+// provided.
+//
+// In this case, the match will check a uint32_t variable representing an IPV4
+// address.
+
+class DerivedV4Check : public Ipv4Check<GeneralAddress> {
 public:
-    // Basic constructor
+    // Basic (and default) constructor
     DerivedV4Check(uint32_t address = 1, size_t masksize = 32,
                    bool inverse = false) :
-                   Ipv4Check<uint32_t>(address, masksize, inverse)
+                   Ipv4Check<GeneralAddress>(address, masksize, inverse)
     {}
 
     // String constructor
-    DerivedV4Check(const std::string& address, bool inverse = false) :
-        Ipv4Check<uint32_t>(address, inverse)
+    DerivedV4Check(const string& address, bool inverse = false) :
+        Ipv4Check<GeneralAddress>(address, inverse)
+    {}
+
+    // Copy constructor
+    DerivedV4Check(const DerivedV4Check& other) :
+        Ipv4Check<GeneralAddress>(other)
     {}
 
+    // Assignment operator
+    DerivedV4Check& operator=(const DerivedV4Check& other) {
+        if (this != &other) {
+            Ipv4Check<GeneralAddress>::operator=(other);
+        }
+        return (*this);
+    }
+
+    // Clone method
+    virtual IpBaseCheck<GeneralAddress>* clone() const {
+        return (new DerivedV4Check(*this));
+    }
+
     // Destructor
     virtual ~DerivedV4Check()
     {}
 
     // Concrete implementation of abstract method
     virtual bool matches(const uint32_t& context) const {
-        return (compare(context));
+        GeneralAddress gen;
+        gen.isv4 = true;
+        gen.v4addr = context;
+        return (Ipv4Check<GeneralAddress>::matches(gen));  // Call parent method
     }
 };
 
+// Check that a default constructor can be instantiated.
 
-/// Tests of the free functions.
-
-TEST(IpCheck, CreateNetmask) {
-    size_t  i;
-
-    // 8-bit tests.
-
-    // Invalid arguments should throw.
-    EXPECT_THROW(createNetmask<uint8_t>(0), isc::OutOfRange);
-    EXPECT_THROW(createNetmask<uint8_t>(9), isc::OutOfRange);
-
-    // Check on all possible 8-bit values.  Use a signed type to generate a
-    // variable with the most significant bits set, as right-shifting it is
-    // guaranteed to introduce additional bits.
-    int8_t  expected8;
-    for (i = 1, expected8 = 0x80; i <= 8; ++i, expected8 >>= 1) {
-        EXPECT_EQ(static_cast<uint8_t>(expected8),
-                  createNetmask<uint8_t>(i));
-    }
+TEST(Ipv4Check, V4DefaultConstructor) {
+    DerivedV4Check acl1;
 
-    // Do the same for 32 bits.
-    EXPECT_THROW(createNetmask<int32_t>(0), isc::OutOfRange);
-    EXPECT_THROW(createNetmask<int32_t>(33), isc::OutOfRange);
-
-    // Check on all possible 8-bit values
-    int32_t expected32;
-    for (i = 1, expected32 = 0x80000000; i <= 32; ++i, expected32 >>= 1) {
-        EXPECT_EQ(static_cast<uint32_t>(expected32),
-                  createNetmask<uint32_t>(i));
-    }
+    // The test is needed to avoid the unused variable causing a warning or
+    // getting optimised away.
+    EXPECT_EQ(1, acl1.getAddress());
 }
-// IPV4 tests
 
-// Check that the constructor expands the network mask and stores the elements
-// correctly.  For these tests, we don't worry about the type of the context,
-// so we declare it as an int.
+// Check that the constructor stores the elements correctly.  As the address
+// is provided in the correct (network-byte order) format, the real test is
+// checking that the network mask is stored in the correct byte order.
 
-TEST(IpCheck, V4ConstructorAddress) {
+TEST(Ipv4Check, V4ConstructorAddress) {
     DerivedV4Check acl1(0x12345678);
     EXPECT_EQ(0x12345678, acl1.getAddress());
 }
 
-// The mask is stored in network byte order, so the pattern expected must
-// also be converted to network byte order for the comparison to succeed.
-TEST(IpCheck, V4ConstructorMask) {
+TEST(Ipv4Check, V4ConstructorMask) {
     // Valid values. Address of "1" is used as a placeholder
     DerivedV4Check acl1(1, 1);
+
+    // The mask is stored in network byte order, so the pattern expected must
+    // also be converted to network byte order for the comparison to succeed.
     uint32_t expected = htonl(0x80000000);
     EXPECT_EQ(expected, acl1.getNetmask());
     EXPECT_EQ(1, acl1.getMasksize());
@@ -106,7 +182,7 @@ TEST(IpCheck, V4ConstructorMask) {
     EXPECT_THROW(DerivedV4Check(1, 33), isc::OutOfRange);
 }
 
-TEST(IpCheck, V4ConstructorInverse) {
+TEST(Ipv4Check, V4ConstructorInverse) {
     // Valid values. Address/mask of "1" is used as a placeholder
     DerivedV4Check acl1(1, 1);
     EXPECT_FALSE(acl1.getInverse());
@@ -118,32 +194,64 @@ TEST(IpCheck, V4ConstructorInverse) {
     EXPECT_FALSE(acl3.getInverse());
 }
 
-TEST(IpCheck, V4StringConstructor) {
-    DerivedV4Check acl1("127.0.0.1");
-    uint32_t expected = htonl(0x7f000001);
+TEST(Ipv4Check, V4StringConstructor) {
+    DerivedV4Check acl1("192.168.132.255");
+    uint32_t expected = htonl(0xc0a884ff);
     EXPECT_EQ(expected, acl1.getAddress());
     EXPECT_EQ(32, acl1.getMasksize());
 
-    DerivedV4Check acl2("255.255.255.0/24");
-    expected = htonl(0xffffff00);
+    DerivedV4Check acl2("192.0.2.0/24");
+    expected = htonl(0xc0000200);
     EXPECT_EQ(expected, acl2.getAddress());
     EXPECT_EQ(24, acl2.getMasksize());
 
-    EXPECT_THROW(DerivedV4Check("255.255.255.0/0"), isc::OutOfRange);
-    EXPECT_THROW(DerivedV4Check("255.255.255.0/33"), isc::OutOfRange);
-    EXPECT_THROW(DerivedV4Check("255.255.255.0/24/3"), isc::InvalidParameter);
-    EXPECT_THROW(DerivedV4Check("255.255.255.0/ww"), isc::InvalidParameter);
+    EXPECT_THROW(DerivedV4Check("192.0.2.0/0"), isc::OutOfRange);
+    EXPECT_THROW(DerivedV4Check("192.0.2.0/33"), isc::OutOfRange);
+    EXPECT_THROW(DerivedV4Check("192.0.2.0/24/3"), isc::InvalidParameter);
+    EXPECT_THROW(DerivedV4Check("192.0.2.0/ww"), isc::InvalidParameter);
     EXPECT_THROW(DerivedV4Check("aa.255.255.0/ww"), isc::InvalidParameter);
 }
 
+TEST(Ipv4Check, V4CopyConstructor) {
+    DerivedV4Check acl1("127.0.0.1/16", true);
+    DerivedV4Check acl2(acl1);
+
+    EXPECT_EQ(acl1.getAddress(), acl2.getAddress());
+    EXPECT_EQ(acl1.getMasksize(), acl2.getMasksize());
+    EXPECT_EQ(acl1.getNetmask(), acl2.getNetmask());
+    EXPECT_EQ(acl1.getInverse(), acl2.getInverse());
+}
+
+TEST(Ipv4Check, V4AssignmentOperator) {
+    DerivedV4Check acl1("127.0.0.1/16", true);
+    DerivedV4Check acl2("192.1.2.3/12", false);
+
+    acl2 = acl1;
+    EXPECT_EQ(acl1.getAddress(), acl2.getAddress());
+    EXPECT_EQ(acl1.getMasksize(), acl2.getMasksize());
+    EXPECT_EQ(acl1.getNetmask(), acl2.getNetmask());
+    EXPECT_EQ(acl1.getInverse(), acl2.getInverse());
+}
+
+TEST(IPv4Check, V4Clone) {
+    DerivedV4Check acl1("127.0.0.1/16", true);
+
+    boost::scoped_ptr<DerivedV4Check> acl2(
+            static_cast<DerivedV4Check*>(acl1.clone()));
+    EXPECT_EQ(acl1.getAddress(), acl2->getAddress());
+    EXPECT_EQ(acl1.getMasksize(), acl2->getMasksize());
+    EXPECT_EQ(acl1.getNetmask(), acl2->getNetmask());
+    EXPECT_EQ(acl1.getInverse(), acl2->getInverse());
+}
+
 // Check that the comparison works - note that "matches" just calls the
 // internal compare() code.
 //
-// Note that addresses passed to the class are expected to be in network-
-// byte order.  Therefore for the comparisons to work as expected, we must
-// convert the values to network-byte order first.
+// As before, note that addresses passed to the class are expected to be in
+// network-byte order.  Therefore for the comparisons to work as expected, we
+// must convert the values to network-byte order first.
 
-TEST(IpCheck, V4Compare) {
+TEST(Ipv4Check, V4Compare) {
     // Exact address - match if given address matches stored address.
     DerivedV4Check acl1(htonl(0x23457f13), 32);
     EXPECT_TRUE(acl1.matches(htonl(0x23457f13)));
@@ -171,5 +279,356 @@ TEST(IpCheck, V4Compare) {
     EXPECT_FALSE(acl4.matches(htonl(0x2345ffff)));
     EXPECT_TRUE(acl4.matches(htonl(0x23460000)));
     EXPECT_TRUE(acl4.matches(htonl(0x2346ffff)));
+}
+
+
+
+// *** IPV6 Tests ***
+//
+// Declare a derived class to allow a definition of the match() method to be
+// provided.
+//
+// In this case, the match will check a vector of uint8_t s representing an IPV6
+// address.
+
+class DerivedV6Check : public Ipv6Check<GeneralAddress> {
+public:
+    // default constructor
+    DerivedV6Check() : Ipv6Check<GeneralAddress>()
+    {}
+
+    // Basic constructor
+    DerivedV6Check(const uint8_t* address, size_t masksize = 128,
+                   bool inverse = false) :
+                   Ipv6Check<GeneralAddress>(address, masksize, inverse)
+    {}
+
+    // String constructor
+    DerivedV6Check(const string& address, bool inverse = false) :
+        Ipv6Check<GeneralAddress>(address, inverse)
+    {}
+
+    // Clone method
+    virtual IpBaseCheck<GeneralAddress>* clone() const {
+        return (new DerivedV6Check(*this));
+    }
+
+    // Destructor
+    virtual ~DerivedV6Check()
+    {}
+
+    // Concrete implementation of abstract method
+    virtual bool matches(const vector<uint8_t>& context) const {
+        GeneralAddress gen;
+        gen.isv4 = false;
+        copy(context.begin(), context.end(), gen.v6addr);
+        return (Ipv6Check<GeneralAddress>::matches(gen));  // Parent method
+    }
+};
+
+// Some constants used in the tests
+static const char* V6ADDR_1_STRING = "2001:0db8:1122:3344:5566:7788:99aa:bbcc";
+static const uint8_t V6ADDR_1[] = {
+    0x20, 0x01, 0x0d, 0xb8, 0x11, 0x22, 0x33, 0x44,
+    0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc
+};
+
+static const char* V6ADDR_2_STRING = "2001:0db8::dead:beef";
+static const uint8_t V6ADDR_2[] = {
+    0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef
+};
+
+// Identical to V6ADDR_2 to 48 bits
+static const uint8_t V6ADDR_2_48[] = {
+    0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x55, 0x66,
+    0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef
+};
+
+// Identical to V6ADDR_2 to 52 bits
+static const uint8_t V6ADDR_2_52[] = {
+    0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x05, 0x66,
+    0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef
+};
+
+static const char* V6ADDR_3_STRING = "::1";
+static const uint8_t V6ADDR_3[] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
+};
+
+
+// Mask with MS bit set
+static const uint8_t MASK_1[] = {
+    0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t MASK_8[] = {
+    0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t MASK_48[] = {
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t MASK_51[] = {
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t MASK_128[] = {
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+// Check that a default constructor can be instantiated.
+
+TEST(Ipv6Check, V6DefaultConstructor) {
+    DerivedV6Check acl1;
+
+    // The test is needed to avoid the unused variable causing a warning or
+    // getting optimised away.
+    EXPECT_EQ(0, acl1.getMasksize());
+}
+
+TEST(Ipv6Check, V6ConstructorAddress) {
+    DerivedV6Check acl1(V6ADDR_1);
+    vector<uint8_t> stored = acl1.getAddress();
+    EXPECT_EQ(sizeof(V6ADDR_1), stored.size());
+    EXPECT_TRUE(equal(stored.begin(), stored.end(), V6ADDR_1));
+}
+
+TEST(Ipv6Check, V6ConstructorMask) {
+
+    // Valid masks...
+    DerivedV6Check acl1(V6ADDR_1, 1);
+    vector<uint8_t> stored = acl1.getNetmask();
+    EXPECT_EQ(sizeof(MASK_1), stored.size());
+    EXPECT_TRUE(equal(stored.begin(), stored.end(), MASK_1));
+
+    DerivedV6Check acl2(V6ADDR_1, 8);
+    stored = acl2.getNetmask();
+    EXPECT_TRUE(equal(stored.begin(), stored.end(), MASK_8));
+
+    DerivedV6Check acl3(V6ADDR_1, 48);
+    stored = acl3.getNetmask();
+    EXPECT_TRUE(equal(stored.begin(), stored.end(), MASK_48));
+
+    DerivedV6Check acl4(V6ADDR_1, 51);
+    stored = acl4.getNetmask();
+    EXPECT_TRUE(equal(stored.begin(), stored.end(), MASK_51));
+
+    DerivedV6Check acl5(V6ADDR_1, 128);
+    stored = acl5.getNetmask();
+    EXPECT_TRUE(equal(stored.begin(), stored.end(), MASK_128));
+
+    // ... and some invalid network masks
+    EXPECT_THROW(DerivedV6Check(V6ADDR_1, 0), isc::OutOfRange);
+    EXPECT_THROW(DerivedV6Check(V6ADDR_1, 129), isc::OutOfRange);
+}
+
+TEST(Ipv6Check, V6ConstructorInverse) {
+    // Valid values. Address/mask of "1" is used as a placeholder
+    DerivedV6Check acl1(V6ADDR_1, 1);
+    EXPECT_FALSE(acl1.getInverse());
+
+    DerivedV6Check acl2(V6ADDR_1, 1, true);
+    EXPECT_TRUE(acl2.getInverse());
+
+    DerivedV6Check acl3(V6ADDR_1, 1, false);
+    EXPECT_FALSE(acl3.getInverse());
+}
+
+TEST(Ipv6Check, V6StringConstructor) {
+    DerivedV6Check acl1(V6ADDR_1_STRING);
+    vector<uint8_t> address = acl1.getAddress();
+    EXPECT_EQ(128, acl1.getMasksize());
+    EXPECT_TRUE(equal(address.begin(), address.end(), V6ADDR_1));
+
+    DerivedV6Check acl2(string(V6ADDR_2_STRING) + string("/48"));
+    address = acl2.getAddress();
+    EXPECT_EQ(48, acl2.getMasksize());
+    EXPECT_TRUE(equal(address.begin(), address.end(), V6ADDR_2));
+
+    DerivedV6Check acl3("::1");
+    address = acl3.getAddress();
+    EXPECT_EQ(128, acl3.getMasksize());
+    EXPECT_TRUE(equal(address.begin(), address.end(), V6ADDR_3));
+
+    EXPECT_THROW(DerivedV6Check("::1/0"), isc::OutOfRange);
+    EXPECT_THROW(DerivedV6Check("::1/129"), isc::OutOfRange);
+    EXPECT_THROW(DerivedV6Check("::1/24/3"), isc::InvalidParameter);
+    EXPECT_THROW(DerivedV6Check("2001:0db8::abcd/ww"), isc::InvalidParameter);
+    EXPECT_THROW(DerivedV6Check("2xx1:0db8::abcd/32"), isc::InvalidParameter);
+}
+
+TEST(Ipv6Check, V6CopyConstructor) {
+    DerivedV6Check acl1(string(V6ADDR_2_STRING) + string("/52"));
+    DerivedV6Check acl2(acl1);
+
+    vector<uint8_t> acl1_address = acl1.getAddress();
+    vector<uint8_t> acl2_address = acl1.getAddress();
+    EXPECT_EQ(sizeof(V6ADDR_1), acl1_address.size());
+    EXPECT_EQ(acl1_address.size(), acl2_address.size());
+    EXPECT_TRUE(equal(acl1_address.begin(), acl1_address.end(),
+                acl2_address.begin()));
+
+    EXPECT_EQ(acl1.getMasksize(), acl2.getMasksize());
+
+    vector<uint8_t> acl1_netmask = acl1.getNetmask();
+    vector<uint8_t> acl2_netmask = acl1.getNetmask();
+    EXPECT_EQ(sizeof(V6ADDR_1), acl1_netmask.size());
+    EXPECT_EQ(acl1_netmask.size(), acl2_netmask.size());
+    EXPECT_TRUE(equal(acl1_netmask.begin(), acl1_netmask.end(),
+                acl2_netmask.begin()));
+
+    EXPECT_EQ(acl1.getInverse(), acl2.getInverse());
+}
+
+TEST(Ipv6Check, V6AssignmentOperator) {
+    DerivedV6Check acl1(string(V6ADDR_2_STRING) + string("/52"));
+    DerivedV6Check acl2(string(V6ADDR_1_STRING) + string("/48"));
+
+    acl2 = acl1;
+
+    vector<uint8_t> acl1_address = acl1.getAddress();
+    vector<uint8_t> acl2_address = acl2.getAddress();
+    EXPECT_EQ(sizeof(V6ADDR_1), acl1_address.size());
+    EXPECT_EQ(acl1_address.size(), acl2_address.size());
+    EXPECT_TRUE(equal(acl1_address.begin(), acl1_address.end(),
+                acl2_address.begin()));
+
+    EXPECT_EQ(acl1.getMasksize(), acl2.getMasksize());
+
+    vector<uint8_t> acl1_netmask = acl1.getNetmask();
+    vector<uint8_t> acl2_netmask = acl2.getNetmask();
+    EXPECT_EQ(sizeof(V6ADDR_1), acl1_netmask.size());
+    EXPECT_EQ(acl1_netmask.size(), acl2_netmask.size());
+    EXPECT_TRUE(equal(acl1_netmask.begin(), acl1_netmask.end(),
+                acl2_netmask.begin()));
+
+    EXPECT_EQ(acl1.getInverse(), acl2.getInverse());
+}
+
+TEST(Ipv6Check, V6Clone) {
+    DerivedV6Check acl1(string(V6ADDR_2_STRING) + string("/52"));
+    boost::scoped_ptr<DerivedV6Check> acl2(
+        static_cast<DerivedV6Check*>(acl1.clone()));
+
+    vector<uint8_t> acl1_address = acl1.getAddress();
+    vector<uint8_t> acl2_address = acl2->getAddress();
+    EXPECT_EQ(sizeof(V6ADDR_1), acl1_address.size());
+    EXPECT_EQ(acl1_address.size(), acl2_address.size());
+    EXPECT_TRUE(equal(acl1_address.begin(), acl1_address.end(),
+                acl2_address.begin()));
+
+    EXPECT_EQ(acl1.getMasksize(), acl2->getMasksize());
+
+    vector<uint8_t> acl1_netmask = acl1.getNetmask();
+    vector<uint8_t> acl2_netmask = acl2->getNetmask();
+    EXPECT_EQ(sizeof(V6ADDR_1), acl1_netmask.size());
+    EXPECT_EQ(acl1_netmask.size(), acl2_netmask.size());
+    EXPECT_TRUE(equal(acl1_netmask.begin(), acl1_netmask.end(),
+                acl2_netmask.begin()));
+
+    EXPECT_EQ(acl1.getInverse(), acl2->getInverse());
+}
+
+TEST(Ipv6Check, V6Compare) {
+    // Set up some data.  The constant doesn't depend on the template parameter,
+    // so use a type name that's short.
+    vector<uint8_t> v6addr_2(V6ADDR_2, V6ADDR_2 + DerivedV6Check::IPV6_SIZE);
+    vector<uint8_t> v6addr_2_48(V6ADDR_2_48,
+                                V6ADDR_2_48 + DerivedV6Check::IPV6_SIZE);
+    vector<uint8_t> v6addr_2_52(V6ADDR_2_52,
+                                V6ADDR_2_52 + DerivedV6Check::IPV6_SIZE);
+    vector<uint8_t> v6addr_3(V6ADDR_3, V6ADDR_3 + DerivedV6Check::IPV6_SIZE);
+
+    // Exact address - match if given address matches stored address.
+    DerivedV6Check acl1(string(V6ADDR_2_STRING) + string("/128"));
+    EXPECT_TRUE(acl1.matches(v6addr_2));
+    EXPECT_FALSE(acl1.matches(v6addr_2_52));
+    EXPECT_FALSE(acl1.matches(v6addr_2_48));
+    EXPECT_FALSE(acl1.matches(v6addr_3));
+
+    // Exact address - match if address does not match stored address
+    DerivedV6Check acl2(string(V6ADDR_2_STRING) + string("/128"), true);
+    EXPECT_FALSE(acl2.matches(v6addr_2));
+    EXPECT_TRUE(acl2.matches(v6addr_2_52));
+    EXPECT_TRUE(acl2.matches(v6addr_2_48));
+    EXPECT_TRUE(acl2.matches(v6addr_3));
+
+    // Match if the address matches a mask
+    DerivedV6Check acl3(string(V6ADDR_2_STRING) + string("/52"));
+    EXPECT_TRUE(acl3.matches(v6addr_2));
+    EXPECT_TRUE(acl3.matches(v6addr_2_52));
+    EXPECT_FALSE(acl3.matches(v6addr_2_48));
+    EXPECT_FALSE(acl3.matches(v6addr_3));
+
+    // Match if the address does not match a mask
+    DerivedV6Check acl4(string(V6ADDR_2_STRING) + string("/52"), true);
+    EXPECT_FALSE(acl4.matches(v6addr_2));
+    EXPECT_FALSE(acl4.matches(v6addr_2_52));
+    EXPECT_TRUE(acl4.matches(v6addr_2_48));
+    EXPECT_TRUE(acl4.matches(v6addr_3));
+}
+
+// *** IP Tests ***
+
+TEST(IpCheck, V4Test) {
+    IpCheck<GeneralAddress> acl("192.168.132.255/16");
+                               //c0  a8  84  ff
+
+    GeneralAddress test;
+    test.isv4 = true;
+    test.v4addr = htonl(0xc0a884ff);
+    EXPECT_TRUE(acl.matches(test));
+
+    test.v4addr = htonl(0xc0a884ee);    // Correct to 24 bits
+    EXPECT_TRUE(acl.matches(test));
+
+    test.v4addr = htonl(0xc0a8ffee);    // Correct to 16 bits
+    EXPECT_TRUE(acl.matches(test));
+
+    test.v4addr = htonl(0xc000ffee);    // Incorrect
+    EXPECT_FALSE(acl.matches(test));
+
+    test.isv4 = false;
+    test.v4addr = htonl(0xc0a884ff);    // Correct IPV4 address
+    EXPECT_FALSE(acl.matches(test));
+
+    // Quick check for negative match
+    IpCheck<GeneralAddress> acl2("192.168.132.255/16", true);
+    test.isv4 = true;
+    test.v4addr = htonl(0xc0a884ff);
+    EXPECT_FALSE(acl2.matches(test));
+}
+
+TEST(IpCheck, V6Test) {
+    IpCheck<GeneralAddress> acl(string(V6ADDR_2_STRING) + string("/52"));
+
+    GeneralAddress test;
+    test.isv4 = false;
+    copy(V6ADDR_2, V6ADDR_2 + sizeof(V6ADDR_2), test.v6addr);
+    EXPECT_TRUE(acl.matches(test));
+
+    copy(V6ADDR_2_52, V6ADDR_2_52 + sizeof(V6ADDR_2_52), test.v6addr);
+    EXPECT_TRUE(acl.matches(test));
+
+    copy(V6ADDR_2_48, V6ADDR_2_48 + sizeof(V6ADDR_2_48), test.v6addr);
+    EXPECT_FALSE(acl.matches(test));
+
+    test.isv4 = true;
+    copy(V6ADDR_2, V6ADDR_2 + sizeof(V6ADDR_2), test.v6addr);
+                                        // Correct V6 address
+    EXPECT_FALSE(acl.matches(test));
 
+    // Quick check for negative match
+    IpCheck<GeneralAddress> acl2(string(V6ADDR_2_STRING) + string("/52"), true);
+    test.isv4 = false;
+    copy(V6ADDR_2, V6ADDR_2 + sizeof(V6ADDR_2), test.v6addr);
+    EXPECT_FALSE(acl2.matches(test));
 }




More information about the bind10-changes mailing list