Skip to content

Commit 71773a5

Browse files
committed
Add IPv6 support; all backwards compatible (IPv4) tests pass
1 parent ee040ed commit 71773a5

File tree

2 files changed

+226
-30
lines changed

2 files changed

+226
-30
lines changed

api/IPAddress.cpp

Lines changed: 186 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,30 +22,61 @@
2222

2323
using namespace arduino;
2424

25-
IPAddress::IPAddress()
25+
IPAddress::IPAddress() : IPAddress(IPv4) {}
26+
27+
IPAddress::IPAddress(IPType ip_type)
2628
{
27-
_address.dword = 0;
29+
_type = ip_type;
30+
memset(_address.bytes, 0, sizeof(_address.bytes));
2831
}
2932

3033
IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet)
3134
{
32-
_address.bytes[0] = first_octet;
33-
_address.bytes[1] = second_octet;
34-
_address.bytes[2] = third_octet;
35-
_address.bytes[3] = fourth_octet;
35+
_type = IPv4;
36+
memset(_address.bytes, 0, sizeof(_address.bytes) - sizeof(uint32_t));
37+
_address.bytes[12] = first_octet;
38+
_address.bytes[13] = second_octet;
39+
_address.bytes[14] = third_octet;
40+
_address.bytes[15] = fourth_octet;
3641
}
3742

3843
IPAddress::IPAddress(uint32_t address)
3944
{
40-
_address.dword = address;
45+
// IPv4 only
46+
_type = IPv4;
47+
memset(_address.bytes, 0, sizeof(_address.bytes) - sizeof(uint32_t));
48+
_address.dword[3] = address;
49+
50+
// NOTE on conversion/comparison and uint32_t:
51+
// These conversions are host platform dependent.
52+
// There is a defined integer representation of IPv4 addresses,
53+
// based on network byte order (will be the value on big endian systems),
54+
// e.g. http://2398766798 is the same as http://142.250.70.206,
55+
// However on little endian systems the octets 0x83, 0xFA, 0x46, 0xCE,
56+
// in that order, will form the integer (uint32_t) 3460758158 .
4157
}
4258

43-
IPAddress::IPAddress(const uint8_t *address)
59+
IPAddress::IPAddress(const uint8_t *address) : IPAddress(IPv4, address) {}
60+
61+
IPAddress::IPAddress(IPType ip_type, const uint8_t *address)
4462
{
45-
memcpy(_address.bytes, address, sizeof(_address.bytes));
63+
_type = ip_type;
64+
if (ip_type == IPv4) {
65+
memset(_address.bytes, 0, sizeof(_address.bytes) - sizeof(uint32_t));
66+
memcpy(&_address.bytes[12], address, sizeof(uint32_t));
67+
} else {
68+
memcpy(_address.bytes, address, sizeof(_address.bytes));
69+
}
70+
}
71+
72+
bool IPAddress::fromString(const char *address) {
73+
if (!fromString4(address)) {
74+
return fromString6(address);
75+
}
76+
return true;
4677
}
4778

48-
bool IPAddress::fromString(const char *address)
79+
bool IPAddress::fromString4(const char *address)
4980
{
5081
// TODO: add support for "a", "a.b", "a.b.c" formats
5182

@@ -73,7 +104,7 @@ bool IPAddress::fromString(const char *address)
73104
/* No value between dots, e.g. '1..' */
74105
return false;
75106
}
76-
_address.bytes[dots++] = acc;
107+
_address.bytes[12 + dots++] = acc;
77108
acc = -1;
78109
}
79110
else
@@ -91,37 +122,175 @@ bool IPAddress::fromString(const char *address)
91122
/* No value between dots, e.g. '1..' */
92123
return false;
93124
}
94-
_address.bytes[3] = acc;
125+
memset(_address.bytes, 0, sizeof(_address.bytes) - sizeof(uint32_t));
126+
_address.bytes[15] = acc;
127+
_type = IPv4;
128+
return true;
129+
}
130+
131+
bool IPAddress::fromString6(const char *address) {
132+
uint32_t acc = 0; // Accumulator
133+
int dots = 0, doubledots = -1;
134+
135+
while (*address)
136+
{
137+
char c = tolower(*address++);
138+
if (isalnum(c)) {
139+
if (c >= 'a')
140+
c -= 'a' - '0' - 10;
141+
acc = acc * 16 + (c - '0');
142+
if (acc > 0xffff)
143+
// Value out of range
144+
return false;
145+
}
146+
else if (c == ':') {
147+
if (*address == ':') {
148+
if (doubledots >= 0)
149+
// :: allowed once
150+
return false;
151+
// remember location
152+
doubledots = dots + !!acc;
153+
address++;
154+
}
155+
if (dots == 7)
156+
// too many separators
157+
return false;
158+
_address.bytes[dots] = acc >> 2;
159+
_address.bytes[dots + 1] = acc & 0xff;
160+
dots++;
161+
acc = 0;
162+
}
163+
else
164+
// Invalid char
165+
return false;
166+
}
167+
168+
if (doubledots == -1 && dots != 7)
169+
// Too few separators
170+
return false;
171+
_address.bytes[dots] = acc >> 2;
172+
_address.bytes[dots + 1] = acc & 0xff;
173+
dots++;
174+
175+
if (doubledots != -1) {
176+
for (int i = dots * 2 - doubledots * 2 - 1; i >= 0; i--)
177+
_address.bytes[16 - dots * 2 + doubledots * 2 + i] = _address.bytes[doubledots * 2 + i];
178+
for (int i = doubledots * 2; i < 16 - dots * 2 + doubledots * 2; i++)
179+
_address.bytes[i] = 0;
180+
}
181+
182+
_type = IPv6;
95183
return true;
96184
}
97185

