1
0
mirror of https://github.com/mfontanini/libtins synced 2026-01-28 04:34:27 +01:00

Added AddressRange class template.

This commit is contained in:
Matias Fontanini
2013-07-06 17:48:26 -03:00
parent f385e4e975
commit 3b349471ea
12 changed files with 598 additions and 6 deletions

View File

@@ -109,7 +109,8 @@ libtins_HEADERS = include/internals.h \
include/cxxstd.h \
include/stp.h \
include/exceptions.h \
include/config.h
include/config.h \
include/address_range.h
libtins_dot11_HEADERS = include/dot11/dot11_base.h \
include/dot11/dot11_beacon.h \

View File

@@ -396,7 +396,8 @@ libtins_HEADERS = include/internals.h \
include/cxxstd.h \
include/stp.h \
include/exceptions.h \
include/config.h
include/config.h \
include/address_range.h
libtins_dot11_HEADERS = include/dot11/dot11_base.h \
include/dot11/dot11_beacon.h \

299
include/address_range.h Normal file
View File

@@ -0,0 +1,299 @@
/*
* Copyright (c) 2012, Matias Fontanini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef TINS_ADDRESS_RANGE
#define TINS_ADDRESS_RANGE
#include <stdexcept>
#include <iterator>
#include "ip_address.h"
#include "ipv6_address.h"
#include "endianness.h"
#include "internals.h"
namespace Tins {
/**
* \brief AddressRange iterator class.
*/
template<typename Address>
class AddressRangeIterator : public std::iterator<std::forward_iterator_tag, const Address> {
public:
typedef typename std::iterator<std::forward_iterator_tag, const Address>::value_type value_type;
struct end_iterator {
};
/**
* Constructs an iterator.
*
* \param first The address held by this iterator.
*/
AddressRangeIterator(const value_type &addr)
: addr(addr), reached_end(false)
{
}
/**
* Constructs an iterator.
*
* \param first The address held by this iterator.
*/
AddressRangeIterator(const value_type &address, end_iterator)
: addr(address)
{
reached_end = Internals::increment(addr);
}
/**
* Retrieves the current address pointed by this iterator.
*/
const value_type& operator*() const {
return addr;
}
/**
* Retrieves a pointer to the current address pointed by this iterator.
*/
const value_type* operator->() const {
return &addr;
}
/**
* Compares two iterators for equality.
*
* \param rhs The iterator with which to compare.
*/
bool operator==(const AddressRangeIterator &rhs) const {
return reached_end == rhs.reached_end && addr == rhs.addr;
}
/**
* Compares two iterators for inequality.
*
* \param rhs The iterator with which to compare.
*/
bool operator!=(const AddressRangeIterator &rhs) const {
return !(*this == rhs);
}
/**
* Increments this iterator.
*/
AddressRangeIterator& operator++() {
reached_end = Internals::increment(addr);
return *this;
}
/**
* Increments this iterator.
*/
AddressRangeIterator operator++(int) {
AddressRangeIterator copy(*this);
(*this)++;
return copy;
}
private:
Address addr;
bool reached_end;
};
/**
* \brief Represents a range of addresses.
*
* This class provides a begin()/end() interface which allows
* iterating through every address stored in it.
*
* Note that when iterating a range that was created using
* operator/(IPv4Address, int) and the analog for IPv6, the
* network and broadcast addresses are discarded:
*
* \code
* auto range = IPv4Address("192.168.5.0") / 24;
* for(const auto &addr : range) {
* // process 192.168.5.1-254, .0 and .255 are discarded
* process(addr);
* }
*
* // That's only valid for iteration, not for AddressRange<>::contains
*
* assert(range.contains("192.168.5.0")); // works
* assert(range.contains("192.168.5.255")); // works
* \endcode
*
* Ranges created using AddressRange(address_type, address_type)
* will allow the iteration over the entire range:
*
* \code
* AddressRange<IPv4Address> range("192.168.5.0", "192.168.5.255");
* for(const auto &addr : range) {
* // process 192.168.5.0-255, no addresses are discarded
* process(addr);
* }
*
* assert(range.contains("192.168.5.0")); // still valid
* assert(range.contains("192.168.5.255")); // still valid
* \endcode
*
*/
template<typename Address>
class AddressRange {
public:
/**
* The type of addresses stored in the range.
*/
typedef Address address_type;
/**
* The iterator type.
*/
typedef AddressRangeIterator<address_type> const_iterator;
/**
* \brief The iterator type.
*
* This is the same type as const_iterator, since the
* addresses stored in this range are read only.
*/
typedef const_iterator iterator;
/**
* \brief Constructs an address range from two addresses.
*
* The range will consist of the addresses [first, last].
*
* If only_hosts is true, then the network and broadcast addresses
* will not be available when iterating the range.
*
* If last < first, an std::runtime_error exception is thrown.
*
* \param first The first address in the range.
* \param last The last address(inclusive) in the range.
* \param only_hosts Indicates whether only host addresses
* should be accessed when using iterators.
*/
AddressRange(const address_type &first, const address_type &last, bool only_hosts = false)
: first(first), last(last), only_hosts(only_hosts)
{
if(last < first)
throw std::runtime_error("Invalid address range");
}
/**
* \brief Creates an address range from a base address
* and a network mask.
*
* \param first The base address.
* \param mask The network mask to be used.
*/
static AddressRange from_mask(const address_type &first, const address_type &mask) {
return AddressRange<address_type>(first, last_address_from_mask(first, mask), true);
}
/**
* \brief Indicates whether an address is included in this range.
* \param addr The address to test.
* \return a bool indicating whether the address is in the range.
*/
bool contains(const address_type &addr) const {
return (first < addr && addr < last) || addr == first || addr == last;
}
/**
* \brief Returns an interator to the beginning of this range.
* \brief const_iterator pointing to the beginning of this range.
*/
const_iterator begin() const {
address_type addr = first;
if(only_hosts)
Internals::increment(addr);
return const_iterator(addr);
}
/**
* \brief Returns an interator to the end of this range.
* \brief const_iterator pointing to the end of this range.
*/
const_iterator end() const {
address_type addr = last;
if(only_hosts)
Internals::decrement(addr);
return const_iterator(addr, typename const_iterator::end_iterator());
}
/**
* \brief Indicates whether this range is iterable.
*
* Iterable ranges are those for which there is at least one
* address that could represent a host. For IPv4 ranges, a /31 or
* /32 ranges does not contain any, therefore it's not iterable.
* The same is true for /127 and /128 IPv6 ranges.
*
* If is_iterable returns false for a range, then iterating it
* through the iterators returned by begin() and end() is
* undefined.
*
* \return bool indicating whether this range is iterable.
*/
bool is_iterable() const {
// Since first < last, it's iterable
if(!only_hosts)
return true;
// We need that distance(first, last) >= 4
address_type addr(first);
for(int i = 0; i < 3; ++i) {
// If there's overflow before the last iteration, we're done
if(Internals::increment(addr) && i != 2)
return false;
}
// If addr <= last, it's OK.
return addr < last || addr == last;
}
private:
static address_type last_address_from_mask(IPv4Address addr, IPv4Address mask) {
uint32_t addr_int = Endian::be_to_host<uint32_t>(addr),
mask_int = Endian::be_to_host<uint32_t>(mask);
return address_type(Endian::host_to_be(addr_int | ~mask_int));
}
static address_type last_address_from_mask(IPv6Address addr, const IPv6Address &mask) {
IPv6Address::iterator addr_iter = addr.begin();
for(IPv6Address::const_iterator it = mask.begin(); it != mask.end(); ++it, ++addr_iter) {
*addr_iter = *addr_iter | ~*it;
}
return addr;
}
address_type first, last;
bool only_hosts;
};
} // namespace Tins
#endif // TINS_ADDRESS_RANGE

