-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Adding IPv6 Support for Arduino 3.0.0 #8907
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
23fa1f8
a78841a
13d55a7
0cff29d
b88aa54
9f680df
ac21e7d
5740c69
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,68 +20,86 @@ | |
#include <Arduino.h> | ||
#include <IPAddress.h> | ||
#include <Print.h> | ||
#include <StreamString.h> | ||
|
||
IPAddress::IPAddress() | ||
{ | ||
_address.dword = 0; | ||
IPAddress::IPAddress() { | ||
#if LWIP_IPV6 | ||
_ip = *IP6_ADDR_ANY; | ||
#else | ||
_ip = *IP_ADDR_ANY; | ||
#endif | ||
} | ||
|
||
IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) | ||
IPAddress::IPAddress(const IPAddress& from) | ||
{ | ||
_address.bytes[0] = first_octet; | ||
_address.bytes[1] = second_octet; | ||
_address.bytes[2] = third_octet; | ||
_address.bytes[3] = fourth_octet; | ||
ip_addr_copy(_ip, from._ip); | ||
} | ||
|
||
IPAddress::IPAddress(uint32_t address) | ||
{ | ||
_address.dword = address; | ||
IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) { | ||
uint8_t addr[] { | ||
first_octet, | ||
second_octet, | ||
third_octet, | ||
fourth_octet, | ||
}; | ||
*this = &addr[0]; | ||
} | ||
|
||
IPAddress::IPAddress(const uint8_t *address) | ||
{ | ||
memcpy(_address.bytes, address, sizeof(_address.bytes)); | ||
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) { | ||
setV6(); | ||
(*this)[0] = o1; | ||
(*this)[1] = o2; | ||
(*this)[2] = o3; | ||
(*this)[3] = o4; | ||
(*this)[4] = o5; | ||
(*this)[5] = o6; | ||
(*this)[6] = o7; | ||
(*this)[7] = o8; | ||
(*this)[8] = o9; | ||
(*this)[9] = o10; | ||
(*this)[10] = o11; | ||
(*this)[11] = o12; | ||
(*this)[12] = o13; | ||
(*this)[13] = o14; | ||
(*this)[14] = o15; | ||
(*this)[15] = o16; | ||
} | ||
|
||
IPAddress& IPAddress::operator=(const uint8_t *address) | ||
{ | ||
memcpy(_address.bytes, address, sizeof(_address.bytes)); | ||
return *this; | ||
IPAddress::IPAddress(IPType type, const uint8_t *address) { | ||
IPAddress(type, address, 0); | ||
} | ||
|
||
IPAddress& IPAddress::operator=(uint32_t address) | ||
{ | ||
_address.dword = address; | ||
return *this; | ||
} | ||
IPAddress::IPAddress(IPType type, const uint8_t *address, uint8_t zone) { | ||
if (type == IPv4) { | ||
setV4(); | ||
memcpy(&this->_ip.u_addr.ip4, address, 4); | ||
} else if (type == IPv6) { | ||
setV6(); | ||
memcpy(&this->_ip.u_addr.ip6.addr[0], address, 16); | ||
setZone(zone); | ||
} else { | ||
#if LWIP_IPV6 | ||
_ip = *IP6_ADDR_ANY; | ||
#else | ||
_ip = *IP_ADDR_ANY; | ||
#endif | ||
} | ||
|
||
bool IPAddress::operator==(const uint8_t* addr) const | ||
{ | ||
return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0; | ||
} | ||
|
||
size_t IPAddress::printTo(Print& p) const | ||
{ | ||
size_t n = 0; | ||
for(int i = 0; i < 3; i++) { | ||
n += p.print(_address.bytes[i], DEC); | ||
n += p.print('.'); | ||
bool IPAddress::fromString(const char *address) { | ||
if (!fromString4(address)) { | ||
#if LWIP_IPV6 | ||
return fromString6(address); | ||
#else | ||
return false; | ||
#endif | ||
} | ||
n += p.print(_address.bytes[3], DEC); | ||
return n; | ||
} | ||
|
||
String IPAddress::toString() const | ||
{ | ||
char szRet[16]; | ||
sprintf(szRet,"%u.%u.%u.%u", _address.bytes[0], _address.bytes[1], _address.bytes[2], _address.bytes[3]); | ||
return String(szRet); | ||
return true; | ||
} | ||
|
||
bool IPAddress::fromString(const char *address) | ||
{ | ||
// TODO: add support for "a", "a.b", "a.b.c" formats | ||
bool IPAddress::fromString4(const char *address) { | ||
// TODO: (IPv4) add support for "a", "a.b", "a.b.c" formats | ||
|
||
uint16_t acc = 0; // Accumulator | ||
uint8_t dots = 0; | ||
|
@@ -103,7 +121,7 @@ bool IPAddress::fromString(const char *address) | |
// Too much dots (there must be 3 dots) | ||
return false; | ||
} | ||
_address.bytes[dots++] = acc; | ||
(*this)[dots++] = acc; | ||
acc = 0; | ||
} | ||
else | ||
|
@@ -117,9 +135,122 @@ bool IPAddress::fromString(const char *address) | |
// Too few dots (there must be 3 dots) | ||
return false; | ||
} | ||
_address.bytes[3] = acc; | ||
(*this)[3] = acc; | ||
|
||
setV4(); | ||
return true; | ||
} | ||
|
||
IPAddress& IPAddress::operator=(const uint8_t *address) { | ||
uint32_t value; | ||
memcpy_P(&value, address, sizeof(value)); | ||
*this = value; | ||
return *this; | ||
} | ||
|
||
IPAddress& IPAddress::operator=(uint32_t address) { | ||
setV4(); | ||
v4() = address; | ||
return *this; | ||
} | ||
|
||
bool IPAddress::operator==(const uint8_t* addr) const { | ||
if (!isV4()) { | ||
return false; | ||
} | ||
|
||
uint32_t value; | ||
memcpy_P(&value, addr, sizeof(value)); | ||
|
||
return v4() == value; | ||
} | ||
|
||
size_t IPAddress::printTo(Print& p) const { | ||
size_t n = 0; | ||
|
||
#if LWIP_IPV6 | ||
if (isV6()) { | ||
int count0 = 0; | ||
for (int i = 0; i < 8; i++) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this supposed to output the canonical format, i.e. with consecutive zeros of the longest run (not necessarily the first) combined? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a ip6addr_ntoa() that will do the convert for us (and we then copy to the print buffer). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is also an It does two passes... first start from the left to find a run of two or more zero fields, only replacing if a later run is longer (not the same). This gives a single location of the left-most longest run. The second pass then outputs, adding the compression at the detected location. ... but if we have access to ip6addr_ntoa() it might be better to just use that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also suggest adding an automated test project. The ArduinoCore-API has automated tests for a whole bunch of corner cases of canonical to string, at https://github.com/arduino/ArduinoCore-API/blob/master/test/src/IPAddress/test_printTo6.cpp e.g. where there is a run of 2 zeros followed by a later run of 3 zeros. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a cut and paste from ESP8266. I have not experienced any issue in IPv6 formatting. Do you have a breaking example? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A tricky example, where the last run of zeros is longest is: ip(0,0, 0,1, 0,0, 0,0, 0,2, 0,0, 0,0, 0,0). This should output: "0:1:0:0:2::" Executing the current algorithm by hand gives ":1:0:0:2:0:0:0" (I think) Some other tricky ones to check ip(0,0, 0,1, 0,0, 0,0, 0,0, 0,2, 0,0, 0,0); => "0:1::2:0:0" If you have automated tests, just copy the cases from ArduinoCore |
||
uint16_t bit = PP_NTOHS(raw6()[i]); | ||
if (bit || count0 < 0) { | ||
n += p.printf("%x", bit); | ||
if (count0 > 0) | ||
// no more hiding 0 | ||
count0 = -8; | ||
} else | ||
count0++; | ||
if ((i != 7 && count0 < 2) || count0 == 7) | ||
n += p.print(':'); | ||
} | ||
// add a zone if zone-id si non-zero | ||
if (_ip.u_addr.ip6.zone) { | ||
n += p.print('%'); | ||
char if_name[NETIF_NAMESIZE]; | ||
netif_index_to_name(_ip.u_addr.ip6.zone, if_name); | ||
n += p.print(if_name); | ||
} | ||
return n; | ||
} | ||
#endif | ||
|
||
for(int i = 0; i < 4; i++) { | ||
n += p.print((*this)[i], DEC); | ||
if (i != 3) | ||
n += p.print('.'); | ||
} | ||
return n; | ||
} | ||
|
||
String IPAddress::toString() const | ||
{ | ||
StreamString sstr; | ||
#if LWIP_IPV6 | ||
if (isV6()) | ||
sstr.reserve(44); // 8 shorts x 4 chars each + 7 colons + nullterm + '%' + zone-id | ||
else | ||
#endif | ||
sstr.reserve(16); // 4 bytes with 3 chars max + 3 dots + nullterm, or '(IP unset)' | ||
printTo(sstr); | ||
return sstr; | ||
} | ||
|
||
bool IPAddress::isValid(const String& arg) { | ||
return IPAddress().fromString(arg); | ||
} | ||
|
||
bool IPAddress::isValid(const char* arg) { | ||
return IPAddress().fromString(arg); | ||
} | ||
|
||
const IPAddress INADDR46_ANY; // generic "0.0.0.0" for IPv4 & IPv6 | ||
const IPAddress INADDR46_NONE(255,255,255,255); | ||
|
||
void IPAddress::clear() { | ||
(*this) = INADDR46_ANY; | ||
} | ||
|
||
/**************************************/ | ||
|
||
#if LWIP_IPV6 | ||
|
||
bool IPAddress::fromString6(const char *address) { | ||
ip6_addr_t ip6; | ||
if (ip6addr_aton(address, &ip6)) { | ||
setV6(); | ||
memcpy(&this->_ip.u_addr.ip6.addr[0], ip6.addr, 16); | ||
// look for '%' in string | ||
const char *s = address; | ||
while (*s && *s != '%') { s++; } | ||
if (*s == '%') { | ||
// we have a zone id | ||
setZone(netif_name_to_index(s + 1)); | ||
} | ||
return true; | ||
} | ||
return false; | ||
} | ||
#endif // LWIP_IPV6 | ||
|
||
// declared one time - as external in IPAddress.h | ||
IPAddress INADDR_NONE(0, 0, 0, 0); | ||
IPAddress INADDR_NONE(0, 0, 0, 0); // TODO |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Default constructor does not set all bytes to 0.