Skip to content

Commit

Permalink
Merge pull request #1901 from peternewman/ipv6-type
Browse files Browse the repository at this point in the history
Initial IPv6 classes
  • Loading branch information
peternewman authored Oct 29, 2023
2 parents 15a6188 + cd9d8de commit 1a68747
Show file tree
Hide file tree
Showing 10 changed files with 705 additions and 2 deletions.
2 changes: 1 addition & 1 deletion common/network/IPV4Address.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* IPV4Address.cpp
* A IPV4 address
* An IPV4 address
* Copyright (C) 2011 Simon Newton
*/

Expand Down
170 changes: 170 additions & 0 deletions common/network/IPV6Address.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* IPV6Address.cpp
* An IPV6 address
* Copyright (C) 2023 Peter Newman
*/

#if HAVE_CONFIG_H
#include <config.h>
#endif // HAVE_CONFIG_H

#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h> // Required by FreeBSD
#endif // HAVE_SYS_SOCKET_H
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif // HAVE_ARPA_INET_H
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h> // Required by FreeBSD
#endif // HAVE_NETINET_IN_H

#include <assert.h>
#include <math.h>
#include <stdint.h>
#include <limits>
#include <string>

#include "common/network/NetworkUtilsInternal.h"
#include "ola/Logging.h"
#include "ola/network/IPV6Address.h"
#include "ola/network/NetworkUtils.h"

namespace ola {
namespace network {

using std::string;

IPV6Address::IPV6Address(const uint8_t *address) {
// TODO(Peter): Deal with any network byte order conversion?
memcpy(&m_address.s6_addr[0], address, sizeof (struct in6_addr));
}

bool IPV6Address::operator<(const IPV6Address &other) const {
// TODO(Peter): Deal with any network byte order conversion?
return (memcmp(&m_address.s6_addr[0],
&other.m_address.s6_addr[0],
sizeof (struct in6_addr)) < 0);
}

bool IPV6Address::operator>(const IPV6Address &other) const {
// TODO(Peter): Deal with any network byte order conversion?
return (memcmp(&m_address.s6_addr[0],
&other.m_address.s6_addr[0],
sizeof (struct in6_addr)) > 0);
}

bool IPV6StringToAddress(const string &address, struct in6_addr *addr) {
bool ok;
//// TODO(Peter): This currently allows some rather quirky values as per
//// inet_pton, we may want to restrict that in future to match IPV6Validator
//// if that deviates

if (address.empty()) {
// Don't bother trying to extract an address if we weren't given one
return false;
}

#ifdef HAVE_INET_PTON
ok = (1 == inet_pton(AF_INET6, address.data(), addr));
#else
OLA_FATAL << "Failed to convert string to address, inet_pton unavailable";
return false;
#endif // HAVE_INET_PTON

if (!ok) {
OLA_WARN << "Could not convert address " << address;
}
return ok;
}

bool IPV6Address::IsWildcard() const {
return IN6_IS_ADDR_UNSPECIFIED(&m_address);
}

string IPV6Address::ToString() const {
struct in6_addr addr;
addr = m_address;
#ifdef HAVE_INET_NTOP
char str[INET6_ADDRSTRLEN];
if (inet_ntop(AF_INET6, &addr, str, INET6_ADDRSTRLEN) == NULL) {
OLA_FATAL << "Failed to convert address to string using inet_ntop";
return NULL;
}
return str;
#else
OLA_FATAL << "Failed to convert address to string, inet_ntop unavailable";
return NULL;
#endif // HAVE_INET_NTOP
}

IPV6Address* IPV6Address::FromString(const string &address) {
struct in6_addr addr;
if (!IPV6StringToAddress(address, &addr)) {
return NULL;
}

return new IPV6Address(addr);
}

bool IPV6Address::FromString(const string &address, IPV6Address *target) {
struct in6_addr addr;
if (!IPV6StringToAddress(address, &addr)) {
return false;
}
*target = IPV6Address(addr);
return true;
}

IPV6Address IPV6Address::FromStringOrDie(const string &address) {
struct in6_addr addr;
assert(IPV6StringToAddress(address, &addr));
return IPV6Address(addr);
}

/*bool IPV6Address::ToCIDRMask(IPV6Address address, uint8_t *mask) {
uint32_t netmask = NetworkToHost(address.AsInt());
uint8_t bits = 0;
bool seen_one = false;
for (uint8_t i = 0; i < std::numeric_limits<uint32_t>::digits; i++) {
if (netmask & 1) {
bits++;
seen_one = true;
} else {
if (seen_one) {
return false;
}
}
netmask = netmask >> 1;
}
*mask = bits;
return true;
}*/

IPV6Address IPV6Address::WildCard() {
in6_addr wildCard = IN6ADDR_ANY_INIT;
// TODO(Peter): Deal with any host to network conversion...
return IPV6Address(wildCard);
}

IPV6Address IPV6Address::Loopback() {
in6_addr loopback = IN6ADDR_LOOPBACK_INIT;
// TODO(Peter): Deal with any host to network conversion...
// return IPV6Address(HostToNetwork(IN6ADDR_LOOPBACK_INIT));
return IPV6Address(loopback);
}
} // namespace network
} // namespace ola
209 changes: 209 additions & 0 deletions common/network/IPV6AddressTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* IPV6AddressTest.cpp
* Test fixture for the IPV6Address class
* Copyright (C) 2023 Peter Newman
*/

