diff --git a/CHANGELOG.md b/CHANGELOG.md index 11bcdb256..616551629 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ All notable changes to this project will be documented in this file. - Teleinfo TEMPO (BBR) contract (#17160) - Support for HLK-LD2410 24GHz smart wave motion sensor - Berry ``mdns`` module (#17202) +- IPv6 preview for ESP32, also working for ESP8266 ### Changed - Serial Bridge default internal serial rx buffer size from 64 to 256 (#17120) diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/AddrList.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/AddrList46.h similarity index 68% rename from lib/libesp32/ESP32-to-ESP8266-compat/src/AddrList.h rename to lib/libesp32/ESP32-to-ESP8266-compat/src/AddrList46.h index cc32ea232..bc2d07a39 100644 --- a/lib/libesp32/ESP32-to-ESP8266-compat/src/AddrList.h +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/AddrList46.h @@ -77,6 +77,7 @@ #include #include +#include "IPAddress46.h" #if LWIP_IPV6 #define IF_NUM_ADDRESSES (1 + LWIP_IPV6_NUM_ADDRESSES) @@ -84,80 +85,63 @@ #define IF_NUM_ADDRESSES (1) #endif - namespace esp8266 { - namespace AddressListImplementation { - - struct netifWrapper { - netifWrapper (netif* netif) : _netif(netif), _num(-1) {} - netifWrapper (const netifWrapper& o) : _netif(o._netif), _num(o._num) {} + netifWrapper (netif* netif) : _netif(netif), _num(-1) {} + netifWrapper (const netifWrapper& o) : _netif(o._netif), _num(o._num) {} - netifWrapper& operator= (const netifWrapper& o) - { - _netif = o._netif; - _num = o._num; - return *this; - } + netifWrapper& operator= (const netifWrapper& o) + { + _netif = o._netif; + _num = o._num; + return *this; + } - bool equal(const netifWrapper& o) - { - return _netif == o._netif && (!_netif || _num == o._num); - } + bool equal(const netifWrapper& o) + { + return _netif == o._netif && (!_netif || _num == o._num); + } - // address properties - class IPAddress4 : public IPAddress - { - public: - bool isV6() const - { - return false; - } - bool isLocal() const - { - return false; - } - }; - IPAddress4 addr () const { return ipFromNetifNum(); } - bool isLegacy () const { return _num == 0; } - //bool isLocal () const { return addr().isLocal(); } - bool isV4 () const { return addr().isV4(); } - bool isV6 () const { return !addr().isV4(); } - String toString() const { return addr().toString(); } + IPAddress46 addr () const { return ipFromNetifNum(); } + bool isLegacy () const { return _num == 0; } + bool isLocal () const { return addr().isLocal(); } + bool isV4 () const { return addr().isV4(); } + bool isV6 () const { return !addr().isV4(); } + String toString() const { return addr().toString(); } - // related to legacy address (_num=0, ipv4) - IPAddress ipv4 () const { return _netif->ip_addr; } - IPAddress netmask () const { return _netif->netmask; } - IPAddress gw () const { return _netif->gw; } + // related to legacy address (_num=0, ipv4) + IPAddress46 ipv4 () const { return _netif->ip_addr; } + IPAddress46 netmask () const { return _netif->netmask; } + IPAddress46 gw () const { return _netif->gw; } - // common to all addresses of this interface - String ifname () const { return String(_netif->name[0]) + _netif->name[1]; } - const char* ifhostname () const { return _netif->hostname?: emptyString.c_str(); } - const char* ifmac () const { return (const char*)_netif->hwaddr; } - int ifnumber () const { return _netif->num; } - bool ifUp () const { return !!(_netif->flags & NETIF_FLAG_UP); } - const netif* interface () const { return _netif; } + // common to all addresses of this interface + String ifname () const { return String(_netif->name[0]) + _netif->name[1]; } + const char* ifhostname () const { return _netif->hostname?: emptyString.c_str(); } + const char* ifmac () const { return (const char*)_netif->hwaddr; } + int ifnumber () const { return _netif->num; } + bool ifUp () const { return !!(_netif->flags & NETIF_FLAG_UP); } + const netif* interface () const { return _netif; } - const ip_addr_t* ipFromNetifNum () const - { + const ip_addr_t* ipFromNetifNum () const + { #if LWIP_IPV6 - return _num ? &_netif->ip6_addr[_num - 1] : &_netif->ip_addr; + return _num ? &_netif->ip6_addr[_num - 1] : &_netif->ip_addr; #else - return &_netif->ip_addr; + return &_netif->ip_addr; #endif - } + } - // lwIP interface - netif* _netif; + // lwIP interface + netif* _netif; - // address index within interface - // 0: legacy address (IPv4) - // n>0: (_num-1) is IPv6 index for netif->ip6_addr[] - int _num; + // address index within interface + // 0: legacy address (IPv4) + // n>0: (_num-1) is IPv6 index for netif->ip6_addr[] + int _num; }; @@ -223,11 +207,10 @@ inline AddressList::const_iterator begin (const AddressList& a) { return a.begin inline AddressList::const_iterator end (const AddressList& a) { return a.end(); } -} // AddressListImplementation +} // namespace AddressListImplementation +} // namespace esp8266 -} // esp8266 - -extern AddressList addrList; +extern esp8266::AddressListImplementation::AddressList addrList; #endif diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/ESP8266WiFi.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/ESP8266WiFi.h index e7a760164..f0a8476f6 100644 --- a/lib/libesp32/ESP32-to-ESP8266-compat/src/ESP8266WiFi.h +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/ESP8266WiFi.h @@ -19,9 +19,6 @@ #pragma once #include -// sorry, no -#undef LWIP_IPV6 - #define ENC_TYPE_NONE WIFI_AUTH_OPEN #define ENC_TYPE_WEP WIFI_AUTH_WEP #define ENC_TYPE_CCMP WIFI_AUTH_WPA2_PSK diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/IPAddress46.cpp b/lib/libesp32/ESP32-to-ESP8266-compat/src/IPAddress46.cpp new file mode 100644 index 000000000..b0db1a5b8 --- /dev/null +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/IPAddress46.cpp @@ -0,0 +1,270 @@ +/* + IPAddress46.cpp - IPv6 support for ESP32 + + This class is copied from ESP8266 Arduino framework and provides + temporary support for IPv6 on ESP32. + + Copyright (C) 2021 Theo Arends and Stephan Hadinger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* + IPAddress.cpp - Base class that provides IPAddress + + Copyright (c) 2011 Adrian McEwen. All right reserved. + + 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 St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include + +IPAddress46::IPAddress46(const IPAddress46& from) +{ + ip_addr_copy(_ip, from._ip); +} + +IPAddress46::IPAddress46() { + _ip = *IP_ANY_TYPE; // lwIP's v4-or-v6 generic address +} + +bool IPAddress46::isSet () const { + return !ip_addr_isany(&_ip) && ((*this) != IPADDR_NONE); +} + +IPAddress46::IPAddress46(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) { + setV4(); + (*this)[0] = first_octet; + (*this)[1] = second_octet; + (*this)[2] = third_octet; + (*this)[3] = fourth_octet; +} + +void IPAddress46::ctor32(uint32_t address) { + setV4(); + v4() = address; +} + +IPAddress46::IPAddress46(const uint8_t *address) { + setV4(); + (*this)[0] = address[0]; + (*this)[1] = address[1]; + (*this)[2] = address[2]; + (*this)[3] = address[3]; +} + +bool IPAddress46::fromString(const char *address) { + if (!fromString4(address)) { +#if LWIP_IPV6 + return fromString6(address); +#else + return false; +#endif + } + return true; +} + +bool IPAddress46::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; + + while (*address) + { + char c = *address++; + if (c >= '0' && c <= '9') + { + acc = acc * 10 + (c - '0'); + if (acc > 255) { + // Value out of [0..255] range + return false; + } + } + else if (c == '.') + { + if (dots == 3) { + // Too much dots (there must be 3 dots) + return false; + } + (*this)[dots++] = acc; + acc = 0; + } + else + { + // Invalid char + return false; + } + } + + if (dots != 3) { + // Too few dots (there must be 3 dots) + return false; + } + (*this)[3] = acc; + + setV4(); + return true; +} + +IPAddress46& IPAddress46::operator=(const uint8_t *address) { + setV4(); + v4() = *reinterpret_cast(address); + return *this; +} + +IPAddress46& IPAddress46::operator=(uint32_t address) { + setV4(); + v4() = address; + return *this; +} + +bool IPAddress46::operator==(const uint8_t* addr) const { + return isV4() && v4() == *reinterpret_cast(addr); +} + +size_t IPAddress46::printTo(Print& p) const { + size_t n = 0; + + if (!isSet()) + return p.print(F("(IP unset)")); + +#if LWIP_IPV6 + if (isV6()) { + int count0 = 0; + for (int i = 0; i < 8; i++) { + 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(':'); + } + 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 IPAddress46::toString() const +{ + StreamString sstr; +#if LWIP_IPV6 + if (isV6()) + sstr.reserve(40); // 8 shorts x 4 chars each + 7 colons + nullterm + else +#endif + sstr.reserve(16); // 4 bytes with 3 chars max + 3 dots + nullterm, or '(IP unset)' + printTo(sstr); + return sstr; +} + +bool IPAddress46::isValid(const String& arg) { + return IPAddress46().fromString(arg); +} + +bool IPAddress46::isValid(const char* arg) { + return IPAddress46().fromString(arg); +} + +const IPAddress46 INADDR46_ANY; // generic "0.0.0.0" for IPv4 & IPv6 +const IPAddress46 INADDR46_NONE(255,255,255,255); + +void IPAddress46::clear() { + (*this) = INADDR46_ANY; +} + +/**************************************/ + +#if LWIP_IPV6 + +bool IPAddress46::fromString6(const char *address) { + // TODO: test test test + + uint32_t acc = 0; // Accumulator + int dots = 0, doubledots = -1; + + while (*address) + { + char c = tolower(*address++); + if (isalnum(c)) { + if (c >= 'a') + c -= 'a' - '0' - 10; + acc = acc * 16 + (c - '0'); + if (acc > 0xffff) + // Value out of range + return false; + } + else if (c == ':') { + if (*address == ':') { + if (doubledots >= 0) + // :: allowed once + return false; + // remember location + doubledots = dots + !!acc; + address++; + } + if (dots == 7) + // too many separators + return false; + raw6()[dots++] = PP_HTONS(acc); + acc = 0; + } + else + // Invalid char + return false; + } + + if (doubledots == -1 && dots != 7) + // Too few separators + return false; + raw6()[dots++] = PP_HTONS(acc); + + if (doubledots != -1) { + for (int i = dots - doubledots - 1; i >= 0; i--) + raw6()[8 - dots + doubledots + i] = raw6()[doubledots + i]; + for (int i = doubledots; i < 8 - dots + doubledots; i++) + raw6()[i] = 0; + } + + setV6(); + return true; +} + +#endif diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/IPAddress46.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/IPAddress46.h new file mode 100644 index 000000000..952c3ef7d --- /dev/null +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/IPAddress46.h @@ -0,0 +1,184 @@ +/* + IPAddress46.h - IPv6 support for ESP32 + + This class is copied from ESP8266 Arduino framework and provides + temporary support for IPv6 on ESP32. + + Copyright (C) 2021 Theo Arends and Stephan Hadinger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __IPADDRESS46_H +#define __IPADDRESS46_H + +#include +#include +#include + +class IPAddress46: public Printable { + private: + + ip_addr_t _ip; + + // Access the raw byte array containing the address. Because this returns a pointer + // to the internal structure rather than a copy of the address this function should only + // be used when you know that the usage of the returned uint8_t* will be transient and not + // stored. + uint8_t* raw_address() { + return reinterpret_cast(&v4()); + } + const uint8_t* raw_address() const { + return reinterpret_cast(&v4()); + } + + void ctor32 (uint32_t); + + public: + // Constructors + IPAddress46(); + IPAddress46(const IPAddress46& from); + IPAddress46(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet); + IPAddress46(uint32_t address) { ctor32(address); } + IPAddress46(unsigned long address) { ctor32(address); } + IPAddress46(int address) { ctor32(address); } + IPAddress46(const uint8_t *address); + + bool fromString(const char *address); + bool fromString(const String &address) { return fromString(address.c_str()); } + + // Overloaded cast operator to allow IPAddress46 objects to be used where a pointer + // to a four-byte uint8_t array is expected + operator uint32_t() const { return isV4()? v4(): (uint32_t)0; } + operator uint32_t() { return isV4()? v4(): (uint32_t)0; } + + bool isSet () const; + operator bool () const { return isSet(); } // <- + operator bool () { return isSet(); } // <- both are needed + + // generic IPv4 wrapper to uint32-view like arduino loves to see it + const uint32_t& v4() const { return ip_2_ip4(&_ip)->addr; } // for raw_address(const) + uint32_t& v4() { return ip_2_ip4(&_ip)->addr; } + + bool operator==(const IPAddress46& addr) const { + return ip_addr_cmp(&_ip, &addr._ip); + } + bool operator!=(const IPAddress46& addr) const { + return !ip_addr_cmp(&_ip, &addr._ip); + } + bool operator==(uint32_t addr) const { + return isV4() && v4() == addr; + } + bool operator==(unsigned long addr) const { + return isV4() && v4() == (uint32_t)addr; + } + bool operator!=(uint32_t addr) const { + return !(isV4() && v4() == addr); + } + bool operator!=(unsigned long addr) const { + return isV4() && v4() != (uint32_t)addr; + } + bool operator==(const uint8_t* addr) const; + + int operator>>(int n) const { + return isV4()? v4() >> n: 0; + } + + // Overloaded index operator to allow getting and setting individual octets of the address + uint8_t operator[](int index) const { + return isV4()? *(raw_address() + index): 0; + } + uint8_t& operator[](int index) { + setV4(); + return *(raw_address() + index); + } + + // Overloaded copy operators to allow initialisation of IPAddress46 objects from other types + IPAddress46& operator=(const uint8_t *address); + IPAddress46& operator=(uint32_t address); + IPAddress46& operator=(const IPAddress46&) = default; + + virtual size_t printTo(Print& p) const; + String toString() const; + + void clear(); + + /* + check if input string(arg) is a valid IPV4 address or not. + return true on valid. + return false on invalid. + */ + static bool isValid(const String& arg); + static bool isValid(const char* arg); + + friend class EthernetClass; + friend class UDP; + friend class Client; + friend class Server; + friend class DhcpClass; + friend class DNSClient; + + operator ip_addr_t () const { return _ip; } + operator const ip_addr_t*() const { return &_ip; } + operator ip_addr_t*() { return &_ip; } + + bool isV4() const { return IP_IS_V4_VAL(_ip); } + void setV4() { IP_SET_TYPE_VAL(_ip, IPADDR_TYPE_V4); } + + bool isLocal () const { return ip_addr_islinklocal(&_ip); } + +#if LWIP_IPV6 + IPAddress46(const ip_addr_t& lwip_addr) { ip_addr_copy(_ip, lwip_addr); } + IPAddress46(const ip_addr_t* lwip_addr) { ip_addr_copy(_ip, *lwip_addr); } + + IPAddress46& operator=(const ip_addr_t& lwip_addr) { ip_addr_copy(_ip, lwip_addr); return *this; } + IPAddress46& operator=(const ip_addr_t* lwip_addr) { ip_addr_copy(_ip, *lwip_addr); return *this; } + + uint16_t* raw6() + { + setV6(); + return reinterpret_cast(ip_2_ip6(&_ip)); + } + + const uint16_t* raw6() const + { + return isV6()? reinterpret_cast(ip_2_ip6(&_ip)): nullptr; + } + + // when not IPv6, ip_addr_t == ip4_addr_t so this one would be ambiguous + // required otherwise + operator const ip4_addr_t*() const { return isV4()? ip_2_ip4(&_ip): nullptr; } + + bool isV6() const { return IP_IS_V6_VAL(_ip); } + void setV6() { IP_SET_TYPE_VAL(_ip, IPADDR_TYPE_V6); } + + protected: + bool fromString6(const char *address); + +#else + + // allow portable code when IPv6 is not enabled + + uint16_t* raw6() { return nullptr; } + const uint16_t* raw6() const { return nullptr; } + bool isV6() const { return false; } + void setV6() { } + +#endif + + protected: + bool fromString4(const char *address); +}; + +#endif // __IPADDRESS46_H diff --git a/platformio.ini b/platformio.ini index 4ac907b33..3e08f6cb2 100644 --- a/platformio.ini +++ b/platformio.ini @@ -99,6 +99,7 @@ build_flags = ${esp_defaults.build_flags} ; NONOSDK22x_190703 = 2.2.2-dev(38a443e) -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703 -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH + ; -DPIO_FRAMEWORK_ARDUINO_LWIP2_IPV6_HIGHER_BANDWIDTH ; enables IPv6 ; VTABLES in Flash -DVTABLES_IN_FLASH ; remove the 4-bytes alignment for PSTR() diff --git a/tasmota/include/i18n.h b/tasmota/include/i18n.h index b40438194..4028b33d2 100644 --- a/tasmota/include/i18n.h +++ b/tasmota/include/i18n.h @@ -110,6 +110,8 @@ #define D_JSON_IMPORT_REACTIVE "ImportReactive" #define D_JSON_INFRARED "Infrared" #define D_JSON_INVALID_FILE_TYPE "Invalid filetype or buffer" +#define D_JSON_IP6_GLOBAL "IP6Global" +#define D_JSON_IP6_LOCAL "IP6Local" #define D_JSON_UNKNOWN "Unknown" #define D_JSON_LIGHT "Light" #define D_JSON_LINK_COUNT "LinkCount" diff --git a/tasmota/tasmota_support/support_command.ino b/tasmota/tasmota_support/support_command.ino index aaedcf494..51f7b41ca 100644 --- a/tasmota/tasmota_support/support_command.ino +++ b/tasmota/tasmota_support/support_command.ino @@ -801,14 +801,23 @@ void CmndStatus(void) } if ((0 == payload) || (5 == payload)) { + // WifiDumpAddressesIPv6(); Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS5_NETWORK "\":{\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%_I\",\"" D_JSON_GATEWAY "\":\"%_I\",\"" D_JSON_SUBNETMASK "\":\"%_I\",\"" D_JSON_DNSSERVER "1\":\"%_I\",\"" D_JSON_DNSSERVER "2\":\"%_I\",\"" - D_JSON_MAC "\":\"%s\""), + D_JSON_MAC "\":\"%s\"" +#if LWIP_IPV6 + ",\"" D_JSON_IP6_GLOBAL "\":\"%s\",\"" D_JSON_IP6_LOCAL "\":\"%s\"" +#endif // LWIP_IPV6 + ), TasmotaGlobal.hostname, (uint32_t)WiFi.localIP(), Settings->ipv4_address[1], Settings->ipv4_address[2], Settings->ipv4_address[3], Settings->ipv4_address[4], - WiFi.macAddress().c_str()); + WiFi.macAddress().c_str() +#if LWIP_IPV6 + ,WifiGetIPv6().c_str(), WifiGetIPv6LinkLocal().c_str() +#endif // LWIP_IPV6 + ); #ifdef USE_TASMESH ResponseAppend_P(PSTR(",\"SoftAPMac\":\"%s\""), WiFi.softAPmacAddress().c_str()); #endif // USE_TASMESH diff --git a/tasmota/tasmota_support/support_wifi.ino b/tasmota/tasmota_support/support_wifi.ino index a64d9b974..8fb65b4ed 100644 --- a/tasmota/tasmota_support/support_wifi.ino +++ b/tasmota/tasmota_support/support_wifi.ino @@ -42,7 +42,11 @@ const uint8_t WIFI_RETRY_OFFSET_SEC = WIFI_RETRY_SECONDS; // seconds #include // Wifi, MQTT, Ota, WifiManager #if LWIP_IPV6 -#include // IPv6 DualStack + #ifdef ESP8266 + #include // IPv6 DualStack + #else + #include // IPv6 DualStack + #endif #endif // LWIP_IPV6=1 int WifiGetRssiAsQuality(int rssi) { @@ -263,20 +267,6 @@ void WifiBegin(uint8_t flag, uint8_t channel) { if (Settings->flag5.wait_for_wifi_result) { // SetOption142 - (Wifi) Wait 1 second for wifi connection solving some FRITZ!Box modem issues (1) WiFi.waitForConnectResult(1000); // https://github.com/arendst/Tasmota/issues/14985 } - -#if LWIP_IPV6 - for (bool configured = false; !configured;) { - uint16_t cfgcnt = 0; - for (auto addr : addrList) { - if ((configured = !addr.isLocal() && addr.isV6()) || cfgcnt==30) { - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI "Got IPv6 global address %s"), addr.toString().c_str()); - break; // IPv6 is mandatory but stop after 15 seconds - } - delay(500); // Loop until real IPv6 address is aquired or too many tries failed - cfgcnt++; - } - } -#endif // LWIP_IPV6=1 } void WifiBeginAfterScan(void) @@ -473,19 +463,55 @@ void WifiSetState(uint8_t state) } #if LWIP_IPV6 +// Returns only IPv6 global address (no loopback and no link-local) String WifiGetIPv6(void) { for (auto a : addrList) { - if(!a.isLocal() && a.isV6()) return a.toString(); + if(!ip_addr_isloopback((ip_addr_t*)a.addr()) && !a.isLocal() && a.isV6()) return a.toString(); } return ""; } + +String WifiGetIPv6LinkLocal(void) +{ + for (auto a : addrList) { + if(!ip_addr_isloopback((ip_addr_t*)a.addr()) && a.isLocal() && a.isV6()) return a.toString(); + } + return ""; +} + +// add an IPv6 link-local address to all netif +void CreateLinkLocalIPv6(void) +{ +#ifdef ESP32 + for (auto intf = esp_netif_next(NULL); intf != NULL; intf = esp_netif_next(intf)) { + esp_netif_create_ip6_linklocal(intf); + } +#endif // ESP32 +} + +void WifiDumpAddressesIPv6(void) +{ + for (auto a: addrList) + AddLog(LOG_LEVEL_DEBUG, PSTR("IF='%s' index=%d legacy=%d IPv4=%d local=%d addr='%s'"), + a.ifname().c_str(), + a.ifnumber(), + a.isLegacy(), + a.addr().isV4(), + a.addr().isLocal(), + a.toString().c_str()); +} #endif // LWIP_IPV6=1 // Check to see if we have any routable IP address bool WifiHasIP(void) { -#ifdef LWIP2_IPV6 - return !a.isLocal(); +#if LWIP_IPV6 + for (auto a : addrList) { + if(!ip_addr_isloopback((ip_addr_t*)a.addr()) && !a.isLocal()) { + return true; + } + } + return false; #else return (uint32_t)WiFi.localIP() != 0; #endif @@ -504,6 +530,11 @@ void WifiCheckIp(void) { Settings->ipv4_address[2] = (uint32_t)WiFi.subnetMask(); Settings->ipv4_address[3] = (uint32_t)WiFi.dnsIP(); Settings->ipv4_address[4] = (uint32_t)WiFi.dnsIP(1); +#if LWIP_IPV6 + // create Link-local address + CreateLinkLocalIPv6(); + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI "IPv6 Link-Local %s"), WifiGetIPv6LinkLocal().c_str()); +#endif // LWIP_IPV6 // Save current AP parameters for quick reconnect Settings->wifi_channel = WiFi.channel(); diff --git a/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino b/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino index b62176159..a681b3603 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino @@ -2362,7 +2362,11 @@ void HandleInformation(void) #if LWIP_IPV6 String ipv6_addr = WifiGetIPv6(); if (ipv6_addr != "") { - WSContentSend_P(PSTR("}1 IPv6 Address }2%s"), ipv6_addr.c_str()); + WSContentSend_P(PSTR("}1 IPv6 Global }2%s"), ipv6_addr.c_str()); + } + ipv6_addr = WifiGetIPv6LinkLocal(); + if (ipv6_addr != "") { + WSContentSend_P(PSTR("}1 IPv6 Link-Local }2%s"), ipv6_addr.c_str()); } #endif // LWIP_IPV6 = 1 if (static_cast(WiFi.localIP()) != 0) { diff --git a/tasmota/tasmota_xdrv_driver/xdrv_02_9_mqtt.ino b/tasmota/tasmota_xdrv_driver/xdrv_02_9_mqtt.ino index 678d148f1..fdd2e3703 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_02_9_mqtt.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_02_9_mqtt.ino @@ -972,7 +972,8 @@ void MqttConnected(void) { ResponseAppend_P(PSTR(",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%_I\""), TasmotaGlobal.hostname, (uint32_t)WiFi.localIP()); #if LWIP_IPV6 - ResponseAppend_P(PSTR(",\"IPv6Address\":\"%s\""), WifiGetIPv6().c_str()); + ResponseAppend_P(PSTR(",\"" D_JSON_IP6_GLOBAL "\":\"%s\""), WifiGetIPv6().c_str()); + ResponseAppend_P(PSTR(",\"" D_JSON_IP6_LOCAL "\":\"%s\""), WifiGetIPv6LinkLocal().c_str()); #endif // LWIP_IPV6 = 1 } #if defined(ESP32) && CONFIG_IDF_TARGET_ESP32 && defined(USE_ETHERNET) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_38_ping.ino b/tasmota/tasmota_xdrv_driver/xdrv_38_ping.ino index acf6181e3..25cbce72d 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_38_ping.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_38_ping.ino @@ -131,12 +131,7 @@ extern "C" { if ((p->len == p->tot_len) && (p->next == nullptr)) { ip_addr_t ping_target; struct icmp_echo_hdr *iecho; -#ifdef ESP8266 - ping_target.addr = ping->ip; -#endif // ESP8266 -#ifdef ESP32 ip_addr_set_ip4_u32(&ping_target, ping->ip); -#endif // ESP32 iecho = (struct icmp_echo_hdr *) p->payload; t_ping_prepare_echo(iecho, ping_size, ping); @@ -171,12 +166,7 @@ extern "C" { // Reveived packet // static uint8_t ICACHE_FLASH_ATTR t_ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *addr) { -#ifdef ESP8266 - Ping_t *ping = t_ping_find(addr->addr); -#endif // ESP8266 -#ifdef ESP32 - Ping_t *ping = t_ping_find(addr->u_addr.ip4.addr); -#endif // ESP32 + Ping_t *ping = t_ping_find(ip_addr_get_ip4_u32(addr)); if (nullptr == ping) { // unknown source address return 0; // don't eat the packet and ignore it diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tasmota.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tasmota.ino index 624bbc5d9..c6246514e 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tasmota.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tasmota.ino @@ -220,6 +220,11 @@ extern "C" { be_map_insert_str(vm, "ip6", ipv6_addr.c_str()); show_rssi = true; } + ipv6_addr = WifiGetIPv6LinkLocal(); + if (ipv6_addr != "") { + be_map_insert_str(vm, "ip6local", ipv6_addr.c_str()); + show_rssi = true; + } #endif if (static_cast(WiFi.localIP()) != 0) { be_map_insert_str(vm, "mac", WiFi.macAddress().c_str());