Skip to content

Commit 1a68747

Browse files
authored
Merge pull request #1901 from peternewman/ipv6-type
Initial IPv6 classes
2 parents 15a6188 + cd9d8de commit 1a68747

File tree

10 files changed

+705
-2
lines changed

10 files changed

+705
-2
lines changed

common/network/IPV4Address.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1515
*
1616
* IPV4Address.cpp
17-
* A IPV4 address
17+
* An IPV4 address
1818
* Copyright (C) 2011 Simon Newton
1919
*/
2020

common/network/IPV6Address.cpp

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
* This library is free software; you can redistribute it and/or
3+
* modify it under the terms of the GNU Lesser General Public
4+
* License as published by the Free Software Foundation; either
5+
* version 2.1 of the License, or (at your option) any later version.
6+
*
7+
* This library is distributed in the hope that it will be useful,
8+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10+
* Lesser General Public License for more details.
11+
*
12+
* You should have received a copy of the GNU Lesser General Public
13+
* License along with this library; if not, write to the Free Software
14+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15+
*
16+
* IPV6Address.cpp
17+
* An IPV6 address
18+
* Copyright (C) 2023 Peter Newman
19+
*/
20+
21+
#if HAVE_CONFIG_H
22+
#include <config.h>
23+
#endif // HAVE_CONFIG_H
24+
25+
#ifdef HAVE_SYS_SOCKET_H
26+
#include <sys/socket.h> // Required by FreeBSD
27+
#endif // HAVE_SYS_SOCKET_H
28+
#ifdef HAVE_ARPA_INET_H
29+
#include <arpa/inet.h>
30+
#endif // HAVE_ARPA_INET_H
31+
#ifdef HAVE_NETINET_IN_H
32+
#include <netinet/in.h> // Required by FreeBSD
33+
#endif // HAVE_NETINET_IN_H
34+
35+
#include <assert.h>
36+
#include <math.h>
37+
#include <stdint.h>
38+
#include <limits>
39+
#include <string>
40+
41+
#include "common/network/NetworkUtilsInternal.h"
42+
#include "ola/Logging.h"
43+
#include "ola/network/IPV6Address.h"
44+
#include "ola/network/NetworkUtils.h"
45+
46+
namespace ola {
47+
namespace network {
48+
49+
using std::string;
50+
51+
IPV6Address::IPV6Address(const uint8_t *address) {
52+
// TODO(Peter): Deal with any network byte order conversion?
53+
memcpy(&m_address.s6_addr[0], address, sizeof (struct in6_addr));
54+
}
55+
56+
bool IPV6Address::operator<(const IPV6Address &other) const {
57+
// TODO(Peter): Deal with any network byte order conversion?
58+
return (memcmp(&m_address.s6_addr[0],
59+
&other.m_address.s6_addr[0],
60+
sizeof (struct in6_addr)) < 0);
61+
}
62+
63+
bool IPV6Address::operator>(const IPV6Address &other) const {
64+
// TODO(Peter): Deal with any network byte order conversion?
65+
return (memcmp(&m_address.s6_addr[0],
66+
&other.m_address.s6_addr[0],
67+
sizeof (struct in6_addr)) > 0);
68+
}
69+
70+
bool IPV6StringToAddress(const string &address, struct in6_addr *addr) {
71+
bool ok;
72+
//// TODO(Peter): This currently allows some rather quirky values as per
73+
//// inet_pton, we may want to restrict that in future to match IPV6Validator
74+
//// if that deviates
75+
76+
if (address.empty()) {
77+
// Don't bother trying to extract an address if we weren't given one
78+
return false;
79+
}
80+
81+
#ifdef HAVE_INET_PTON
82+
ok = (1 == inet_pton(AF_INET6, address.data(), addr));
83+
#else
84+
OLA_FATAL << "Failed to convert string to address, inet_pton unavailable";
85+
return false;
86+
#endif // HAVE_INET_PTON
87+
88+
if (!ok) {
89+
OLA_WARN << "Could not convert address " << address;
90+
}
91+
return ok;
92+
}
93+
94+
bool IPV6Address::IsWildcard() const {
95+
return IN6_IS_ADDR_UNSPECIFIED(&m_address);
96+
}
97+
98+
string IPV6Address::ToString() const {
99+
struct in6_addr addr;
100+
addr = m_address;
101+
#ifdef HAVE_INET_NTOP
102+
char str[INET6_ADDRSTRLEN];
103+
if (inet_ntop(AF_INET6, &addr, str, INET6_ADDRSTRLEN) == NULL) {
104+
OLA_FATAL << "Failed to convert address to string using inet_ntop";
105+
return NULL;
106+
}
107+
return str;
108+
#else
109+
OLA_FATAL << "Failed to convert address to string, inet_ntop unavailable";
110+
return NULL;
111+
#endif // HAVE_INET_NTOP
112+
}
113+
114+
IPV6Address* IPV6Address::FromString(const string &address) {
115+
struct in6_addr addr;
116+
if (!IPV6StringToAddress(address, &addr)) {
117+
return NULL;
118+
}
119+
120+
return new IPV6Address(addr);
121+
}
122+
123+
bool IPV6Address::FromString(const string &address, IPV6Address *target) {
124+
struct in6_addr addr;
125+
if (!IPV6StringToAddress(address, &addr)) {
126+
return false;
127+
}
128+
*target = IPV6Address(addr);
129+
return true;
130+
}
131+
132+
IPV6Address IPV6Address::FromStringOrDie(const string &address) {
133+
struct in6_addr addr;
134+
assert(IPV6StringToAddress(address, &addr));
135+
return IPV6Address(addr);
136+
}
137+
138+
/*bool IPV6Address::ToCIDRMask(IPV6Address address, uint8_t *mask) {
139+
uint32_t netmask = NetworkToHost(address.AsInt());
140+
uint8_t bits = 0;
141+
bool seen_one = false;
142+
for (uint8_t i = 0; i < std::numeric_limits<uint32_t>::digits; i++) {
143+
if (netmask & 1) {
144+
bits++;
145+
seen_one = true;
146+
} else {
147+
if (seen_one) {
148+
return false;
149+
}
150+
}
151+
netmask = netmask >> 1;
152+
}
153+
*mask = bits;
154+
return true;
155+
}*/
156+
157+
IPV6Address IPV6Address::WildCard() {
158+
in6_addr wildCard = IN6ADDR_ANY_INIT;
159+
// TODO(Peter): Deal with any host to network conversion...
160+
return IPV6Address(wildCard);
161+
}
162+
163+
IPV6Address IPV6Address::Loopback() {
164+
in6_addr loopback = IN6ADDR_LOOPBACK_INIT;
165+
// TODO(Peter): Deal with any host to network conversion...
166+
// return IPV6Address(HostToNetwork(IN6ADDR_LOOPBACK_INIT));
167+
return IPV6Address(loopback);
168+
}
169+
} // namespace network
170+
} // namespace ola