#include <cppunit/extensions/HelperMacros.h>

#include <stdint.h>
#include <algorithm>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include "common/network/NetworkUtilsInternal.h"
#include "ola/network/IPV6Address.h"
#include "ola/network/NetworkUtils.h"
#include "ola/testing/TestUtils.h"


using ola::network::IPV6Address;
using ola::network::HostToNetwork;
using std::auto_ptr;
using std::string;
using std::vector;

class IPV6AddressTest: public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(IPV6AddressTest);
CPPUNIT_TEST(testIPV6Address);
CPPUNIT_TEST(testWildcard);
CPPUNIT_TEST(testLoopback);
CPPUNIT_TEST_SUITE_END();

public:
void testIPV6Address();
void testWildcard();
// TODO(Peter): Test the all-nodes link-local multicast group if we add it
void testLoopback();
};

CPPUNIT_TEST_SUITE_REGISTRATION(IPV6AddressTest);


/*
* Test the IPV6 Address class works
*/
void IPV6AddressTest::testIPV6Address() {
IPV6Address wildcard_address;
OLA_ASSERT_EQ(string("::"), wildcard_address.ToString());
// OLA_ASSERT_EQ(static_cast<in_addr_t>(0), wildcard_address.AsInt());
OLA_ASSERT_TRUE(wildcard_address.IsWildcard());

IPV6Address address1 = IPV6Address::FromStringOrDie("::ffff:c0a8:101");
// int ip_as_int = address1.AsInt();
OLA_ASSERT_NE(wildcard_address, address1);
// OLA_ASSERT_NE(HostToNetwork(0xc0a811), ip_as_int);
// OLA_ASSERT_EQ(HostToNetwork(0xc0a80101), static_cast<uint32_t>(ip_as_int));
OLA_ASSERT_EQ(string("::ffff:192.168.1.1"), address1.ToString());

IPV6Address address2 = IPV6Address::FromStringOrDie(
"2001:db8:1234:5678:90ab:cdef:feed:face");
// int ip_as_int = address2.AsInt();
OLA_ASSERT_NE(wildcard_address, address2);
// OLA_ASSERT_NE(HostToNetwork(0xc0a811), ip_as_int);
// OLA_ASSERT_EQ(HostToNetwork(0xc0a80101), static_cast<uint32_t>(ip_as_int));

const uint8_t big_endian_address_data[] =
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 10, 0, 0, 1};
IPV6Address binary_address(big_endian_address_data);
OLA_ASSERT_EQ(string("::ffff:10.0.0.1"), binary_address.ToString());

// Test Get()
uint8_t address_data[] = {32, 1, 13, 184, 18, 52, 86, 120,
144, 171, 205, 239, 254, 237, 250, 206};
uint8_t addr[IPV6Address::LENGTH];
address2.Get(addr);
OLA_ASSERT_DATA_EQUALS(addr,
sizeof(addr),
reinterpret_cast<uint8_t*>(&address_data),
sizeof(address_data));

// test copy and assignment
IPV6Address address3(address1);
OLA_ASSERT_EQ(address1, address3);
IPV6Address address4 = address1;
OLA_ASSERT_EQ(address1, address4);