View File

@@ -40,6 +40,8 @@
* \cond
*/
namespace Tins {
class IPv4Address;
class IPv6Address;
namespace Internals {
template<size_t n>
class byte_array {
@@ -111,6 +113,11 @@ PDU *pdu_from_flag(Constants::Ethernet::e flag, const uint8_t *buffer,
PDU *pdu_from_flag(PDU::PDUType type, const uint8_t *buffer, uint32_t size);
Constants::Ethernet::e pdu_flag_to_ether_type(PDU::PDUType flag);
bool increment(IPv4Address &addr);
bool increment(IPv6Address &addr);
bool decrement(IPv4Address &addr);
bool decrement(IPv6Address &addr);
} // namespace Internals
} // namespace Tins
/**

View File

@@ -36,6 +36,9 @@
#include "cxxstd.h"
namespace Tins {
template<typename Address>
class AddressRange;
/**
* \class IPv4Address
* \brief Abstraction of an IPv4 address.
@@ -137,6 +140,13 @@ private:
uint32_t ip_addr;
};
/**
* \brief Constructs an AddressRange from a base address and a mask.
* \param addr The range's first address.
* \param mask The bit-length of the prefix.
*/
AddressRange<IPv4Address> operator/(const IPv4Address &addr, int mask);
} //namespace Tins
#if TINS_IS_CXX11

View File

@@ -36,6 +36,12 @@
#include "cxxstd.h"
namespace Tins {
template<typename Address>
class AddressRange;
/**
* Represents an IPv6 address.
*/
class IPv6Address {
public:
/**
@@ -193,6 +199,13 @@ private:
uint8_t address[address_size];
};
/**
* \brief Constructs an AddressRange from a base address and a mask.
* \param addr The range's first address.
* \param mask The bit-length of the prefix.
*/
AddressRange<IPv6Address> operator/(const IPv6Address &addr, int mask);
} //namespace Tins
#if TINS_IS_CXX11

View File

@@ -68,5 +68,6 @@
#include "pppoe.h"
#include "stp.h"
#include "handshake_capturer.h"
#include "address_range.h"
#endif // TINS_TINS_H

View File

@@ -39,6 +39,8 @@
#include "rawpdu.h"
#include "dot1q.h"
#include "pppoe.h"
#include "ip_address.h"
#include "ipv6_address.h"
using std::string;
@@ -149,5 +151,45 @@ Constants::Ethernet::e pdu_flag_to_ether_type(PDU::PDUType flag) {
return Constants::Ethernet::UNKNOWN;
}
}
bool increment(IPv4Address &addr) {
uint32_t addr_int = Endian::be_to_host<uint32_t>(addr);
bool reached_end = ++addr_int == 0xffffffff;
addr = IPv4Address(Endian::be_to_host<uint32_t>(addr_int));
return reached_end;
}
bool increment(IPv6Address &addr) {
IPv6Address::iterator it = addr.end() - 1;
while(it >= addr.begin() && *it == 0xff) {
*it = 0;
--it;
}
// reached end
if(it < addr.begin())
return true;
(*it)++;
return false;
}
bool decrement(IPv4Address &addr) {
uint32_t addr_int = Endian::be_to_host<uint32_t>(addr);
bool reached_end = --addr_int == 0;
addr = IPv4Address(Endian::be_to_host<uint32_t>(addr_int));
return reached_end;
}
bool decrement(IPv6Address &addr) {
IPv6Address::iterator it = addr.end() - 1;
while(it >= addr.begin() && *it == 0) {
*it = 0xff;
--it;
}
// reached end
if(it < addr.begin())
return true;
(*it)--;
return false;
}
} // namespace Internals
} // namespace Tins