common/network/IPV6AddressTest.cpp

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
/*
2+
* This library is free software; you can redistribute it and/or
3+
* modify it under the terms of the GNU Lesser General Public
4+
* License as published by the Free Software Foundation; either
5+
* version 2.1 of the License, or (at your option) any later version.
6+
*
7+
* This library is distributed in the hope that it will be useful,
8+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10+
* Lesser General Public License for more details.
11+
*
12+
* You should have received a copy of the GNU Lesser General Public
13+
* License along with this library; if not, write to the Free Software
14+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15+
*
16+
* IPV6AddressTest.cpp
17+
* Test fixture for the IPV6Address class
18+
* Copyright (C) 2023 Peter Newman
19+
*/
20+
21+
#include <cppunit/extensions/HelperMacros.h>
22+
23+
#include <stdint.h>
24+
#include <algorithm>
25+
#include <iostream>
26+
#include <memory>
27+
#include <string>
28+
#include <vector>
29+
#include "common/network/NetworkUtilsInternal.h"
30+
#include "ola/network/IPV6Address.h"
31+
#include "ola/network/NetworkUtils.h"
32+
#include "ola/testing/TestUtils.h"
33+
34+
35+
using ola::network::IPV6Address;
36+
using ola::network::HostToNetwork;
37+
using std::auto_ptr;
38+
using std::string;
39+
using std::vector;
40+
41+
class IPV6AddressTest: public CppUnit::TestFixture {
42+
CPPUNIT_TEST_SUITE(IPV6AddressTest);
43+
CPPUNIT_TEST(testIPV6Address);
44+
CPPUNIT_TEST(testWildcard);
45+
CPPUNIT_TEST(testLoopback);
46+
CPPUNIT_TEST_SUITE_END();
47+
48+
public:
49+
void testIPV6Address();
50+
void testWildcard();
51+
// TODO(Peter): Test the all-nodes link-local multicast group if we add it
52+
void testLoopback();
53+
};
54+
55+
CPPUNIT_TEST_SUITE_REGISTRATION(IPV6AddressTest);
56+
57+
58+
/*
59+
* Test the IPV6 Address class works
60+
*/
61+
void IPV6AddressTest::testIPV6Address() {
62+
IPV6Address wildcard_address;
63+
OLA_ASSERT_EQ(string("::"), wildcard_address.ToString());
64+
// OLA_ASSERT_EQ(static_cast<in_addr_t>(0), wildcard_address.AsInt());
65+
OLA_ASSERT_TRUE(wildcard_address.IsWildcard());
66+
67+
IPV6Address address1 = IPV6Address::FromStringOrDie("::ffff:c0a8:101");
68+
// int ip_as_int = address1.AsInt();
69+
OLA_ASSERT_NE(wildcard_address, address1);
70+
// OLA_ASSERT_NE(HostToNetwork(0xc0a811), ip_as_int);
71+
// OLA_ASSERT_EQ(HostToNetwork(0xc0a80101), static_cast<uint32_t>(ip_as_int));
72+
OLA_ASSERT_EQ(string("::ffff:192.168.1.1"), address1.ToString());
73+
74+
IPV6Address address2 = IPV6Address::FromStringOrDie(
75+
"2001:db8:1234:5678:90ab:cdef:feed:face");
76+
// int ip_as_int = address2.AsInt();
77+
OLA_ASSERT_NE(wildcard_address, address2);
78+
// OLA_ASSERT_NE(HostToNetwork(0xc0a811), ip_as_int);
79+
// OLA_ASSERT_EQ(HostToNetwork(0xc0a80101), static_cast<uint32_t>(ip_as_int));
80+
81+
const uint8_t big_endian_address_data[] =
82+
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 10, 0, 0, 1};
83+
IPV6Address binary_address(big_endian_address_data);
84+
OLA_ASSERT_EQ(string("::ffff:10.0.0.1"), binary_address.ToString());
85+
86+
// Test Get()
87+
uint8_t address_data[] = {32, 1, 13, 184, 18, 52, 86, 120,
88+
144, 171, 205, 239, 254, 237, 250, 206};
89+
uint8_t addr[IPV6Address::LENGTH];
90+
address2.Get(addr);
91+
OLA_ASSERT_DATA_EQUALS(addr,
92+
sizeof(addr),
93+
reinterpret_cast<uint8_t*>(&address_data),
94+
sizeof(address_data));
95+
96+
// test copy and assignment
97+
IPV6Address address3(address1);
98+
OLA_ASSERT_EQ(address1, address3);
99+
IPV6Address address4 = address1;
100+
OLA_ASSERT_EQ(address1, address4);
101+
102+
// test stringification
103+
OLA_ASSERT_EQ(string("::ffff:192.168.1.1"), address1.ToString());
104+
std::ostringstream str;
105+
str << address1;
106+
OLA_ASSERT_EQ(string("::ffff:192.168.1.1"), str.str());
107+
108+
// test from string
109+
auto_ptr<IPV6Address> string_address(
110+
IPV6Address::FromString("::ffff:10.0.0.1"));
111+
OLA_ASSERT_NOT_NULL(string_address.get());
112+
OLA_ASSERT_EQ(string("::ffff:10.0.0.1"), string_address->ToString());
113+
114+
auto_ptr<IPV6Address> string_address2(IPV6Address::FromString("foo"));
115+
OLA_ASSERT_NULL(string_address2.get());
116+
117+
// and the second form
118+
IPV6Address string_address3;
119+
OLA_ASSERT_TRUE(IPV6Address::FromString(
120+
"::ffff:172.16.4.1", &string_address3));
121+
OLA_ASSERT_EQ(string("::ffff:172.16.4.1"), string_address3.ToString());
122+
123+
IPV6Address string_address4;
124+
// Add the leading zero to the second group
125+
OLA_ASSERT_TRUE(IPV6Address::FromString(
126+
"2001:0db8:1234:5678:90ab:cdef:feed:face", &string_address4));
127+
// Confirm it's not rendered when we convert to a string
128+
OLA_ASSERT_EQ(string("2001:db8:1234:5678:90ab:cdef:feed:face"),
129+
string_address4.ToString());
130+
131+
IPV6Address string_address5;
132+
OLA_ASSERT_TRUE(IPV6Address::FromString(
133+
"2001:db8:dead:beef:dead:beef:dead:beef", &string_address5));
134+
OLA_ASSERT_EQ(string("2001:db8:dead:beef:dead:beef:dead:beef"),
135+
string_address5.ToString());
136+
137+
IPV6Address string_address6;
138+
OLA_ASSERT_FALSE(IPV6Address::FromString("", &string_address6));
139+
140+
// make sure sorting works
141+
vector<IPV6Address> addresses;
142+
addresses.push_back(address1);
143+
addresses.push_back(*string_address);
144+
addresses.push_back(string_address3);
145+
addresses.push_back(string_address4);
146+
addresses.push_back(string_address5);
147+
std::sort(addresses.begin(), addresses.end());
148+
149+
// The comparisons take into account network byte order automagically.
150+
OLA_ASSERT_EQ(string("::ffff:10.0.0.1"), addresses[0].ToString());
151+
OLA_ASSERT_EQ(string("::ffff:172.16.4.1"), addresses[1].ToString());
152+
OLA_ASSERT_EQ(string("::ffff:192.168.1.1"), addresses[2].ToString());
153+
OLA_ASSERT_EQ(string("2001:db8:1234:5678:90ab:cdef:feed:face"),
154+
addresses[3].ToString());
155+
OLA_ASSERT_EQ(string("2001:db8:dead:beef:dead:beef:dead:beef"),
156+
addresses[4].ToString());
157+
158+
/* uint8_t mask = 255; // UINT8_MAX;
159+
OLA_ASSERT_TRUE(
160+
IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("0.0.0.0"), &mask));
161+
OLA_ASSERT_EQ(0, static_cast<int>(mask));
162+
163+
OLA_ASSERT_TRUE(
164+
IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("255.0.0.0"),
165+
&mask));
166+
OLA_ASSERT_EQ(8, static_cast<int>(mask));
167+
168+
OLA_ASSERT_TRUE(
169+
IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("255.255.255.0"),
170+
&mask));
171+
OLA_ASSERT_EQ(24, static_cast<int>(mask));
172+
173+
OLA_ASSERT_TRUE(
174+
IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("255.255.255.252"),
175+
&mask));
176+
OLA_ASSERT_EQ(30, static_cast<int>(mask));
177+
178+
OLA_ASSERT_TRUE(
179+
IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("255.255.255.255"),
180+
&mask));
181+
OLA_ASSERT_EQ(32, static_cast<int>(mask));
182+
183+
OLA_ASSERT_FALSE(
184+
IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("255.0.0.255"),
185+
&mask));*/
186+
}
187+
188+
189+
/*
190+
* Test the wildcard address works.
191+
*/
192+
void IPV6AddressTest::testWildcard() {
193+
IPV6Address wildcard_address;
194+
OLA_ASSERT_EQ(string("::"), wildcard_address.ToString());
195+
// OLA_ASSERT_EQ(static_cast<in_addr_t>(0), wildcard_address.AsInt());
196+
OLA_ASSERT_TRUE(wildcard_address.IsWildcard());
197+
198+
IPV6Address wildcard_address2 = IPV6Address::WildCard();
199+
OLA_ASSERT_EQ(wildcard_address, wildcard_address2);
200+
}
201+
202+
203+
/*
204+
* Test the loopback address works.
205+
*/
206+
void IPV6AddressTest::testLoopback() {
207+
IPV6Address loopback_address = IPV6Address::Loopback();
208+
OLA_ASSERT_EQ(string("::1"), loopback_address.ToString());
209+
}

0 commit comments

Comments
 (0)