// test stringification
OLA_ASSERT_EQ(string("::ffff:192.168.1.1"), address1.ToString());
std::ostringstream str;
str << address1;
OLA_ASSERT_EQ(string("::ffff:192.168.1.1"), str.str());

// test from string
auto_ptr<IPV6Address> string_address(
IPV6Address::FromString("::ffff:10.0.0.1"));
OLA_ASSERT_NOT_NULL(string_address.get());
OLA_ASSERT_EQ(string("::ffff:10.0.0.1"), string_address->ToString());

auto_ptr<IPV6Address> string_address2(IPV6Address::FromString("foo"));
OLA_ASSERT_NULL(string_address2.get());

// and the second form
IPV6Address string_address3;
OLA_ASSERT_TRUE(IPV6Address::FromString(
"::ffff:172.16.4.1", &string_address3));
OLA_ASSERT_EQ(string("::ffff:172.16.4.1"), string_address3.ToString());

IPV6Address string_address4;
// Add the leading zero to the second group
OLA_ASSERT_TRUE(IPV6Address::FromString(
"2001:0db8:1234:5678:90ab:cdef:feed:face", &string_address4));
// Confirm it's not rendered when we convert to a string
OLA_ASSERT_EQ(string("2001:db8:1234:5678:90ab:cdef:feed:face"),
string_address4.ToString());

IPV6Address string_address5;
OLA_ASSERT_TRUE(IPV6Address::FromString(
"2001:db8:dead:beef:dead:beef:dead:beef", &string_address5));
OLA_ASSERT_EQ(string("2001:db8:dead:beef:dead:beef:dead:beef"),
string_address5.ToString());

IPV6Address string_address6;
OLA_ASSERT_FALSE(IPV6Address::FromString("", &string_address6));

// make sure sorting works
vector<IPV6Address> addresses;
addresses.push_back(address1);
addresses.push_back(*string_address);
addresses.push_back(string_address3);
addresses.push_back(string_address4);
addresses.push_back(string_address5);
std::sort(addresses.begin(), addresses.end());

// The comparisons take into account network byte order automagically.
OLA_ASSERT_EQ(string("::ffff:10.0.0.1"), addresses[0].ToString());
OLA_ASSERT_EQ(string("::ffff:172.16.4.1"), addresses[1].ToString());
OLA_ASSERT_EQ(string("::ffff:192.168.1.1"), addresses[2].ToString());
OLA_ASSERT_EQ(string("2001:db8:1234:5678:90ab:cdef:feed:face"),
addresses[3].ToString());
OLA_ASSERT_EQ(string("2001:db8:dead:beef:dead:beef:dead:beef"),
addresses[4].ToString());

/* uint8_t mask = 255; // UINT8_MAX;
OLA_ASSERT_TRUE(
IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("0.0.0.0"), &mask));
OLA_ASSERT_EQ(0, static_cast<int>(mask));
OLA_ASSERT_TRUE(
IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("255.0.0.0"),
&mask));
OLA_ASSERT_EQ(8, static_cast<int>(mask));
OLA_ASSERT_TRUE(
IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("255.255.255.0"),
&mask));
OLA_ASSERT_EQ(24, static_cast<int>(mask));
OLA_ASSERT_TRUE(
IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("255.255.255.252"),
&mask));
OLA_ASSERT_EQ(30, static_cast<int>(mask));
OLA_ASSERT_TRUE(
IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("255.255.255.255"),
&mask));
OLA_ASSERT_EQ(32, static_cast<int>(mask));
OLA_ASSERT_FALSE(
IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("255.0.0.255"),
&mask));*/
}


/*
* Test the wildcard address works.
*/
void IPV6AddressTest::testWildcard() {
IPV6Address wildcard_address;
OLA_ASSERT_EQ(string("::"), wildcard_address.ToString());
// OLA_ASSERT_EQ(static_cast<in_addr_t>(0), wildcard_address.AsInt());
OLA_ASSERT_TRUE(wildcard_address.IsWildcard());

IPV6Address wildcard_address2 = IPV6Address::WildCard();
OLA_ASSERT_EQ(wildcard_address, wildcard_address2);
}


/*
* Test the loopback address works.
*/
void IPV6AddressTest::testLoopback() {
IPV6Address loopback_address = IPV6Address::Loopback();
OLA_ASSERT_EQ(string("::1"), loopback_address.ToString());
}
Loading

0 comments on commit 1a68747

Please sign in to comment.