Commit 73ac6b09 authored by Stephen Morris's avatar Stephen Morris
Browse files

[trac998] End of week checkpoint

parent 86a307f0
SUBDIRS = tests
EXTRA_DIST = check.h ip_check.h
# TODO: Once we have some cc file we are able to compile, create the library.
# For now, we have only header files, not creating empty library.
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef __IP_CHECK_H
#define __IP_CHECK_H
#include <boost/lexical_cast.hpp>
#include <vector>
#include <stdint.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <acl/check.h>
#include <util/strutil.h>
#include <exceptions/exceptions.h>
namespace isc {
namespace acl {
/// \brief Convert Mask Size to Mask
///
/// Given a mask size and a data type, return a value of that data type with the
/// most significant maasksize bits set. For example, if the data type is an
/// unsigned either-bit byte and the masksize is 3, the function would return
/// an eight-bit byte with the binary value 11100000.
///
/// This is a templated function. The template parameter must be a signed type.
///
/// \param masksize Size of the mask. This must be between 1 and sizeof(T).
/// An out of range exception is thrown if this is not the case.
///
/// \return Value with the most significant "masksize" bits set.
template <typename T>
T createNetmask(size_t masksize) {
if ((masksize > 0) && (masksize <= 8 * sizeof(T))) {
// To explain the logic, consider a single 4-bit word. The masksize in
// this case can be between 1 and 4. The following table has the
// following columns:
//
// Mask size (m): number of contiguous bits set
//
// Low value (lo): unsigned value of 4-bit word with the least-
// significant m contiguous bits set.
//
// High value (hi): unsigned value of 4-bit word with the most-
// significant m contiguous bits set.
//
// m lo hi
// 1 1 8
// 2 3 12
// 3 7 14
// 4 15 15
//
// Clearly the low value is equal to (2**m - 1) (using ** to indicate
// exponentiation). It takes a little thought to see that the high
// value is equal to 2**4 - 2**(4-m). Unfortunately, this formula will
// overflow as the intermediate value 2 << sizeof(T) will overflow in an
// element of type T.
//
// However, another way of looking it is that to set the most signifcant
// m bits, we set all bits and clear the least-significant 4-m bits. If
// T is a signed value, we can set all bits by setting it to -1. If m
// is 4, we omit clearing any bits, otherwise we clear the bits
// represented by the bit pattern 2**(3-m) - 1. (The value 2**(3-m)
// will be greater than 0 and within the range of an unsigned data
// type of the same size as T. So it should not overflow.)
//
// Therefore we proceed on the assumption that T is signed
T mask = -1;
if (masksize < 8 * sizeof(T)) {
mask &= ~((2 << (8 * sizeof(T) - 1 - masksize)) - 1);
}
return (mask);
}
// Invalid mask size
isc_throw(isc::OutOfRange, "mask size of " << masksize << " is invalid " <<
"for the data type which is " << sizeof(T) <<
" bytes long");
}
/// \brief IP V4 Check
///
/// This class performs a match between an IPv4 address specified in an ACL
/// (IP address, network mask and a flag indicating whether the check should
/// be for a match or for no-match) and a given IPv4 address.
///
/// \param Context Structure holding address to be matched.
template <typename Context> class Ipv4Check : public Check<Context> {
public:
/// \brief Constructor
///
/// \param address IP address to check for (as an address in host-byte
/// order).
/// \param mask The network mask specified as an integer between 1 and
/// 32 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.
Ipv4Check(uint32_t address, size_t masksize = 32, bool inverse = false) :
address_(address), masksize_(masksize), netmask_(0), inverse_(inverse)
{
init();
}
/// \brief Constructor
///
/// \param address IP address and netmask in the form "a.b.c.d/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.
Ipv4Check(const std::string& address, bool inverse = false) :
address_(0), masksize_(32), netmask_(0), inverse_(inverse)
{
// See if there is a netmask.
std::vector<std::string> components =
isc::util::str::tokens(address, "/");
if (components.size() == 2) {
// Yes there is, convert to a mask
try {
masksize_ = boost::lexical_cast<size_t>(components[1]);
} catch (boost::bad_lexical_cast&) {
isc_throw(isc::InvalidParameter,
"mask specified in address/mask " << address <<
" is not valid");
}
} else if (components.size() > 2) {
isc_throw(isc::InvalidParameter, "address/mask of " <<
address << " is not valid");
}
// Try to convert the address.
int result = inet_pton(AF_INET, components[0].c_str(), &address_);
if (result == 0) {
isc_throw(isc::InvalidParameter, "address/mask of " <<
address << " is not valid");
}
address_ = ntohl(address_);
// All done, so finish initialization.
init();
}
/// \brief Destructor
virtual ~Ipv4Check() {}
/// \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.
///
/// \param address Address to match against the check condition in the
/// class.
///
/// \return true if the address matches, false if it does not.
virtual bool compare(uint32_t address) {
// To check that the address given matches the stored network address
// and netmask, we check the simple condition that:
//
// address_given & netmask_ == maskaddr_.
//
// However, we must return the negation of the result if inverse_ is
// set. This leads to the truth table:
//
// Result inverse_ Return
// false false false
// false true true
// true false true
// true true false
//
// ... which is an XOR function.
return (((address & netmask_) == maskaddr_) ^ inverse_);
}
/// \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 false; }
/// \brief Estimated cost
///
/// Assume that the cost of the match is linear and depends on the number
/// of compariosn operations.
virtual unsigned cost() const {
return (1); // Single check on a 32-bit word
}
///@{
/// Access methods - mainly for testing
/// \return Stored IP address
uint32_t getAddress() const {
return (address_);
}
/// \return Network mask applied to match
const uint32_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_);
}
///@}
private:
/// \brief Initialization
///
/// Common code shared by all constructors to set up the net mask and
/// addresses.
void init() {
// Validate that the mask is valid.
if ((masksize_ >= 1) && (masksize_ <= 32)) {
// Calculate the bitmask given by the number of bits.
netmask_ = isc::acl::createNetmask<int32_t>(masksize_);
// For speed, store the masked off address. This saves a mask
// operation every time the value is checked.
maskaddr_ = address_ & netmask_;
} else {
isc_throw(isc::OutOfRange,
"mask size of " << masksize_ << " is invalid " <<
"for the data type which is " << sizeof(uint32_t) <<
" bytes long");
}
}
uint32_t address_; ///< IPv4 address
uint32_t maskaddr_; ///< Masked IPV4 address
size_t masksize_; ///< Mask size passed to constructor
int32_t netmask_; ///< Network mask applied to match
bool inverse_; ///< test for equality or inequality
};
} // namespace acl
} // namespace isc
#endif // __IP_CHECK_H
......@@ -5,11 +5,15 @@ if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += check_test.cc
run_unittests_SOURCES += ip_check_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
noinst_PROGRAMS = $(TESTS)
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
#include <acl/ip_check.h>
using namespace isc::acl;
/// General tests
TEST(IpCheck, CreateNetmask) {
size_t i;
// 8-bit tests.
// Invalid arguments should throw.
EXPECT_THROW(createNetmask<int8_t>(0), isc::OutOfRange);
EXPECT_THROW(createNetmask<int8_t>(9), isc::OutOfRange);
// Check on all possible 8-bit values
int8_t expected8;
for (i = 1, expected8 = 0x80; i <= 8; ++i, expected8 >>= 1) {
EXPECT_EQ(static_cast<int32_t>(expected8),
static_cast<int32_t>(createNetmask<int8_t>(i)));
}
// 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(expected32, createNetmask<int32_t>(i));
}
}
// V4 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.
TEST(IpCheck, V4ConstructorAddress) {
// Alternating bits
Ipv4Check<int> acl1(0x55555555);
EXPECT_EQ(0x55555555, acl1.getAddress());
Ipv4Check<int> acl2(0xcccccccc);
EXPECT_EQ(0xcccccccc, acl2.getAddress());
}
TEST(IpCheck, V4ConstructorMask) {
// Valid values. Address of "1" is used as a placeholder
Ipv4Check<int> acl1(1, 1);
EXPECT_EQ(0x80000000, acl1.getNetmask());
EXPECT_EQ(1, acl1.getMasksize());
Ipv4Check<int> acl2(1, 24);
EXPECT_EQ(0xffffff00, acl2.getNetmask());
EXPECT_EQ(24, acl2.getMasksize());
// ... and some invalid network masks
EXPECT_THROW(Ipv4Check<int>(1, 0), isc::OutOfRange);
}
TEST(IpCheck, V4ConstructorInverse) {
// Valid values. Address/mask of "1" is used as a placeholder
Ipv4Check<int> acl1(1, 1);
EXPECT_FALSE(acl1.getInverse());
Ipv4Check<int> acl2(1, 1, true);
EXPECT_TRUE(acl2.getInverse());
Ipv4Check<int> acl3(1, 1, false);
EXPECT_FALSE(acl3.getInverse());
}
TEST(IpCheck, V4StringConstructor) {
Ipv4Check<int> acl1("127.0.0.1");
EXPECT_EQ(0x7f000001, acl1.getAddress());
EXPECT_EQ(32, acl1.getMasksize());
Ipv4Check<int> acl2("255.255.255.0/24");
EXPECT_EQ(0xffffff00, acl2.getAddress());
EXPECT_EQ(24, acl2.getMasksize());
EXPECT_THROW(Ipv4Check<int>("255.255.255.0/0"), isc::OutOfRange);
EXPECT_THROW(Ipv4Check<int>("255.255.255.0/33"), isc::OutOfRange);
EXPECT_THROW(Ipv4Check<int>("255.255.255.0/24/3"), isc::InvalidParameter);
EXPECT_THROW(Ipv4Check<int>("255.255.255.0/ww"), isc::InvalidParameter);
EXPECT_THROW(Ipv4Check<int>("aa.255.255.0/ww"), isc::InvalidParameter);
}
// Check that the comparison works - until we have a a message structure,
// we can't check the matches function.
TEST(IpCheck, V4Compare) {
// Exact address - match if given address matches stored address
Ipv4Check<int> acl1(0x23457f13, 32);
EXPECT_TRUE(acl1.compare(0x23457f13));
EXPECT_FALSE(acl1.compare(0x23457f12));
EXPECT_FALSE(acl1.compare(0x13457f13));
// Exact address - match if address does not match stored address
Ipv4Check<int> acl2(0x23457f13, 32, true);
EXPECT_FALSE(acl2.compare(0x23457f13));
EXPECT_TRUE(acl2.compare(0x23457f12));
EXPECT_TRUE(acl2.compare(0x13457f13));
// Match if the address matches a mask
Ipv4Check<int> acl3(0x23450000, 16);
EXPECT_TRUE(acl3.compare(0x23450000));
EXPECT_TRUE(acl3.compare(0x23450001));
EXPECT_TRUE(acl3.compare(0x2345ffff));
EXPECT_FALSE(acl3.compare(0x23460000));
EXPECT_FALSE(acl3.compare(0x2346ffff));
// Match if the address does not match a mask
Ipv4Check<int> acl4(0x23450000, 16, true);
EXPECT_FALSE(acl4.compare(0x23450000));
EXPECT_FALSE(acl4.compare(0x23450001));
EXPECT_FALSE(acl4.compare(0x2345ffff));
EXPECT_TRUE(acl4.compare(0x23460000));
EXPECT_TRUE(acl4.compare(0x2346ffff));
//
}
......@@ -13,11 +13,12 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
#include <log/logger_support.h>
#include <util/unittests/run_all.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
isc::log::initLogger();
return (isc::util::unittests::run_all());
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment