Skip to content

Commit d82b443

Browse files
Jason2866sgryphonnuclearcat
authored
Ipv6 support v2
* Update header * Update IPAddress implementation to support V6 * Fix cut and paste error * Explicit not equals * Explicitly reference StreamString * Remove != test (it is causing a conflict) * IPv6 support: Add proper link-local and SLAAC in STA and WifiMulti This patch partially depends on: espressif/esp32-arduino-lib-builder#67 Without this patch we will get only Link local IPv6 (still useful for MDNS and etc). With patch we will get also global IPv6 address by SLAAC. By default IPv6 disabled, until it is properly tested. Tested on BasicHttpClient by adding: wifiMulti.IPv6(true); before: wifiMulti.addAP() call Enabling Core Debug Level: verbose If IP6 obtained, in logs will be visible: [ 8028][V][WiFiGeneric.cpp:380] _arduino_event_cb(): IF[0] Got IPv6: IP Index: 0, Zone: 2, fe80:0000:0000:0000:xxxx:xxxx:xxxx:xxxx [ 8028][D][WiFiGeneric.cpp:852] _eventCallback(): Arduino Event: 8 - STA_GOT_IP6 [ 11028][V][WiFiGeneric.cpp:380] _arduino_event_cb(): IF[0] Got IPv6: IP Index: 1, Zone: 0, 2a0d:yyyy:0000:4000:yyyy:yyyy:yyyy:yyyy [ 11028][D][WiFiGeneric.cpp:852] _eventCallback(): Arduino Event: 8 - STA_GOT_IP6 This is linked to: espressif#6242 Signed-off-by: Denys Fedoryshchenko <[email protected]> * Add IPv6 support to WiFiServer One of most useful features of IPv6 to have arduino accessible from internet, without any port forward and etc. Change is fairly trivial and backward compatible with old code, tested with WiFiTelnetToSerial and AsyncUDPServer. Signed-off-by: Denys Fedoryshchenko <[email protected]> * WiFiClient::remoteIP and remoteIP6 IPv6 support For RemoteIP and AF_INET6 socket i added support ip6 to ip4 mapping, so .remoteIP will return IPv4 address on dual stack socket, if available. Scenarios tested: WiFiTelnetToSerial, wifiMulti.IPv6(true), connect both from IPv4 and IPv6 WiFiTelnetToSerial, wifiMulti.IPv6(true); but set to listen on IPv4 only. WiFiTelnetToSerial, IPv6 disabled, with or without bind to specific IP4. AsyncUDPServer, without IPv6 support. Signed-off-by: Denys Fedoryshchenko <[email protected]> * Add WiFiTelnetToSerialIPv6 example To demonstrate new abilities of dual stack WiFiServer and remoteIP6 we add this example. Signed-off-by: Denys Fedoryshchenko <[email protected]> * Add IPv6 to WifiClient (client) We need to be able to connect to remote servers over IPv6 too, and thats need different approach in DNS queries and connect(). As i'm trying to keep maximum compatibility, i introduce different behaviour if IPv6 is enabled, and backward compatible (as much as possible), if IPv6 is not enabled. IN future when IPv6 functions are tested well enough, it can be simplified. This implementation tested on esp32 in following scenarios using BasicHttpClient: IPv6 true: IPv6 only website (caveat 1) - OK Website with A and AAAA is present (caveat 1) - OK IPv4 only website - OK IPv6 not enabled: IPv6 only website - wont open (expected) Website with A and AAAA is present - OK, opens over IPv4 IPv4 only website - OK caveat 1 - sometimes SLAAC is slower than DHCPv4, so we might have status WL_CONNECTED, but IPv6 global scope is not ready yet. Signed-off-by: Denys Fedoryshchenko <[email protected]> * WiFiTelnetToSerialIPv6.ino: fix obsolete remoteIP6 call to remoteIP Example contained API from previous IPv6 implementation, fixing it. Signed-off-by: Denys Fedoryshchenko <[email protected]> Signed-off-by: Denys Fedoryshchenko <[email protected]> Co-authored-by: Sly Gryphon <[email protected]> Co-authored-by: Denys Fedoryshchenko <[email protected]>
1 parent aa94778 commit d82b443

File tree

12 files changed

+562
-60
lines changed

12 files changed

+562
-60
lines changed

Diff for: cores/esp32/IPAddress.cpp

+245-21
Original file line numberDiff line numberDiff line change
@@ -20,78 +20,225 @@
2020
#include <Arduino.h>
2121
#include <IPAddress.h>
2222
#include <Print.h>
23+
#include <StreamString.h>
2324