View File

@@ -31,6 +31,7 @@
#include <sstream>
#include "ip_address.h"
#include "endianness.h"
#include "address_range.h"
using std::string;
@@ -94,4 +95,13 @@ std::ostream &operator<<(std::ostream &output, const IPv4Address &addr) {
}
return output;;
}
AddressRange<IPv4Address> operator/(const IPv4Address &addr, int mask) {
if(mask > 32)
throw std::logic_error("Prefix length cannot exceed 32");
return AddressRange<IPv4Address>::from_mask(
addr,
IPv4Address(Endian::host_to_be(0xffffffff << (32 - mask)))
);
}
}

View File

@@ -41,6 +41,7 @@
#include <limits>
#include <sstream>
#include "ipv6_address.h"
#include "address_range.h"
namespace Tins {
IPv6Address::IPv6Address() {
@@ -96,6 +97,19 @@ namespace Tins {
#endif
return buffer;
}
AddressRange<IPv6Address> operator/(const IPv6Address &addr, int mask) {
if(mask > 128)
throw std::logic_error("Prefix length cannot exceed 128");
IPv6Address last_addr;
IPv6Address::iterator it = last_addr.begin();
while(mask > 8) {
*it = 0xff;
++it;
mask -= 8;
}
*it = 0xff << (8 - mask);
return AddressRange<IPv6Address>::from_mask(addr, last_addr);
}
}

View File

@@ -874,7 +874,7 @@
../include/dot11/../cxxstd.h ../include/dot11/../macros.h \
../include/ipv6.h ../include/ipv6_address.h ../include/arp.h \
../include/eapol.h ../include/rawpdu.h ../include/dot1q.h \
../include/pppoe.h
../include/pppoe.h ../include/ip_address.h ../include/ipv6_address.h
../include/internals.h:
@@ -937,6 +937,10 @@
../include/dot1q.h:
../include/pppoe.h:
../include/ip_address.h:
../include/ipv6_address.h:
../src/ip.o: ../src/ip.cpp ../include/ip.h ../include/pdu.h \
../include/macros.h ../include/cxxstd.h ../include/exceptions.h \
../include/small_uint.h ../include/endianness.h ../include/ip_address.h \
@@ -995,7 +999,10 @@
../include/exceptions.h:
../src/ip_address.o: ../src/ip_address.cpp ../include/ip_address.h \
../include/cxxstd.h ../include/endianness.h ../include/macros.h
../include/cxxstd.h ../include/endianness.h ../include/macros.h \
../include/address_range.h ../include/ip_address.h \
../include/ipv6_address.h ../include/endianness.h ../include/internals.h \
../include/constants.h ../include/pdu.h ../include/exceptions.h
../include/ip_address.h:
@@ -1004,6 +1011,22 @@
../include/endianness.h:
../include/macros.h:
../include/address_range.h:
../include/ip_address.h:
../include/ipv6_address.h:
../include/endianness.h:
../include/internals.h:
../include/constants.h:
../include/pdu.h:
../include/exceptions.h:
../src/ipv6.o: ../src/ipv6.cpp ../include/ipv6.h ../include/macros.h \
../include/pdu.h ../include/cxxstd.h ../include/exceptions.h \
../include/endianness.h ../include/small_uint.h ../include/pdu_option.h \
@@ -1749,6 +1772,35 @@
../include/network_interface.h:
../include/cxxstd.h:
src/address_range.o: src/address_range.cpp ../include/address_range.h \
../include/ip_address.h ../include/cxxstd.h ../include/ipv6_address.h \
../include/endianness.h ../include/macros.h ../include/internals.h \
../include/constants.h ../include/pdu.h ../include/exceptions.h \
../include/ip_address.h ../include/ipv6_address.h
../include/address_range.h:
../include/ip_address.h:
../include/cxxstd.h:
../include/ipv6_address.h:
../include/endianness.h:
../include/macros.h:
../include/internals.h:
../include/constants.h:
../include/pdu.h:
../include/exceptions.h:
../include/ip_address.h:
../include/ipv6_address.h:
src/arp.o: src/arp.cpp ../include/arp.h ../include/macros.h \
../include/pdu.h ../include/cxxstd.h ../include/exceptions.h \
../include/endianness.h ../include/hw_address.h ../include/ip_address.h \
@@ -3599,7 +3651,8 @@ src/wpa2_decrypt.o: src/wpa2_decrypt.cpp ../include/crypto.h \
../include/exceptions.h ../include/snap.h ../include/endianness.h \
../include/small_uint.h ../include/rawpdu.h \
../include/handshake_capturer.h ../include/eapol.h ../include/config.h \
../include/radiotap.h ../include/udp.h
../include/radiotap.h ../include/udp.h ../include/tcp.h \
../include/pdu_option.h
../include/crypto.h:
@@ -3640,3 +3693,7 @@ src/wpa2_decrypt.o: src/wpa2_decrypt.cpp ../include/crypto.h \
../include/radiotap.h:
../include/udp.h:
../include/tcp.h:
../include/pdu_option.h:

137
tests/src/address_range.cpp Normal file
View File

@@ -0,0 +1,137 @@
#include <gtest/gtest.h>
#include <cstring>
#include <string>
#include <algorithm>
#include <stdint.h>
#include "address_range.h"
#include "ip_address.h"
#include "ipv6_address.h"
using namespace std;
using namespace Tins;
class AddressRangeTest : public testing::Test {
public:
void contain_tests24(const AddressRange<IPv4Address> &range);
void contain_tests24(const AddressRange<IPv6Address> &range);
void contain_tests26(const AddressRange<IPv4Address> &range);
};
void AddressRangeTest::contain_tests24(const AddressRange<IPv4Address> &range) {
EXPECT_TRUE(range.contains("192.168.0.0"));
EXPECT_TRUE(range.contains("192.168.0.1"));
EXPECT_TRUE(range.contains("192.168.0.254"));
EXPECT_TRUE(range.contains("192.168.0.255"));
EXPECT_TRUE(range.contains("192.168.0.123"));
EXPECT_FALSE(range.contains("192.168.1.1"));
}
void AddressRangeTest::contain_tests26(const AddressRange<IPv4Address> &range) {
EXPECT_TRUE(range.contains("192.168.254.192"));
EXPECT_TRUE(range.contains("192.168.254.255"));
EXPECT_FALSE(range.contains("192.168.254.0"));
EXPECT_FALSE(range.contains("192.168.254.191"));
}
void AddressRangeTest::contain_tests24(const AddressRange<IPv6Address> &range) {
EXPECT_TRUE(range.contains("dead::1"));
EXPECT_TRUE(range.contains("dead::1fee"));
EXPECT_TRUE(range.contains("dead::ffee"));
EXPECT_FALSE(range.contains("dead::1:1"));
EXPECT_FALSE(range.contains("dead::2:0"));
}
TEST_F(AddressRangeTest, Contains) {
contain_tests24(AddressRange<IPv4Address>("192.168.0.0", "192.168.0.255"));
contain_tests24(AddressRange<IPv4Address>::from_mask("192.168.0.0", "255.255.255.0"));
contain_tests26(AddressRange<IPv4Address>("192.168.254.192", "192.168.254.255"));
contain_tests26(AddressRange<IPv4Address>::from_mask("192.168.254.192", "255.255.255.192"));
contain_tests24(AddressRange<IPv6Address>("dead::0", "dead::ffff"));
contain_tests24(AddressRange<IPv6Address>::from_mask("dead::0", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:0"));
}
TEST_F(AddressRangeTest, Iterators) {
// v4
{
AddressRange<IPv4Address> addr = AddressRange<IPv4Address>::from_mask("192.168.0.0", "255.255.255.252");
std::vector<IPv4Address> addresses;
addresses.push_back("192.168.0.1");
addresses.push_back("192.168.0.2");
EXPECT_TRUE(std::equal(addr.begin(), addr.end(), addresses.begin()));
EXPECT_TRUE(addr.is_iterable());
}
{
AddressRange<IPv4Address> addr = AddressRange<IPv4Address>::from_mask("255.255.255.252", "255.255.255.252");
std::vector<IPv4Address> addresses;
addresses.push_back("255.255.255.253");
addresses.push_back("255.255.255.254");
EXPECT_TRUE(std::equal(addr.begin(), addr.end(), addresses.begin()));
EXPECT_TRUE(addr.is_iterable());
}
// v6
{
AddressRange<IPv6Address> addr = AddressRange<IPv6Address>::from_mask("dead::0", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc");
std::vector<IPv6Address> addresses;
addresses.push_back("dead::1");
addresses.push_back("dead::2");
EXPECT_TRUE(std::equal(addr.begin(), addr.end(), addresses.begin()));
EXPECT_TRUE(addr.is_iterable());
}
{
AddressRange<IPv6Address> addr =
AddressRange<IPv6Address>::from_mask(
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc",
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc"
);
std::vector<IPv6Address> addresses;
addresses.push_back("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffd");
addresses.push_back("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe");
EXPECT_TRUE(std::equal(addr.begin(), addr.end(), addresses.begin()));
EXPECT_TRUE(addr.is_iterable());
}
}
TEST_F(AddressRangeTest, Slash) {
// v4
{
AddressRange<IPv4Address> range1 = AddressRange<IPv4Address>::from_mask("192.168.0.0", "255.255.255.252");
AddressRange<IPv4Address> range2 = IPv4Address("192.168.0.0") / 30;
EXPECT_TRUE(std::equal(range1.begin(), range1.end(), range2.begin()));
EXPECT_TRUE(std::equal(range2.begin(), range2.end(), range1.begin()));
EXPECT_TRUE(range1.is_iterable());
EXPECT_TRUE(range2.is_iterable());
}
{
AddressRange<IPv4Address> range1 = AddressRange<IPv4Address>::from_mask("255.255.255.252", "255.255.255.252");
AddressRange<IPv4Address> range2 = IPv4Address("255.255.255.252") / 30;
EXPECT_TRUE(std::equal(range1.begin(), range1.end(), range2.begin()));
EXPECT_TRUE(std::equal(range2.begin(), range2.end(), range1.begin()));
EXPECT_TRUE(range1.is_iterable());
EXPECT_TRUE(range2.is_iterable());
}
// v6
{
AddressRange<IPv6Address> range1 = AddressRange<IPv6Address>::from_mask("dead::0", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc");
AddressRange<IPv6Address> range2 = IPv6Address("dead::0") / 126;
EXPECT_TRUE(std::equal(range1.begin(), range1.end(), range2.begin()));
EXPECT_TRUE(std::equal(range2.begin(), range2.end(), range1.begin()));
EXPECT_TRUE(range1.is_iterable());
EXPECT_TRUE(range2.is_iterable());
}
{
AddressRange<IPv6Address> range1 =
AddressRange<IPv6Address>::from_mask(
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc",
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc"
);
AddressRange<IPv6Address> range2 = IPv6Address("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc") / 126;
EXPECT_TRUE(std::equal(range1.begin(), range1.end(), range2.begin()));
EXPECT_TRUE(std::equal(range2.begin(), range2.end(), range1.begin()));
EXPECT_TRUE(range1.is_iterable());
EXPECT_TRUE(range2.is_iterable());
}
}