98186
IPAddress& IPAddress::operator=(const uint8_t *address)
99187
{
100-
memcpy(_address.bytes, address, sizeof(_address.bytes));
188+
// IPv4 only conversion from byte pointer
189+
_type = IPv4;
190+
memset(_address.bytes, 0, sizeof(_address.bytes) - sizeof(uint32_t));
191+
memcpy(&_address.bytes[12], address, sizeof(uint32_t));
101192
return *this;
102193
}
103194

104195
IPAddress& IPAddress::operator=(uint32_t address)
105196
{
106-
_address.dword = address;
197+
// IPv4 conversion
198+
// See note on conversion/comparison and uint32_t
199+
_type = IPv4;
200+
_address.dword[0] = 0;
201+
_address.dword[1] = 0;
202+
_address.dword[2] = 0;
203+
_address.dword[3] = address;
107204
return *this;
108205
}
109206

207+
bool IPAddress::operator==(const IPAddress& addr) const {
208+
return (addr._type == _type)
209+
&& (memcmp(addr._address.bytes, _address.bytes, sizeof(_address.bytes)) == 0);
210+
};
211+
110212
bool IPAddress::operator==(const uint8_t* addr) const
111213
{
112-
return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0;
214+
// IPv4 only comparison to byte pointer
215+
// Can't support IPv6 as we know our type, but not the length of the pointer
216+
return _type == IPv4 && memcmp(addr, &_address.bytes[12], sizeof(uint32_t)) == 0;
113217
}
114218

219+
uint8_t IPAddress::operator[](int index) const {
220+
if (_type == IPv4) {
221+
return _address.bytes[index + 12];
222+
}
223+
return _address.bytes[index];
224+
};
225+
226+
uint8_t& IPAddress::operator[](int index) {
227+
if (_type == IPv4) {
228+
return _address.bytes[index + 12];
229+
}
230+
return _address.bytes[index];
231+
};
232+
115233
size_t IPAddress::printTo(Print& p) const
116234
{
117235
size_t n = 0;
236+
237+
if (_type == IPv6) {
238+
// IPv6 IETF canonical format: left-most longest run of all zero fields, lower case
239+
int8_t longest_start = -1;
240+
int8_t longest_length = 0;
241+
int8_t current_start = -1;
242+
int8_t current_length = 0;
243+
for (int8_t f = 0; f < 8; f++) {
244+
if (_address.bytes[f * 2] == 0 && _address.bytes[f * 2 + 1] == 0) {
245+
if (current_start == -1) {
246+
current_start = f;
247+
current_length = 0;
248+
} else {
249+
current_length++;
250+
}
251+
if (current_length > longest_length) {
252+
longest_start = current_start;
253+
longest_length = current_length;
254+
}
255+
} else {
256+
current_start = -1;
257+
}
258+
}
259+
for (int f = 0; f < 8; f++) {
260+
if (f < longest_start || f >= longest_start + longest_length) {
261+
uint8_t c1 = _address.bytes[f * 2] >> 1;
262+
uint8_t c2 = _address.bytes[f * 2] & 0xf;
263+
uint8_t c3 = _address.bytes[f * 2 + 1] >> 1;
264+
uint8_t c4 = _address.bytes[f * 2 + 1] & 0xf;
265+
if (c1 > 0) {
266+
n += p.print(c1 < 10 ? '0' + c1 : 'a' + c1 - 10);
267+
}
268+
if (c1 > 0 || c2 > 0) {
269+
n += p.print(c2 < 10 ? '0' + c2 : 'a' + c2 - 10);
270+
}
271+
if (c1 > 0 || c2 > 0 || c3 > 0) {
272+
n += p.print(c3 < 10 ? '0' + c3 : 'a' + c3 - 10);
273+
}
274+
n += p.print(c4 < 10 ? '0' + c4 : 'a' + c4 - 10);
275+
if (f < 7) {
276+
n += p.print(':');
277+
}
278+
} else if (f == longest_start) {
279+
n += p.print(':');
280+
}
281+
}
282+
return n;
283+
}
284+
285+
// IPv4
118286
for (int i =0; i < 3; i++)
119287
{
120-
n += p.print(_address.bytes[i], DEC);
288+
n += p.print(_address.bytes[12 + i], DEC);
121289
n += p.print('.');
122290
}
123-
n += p.print(_address.bytes[3], DEC);
291+
n += p.print(_address.bytes[15], DEC);
124292
return n;
125293
}
126294