24-
IPAddress::IPAddress()
25+
IPAddress::IPAddress() : IPAddress(IPv4) {}
26+
27+
IPAddress::IPAddress(IPType ip_type)
2528
{
26-
_address.dword = 0;
29+
_type = ip_type;
30+
memset(_address.bytes, 0, sizeof(_address.bytes));
2731
}
2832

2933
IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet)
3034
{
31-
_address.bytes[0] = first_octet;
32-
_address.bytes[1] = second_octet;
33-
_address.bytes[2] = third_octet;
34-
_address.bytes[3] = fourth_octet;
35+
_type = IPv4;
36+
memset(_address.bytes, 0, sizeof(_address.bytes));
37+
_address.bytes[IPADDRESS_V4_BYTES_INDEX] = first_octet;
38+
_address.bytes[IPADDRESS_V4_BYTES_INDEX + 1] = second_octet;
39+
_address.bytes[IPADDRESS_V4_BYTES_INDEX + 2] = third_octet;
40+
_address.bytes[IPADDRESS_V4_BYTES_INDEX + 3] = fourth_octet;
41+
}
42+
43+
IPAddress::IPAddress(uint8_t o1, uint8_t o2, uint8_t o3, uint8_t o4, uint8_t o5, uint8_t o6, uint8_t o7, uint8_t o8, uint8_t o9, uint8_t o10, uint8_t o11, uint8_t o12, uint8_t o13, uint8_t o14, uint8_t o15, uint8_t o16) {
44+
_type = IPv6;
45+
_address.bytes[0] = o1;
46+
_address.bytes[1] = o2;
47+
_address.bytes[2] = o3;
48+
_address.bytes[3] = o4;
49+
_address.bytes[4] = o5;
50+
_address.bytes[5] = o6;
51+
_address.bytes[6] = o7;
52+
_address.bytes[7] = o8;
53+
_address.bytes[8] = o9;
54+
_address.bytes[9] = o10;
55+
_address.bytes[10] = o11;
56+
_address.bytes[11] = o12;
57+
_address.bytes[12] = o13;
58+
_address.bytes[13] = o14;
59+
_address.bytes[14] = o15;
60+
_address.bytes[15] = o16;
3561
}
3662

3763
IPAddress::IPAddress(uint32_t address)
3864
{
39-
_address.dword = address;
65+
// IPv4 only
66+
_type = IPv4;
67+
memset(_address.bytes, 0, sizeof(_address.bytes));
68+
_address.dword[IPADDRESS_V4_DWORD_INDEX] = address;
69+
70+
// NOTE on conversion/comparison and uint32_t:
71+
// These conversions are host platform dependent.
72+
// There is a defined integer representation of IPv4 addresses,
73+
// based on network byte order (will be the value on big endian systems),
74+
// e.g. http://2398766798 is the same as http://142.250.70.206,
75+
// However on little endian systems the octets 0x83, 0xFA, 0x46, 0xCE,
76+
// in that order, will form the integer (uint32_t) 3460758158 .
4077
}
4178

42-
IPAddress::IPAddress(const uint8_t *address)
79+
IPAddress::IPAddress(const uint8_t *address) : IPAddress(IPv4, address) {}
80+
81+
IPAddress::IPAddress(IPType ip_type, const uint8_t *address)
4382
{
44-
memcpy(_address.bytes, address, sizeof(_address.bytes));
83+
_type = ip_type;
84+
if (ip_type == IPv4) {
85+
memset(_address.bytes, 0, sizeof(_address.bytes));
86+
memcpy(&_address.bytes[IPADDRESS_V4_BYTES_INDEX], address, sizeof(uint32_t));
87+
} else {
88+
memcpy(_address.bytes, address, sizeof(_address.bytes));
89+
}
4590
}
4691

4792
IPAddress& IPAddress::operator=(const uint8_t *address)
4893
{
49-
memcpy(_address.bytes, address, sizeof(_address.bytes));
94+
// IPv4 only conversion from byte pointer
95+
_type = IPv4;
96+
memset(_address.bytes, 0, sizeof(_address.bytes));
97+
memcpy(&_address.bytes[IPADDRESS_V4_BYTES_INDEX], address, sizeof(uint32_t));
5098
return *this;
5199
}
52100

