-
Notifications
You must be signed in to change notification settings - Fork 207
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1901 from peternewman/ipv6-type
Initial IPv6 classes
- Loading branch information
Showing
10 changed files
with
705 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()); | ||
} |
Oops, something went wrong.