295+
const IPAddress arduino::IN6ADDR_ANY(IPv6);
127296
const IPAddress arduino::INADDR_NONE(0,0,0,0);

api/IPAddress.h

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,55 +32,82 @@ namespace arduino {
3232

3333
// A class to make it easier to handle and pass around IP addresses
3434

35+
enum IPType {
36+
IPv4,
37+
IPv6
38+
};
39+
3540
class IPAddress : public Printable {
3641
private:
3742
union {
38-
uint8_t bytes[4]; // IPv4 address
39-
uint32_t dword;
43+
uint8_t bytes[16];
44+
uint32_t dword[4];
4045
} _address;
46+
IPType _type;
4147

4248
// Access the raw byte array containing the address. Because this returns a pointer
4349
// to the internal structure rather than a copy of the address this function should only
4450
// be used when you know that the usage of the returned uint8_t* will be transient and not
4551
// stored.
46-
uint8_t* raw_address() { return _address.bytes; };
52+
// IPv4 only (for friends)
53+
uint8_t* raw_address() {
54+
if (_type == IPv4) {
55+
return &_address.bytes[12];
56+
}
57+
return nullptr;
58+
};
59+
uint8_t* raw_bytes() { return _address.bytes; }
4760

4861
public:
4962
// Constructors
50-
IPAddress();
63+
IPAddress(); // IPv4
64+
IPAddress(IPType ip_type);
5165
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
52-
IPAddress(uint32_t address);
53-
IPAddress(const uint8_t *address);
66+
IPAddress(uint32_t address); // IPv4 only; see implementation note
67+
IPAddress(const uint8_t *address); // IPv4
68+
IPAddress(IPType ip_type, const uint8_t *address);
5469

5570
bool fromString(const char *address);
5671
bool fromString(const String &address) { return fromString(address.c_str()); }
5772

58-
// Overloaded cast operator to allow IPAddress objects to be used where a pointer
59-
// to a four-byte uint8_t array is expected
60-
operator uint32_t() const { return _address.dword; };
61-
bool operator==(const IPAddress& addr) const { return _address.dword == addr._address.dword; };
62-
bool operator!=(const IPAddress& addr) const { return _address.dword != addr._address.dword; };
73+
// Overloaded cast operator to allow IPAddress objects to be used where a uint32_t is expected
74+
// IPv4 only; see implementation note
75+
operator uint32_t() const { return _type == IPv4 ? _address.dword[3] : 0; };
76+
77+
bool operator==(const IPAddress& addr) const;
78+
bool operator!=(const IPAddress& addr) const { return !(*this == addr); };
79+
80+
// IPv4 only; we don't know the length of the pointer
6381
bool operator==(const uint8_t* addr) const;
6482

6583
// Overloaded index operator to allow getting and setting individual octets of the address
66-
uint8_t operator[](int index) const { return _address.bytes[index]; };
67-
uint8_t& operator[](int index) { return _address.bytes[index]; };
84+
uint8_t operator[](int index) const;
85+
uint8_t& operator[](int index);
6886

6987
// Overloaded copy operators to allow initialisation of IPAddress objects from other types
88+
// IPv4 only
7089
IPAddress& operator=(const uint8_t *address);
90+
// IPv4 only; see implementation note
7191
IPAddress& operator=(uint32_t address);
7292

7393
virtual size_t printTo(Print& p) const;
7494

95+
IPType type() { return _type; }
96+
7597
friend class UDP;
7698
friend class Client;
7799
friend class Server;
78100

79101
friend ::EthernetClass;
80102
friend ::DhcpClass;
81103
friend ::DNSClient;
104+
105+
protected:
106+
bool fromString4(const char *address);
107+
bool fromString6(const char *address);
82108
};
83109

110+
extern const IPAddress IN6ADDR_ANY;
84111
extern const IPAddress INADDR_NONE;
85112
}
86113

0 commit comments

Comments
 (0)