53101
IPAddress& IPAddress::operator=(uint32_t address)
54102
{
55-
_address.dword = address;
103+
// IPv4 conversion
104+
// See note on conversion/comparison and uint32_t
105+
_type = IPv4;
106+
memset(_address.bytes, 0, sizeof(_address.bytes));
107+
_address.dword[IPADDRESS_V4_DWORD_INDEX] = address;
56108
return *this;
57109
}
58110

111+
bool IPAddress::operator==(const IPAddress& addr) const
112+
{
113+
return (addr._type == _type)
114+
&& (memcmp(addr._address.bytes, _address.bytes, sizeof(_address.bytes)) == 0);
115+
}
116+
59117
bool IPAddress::operator==(const uint8_t* addr) const
60118
{
61-
return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0;
119+
// IPv4 only comparison to byte pointer
120+
// Can't support IPv6 as we know our type, but not the length of the pointer
121+
return _type == IPv4 && memcmp(addr, &_address.bytes[IPADDRESS_V4_BYTES_INDEX], sizeof(uint32_t)) == 0;
122+
}
123+
124+
uint8_t IPAddress::operator[](int index) const {
125+
if (_type == IPv4) {
126+
return _address.bytes[IPADDRESS_V4_BYTES_INDEX + index];
127+
}
128+
return _address.bytes[index];
129+
}
130+
131+
uint8_t& IPAddress::operator[](int index) {
132+
if (_type == IPv4) {
133+
return _address.bytes[IPADDRESS_V4_BYTES_INDEX + index];
134+
}
135+
return _address.bytes[index];
62136
}
63137

64138
size_t IPAddress::printTo(Print& p) const
65139
{
66140
size_t n = 0;
67-
for(int i = 0; i < 3; i++) {
68-
n += p.print(_address.bytes[i], DEC);
141+
142+
if (_type == IPv6) {
143+
// IPv6 IETF canonical format: compress left-most longest run of two or more zero fields, lower case
144+
int8_t longest_start = -1;
145+
int8_t longest_length = 1;
146+
int8_t current_start = -1;
147+
int8_t current_length = 0;
148+
for (int8_t f = 0; f < 8; f++) {
149+
if (_address.bytes[f * 2] == 0 && _address.bytes[f * 2 + 1] == 0) {
150+
if (current_start == -1) {
151+
current_start = f;
152+
current_length = 1;
153+
} else {
154+
current_length++;
155+
}
156+
if (current_length > longest_length) {
157+
longest_start = current_start;
158+
longest_length = current_length;
159+
}
160+
} else {
161+
current_start = -1;
162+
}
163+
}
164+
for (int f = 0; f < 8; f++) {
165+
if (f < longest_start || f >= longest_start + longest_length) {
166+
uint8_t c1 = _address.bytes[f * 2] >> 4;
167+
uint8_t c2 = _address.bytes[f * 2] & 0xf;
168+
uint8_t c3 = _address.bytes[f * 2 + 1] >> 4;
169+
uint8_t c4 = _address.bytes[f * 2 + 1] & 0xf;
170+
if (c1 > 0) {
171+
n += p.print((char)(c1 < 10 ? '0' + c1 : 'a' + c1 - 10));
172+
}
173+
if (c1 > 0 || c2 > 0) {
174+
n += p.print((char)(c2 < 10 ? '0' + c2 : 'a' + c2 - 10));
175+
}
176+
if (c1 > 0 || c2 > 0 || c3 > 0) {
177+
n += p.print((char)(c3 < 10 ? '0' + c3 : 'a' + c3 - 10));
178+
}
179+
n += p.print((char)(c4 < 10 ? '0' + c4 : 'a' + c4 - 10));
180+
if (f < 7) {
181+
n += p.print(':');
182+
}
183+
} else if (f == longest_start) {
184+
if (longest_start == 0) {
185+
n += p.print(':');
186+
}
187+
n += p.print(':');
188+
}
189+
}
190+
return n;
191+
}
192+
193+
// IPv4
194+
for (int i =0; i < 3; i++)
195+
{
196+
n += p.print(_address.bytes[IPADDRESS_V4_BYTES_INDEX + i], DEC);
69197
n += p.print('.');
70198
}
71-
n += p.print(_address.bytes[3], DEC);
199+
n += p.print(_address.bytes[IPADDRESS_V4_BYTES_INDEX + 3], DEC);
72200
return n;
73201
}
74202

75203
String IPAddress::toString() const
76204
{
205+
if (_type == IPv6)
206+
{
207+
StreamString s;
208+
s.reserve(40);
209+
printTo(s);
210+
return s;
211+
}
212+
213+
// IPv4
77214
char szRet[16];
78215
sprintf(szRet,"%u.%u.%u.%u", _address.bytes[0], _address.bytes[1], _address.bytes[2], _address.bytes[3]);
79216
return String(szRet);
80217
}
81218

82219
bool IPAddress::fromString(const char *address)
220+
{
221+
if (!fromString4(address))
222+
{
223+
return fromString6(address);
224+
}
225+
return true;
226+
}
227+
228+
bool IPAddress::fromString4(const char *address)
83229
{
84230
// TODO: add support for "a", "a.b", "a.b.c" formats
85231

86-
uint16_t acc = 0; // Accumulator
232+
int16_t acc = -1; // Accumulator
87233
uint8_t dots = 0;
88234

235+
memset(_address.bytes, 0, sizeof(_address.bytes));
89236
while (*address)
90237
{
91238
char c = *address++;
92239
if (c >= '0' && c <= '9')
93240
{
94-
acc = acc * 10 + (c - '0');
241+
acc = (acc < 0) ? (c - '0') : acc * 10 + (c - '0');
95242
if (acc > 255) {
96243
// Value out of [0..255] range
97244
return false;
@@ -100,11 +247,15 @@ bool IPAddress::fromString(const char *address)
100247
else if (c == '.')
101248
{
102249
if (dots == 3) {
103-
// Too much dots (there must be 3 dots)
250+
// Too many dots (there must be 3 dots)
104251
return false;
105252
}
106-
_address.bytes[dots++] = acc;
107-
acc = 0;
253+
if (acc < 0) {
254+
/* No value between dots, e.g. '1..' */
255+
return false;
256+
}
257+
_address.bytes[IPADDRESS_V4_BYTES_INDEX + dots++] = acc;
258+
acc = -1;
108259
}
109260
else
110261
{
@@ -117,7 +268,80 @@ bool IPAddress::fromString(const char *address)
117268
// Too few dots (there must be 3 dots)
118269
return false;
119270
}
120-
_address.bytes[3] = acc;
271+
if (acc < 0) {
272+
/* No value between dots, e.g. '1..' */
273+
return false;
274+
}
275+
_address.bytes[IPADDRESS_V4_BYTES_INDEX + 3] = acc;
276+
_type = IPv4;
277+
return true;
278+
}
279+
280+
bool IPAddress::fromString6(const char *address) {
281+
uint32_t acc = 0; // Accumulator
282+
int colons = 0, double_colons = -1;
283+
284+
while (*address)
285+
{
286+
char c = tolower(*address++);
287+
if (isalnum(c) && c <= 'f') {
288+
if (c >= 'a')
289+
c -= 'a' - '0' - 10;
290+
acc = acc * 16 + (c - '0');
291+
if (acc > 0xffff)
292+
// Value out of range
293+
return false;
294+
}
295+
else if (c == ':') {
296+
if (*address == ':') {
297+
if (double_colons >= 0) {
298+
// :: allowed once
299+
return false;
300+
}
301+
if (*address != '\0' && *(address + 1) == ':') {
302+
// ::: not allowed
303+
return false;
304+
}
305+
// remember location
306+
double_colons = colons + !!acc;
307+
address++;
308+
} else if (*address == '\0') {
309+
// can't end with a single colon
310+
return false;
311+
}
312+
if (colons == 7)
313+
// too many separators
314+
return false;
315+
_address.bytes[colons * 2] = acc >> 8;
316+
_address.bytes[colons * 2 + 1] = acc & 0xff;
317+
colons++;
318+
acc = 0;
319+
}
320+
else
321+
// Invalid char
322+
return false;
323+
}
324+
325+
if (double_colons == -1 && colons != 7) {
326+
// Too few separators
327+
return false;
328+
}
329+
if (double_colons > -1 && colons > 6) {
330+
// Too many segments (double colon must be at least one zero field)
331+
return false;
332+
}
333+
_address.bytes[colons * 2] = acc >> 8;
334+
_address.bytes[colons * 2 + 1] = acc & 0xff;
335+
colons++;
336+
337+
if (double_colons != -1) {
338+
for (int i = colons * 2 - double_colons * 2 - 1; i >= 0; i--)
339+
_address.bytes[16 - colons * 2 + double_colons * 2 + i] = _address.bytes[double_colons * 2 + i];
340+
for (int i = double_colons * 2; i < 16 - colons * 2 + double_colons * 2; i++)
341+
_address.bytes[i] = 0;
342+
}
343+
344+
_type = IPv6;
121345
return true;
122346
}
123347

0 commit comments

Comments
 (0)