From 7b42a93077864c9a5d906ccc2d2a92b6bf6e78f3 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Tue, 21 Nov 2023 18:17:04 +0100
Subject: [PATCH 01/21] IPv6 for Arduino 3.0.0

---
 libraries/WiFi/src/WiFiClient.cpp  |  64 +++++++++--
 libraries/WiFi/src/WiFiGeneric.cpp |  58 +++++++++-
 libraries/WiFi/src/WiFiGeneric.h   |   7 ++
 libraries/WiFi/src/WiFiMulti.cpp   |   7 ++
 libraries/WiFi/src/WiFiMulti.h     |   2 +
 libraries/WiFi/src/WiFiSTA.cpp     |  13 +++
 libraries/WiFi/src/WiFiSTA.h       |   1 +
 libraries/WiFi/src/WiFiServer.cpp  |  28 +++--
 libraries/WiFi/src/WiFiServer.h    |   1 -
 libraries/WiFi/src/WiFiUdp.cpp     | 168 +++++++++++++++++++++++------
 10 files changed, 290 insertions(+), 59 deletions(-)

diff --git a/libraries/WiFi/src/WiFiClient.cpp b/libraries/WiFi/src/WiFiClient.cpp
index 864c33322f3..d8f1a31a62b 100644
--- a/libraries/WiFi/src/WiFiClient.cpp
+++ b/libraries/WiFi/src/WiFiClient.cpp
@@ -23,6 +23,11 @@
 #include <lwip/netdb.h>
 #include <errno.h>
 
+#define IN6_IS_ADDR_V4MAPPED(a) \
+        ((((__const uint32_t *) (a))[0] == 0) \
+         && (((__const uint32_t *) (a))[1] == 0) \
+         && (((__const uint32_t *) (a))[2] == htonl (0xffff)))
+
 #define WIFI_CLIENT_DEF_CONN_TIMEOUT_MS  (3000)
 #define WIFI_CLIENT_MAX_WRITE_RETRY      (10)
 #define WIFI_CLIENT_SELECT_TIMEOUT_US    (1000000)
@@ -219,22 +224,33 @@ int WiFiClient::connect(IPAddress ip, uint16_t port)
 {
     return connect(ip,port,_timeout);
 }
+
 int WiFiClient::connect(IPAddress ip, uint16_t port, int32_t timeout_ms)
 {
+    struct sockaddr_storage serveraddr = {};
     _timeout = timeout_ms;
-    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
+    int sockfd = -1;
+
+    if (ip.type() == IPv6) {
+        struct sockaddr_in6 *tmpaddr = (struct sockaddr_in6 *)&serveraddr;
+        sockfd = socket(AF_INET6, SOCK_STREAM, 0);
+        tmpaddr->sin6_family = AF_INET6;
+        memcpy(tmpaddr->sin6_addr.un.u8_addr, &ip[0], 16);
+        tmpaddr->sin6_port = htons(port);
+        tmpaddr->sin6_scope_id = ip.zone();
+    } else {
+        struct sockaddr_in *tmpaddr = (struct sockaddr_in *)&serveraddr;
+        sockfd = socket(AF_INET, SOCK_STREAM, 0);
+        tmpaddr->sin_family = AF_INET;
+        tmpaddr->sin_addr.s_addr = ip;
+        tmpaddr->sin_port = htons(port);
+    }
     if (sockfd < 0) {
         log_e("socket: %d", errno);
         return 0;
     }
     fcntl( sockfd, F_SETFL, fcntl( sockfd, F_GETFL, 0 ) | O_NONBLOCK );
 
-    uint32_t ip_addr = ip;
-    struct sockaddr_in serveraddr;
-    memset((char *) &serveraddr, 0, sizeof(serveraddr));
-    serveraddr.sin_family = AF_INET;
-    memcpy((void *)&serveraddr.sin_addr.s_addr, (const void *)(&ip_addr), 4);
-    serveraddr.sin_port = htons(port);
     fd_set fdset;
     struct timeval tv;
     FD_ZERO(&fdset);
@@ -303,6 +319,19 @@ int WiFiClient::connect(const char *host, uint16_t port)
 
 int WiFiClient::connect(const char *host, uint16_t port, int32_t timeout_ms)
 {
+    if (WiFiGenericClass::getStatusBits() & WIFI_WANT_IP6_BIT) {
+        ip_addr_t srv6;
+        if(!WiFiGenericClass::hostByName6(host, srv6)){
+            return 0;
+        }
+        if (srv6.type == IPADDR_TYPE_V4) {
+            IPAddress ip(srv6.u_addr.ip4.addr);
+            return connect(ip, port, timeout_ms);
+        } else {
+            IPAddress ip(IPv6, (uint8_t*)&srv6.u_addr.ip6.addr[0]);
+            return connect(ip, port, timeout_ms);
+        }
+    }
     IPAddress srv((uint32_t)0);
     if(!WiFiGenericClass::hostByName(host, srv)){
         return 0;
@@ -574,8 +603,24 @@ IPAddress WiFiClient::remoteIP(int fd) const
     struct sockaddr_storage addr;
     socklen_t len = sizeof addr;
     getpeername(fd, (struct sockaddr*)&addr, &len);
-    struct sockaddr_in *s = (struct sockaddr_in *)&addr;
-    return IPAddress((uint32_t)(s->sin_addr.s_addr));
+
+    // IPv4 socket, old way
+    if (((struct sockaddr*)&addr)->sa_family == AF_INET) {
+        struct sockaddr_in *s = (struct sockaddr_in *)&addr;
+        return IPAddress((uint32_t)(s->sin_addr.s_addr));
+    }
+
+    // IPv6, but it might be IPv4 mapped address
+    if (((struct sockaddr*)&addr)->sa_family == AF_INET6) {
+        struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)&addr;
+        if (IN6_IS_ADDR_V4MAPPED(saddr6->sin6_addr.un.u32_addr)) {
+            return IPAddress(IPv4, (uint8_t*)saddr6->sin6_addr.s6_addr+12);
+        } else {
+            return IPAddress(IPv6, (uint8_t*)(saddr6->sin6_addr.s6_addr), saddr6->sin6_scope_id);
+        }
+    }
+    log_e("WiFiClient::remoteIP Not AF_INET or AF_INET6?");
+    return (IPAddress(0,0,0,0));
 }
 
 uint16_t WiFiClient::remotePort(int fd) const
@@ -638,4 +683,3 @@ int WiFiClient::fd() const
         return clientSocketHandle->fd();
     }
 }
-
diff --git a/libraries/WiFi/src/WiFiGeneric.cpp b/libraries/WiFi/src/WiFiGeneric.cpp
index 8ef803064c4..c43bf46bc30 100644
--- a/libraries/WiFi/src/WiFiGeneric.cpp
+++ b/libraries/WiFi/src/WiFiGeneric.cpp
@@ -1055,8 +1055,8 @@ esp_err_t WiFiGenericClass::_eventCallback(arduino_event_t *event)
     } else if(event->event_id == ARDUINO_EVENT_WIFI_STA_CONNECTED) {
         WiFiSTAClass::_setStatus(WL_IDLE_STATUS);
         setStatusBits(STA_CONNECTED_BIT);
-
-        //esp_netif_create_ip6_linklocal(esp_netifs[ESP_IF_WIFI_STA]);
+        if (getStatusBits() & WIFI_WANT_IP6_BIT)
+            esp_netif_create_ip6_linklocal(esp_netifs[ESP_IF_WIFI_STA]);
     } else if(event->event_id == ARDUINO_EVENT_WIFI_STA_DISCONNECTED) {
         uint8_t reason = event->event_info.wifi_sta_disconnected.reason;
         // Reason 0 causes crash, use reason 1 (UNSPECIFIED) instead
@@ -1577,8 +1577,27 @@ static esp_err_t wifi_gethostbyname_tcpip_ctx(void *param)
 }
 
 /**
- * Resolve the given hostname to an IP address. If passed hostname is an IP address, it will be parsed into IPAddress structure.
- * @param aHostname     Name to be resolved or string containing IP address
+ * IPv6 compatible DNS callback
+ * @param name
+ * @param ipaddr
+ * @param callback_arg
+ */
+static void wifi_dns6_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg)
+{
+    struct dns_api_msg *msg = (struct dns_api_msg *)callback_arg;
+
+    if(ipaddr && !msg->result) {
+        msg->ip_addr = *ipaddr;
+        msg->result = 1;
+    } else {
+        msg->result = -1;
+    }
+    xEventGroupSetBits(_arduino_event_group, WIFI_DNS_DONE_BIT);
+}
+
+/**
+ * Resolve the given hostname to an IP address.
+ * @param aHostname     Name to be resolved
  * @param aResult       IPAddress structure to store the returned IP address
  * @return 1 if aIPAddrString was successfully converted to an IP address,
  *          else error code
@@ -1608,6 +1627,37 @@ int WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult)
     return (uint32_t)aResult != 0;
 }
 
+/**
+ * Resolve the given hostname to an IP6 address.
+ * @param aHostname     Name to be resolved
+ * @param aResult       IPv6Address structure to store the returned IP address
+ * @return 1 if aHostname was successfully converted to an IP address,
+ *          else error code
+ */
+int WiFiGenericClass::hostByName6(const char* aHostname, ip_addr_t& aResult)
+{
+    ip_addr_t addr;
+    struct dns_api_msg arg;
+
+    memset(&arg, 0x0, sizeof(arg));
+    waitStatusBits(WIFI_DNS_IDLE_BIT, 16000);
+    clearStatusBits(WIFI_DNS_IDLE_BIT | WIFI_DNS_DONE_BIT);
+
+    err_t err = dns_gethostbyname_addrtype(aHostname, &addr, &wifi_dns6_found_callback,
+                &arg, LWIP_DNS_ADDRTYPE_IPV6_IPV4);
+    if(err == ERR_OK) {
+        aResult = addr;
+    } else if(err == ERR_INPROGRESS) {
+        waitStatusBits(WIFI_DNS_DONE_BIT, 15000);  //real internal timeout in lwip library is 14[s]
+        clearStatusBits(WIFI_DNS_DONE_BIT);
+        if (arg.result == 1) {
+            aResult = arg.ip_addr;
+        }
+    }
+    setStatusBits(WIFI_DNS_IDLE_BIT);
+    return (uint32_t)err == ERR_OK || (err == ERR_INPROGRESS && arg.result == 1);
+}
+
 IPAddress WiFiGenericClass::calculateNetworkID(IPAddress ip, IPAddress subnet) {
 	IPAddress networkID;
 
diff --git a/libraries/WiFi/src/WiFiGeneric.h b/libraries/WiFi/src/WiFiGeneric.h
index 38396f5a72e..ce723409eca 100644
--- a/libraries/WiFi/src/WiFiGeneric.h
+++ b/libraries/WiFi/src/WiFiGeneric.h
@@ -141,6 +141,7 @@ static const int WIFI_SCANNING_BIT = BIT11;
 static const int WIFI_SCAN_DONE_BIT= BIT12;
 static const int WIFI_DNS_IDLE_BIT = BIT13;
 static const int WIFI_DNS_DONE_BIT = BIT14;
+static const int WIFI_WANT_IP6_BIT = BIT15;
 
 typedef enum {
 	WIFI_RX_ANT0 = 0,
@@ -154,6 +155,11 @@ typedef enum {
 	WIFI_TX_ANT_AUTO
 } wifi_tx_ant_t;
 
+struct dns_api_msg {
+    ip_addr_t ip_addr;
+    int result;
+};
+
 class WiFiGenericClass
 {
   public:
@@ -196,6 +202,7 @@ class WiFiGenericClass
     static const char * getHostname();
     static bool setHostname(const char * hostname);
     static bool hostname(const String& aHostname) { return setHostname(aHostname.c_str()); }
+    static int hostByName6(const char *aHostname, ip_addr_t& aResult);
 
     static esp_err_t _eventCallback(arduino_event_t *event);
     
diff --git a/libraries/WiFi/src/WiFiMulti.cpp b/libraries/WiFi/src/WiFiMulti.cpp
index 3d69e481293..9e7f03c6531 100644
--- a/libraries/WiFi/src/WiFiMulti.cpp
+++ b/libraries/WiFi/src/WiFiMulti.cpp
@@ -30,6 +30,7 @@
 
 WiFiMulti::WiFiMulti()
 {
+    ipv6_support = false;
 }
 
 WiFiMulti::~WiFiMulti()
@@ -160,6 +161,8 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
             log_i("[WIFI] Connecting BSSID: %02X:%02X:%02X:%02X:%02X:%02X SSID: %s Channel: %d (%d)", bestBSSID[0], bestBSSID[1], bestBSSID[2], bestBSSID[3], bestBSSID[4], bestBSSID[5], bestNetwork.ssid, bestChannel, bestNetworkDb);
 
             WiFi.begin(bestNetwork.ssid, bestNetwork.passphrase, bestChannel, bestBSSID);
+            if (ipv6_support == true)
+                WiFi.IPv6(true);
             status = WiFi.status();
 
             auto startTime = millis();
@@ -202,3 +205,7 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
 
     return status;
 }
+
+void WiFiMulti::IPv6(bool state) {
+    ipv6_support = state;
+}
diff --git a/libraries/WiFi/src/WiFiMulti.h b/libraries/WiFi/src/WiFiMulti.h
index 38ddb5d9f95..bbeb78dc860 100644
--- a/libraries/WiFi/src/WiFiMulti.h
+++ b/libraries/WiFi/src/WiFiMulti.h
@@ -42,10 +42,12 @@ class WiFiMulti
 
     bool addAP(const char* ssid, const char *passphrase = NULL);
 
+    void IPv6(bool state);
     uint8_t run(uint32_t connectTimeout=5000);
 
 private:
     std::vector<WifiAPlist_t> APlist;
+    bool ipv6_support;
 };
 
 #endif /* WIFICLIENTMULTI_H_ */
diff --git a/libraries/WiFi/src/WiFiSTA.cpp b/libraries/WiFi/src/WiFiSTA.cpp
index 505940015f9..ca3fb6b5e40 100644
--- a/libraries/WiFi/src/WiFiSTA.cpp
+++ b/libraries/WiFi/src/WiFiSTA.cpp
@@ -816,6 +816,19 @@ bool WiFiSTAClass::enableIpV6()
     return esp_netif_create_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_STA)) == ESP_OK;
 }
 
+/**
+ * Enable IPv6 support on the station interface.
+ * @return true on success
+ */
+bool WiFiSTAClass::IPv6(bool state)
+{
+   if (state)
+       WiFiGenericClass::setStatusBits(WIFI_WANT_IP6_BIT);
+   else
+       WiFiGenericClass::clearStatusBits(WIFI_WANT_IP6_BIT);
+   return true;
+}
+
 /**
  * Get the station interface IPv6 address.
  * @return IPv6Address
diff --git a/libraries/WiFi/src/WiFiSTA.h b/libraries/WiFi/src/WiFiSTA.h
index 07f01922cef..12e2f0bd4a4 100644
--- a/libraries/WiFi/src/WiFiSTA.h
+++ b/libraries/WiFi/src/WiFiSTA.h
@@ -94,6 +94,7 @@ class WiFiSTAClass
     uint8_t subnetCIDR();
     
     bool enableIpV6();
+    bool IPv6(bool state);
     IPv6Address localIPv6();
 
     // STA WiFi info
diff --git a/libraries/WiFi/src/WiFiServer.cpp b/libraries/WiFi/src/WiFiServer.cpp
index ef59cf14d6d..d3f2b0b0120 100644
--- a/libraries/WiFi/src/WiFiServer.cpp
+++ b/libraries/WiFi/src/WiFiServer.cpp
@@ -51,8 +51,8 @@ WiFiClient WiFiServer::accept(){
     _accepted_sockfd = -1;
   }
   else {
-  struct sockaddr_in _client;
-  int cs = sizeof(struct sockaddr_in);
+  struct sockaddr_in6 _client;
+  int cs = sizeof(struct sockaddr_in6);
 #ifdef ESP_IDF_VERSION_MAJOR
     client_sock = lwip_accept(sockfd, (struct sockaddr *)&_client, (socklen_t*)&cs);
 #else
@@ -80,14 +80,23 @@ void WiFiServer::begin(uint16_t port, int enable){
   if(port){
       _port = port;
   }
-  struct sockaddr_in server;
-  sockfd = socket(AF_INET , SOCK_STREAM, 0);
+  struct sockaddr_in6 server;
+  sockfd = socket(AF_INET6 , SOCK_STREAM, 0);
   if (sockfd < 0)
     return;
   setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
-  server.sin_family = AF_INET;
-  server.sin_addr.s_addr = _addr;
-  server.sin_port = htons(_port);
+  server.sin6_family = AF_INET6;
+  if (_addr.type() == IPv4) {
+    // log_e("---------------- IPv4");
+    memcpy(server.sin6_addr.s6_addr+11, (uint8_t*)&_addr[0], 4);
+    server.sin6_addr.s6_addr[10] = 0xFF;
+    server.sin6_addr.s6_addr[11] = 0xFF;
+  } else {
+    // log_e("---------------- IPv6");
+    memcpy(server.sin6_addr.s6_addr, (uint8_t*)&_addr[0], 16);
+  }
+  memset(server.sin6_addr.s6_addr, 0x0, 16);
+  server.sin6_port = htons(_port);
   if(bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0)
     return;
   if(listen(sockfd , _max_clients) < 0)
@@ -110,8 +119,8 @@ bool WiFiServer::hasClient() {
     if (_accepted_sockfd >= 0) {
       return true;
     }
-    struct sockaddr_in _client;
-    int cs = sizeof(struct sockaddr_in);
+    struct sockaddr_in6 _client;
+    int cs = sizeof(struct sockaddr_in6);
 #ifdef ESP_IDF_VERSION_MAJOR
     _accepted_sockfd = lwip_accept(sockfd, (struct sockaddr *)&_client, (socklen_t*)&cs);
 #else
@@ -140,4 +149,3 @@ void WiFiServer::close(){
 void WiFiServer::stop(){
   end();
 }
-
diff --git a/libraries/WiFi/src/WiFiServer.h b/libraries/WiFi/src/WiFiServer.h
index b9db726b9b7..2c2ebc0b8f2 100644
--- a/libraries/WiFi/src/WiFiServer.h
+++ b/libraries/WiFi/src/WiFiServer.h
@@ -37,7 +37,6 @@ class WiFiServer : public Server {
   public:
     void listenOnLocalhost(){}
 
-    // _addr(INADDR_ANY) is the same as _addr() ==> 0.0.0.0
     WiFiServer(uint16_t port=80, uint8_t max_clients=4):sockfd(-1),_accepted_sockfd(-1),_addr(),_port(port),_max_clients(max_clients),_listening(false),_noDelay(false) {
       log_v("WiFiServer::WiFiServer(port=%d, ...)", port);
     }
diff --git a/libraries/WiFi/src/WiFiUdp.cpp b/libraries/WiFi/src/WiFiUdp.cpp
index 6b053bbbd14..7c503cec2f3 100644
--- a/libraries/WiFi/src/WiFiUdp.cpp
+++ b/libraries/WiFi/src/WiFiUdp.cpp
@@ -51,7 +51,11 @@ uint8_t WiFiUDP::begin(IPAddress address, uint16_t port){
   }
   tx_buffer_len = 0;
 
+#if LWIP_IPV6
+  if ((udp_server=socket(address.isV6() ? AF_INET6 : AF_INET, SOCK_DGRAM, 0)) == -1){
+#else
   if ((udp_server=socket(AF_INET, SOCK_DGRAM, 0)) == -1){
+#endif
     log_e("could not create socket: %d", errno);
     return 0;
   }
@@ -63,12 +67,30 @@ uint8_t WiFiUDP::begin(IPAddress address, uint16_t port){
       return 0;
   }
 
-  struct sockaddr_in addr;
-  memset((char *) &addr, 0, sizeof(addr));
-  addr.sin_family = AF_INET;
-  addr.sin_port = htons(server_port);
-  addr.sin_addr.s_addr = (in_addr_t)address;
-  if(bind(udp_server , (struct sockaddr*)&addr, sizeof(addr)) == -1){
+  struct sockaddr_storage serveraddr = {};
+  size_t sock_size = 0;
+#if LWIP_IPV6
+  if (address.isV6()) {
+    struct sockaddr_in6 *tmpaddr = (struct sockaddr_in6 *)&serveraddr;
+    ip_addr_t * ip_addr = (ip_addr_t*) address;
+    memset((char *) tmpaddr, 0, sizeof(struct sockaddr_in));
+    tmpaddr->sin6_family = AF_INET6;
+    tmpaddr->sin6_port = htons(server_port);
+    tmpaddr->sin6_scope_id = ip_addr->u_addr.ip6.zone;
+    inet6_addr_from_ip6addr(&tmpaddr->sin6_addr, ip_2_ip6(ip_addr));
+    tmpaddr->sin6_flowinfo = 0;
+    sock_size = sizeof(sockaddr_in6);
+  } else
+#endif
+  if (1) {
+    struct sockaddr_in *tmpaddr = (struct sockaddr_in *)&serveraddr;
+    memset((char *) tmpaddr, 0, sizeof(struct sockaddr_in));
+    tmpaddr->sin_family = AF_INET;
+    tmpaddr->sin_port = htons(server_port);
+    tmpaddr->sin_addr.s_addr = (in_addr_t)address;
+    sock_size = sizeof(sockaddr_in);
+  }
+  if(bind(udp_server , (sockaddr*)&serveraddr, sock_size) == -1){
     log_e("could not bind socket: %d", errno);
     stop();
     return 0;
@@ -78,23 +100,47 @@ uint8_t WiFiUDP::begin(IPAddress address, uint16_t port){
 }
 
 uint8_t WiFiUDP::begin(uint16_t p){
-  return begin(IPAddress(INADDR_ANY), p);
+  return begin(IPAddress(), p);
 }
 
 uint8_t WiFiUDP::beginMulticast(IPAddress a, uint16_t p){
-  if(begin(IPAddress(INADDR_ANY), p)){
-    if((uint32_t)a != 0){
-      struct ip_mreq mreq;
-      mreq.imr_multiaddr.s_addr = (in_addr_t)a;
-      mreq.imr_interface.s_addr = INADDR_ANY;
-      if (setsockopt(udp_server, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
+  if(begin(IPAddress(), p)){
+    ip_addr_t * ip_addr = (ip_addr_t*) a;
+    if (ip_addr_ismulticast(ip_addr)) {
+#if LWIP_IPV6
+      if (IP_IS_V6_VAL(*ip_addr)) {
+        struct ipv6_mreq mreq;
+        bool joined = false;
+        inet6_addr_from_ip6addr(&mreq.ipv6mr_multiaddr, ip_2_ip6(ip_addr));
+
+        // iterate on each interface
+        for (netif* intf = netif_list; intf != nullptr; intf = intf->next) {
+          mreq.ipv6mr_interface = intf->num + 1;
+          if (intf->name[0] != 'l' || intf->name[1] != 'o') {   // skip 'lo' local interface
+            int ret = setsockopt(udp_server, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq));
+            if (ret >= 0) { joined = true; }
+          }
+        }
+        if (!joined) {
           log_e("could not join igmp: %d", errno);
           stop();
           return 0;
+        }
+      } else
+#endif
+      if (1) {
+        struct ip_mreq mreq;
+        mreq.imr_multiaddr.s_addr = (in_addr_t)a;
+        mreq.imr_interface.s_addr = INADDR_ANY;
+        if (setsockopt(udp_server, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
+            log_e("could not join igmp: %d", errno);
+            stop();
+            return 0;
+        }
       }
       multicast_ip = a;
+      return 1;
     }
-    return 1;
   }
   return 0;
 }
@@ -112,19 +158,37 @@ void WiFiUDP::stop(){
   }
   if(udp_server == -1)
     return;
-  if((uint32_t)multicast_ip != 0){
-    struct ip_mreq mreq;
-    mreq.imr_multiaddr.s_addr = (in_addr_t)multicast_ip;
-    mreq.imr_interface.s_addr = (in_addr_t)0;
-    setsockopt(udp_server, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
-    multicast_ip = IPAddress(INADDR_ANY);
+  ip_addr_t * mcast_addr = (ip_addr_t*) multicast_ip;
+  if (!ip_addr_isany(mcast_addr)) {
+#if LWIP_IPV6
+    if (IP_IS_V6(mcast_addr)) {
+      struct ipv6_mreq mreq;
+      inet6_addr_from_ip6addr(&mreq.ipv6mr_multiaddr, ip_2_ip6(mcast_addr));
+
+      // iterate on each interface
+      for (netif* intf = netif_list; intf != nullptr; intf = intf->next) {
+        mreq.ipv6mr_interface = intf->num + 1;
+        if (intf->name[0] != 'l' || intf->name[1] != 'o') {   // skip 'lo' local interface
+          int ret = setsockopt(udp_server, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq, sizeof(mreq));
+        }
+      }
+    } else
+#endif
+    if (1) {
+      struct ip_mreq mreq;
+      mreq.imr_multiaddr.s_addr = (in_addr_t)multicast_ip;
+      mreq.imr_interface.s_addr = (in_addr_t)0;
+      setsockopt(udp_server, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
+    }
+    // now common code for v4/v6
+    multicast_ip = IPAddress();
   }
   close(udp_server);
   udp_server = -1;
 }
 
 int WiFiUDP::beginMulticastPacket(){
-  if(!server_port || multicast_ip == IPAddress(INADDR_ANY))
+  if(!server_port || multicast_ip == IPAddress())
     return 0;
   remote_ip = multicast_ip;
   remote_port = server_port;
@@ -176,14 +240,29 @@ int WiFiUDP::beginPacket(const char *host, uint16_t port){
 }
 
 int WiFiUDP::endPacket(){
-  struct sockaddr_in recipient;
-  recipient.sin_addr.s_addr = (uint32_t)remote_ip;
-  recipient.sin_family = AF_INET;
-  recipient.sin_port = htons(remote_port);
-  int sent = sendto(udp_server, tx_buffer, tx_buffer_len, 0, (struct sockaddr*) &recipient, sizeof(recipient));
-  if(sent < 0){
-    log_e("could not send data: %d", errno);
-    return 0;
+
+  if (remote_ip.isV4()) {
+    struct sockaddr_in recipient;
+    recipient.sin_addr.s_addr = (uint32_t)remote_ip;
+    recipient.sin_family = AF_INET;
+    recipient.sin_port = htons(remote_port);
+    int sent = sendto(udp_server, tx_buffer, tx_buffer_len, 0, (struct sockaddr*) &recipient, sizeof(recipient));
+    if(sent < 0){
+      log_e("could not send data: %d", errno);
+      return 0;
+    }
+  } else {
+    struct sockaddr_in6 recipient;
+    recipient.sin6_flowinfo = 0;
+    recipient.sin6_addr = *(in6_addr*)(ip_addr_t*)remote_ip;
+    recipient.sin6_family = AF_INET6;
+    recipient.sin6_port = htons(remote_port);
+    recipient.sin6_scope_id = remote_ip.zone();
+    int sent = sendto(udp_server, tx_buffer, tx_buffer_len, 0, (struct sockaddr*) &recipient, sizeof(recipient));
+    if(sent < 0){
+      log_e("could not send data: %d", errno);
+      return 0;
+    }
   }
   return 1;
 }
@@ -207,13 +286,14 @@ size_t WiFiUDP::write(const uint8_t *buffer, size_t size){
 int WiFiUDP::parsePacket(){
   if(rx_buffer)
     return 0;
-  struct sockaddr_in si_other;
-  int slen = sizeof(si_other) , len;
+  struct sockaddr_storage si_other_storage;   // enough storage for v4 and v6
+  socklen_t slen = sizeof(sockaddr_storage);
+  int len;
   char *buf = (char *)malloc(1460);
   if(!buf) {
     return 0;
   }
-  if ((len = recvfrom(udp_server, buf, 1460, MSG_DONTWAIT, (struct sockaddr *) &si_other, (socklen_t *)&slen)) == -1){
+  if ((len = recvfrom(udp_server, buf, 1460, MSG_DONTWAIT, (struct sockaddr *) &si_other_storage, (socklen_t *)&slen)) == -1){
     free(buf);
     if(errno == EWOULDBLOCK){
       return 0;
@@ -221,8 +301,28 @@ int WiFiUDP::parsePacket(){
     log_e("could not receive data: %d", errno);
     return 0;
   }
-  remote_ip = IPAddress(si_other.sin_addr.s_addr);
-  remote_port = ntohs(si_other.sin_port);
+  if (si_other_storage.ss_family == AF_INET) {
+    struct sockaddr_in &si_other = (sockaddr_in&) si_other_storage;
+    remote_ip = IPAddress(si_other.sin_addr.s_addr);
+    remote_port = ntohs(si_other.sin_port);
+  }
+#if LWIP_IPV6 
+  else if (si_other_storage.ss_family == AF_INET6) {
+    struct sockaddr_in6 &si_other = (sockaddr_in6&) si_other_storage;
+    remote_ip = IPAddress(IPv6, (uint8_t*)&si_other.sin6_addr, si_other.sin6_scope_id);   // force IPv6
+    ip_addr_t *ip_addr = (ip_addr_t*) remote_ip;
+    /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
+    if (IP_IS_V6_VAL(*ip_addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(ip_addr))) {
+      unmap_ipv4_mapped_ipv6(ip_2_ip4(ip_addr), ip_2_ip6(ip_addr));
+      IP_SET_TYPE_VAL(*ip_addr, IPADDR_TYPE_V4);
+    }
+    remote_port = ntohs(si_other.sin6_port);
+  }
+#endif // LWIP_IPV6=1
+  else {
+    remote_ip = *IP_ADDR_ANY;
+    remote_port = 0;
+  }
   if (len > 0) {
     rx_buffer = new(std::nothrow) cbuf(len);
     rx_buffer->write(buf, len);

From 12be3695e098f9f330045fe60060e6285b4d0397 Mon Sep 17 00:00:00 2001
From: s-hadinger <49731213+s-hadinger@users.noreply.github.com>
Date: Tue, 21 Nov 2023 21:10:49 +0100
Subject: [PATCH 02/21] Fix warning in WifiUdp

---
 libraries/WiFi/src/WiFiUdp.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/WiFi/src/WiFiUdp.cpp b/libraries/WiFi/src/WiFiUdp.cpp
index 7c503cec2f3..511ee3e6126 100644
--- a/libraries/WiFi/src/WiFiUdp.cpp
+++ b/libraries/WiFi/src/WiFiUdp.cpp
@@ -169,7 +169,7 @@ void WiFiUDP::stop(){
       for (netif* intf = netif_list; intf != nullptr; intf = intf->next) {
         mreq.ipv6mr_interface = intf->num + 1;
         if (intf->name[0] != 'l' || intf->name[1] != 'o') {   // skip 'lo' local interface
-          int ret = setsockopt(udp_server, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq, sizeof(mreq));
+          setsockopt(udp_server, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq, sizeof(mreq));
         }
       }
     } else

From 5987211a7f9cf51d4b719569fd398edef67061d0 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Wed, 22 Nov 2023 16:01:24 +0100
Subject: [PATCH 03/21] remove comment / formating


From b8cc73c108743598a35b97db59ac712c88c6f9d7 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Mon, 18 Dec 2023 17:53:20 +0200
Subject: [PATCH 04/21] Add zone to IPAddress and update WiFiUDP and
 WiFiGeneric

---
 cores/esp32/IPAddress.cpp          | 326 ++++++++++++++++-------------
 cores/esp32/IPAddress.h            |  89 ++++----
 libraries/WiFi/src/WiFiGeneric.cpp |   9 +-
 libraries/WiFi/src/WiFiGeneric.h   |   6 +-
 libraries/WiFi/src/WiFiUdp.cpp     |  58 ++---
 5 files changed, 267 insertions(+), 221 deletions(-)

diff --git a/cores/esp32/IPAddress.cpp b/cores/esp32/IPAddress.cpp
index 002dccb3fcd..bfa481274e1 100644
--- a/cores/esp32/IPAddress.cpp
+++ b/cores/esp32/IPAddress.cpp
@@ -1,38 +1,39 @@
 /*
- IPAddress.cpp - Base class that provides IPAddress
- Copyright (c) 2011 Adrian McEwen.  All right reserved.
+  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 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.
+  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
- */
+  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 <Arduino.h>
-#include <IPAddress.h>
-#include <Print.h>
-#include <StreamString.h>
+#include "IPAddress.h"
+#include "Print.h"
+#include "lwip/netif.h"
 
 IPAddress::IPAddress() : IPAddress(IPv4) {}
 
 IPAddress::IPAddress(IPType ip_type)
 {
     _type = ip_type;
+    _zone = IP6_NO_ZONE;
     memset(_address.bytes, 0, sizeof(_address.bytes));
 }
 
 IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet)
 {
     _type = IPv4;
+    _zone = IP6_NO_ZONE;
     memset(_address.bytes, 0, sizeof(_address.bytes));
     _address.bytes[IPADDRESS_V4_BYTES_INDEX] = first_octet;
     _address.bytes[IPADDRESS_V4_BYTES_INDEX + 1] = second_octet;
@@ -40,7 +41,7 @@ IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_oc
     _address.bytes[IPADDRESS_V4_BYTES_INDEX + 3] = fourth_octet;
 }
 
-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) {
+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, uint8_t z) {
     _type = IPv6;
     _address.bytes[0] = o1;
     _address.bytes[1] = o2;
@@ -58,6 +59,7 @@ IPAddress::IPAddress(uint8_t o1, uint8_t o2, uint8_t o3, uint8_t o4, uint8_t o5,
     _address.bytes[13] = o14;
     _address.bytes[14] = o15;
     _address.bytes[15] = o16;
+    _zone = z;
 }
 
 IPAddress::IPAddress(uint32_t address)
@@ -78,14 +80,16 @@ IPAddress::IPAddress(uint32_t address)
 
 IPAddress::IPAddress(const uint8_t *address) : IPAddress(IPv4, address) {}
 
-IPAddress::IPAddress(IPType ip_type, const uint8_t *address)
+IPAddress::IPAddress(IPType ip_type, const uint8_t *address, uint8_t z)
 {
     _type = ip_type;
     if (ip_type == IPv4) {
         memset(_address.bytes, 0, sizeof(_address.bytes));
         memcpy(&_address.bytes[IPADDRESS_V4_BYTES_INDEX], address, sizeof(uint32_t));
+        _zone = 0;
     } else {
         memcpy(_address.bytes, address, sizeof(_address.bytes));
+        _zone = z;
     }
 }
 
@@ -94,121 +98,9 @@ IPAddress::IPAddress(const char *address)
     fromString(address);
 }
 
-IPAddress& IPAddress::operator=(const uint8_t *address)
-{
-    // IPv4 only conversion from byte pointer
-    _type = IPv4;
-    memset(_address.bytes, 0, sizeof(_address.bytes));
-    memcpy(&_address.bytes[IPADDRESS_V4_BYTES_INDEX], address, sizeof(uint32_t));
-    return *this;
-}
-
-IPAddress& IPAddress::operator=(const char *address)
+IPAddress::IPAddress(const IPAddress& address)
 {
-    fromString(address);
-    return *this;
-}
-
-IPAddress& IPAddress::operator=(uint32_t address)
-{
-    // IPv4 conversion
-    // See note on conversion/comparison and uint32_t
-    _type = IPv4;
-    memset(_address.bytes, 0, sizeof(_address.bytes));
-    _address.dword[IPADDRESS_V4_DWORD_INDEX] = address;
-    return *this;
-}
-
-bool IPAddress::operator==(const IPAddress& addr) const
-{
-    return (addr._type == _type)
-        && (memcmp(addr._address.bytes, _address.bytes, sizeof(_address.bytes)) == 0);
-}
-
-bool IPAddress::operator==(const uint8_t* addr) const
-{
-    // IPv4 only comparison to byte pointer
-    // Can't support IPv6 as we know our type, but not the length of the pointer
-    return _type == IPv4 && memcmp(addr, &_address.bytes[IPADDRESS_V4_BYTES_INDEX], sizeof(uint32_t)) == 0;
-}
-
-uint8_t IPAddress::operator[](int index) const {
-    if (_type == IPv4) {
-        return _address.bytes[IPADDRESS_V4_BYTES_INDEX + index];
-    }
-    return _address.bytes[index];
-}
-
-uint8_t& IPAddress::operator[](int index) {
-    if (_type == IPv4) {
-        return _address.bytes[IPADDRESS_V4_BYTES_INDEX + index];
-    }
-    return _address.bytes[index];
-}
-
-size_t IPAddress::printTo(Print& p) const
-{
-    size_t n = 0;
-
-    if (_type == IPv6) {
-        // IPv6 IETF canonical format: compress left-most longest run of two or more zero fields, lower case
-        int8_t longest_start = -1;
-        int8_t longest_length = 1;
-        int8_t current_start = -1;
-        int8_t current_length = 0;
-        for (int8_t f = 0; f < 8; f++) {
-            if (_address.bytes[f * 2] == 0 && _address.bytes[f * 2 + 1] == 0) {
-                if (current_start == -1) {
-                    current_start = f;
-                    current_length = 1;
-                } else {
-                    current_length++;
-                }
-                if (current_length > longest_length) {
-                    longest_start = current_start;
-                    longest_length = current_length;
-                }
-            } else {
-                current_start = -1;
-            }
-        }
-        for (int f = 0; f < 8; f++) {
-            if (f < longest_start || f >= longest_start + longest_length) {
-                uint8_t c1 = _address.bytes[f * 2] >> 4;
-                uint8_t c2 = _address.bytes[f * 2] & 0xf;
-                uint8_t c3 = _address.bytes[f * 2 + 1] >> 4;
-                uint8_t c4 = _address.bytes[f * 2 + 1] & 0xf;
-                if (c1 > 0) {
-                    n += p.print((char)(c1 < 10 ? '0' + c1 : 'a' + c1 - 10));
-                }
-                if (c1 > 0 || c2 > 0) {
-                    n += p.print((char)(c2 < 10 ? '0' + c2 : 'a' + c2 - 10));
-                }
-                if (c1 > 0 || c2 > 0 || c3 > 0) {
-                    n += p.print((char)(c3 < 10 ? '0' + c3 : 'a' + c3 - 10));
-                }
-                n += p.print((char)(c4 < 10 ? '0' + c4 : 'a' + c4 - 10));
-                if (f < 7) {
-                    n += p.print(':');
-                }
-            } else if (f == longest_start) {
-                if (longest_start == 0) {
-                    n += p.print(':');
-                }
-                n += p.print(':');
-            }
-        }
-        return n;
-    }
-
-    // IPv4
-    for (int i =0; i < 3; i++)
-    {
-        n += p.print(_address.bytes[IPADDRESS_V4_BYTES_INDEX + i], DEC);
-        n += p.print('.');
-    }
-    n += p.print(_address.bytes[IPADDRESS_V4_BYTES_INDEX + 3], DEC);
-    return n;
+    *this = address;
 }
 
 String IPAddress::toString4() const
@@ -220,10 +112,13 @@ String IPAddress::toString4() const
 
 String IPAddress::toString6() const
 {
-    StreamString s;
-    s.reserve(40);
-    printTo(s);
-    return s;
+    char szRet[40];
+    snprintf(szRet, sizeof(szRet), "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+            _address.bytes[0], _address.bytes[1], _address.bytes[2], _address.bytes[3],
+            _address.bytes[4], _address.bytes[5], _address.bytes[6], _address.bytes[7],
+            _address.bytes[8], _address.bytes[9], _address.bytes[10], _address.bytes[11],
+            _address.bytes[12], _address.bytes[13], _address.bytes[14], _address.bytes[15]);
+    return String(szRet);
 }
 
 String IPAddress::toString() const
@@ -235,10 +130,8 @@ String IPAddress::toString() const
     }
 }
 
-bool IPAddress::fromString(const char *address)
-{
-    if (!fromString4(address))
-    {
+bool IPAddress::fromString(const char *address) {
+    if (!fromString4(address)) {
         return fromString6(address);
     }
     return true;
@@ -336,6 +229,12 @@ bool IPAddress::fromString6(const char *address) {
             colons++;
             acc = 0;
         }
+        else if (c == '%') {
+            _zone = netif_name_to_index(address);
+            while(*address != '\0'){
+                address++;
+            }
+        }
         else
             // Invalid char
             return false;
@@ -364,5 +263,144 @@ bool IPAddress::fromString6(const char *address) {
     return true;
 }
 
-// declared one time - as external in IPAddress.h
-IPAddress INADDR_NONE(0, 0, 0, 0);
+IPAddress& IPAddress::operator=(const uint8_t *address)
+{
+    // IPv4 only conversion from byte pointer
+    _type = IPv4;
+    memset(_address.bytes, 0, sizeof(_address.bytes));
+    memcpy(&_address.bytes[IPADDRESS_V4_BYTES_INDEX], address, sizeof(uint32_t));
+    return *this;
+}
+
+IPAddress& IPAddress::operator=(const char *address)
+{
+    fromString(address);
+    return *this;
+}
+
+IPAddress& IPAddress::operator=(uint32_t address)
+{
+    // IPv4 conversion
+    // See note on conversion/comparison and uint32_t
+    _type = IPv4;
+    memset(_address.bytes, 0, sizeof(_address.bytes));
+    _address.dword[IPADDRESS_V4_DWORD_INDEX] = address;
+    return *this;
+}
+
+IPAddress& IPAddress::operator=(const IPAddress& address){
+    _type = address.type();
+    _zone = address.zone();
+    memcpy(_address.bytes, address._address.bytes, sizeof(_address.bytes));
+    return *this;
+}
+
+bool IPAddress::operator==(const IPAddress& addr) const {
+    return (addr._type == _type)
+        && (memcmp(addr._address.bytes, _address.bytes, sizeof(_address.bytes)) == 0);
+}
+
+bool IPAddress::operator==(const uint8_t* addr) const
+{
+    // IPv4 only comparison to byte pointer
+    // Can't support IPv6 as we know our type, but not the length of the pointer
+    return _type == IPv4 && memcmp(addr, &_address.bytes[IPADDRESS_V4_BYTES_INDEX], sizeof(uint32_t)) == 0;
+}
+
+uint8_t IPAddress::operator[](int index) const {
+    if (_type == IPv4) {
+        return _address.bytes[IPADDRESS_V4_BYTES_INDEX + index];
+    }
+    return _address.bytes[index];
+}
+
+uint8_t& IPAddress::operator[](int index) {
+    if (_type == IPv4) {
+        return _address.bytes[IPADDRESS_V4_BYTES_INDEX + index];
+    }
+    return _address.bytes[index];
+}
+
+size_t IPAddress::printTo(Print& p) const
+{
+    size_t n = 0;
+
+    if (_type == IPv6) {
+        // IPv6 IETF canonical format: compress left-most longest run of two or more zero fields, lower case
+        int8_t longest_start = -1;
+        int8_t longest_length = 1;
+        int8_t current_start = -1;
+        int8_t current_length = 0;
+        for (int8_t f = 0; f < 8; f++) {
+            if (_address.bytes[f * 2] == 0 && _address.bytes[f * 2 + 1] == 0) {
+                if (current_start == -1) {
+                    current_start = f;
+                    current_length = 1;
+                } else {
+                    current_length++;
+                }
+                if (current_length > longest_length) {
+                    longest_start = current_start;
+                    longest_length = current_length;
+                }
+            } else {
+                current_start = -1;
+            }
+        }
+        for (int f = 0; f < 8; f++) {
+            if (f < longest_start || f >= longest_start + longest_length) {
+                uint8_t c1 = _address.bytes[f * 2] >> 4;
+                uint8_t c2 = _address.bytes[f * 2] & 0xf;
+                uint8_t c3 = _address.bytes[f * 2 + 1] >> 4;
+                uint8_t c4 = _address.bytes[f * 2 + 1] & 0xf;
+                if (c1 > 0) {
+                    n += p.print((char)(c1 < 10 ? '0' + c1 : 'a' + c1 - 10));
+                }
+                if (c1 > 0 || c2 > 0) {
+                    n += p.print((char)(c2 < 10 ? '0' + c2 : 'a' + c2 - 10));
+                }
+                if (c1 > 0 || c2 > 0 || c3 > 0) {
+                    n += p.print((char)(c3 < 10 ? '0' + c3 : 'a' + c3 - 10));
+                }
+                n += p.print((char)(c4 < 10 ? '0' + c4 : 'a' + c4 - 10));
+                if (f < 7) {
+                    n += p.print(':');
+                }
+            } else if (f == longest_start) {
+                if (longest_start == 0) {
+                    n += p.print(':');
+                }
+                n += p.print(':');
+            }
+        }
+        return n;
+    }
+
+    // IPv4
+    for (int i =0; i < 3; i++)
+    {
+        n += p.print(_address.bytes[IPADDRESS_V4_BYTES_INDEX + i], DEC);
+        n += p.print('.');
+    }
+    n += p.print(_address.bytes[IPADDRESS_V4_BYTES_INDEX + 3], DEC);
+    return n;
+}
+
+void IPAddress::to_ip_addr_t(ip_addr_t* addr){
+    if(_type == IPv6){
+        addr->type = IPADDR_TYPE_V6;
+        addr->u_addr.ip6.addr[0] = _address.dword[0];
+        addr->u_addr.ip6.addr[1] = _address.dword[1];
+        addr->u_addr.ip6.addr[2] = _address.dword[2];
+        addr->u_addr.ip6.addr[3] = _address.dword[3];
+#if LWIP_IPV6_SCOPES
+        addr->u_addr.ip6.zone = _zone;
+#endif /* LWIP_IPV6_SCOPES */
+    } else {
+        addr->type = IPADDR_TYPE_V4;
+        addr->u_addr.ip4.addr = _address.dword[IPADDRESS_V4_DWORD_INDEX];
+    }
+}
+
+const IPAddress IN6ADDR_ANY(IPv6);
+const IPAddress INADDR_NONE(0,0,0,0);
diff --git a/cores/esp32/IPAddress.h b/cores/esp32/IPAddress.h
index 329ca92afe9..c5a8316116b 100644
--- a/cores/esp32/IPAddress.h
+++ b/cores/esp32/IPAddress.h
@@ -1,82 +1,82 @@
 /*
- IPAddress.h - Base class that provides IPAddress
- Copyright (c) 2011 Adrian McEwen.  All right reserved.
+  IPAddress.h - 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 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.
+  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
- */
+  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
+*/
 
-#ifndef IPAddress_h
-#define IPAddress_h
+#pragma once
 
 #include <stdint.h>
-#include <WString.h>
-#include <Printable.h>
-
-// A class to make it easier to handle and pass around IP addresses
+#include "Printable.h"
+#include "WString.h"
+#include "lwip/ip_addr.h"
 
 #define IPADDRESS_V4_BYTES_INDEX 12
 #define IPADDRESS_V4_DWORD_INDEX 3
 
-enum IPType
-{
+// A class to make it easier to handle and pass around IP addresses
+
+enum IPType {
     IPv4,
     IPv6
 };
 
-class IPAddress: public Printable
-{
+class IPAddress : public Printable {
 private:
     union {
         uint8_t bytes[16];
         uint32_t dword[4];
     } _address;
     IPType _type;
+    uint8_t _zone;
 
     // 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 _type == IPv4 ? &_address.bytes[IPADDRESS_V4_BYTES_INDEX] : _address.bytes;
-    }
+    uint8_t* raw_address() { return _type == IPv4 ? &_address.bytes[IPADDRESS_V4_BYTES_INDEX] : _address.bytes; }
 
 public:
     // Constructors
+
+    // Default IPv4
     IPAddress();
     IPAddress(IPType ip_type);
     IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
-    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);
+    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, uint8_t z=0);
+     // IPv4; see implementation note
     IPAddress(uint32_t address);
+     // Default IPv4
     IPAddress(const uint8_t *address);
-    IPAddress(IPType ip_type, const uint8_t *address);
+    IPAddress(IPType ip_type, const uint8_t *address, uint8_t z=0);
     // If IPv4 fails tries IPv6 see fromString function
     IPAddress(const char *address);
-    virtual ~IPAddress() {}
+    IPAddress(const IPAddress& address);
 
     bool fromString(const char *address);
     bool fromString(const String &address) { return fromString(address.c_str()); }
 
-    // Overloaded cast operator to allow IPAddress objects to be used where a
-    // uint32_t is expected
-    operator uint32_t() const
-    {
-        return _type == IPv4 ? _address.dword[IPADDRESS_V4_DWORD_INDEX] : 0;
-    }
+    // Overloaded cast operator to allow IPAddress objects to be used where a uint32_t is expected
+    // NOTE: IPv4 only; see implementation note
+    operator uint32_t() const { return _type == IPv4 ? _address.dword[IPADDRESS_V4_DWORD_INDEX] : 0; };
 
     bool operator==(const IPAddress& addr) const;
+    bool operator!=(const IPAddress& addr) const { return !(*this == addr); };
+
+    // NOTE: IPv4 only; we don't know the length of the pointer
     bool operator==(const uint8_t* addr) const;
 
     // Overloaded index operator to allow getting and setting individual octets of the address
@@ -84,22 +84,25 @@ class IPAddress: public Printable
     uint8_t& operator[](int index);
 
     // Overloaded copy operators to allow initialisation of IPAddress objects from other types
+    // NOTE: IPv4 only
     IPAddress& operator=(const uint8_t *address);
+    // NOTE: IPv4 only; see implementation note
     IPAddress& operator=(uint32_t address);
     // If IPv4 fails tries IPv6 see fromString function
     IPAddress& operator=(const char *address);
+    IPAddress& operator=(const IPAddress& address);
 
     virtual size_t printTo(Print& p) const;
     String toString() const;
 
     IPType type() const { return _type; }
 
-    friend class EthernetClass;
+    void to_ip_addr_t(ip_addr_t* addr);
+    uint8_t zone() const { return (type() == IPv6)?_zone:0; }
+
     friend class UDP;
     friend class Client;
     friend class Server;
-    friend class DhcpClass;
-    friend class DNSClient;
 
 protected:
     bool fromString4(const char *address);
@@ -108,7 +111,5 @@ class IPAddress: public Printable
     String toString6() const;
 };
 
-// changed to extern because const declaration creates copies in BSS of INADDR_NONE for each CPP unit that includes it
-extern IPAddress INADDR_NONE;
-extern IPAddress IN6ADDR_ANY;
-#endif
+extern const IPAddress IN6ADDR_ANY;
+extern const IPAddress INADDR_NONE;
diff --git a/libraries/WiFi/src/WiFiGeneric.cpp b/libraries/WiFi/src/WiFiGeneric.cpp
index c43bf46bc30..c1419f44912 100644
--- a/libraries/WiFi/src/WiFiGeneric.cpp
+++ b/libraries/WiFi/src/WiFiGeneric.cpp
@@ -1546,6 +1546,11 @@ bool WiFiGenericClass::setDualAntennaConfig(uint8_t gpio_ant1, uint8_t gpio_ant2
 // ------------------------------------------------ Generic Network function ---------------------------------------------
 // -----------------------------------------------------------------------------------------------------------------------
 
+struct dns_api_msg6 {
+    ip_addr_t ip_addr;
+    int result;
+};
+
 /**
  * DNS callback
  * @param name
@@ -1584,7 +1589,7 @@ static esp_err_t wifi_gethostbyname_tcpip_ctx(void *param)
  */
 static void wifi_dns6_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg)
 {
-    struct dns_api_msg *msg = (struct dns_api_msg *)callback_arg;
+    struct dns_api_msg6 *msg = (struct dns_api_msg6 *)callback_arg;
 
     if(ipaddr && !msg->result) {
         msg->ip_addr = *ipaddr;
@@ -1637,7 +1642,7 @@ int WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult)
 int WiFiGenericClass::hostByName6(const char* aHostname, ip_addr_t& aResult)
 {
     ip_addr_t addr;
-    struct dns_api_msg arg;
+    struct dns_api_msg6 arg;
 
     memset(&arg, 0x0, sizeof(arg));
     waitStatusBits(WIFI_DNS_IDLE_BIT, 16000);
diff --git a/libraries/WiFi/src/WiFiGeneric.h b/libraries/WiFi/src/WiFiGeneric.h
index ce723409eca..60b60fbef03 100644
--- a/libraries/WiFi/src/WiFiGeneric.h
+++ b/libraries/WiFi/src/WiFiGeneric.h
@@ -32,6 +32,7 @@
 #include "esp_netif_types.h"
 #include "esp_eth_driver.h"
 #include "wifi_provisioning/manager.h"
+#include "lwip/ip_addr.h"
 
 ESP_EVENT_DECLARE_BASE(ARDUINO_EVENTS);
 
@@ -155,11 +156,6 @@ typedef enum {
 	WIFI_TX_ANT_AUTO
 } wifi_tx_ant_t;
 
-struct dns_api_msg {
-    ip_addr_t ip_addr;
-    int result;
-};
-
 class WiFiGenericClass
 {
   public:
diff --git a/libraries/WiFi/src/WiFiUdp.cpp b/libraries/WiFi/src/WiFiUdp.cpp
index 511ee3e6126..683ff90067c 100644
--- a/libraries/WiFi/src/WiFiUdp.cpp
+++ b/libraries/WiFi/src/WiFiUdp.cpp
@@ -52,7 +52,7 @@ uint8_t WiFiUDP::begin(IPAddress address, uint16_t port){
   tx_buffer_len = 0;
 
 #if LWIP_IPV6
-  if ((udp_server=socket(address.isV6() ? AF_INET6 : AF_INET, SOCK_DGRAM, 0)) == -1){
+  if ((udp_server=socket((address.type() == IPv6) ? AF_INET6 : AF_INET, SOCK_DGRAM, 0)) == -1){
 #else
   if ((udp_server=socket(AF_INET, SOCK_DGRAM, 0)) == -1){
 #endif
@@ -70,19 +70,20 @@ uint8_t WiFiUDP::begin(IPAddress address, uint16_t port){
   struct sockaddr_storage serveraddr = {};
   size_t sock_size = 0;
 #if LWIP_IPV6
-  if (address.isV6()) {
+  if (address.type() == IPv6) {
     struct sockaddr_in6 *tmpaddr = (struct sockaddr_in6 *)&serveraddr;
-    ip_addr_t * ip_addr = (ip_addr_t*) address;
+    ip_addr_t addr;
+    address.to_ip_addr_t(&addr);
     memset((char *) tmpaddr, 0, sizeof(struct sockaddr_in));
     tmpaddr->sin6_family = AF_INET6;
     tmpaddr->sin6_port = htons(server_port);
-    tmpaddr->sin6_scope_id = ip_addr->u_addr.ip6.zone;
-    inet6_addr_from_ip6addr(&tmpaddr->sin6_addr, ip_2_ip6(ip_addr));
+    tmpaddr->sin6_scope_id = addr.u_addr.ip6.zone;
+    inet6_addr_from_ip6addr(&tmpaddr->sin6_addr, ip_2_ip6(&addr));
     tmpaddr->sin6_flowinfo = 0;
     sock_size = sizeof(sockaddr_in6);
   } else
 #endif
-  if (1) {
+  {
     struct sockaddr_in *tmpaddr = (struct sockaddr_in *)&serveraddr;
     memset((char *) tmpaddr, 0, sizeof(struct sockaddr_in));
     tmpaddr->sin_family = AF_INET;
@@ -103,15 +104,16 @@ uint8_t WiFiUDP::begin(uint16_t p){
   return begin(IPAddress(), p);
 }
 
-uint8_t WiFiUDP::beginMulticast(IPAddress a, uint16_t p){
+uint8_t WiFiUDP::beginMulticast(IPAddress address, uint16_t p){
   if(begin(IPAddress(), p)){
-    ip_addr_t * ip_addr = (ip_addr_t*) a;
-    if (ip_addr_ismulticast(ip_addr)) {
+    ip_addr_t addr;
+    address.to_ip_addr_t(&addr);
+    if (ip_addr_ismulticast(&addr)) {
 #if LWIP_IPV6
-      if (IP_IS_V6_VAL(*ip_addr)) {
+      if (address.type() == IPv6) {
         struct ipv6_mreq mreq;
         bool joined = false;
-        inet6_addr_from_ip6addr(&mreq.ipv6mr_multiaddr, ip_2_ip6(ip_addr));
+        inet6_addr_from_ip6addr(&mreq.ipv6mr_multiaddr, ip_2_ip6(&addr));
 
         // iterate on each interface
         for (netif* intf = netif_list; intf != nullptr; intf = intf->next) {
@@ -128,9 +130,9 @@ uint8_t WiFiUDP::beginMulticast(IPAddress a, uint16_t p){
         }
       } else
 #endif
-      if (1) {
+      {
         struct ip_mreq mreq;
-        mreq.imr_multiaddr.s_addr = (in_addr_t)a;
+        mreq.imr_multiaddr.s_addr = (in_addr_t)address;
         mreq.imr_interface.s_addr = INADDR_ANY;
         if (setsockopt(udp_server, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
             log_e("could not join igmp: %d", errno);
@@ -138,7 +140,7 @@ uint8_t WiFiUDP::beginMulticast(IPAddress a, uint16_t p){
             return 0;
         }
       }
-      multicast_ip = a;
+      multicast_ip = address;
       return 1;
     }
   }
@@ -158,12 +160,13 @@ void WiFiUDP::stop(){
   }
   if(udp_server == -1)
     return;
-  ip_addr_t * mcast_addr = (ip_addr_t*) multicast_ip;
-  if (!ip_addr_isany(mcast_addr)) {
+  ip_addr_t addr;
+  multicast_ip.to_ip_addr_t(&addr);
+  if (!ip_addr_isany(&addr)) {
 #if LWIP_IPV6
-    if (IP_IS_V6(mcast_addr)) {
+    if (multicast_ip.type() == IPv6) {
       struct ipv6_mreq mreq;
-      inet6_addr_from_ip6addr(&mreq.ipv6mr_multiaddr, ip_2_ip6(mcast_addr));
+      inet6_addr_from_ip6addr(&mreq.ipv6mr_multiaddr, ip_2_ip6(&addr));
 
       // iterate on each interface
       for (netif* intf = netif_list; intf != nullptr; intf = intf->next) {
@@ -174,7 +177,7 @@ void WiFiUDP::stop(){
       }
     } else
 #endif
-    if (1) {
+    {
       struct ip_mreq mreq;
       mreq.imr_multiaddr.s_addr = (in_addr_t)multicast_ip;
       mreq.imr_interface.s_addr = (in_addr_t)0;
@@ -240,8 +243,10 @@ int WiFiUDP::beginPacket(const char *host, uint16_t port){
 }
 
 int WiFiUDP::endPacket(){
+  ip_addr_t addr;
+  remote_ip.to_ip_addr_t(&addr);
 
-  if (remote_ip.isV4()) {
+  if (remote_ip.type() == IPv4) {
     struct sockaddr_in recipient;
     recipient.sin_addr.s_addr = (uint32_t)remote_ip;
     recipient.sin_family = AF_INET;
@@ -254,7 +259,7 @@ int WiFiUDP::endPacket(){
   } else {
     struct sockaddr_in6 recipient;
     recipient.sin6_flowinfo = 0;
-    recipient.sin6_addr = *(in6_addr*)(ip_addr_t*)remote_ip;
+    recipient.sin6_addr = *(in6_addr*)(ip_addr_t*)(&addr);
     recipient.sin6_family = AF_INET6;
     recipient.sin6_port = htons(remote_port);
     recipient.sin6_scope_id = remote_ip.zone();
@@ -310,17 +315,18 @@ int WiFiUDP::parsePacket(){
   else if (si_other_storage.ss_family == AF_INET6) {
     struct sockaddr_in6 &si_other = (sockaddr_in6&) si_other_storage;
     remote_ip = IPAddress(IPv6, (uint8_t*)&si_other.sin6_addr, si_other.sin6_scope_id);   // force IPv6
-    ip_addr_t *ip_addr = (ip_addr_t*) remote_ip;
+    ip_addr_t addr;
+    remote_ip.to_ip_addr_t(&addr);
     /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
-    if (IP_IS_V6_VAL(*ip_addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(ip_addr))) {
-      unmap_ipv4_mapped_ipv6(ip_2_ip4(ip_addr), ip_2_ip6(ip_addr));
-      IP_SET_TYPE_VAL(*ip_addr, IPADDR_TYPE_V4);
+    if (remote_ip.type() == IPv6 && ip6_addr_isipv4mappedipv6(ip_2_ip6(&addr))) {
+      unmap_ipv4_mapped_ipv6(ip_2_ip4(&addr), ip_2_ip6(&addr));
+      IP_SET_TYPE_VAL(addr, IPADDR_TYPE_V4);
     }
     remote_port = ntohs(si_other.sin6_port);
   }
 #endif // LWIP_IPV6=1
   else {
-    remote_ip = *IP_ADDR_ANY;
+    remote_ip = ip_addr_any.u_addr.ip4.addr;
     remote_port = 0;
   }
   if (len > 0) {

From 305d2766e51321ba2f6bbba67e10a70ef09dd185 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Tue, 19 Dec 2023 00:45:33 +0200
Subject: [PATCH 05/21] Add from ip_addr_t conversion and better toString
 implementation

---
 cores/esp32/IPAddress.cpp  | 51 +++++++++++++++++++++-----------------
 cores/esp32/IPAddress.h    |  7 +++---
 cores/esp32/StreamString.h |  3 ++-
 3 files changed, 34 insertions(+), 27 deletions(-)

diff --git a/cores/esp32/IPAddress.cpp b/cores/esp32/IPAddress.cpp
index bfa481274e1..bd88a1a1096 100644
--- a/cores/esp32/IPAddress.cpp
+++ b/cores/esp32/IPAddress.cpp
@@ -20,6 +20,7 @@
 #include "IPAddress.h"
 #include "Print.h"
 #include "lwip/netif.h"
+#include "StreamString.h"
 
 IPAddress::IPAddress() : IPAddress(IPv4) {}
 
@@ -103,31 +104,11 @@ IPAddress::IPAddress(const IPAddress& address)
     *this = address;
 }
 
-String IPAddress::toString4() const
-{
-    char szRet[16];
-    snprintf(szRet, sizeof(szRet), "%u.%u.%u.%u", _address.bytes[IPADDRESS_V4_BYTES_INDEX], _address.bytes[IPADDRESS_V4_BYTES_INDEX + 1], _address.bytes[IPADDRESS_V4_BYTES_INDEX + 2], _address.bytes[IPADDRESS_V4_BYTES_INDEX + 3]);
-    return String(szRet);
-}
-
-String IPAddress::toString6() const
-{
-    char szRet[40];
-    snprintf(szRet, sizeof(szRet), "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
-            _address.bytes[0], _address.bytes[1], _address.bytes[2], _address.bytes[3],
-            _address.bytes[4], _address.bytes[5], _address.bytes[6], _address.bytes[7],
-            _address.bytes[8], _address.bytes[9], _address.bytes[10], _address.bytes[11],
-            _address.bytes[12], _address.bytes[13], _address.bytes[14], _address.bytes[15]);
-    return String(szRet);
-}
-
 String IPAddress::toString() const
 {
-    if (_type == IPv4) {
-        return toString4();
-    } else {
-        return toString6();
-    }
+    StreamString s;
+    printTo(s);
+    return String(s);
 }
 
 bool IPAddress::fromString(const char *address) {
@@ -373,6 +354,13 @@ size_t IPAddress::printTo(Print& p) const
                 n += p.print(':');
             }
         }
+        // add a zone if zone-id is non-zero
+        if(_zone > 0){
+            n += p.print('%');
+            char if_name[NETIF_NAMESIZE];
+            netif_index_to_name(_zone, if_name);
+            n += p.print(if_name);
+        }
         return n;
     }
 
@@ -402,5 +390,22 @@ void IPAddress::to_ip_addr_t(ip_addr_t* addr){
     }
 }
 
+IPAddress& IPAddress::from_ip_addr_t(ip_addr_t* addr){
+    if(addr->type == IPADDR_TYPE_V6){
+        _type = IPv6;
+        _address.dword[0] = addr->u_addr.ip6.addr[0];
+        _address.dword[1] = addr->u_addr.ip6.addr[1];
+        _address.dword[2] = addr->u_addr.ip6.addr[2];
+        _address.dword[3] = addr->u_addr.ip6.addr[3];
+#if LWIP_IPV6_SCOPES
+        _zone = addr->u_addr.ip6.zone;
+#endif /* LWIP_IPV6_SCOPES */
+    } else {
+        _type = IPv4;
+        _address.dword[IPADDRESS_V4_DWORD_INDEX] = addr->u_addr.ip4.addr;
+    }
+    return *this;
+}
+
 const IPAddress IN6ADDR_ANY(IPv6);
 const IPAddress INADDR_NONE(0,0,0,0);
diff --git a/cores/esp32/IPAddress.h b/cores/esp32/IPAddress.h
index c5a8316116b..2eedec83df5 100644
--- a/cores/esp32/IPAddress.h
+++ b/cores/esp32/IPAddress.h
@@ -97,9 +97,12 @@ class IPAddress : public Printable {
 
     IPType type() const { return _type; }
 
-    void to_ip_addr_t(ip_addr_t* addr);
     uint8_t zone() const { return (type() == IPv6)?_zone:0; }
 
+    // LwIP conversions
+    void to_ip_addr_t(ip_addr_t* addr);
+    IPAddress& from_ip_addr_t(ip_addr_t* addr);
+
     friend class UDP;
     friend class Client;
     friend class Server;
@@ -107,8 +110,6 @@ class IPAddress : public Printable {
 protected:
     bool fromString4(const char *address);
     bool fromString6(const char *address);
-    String toString4() const;
-    String toString6() const;
 };
 
 extern const IPAddress IN6ADDR_ANY;
diff --git a/cores/esp32/StreamString.h b/cores/esp32/StreamString.h
index dbdf3fb0097..fa983786c70 100644
--- a/cores/esp32/StreamString.h
+++ b/cores/esp32/StreamString.h
@@ -21,7 +21,8 @@
 
 #ifndef STREAMSTRING_H_
 #define STREAMSTRING_H_
-
+#include "Stream.h"
+#include "WString.h"
 
 class StreamString: public Stream, public String
 {

From 41fb1a1215d842f1977353e0e891b1f4d95c96de Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Tue, 19 Dec 2023 00:50:07 +0200
Subject: [PATCH 06/21] Use constant for IPAddress offset

@sgryphon is this better?
---
 libraries/WiFi/src/WiFiClient.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/WiFi/src/WiFiClient.cpp b/libraries/WiFi/src/WiFiClient.cpp
index d8f1a31a62b..195acbd7c40 100644
--- a/libraries/WiFi/src/WiFiClient.cpp
+++ b/libraries/WiFi/src/WiFiClient.cpp
@@ -614,7 +614,7 @@ IPAddress WiFiClient::remoteIP(int fd) const
     if (((struct sockaddr*)&addr)->sa_family == AF_INET6) {
         struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)&addr;
         if (IN6_IS_ADDR_V4MAPPED(saddr6->sin6_addr.un.u32_addr)) {
-            return IPAddress(IPv4, (uint8_t*)saddr6->sin6_addr.s6_addr+12);
+            return IPAddress(IPv4, (uint8_t*)saddr6->sin6_addr.s6_addr+IPADDRESS_V4_BYTES_INDEX);
         } else {
             return IPAddress(IPv6, (uint8_t*)(saddr6->sin6_addr.s6_addr), saddr6->sin6_scope_id);
         }

From b37ce6c772a1aeddd998cde7119958c220f8fe65 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Tue, 19 Dec 2023 01:54:32 +0200
Subject: [PATCH 07/21] Combine hostByName to support both IPv6 and IPv4
 results

---
 libraries/WiFi/src/WiFiClient.cpp  |  15 +---
 libraries/WiFi/src/WiFiGeneric.cpp | 112 ++++++++++-------------------
 libraries/WiFi/src/WiFiGeneric.h   |   3 +-
 3 files changed, 39 insertions(+), 91 deletions(-)

diff --git a/libraries/WiFi/src/WiFiClient.cpp b/libraries/WiFi/src/WiFiClient.cpp
index 195acbd7c40..99b153c7629 100644
--- a/libraries/WiFi/src/WiFiClient.cpp
+++ b/libraries/WiFi/src/WiFiClient.cpp
@@ -319,21 +319,8 @@ int WiFiClient::connect(const char *host, uint16_t port)
 
 int WiFiClient::connect(const char *host, uint16_t port, int32_t timeout_ms)
 {
-    if (WiFiGenericClass::getStatusBits() & WIFI_WANT_IP6_BIT) {
-        ip_addr_t srv6;
-        if(!WiFiGenericClass::hostByName6(host, srv6)){
-            return 0;
-        }
-        if (srv6.type == IPADDR_TYPE_V4) {
-            IPAddress ip(srv6.u_addr.ip4.addr);
-            return connect(ip, port, timeout_ms);
-        } else {
-            IPAddress ip(IPv6, (uint8_t*)&srv6.u_addr.ip6.addr[0]);
-            return connect(ip, port, timeout_ms);
-        }
-    }
     IPAddress srv((uint32_t)0);
-    if(!WiFiGenericClass::hostByName(host, srv)){
+    if(!WiFiGenericClass::hostByName(host, srv, (WiFiGenericClass::getStatusBits() & WIFI_WANT_IP6_BIT))){
         return 0;
     }
     return connect(srv, port, timeout_ms);
diff --git a/libraries/WiFi/src/WiFiGeneric.cpp b/libraries/WiFi/src/WiFiGeneric.cpp
index c1419f44912..d0075170b76 100644
--- a/libraries/WiFi/src/WiFiGeneric.cpp
+++ b/libraries/WiFi/src/WiFiGeneric.cpp
@@ -1546,10 +1546,12 @@ bool WiFiGenericClass::setDualAntennaConfig(uint8_t gpio_ant1, uint8_t gpio_ant2
 // ------------------------------------------------ Generic Network function ---------------------------------------------
 // -----------------------------------------------------------------------------------------------------------------------
 
-struct dns_api_msg6 {
-    ip_addr_t ip_addr;
+typedef struct gethostbynameParameters {
+    const char *hostname;
+    ip_addr_t addr;
+    uint8_t addr_type;
     int result;
-};
+} gethostbynameParameters_t;
 
 /**
  * DNS callback
@@ -1559,18 +1561,18 @@ struct dns_api_msg6 {
  */
 static void wifi_dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg)
 {
+    gethostbynameParameters_t *parameters = static_cast<gethostbynameParameters_t *>(callback_arg);
     if(ipaddr) {
-        (*reinterpret_cast<IPAddress*>(callback_arg)) = ipaddr->u_addr.ip4.addr;
+        if(parameters->result == 0){
+            memcpy(&(parameters->addr), ipaddr, sizeof(ip_addr_t));
+            parameters->result = 1;
+        }
+    } else {
+        parameters->result = -1;
     }
     xEventGroupSetBits(_arduino_event_group, WIFI_DNS_DONE_BIT);
 }
 
-typedef struct gethostbynameParameters {
-    const char *hostname;
-    ip_addr_t addr;
-    void *callback_arg;
-} gethostbynameParameters_t;
-
 /**
  * Callback to execute dns_gethostbyname in lwIP's TCP/IP context
  * @param param Parameters for dns_gethostbyname call
@@ -1578,26 +1580,7 @@ typedef struct gethostbynameParameters {
 static esp_err_t wifi_gethostbyname_tcpip_ctx(void *param)
 {
     gethostbynameParameters_t *parameters = static_cast<gethostbynameParameters_t *>(param);
-    return dns_gethostbyname(parameters->hostname, &parameters->addr, &wifi_dns_found_callback, parameters->callback_arg);
-}
-
-/**
- * IPv6 compatible DNS callback
- * @param name
- * @param ipaddr
- * @param callback_arg
- */
-static void wifi_dns6_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg)
-{
-    struct dns_api_msg6 *msg = (struct dns_api_msg6 *)callback_arg;
-
-    if(ipaddr && !msg->result) {
-        msg->ip_addr = *ipaddr;
-        msg->result = 1;
-    } else {
-        msg->result = -1;
-    }
-    xEventGroupSetBits(_arduino_event_group, WIFI_DNS_DONE_BIT);
+    return dns_gethostbyname_addrtype(parameters->hostname, &parameters->addr, &wifi_dns_found_callback, parameters, parameters->addr_type);
 }
 
 /**
@@ -1607,60 +1590,39 @@ static void wifi_dns6_found_callback(const char *name, const ip_addr_t *ipaddr,
  * @return 1 if aIPAddrString was successfully converted to an IP address,
  *          else error code
  */
-int WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult)
+int WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult, bool preferV6)
 {
-    if (!aResult.fromString(aHostname))
-    {
-        gethostbynameParameters_t params;
-        params.hostname = aHostname;
-        params.callback_arg = &aResult;
-        aResult = static_cast<uint32_t>(0);
+    err_t err = ERR_OK;
+    gethostbynameParameters_t params;
+
+    aResult = static_cast<uint32_t>(0);
+    params.hostname = aHostname;
+    params.addr_type = preferV6?LWIP_DNS_ADDRTYPE_IPV6_IPV4:LWIP_DNS_ADDRTYPE_IPV4_IPV6;
+    params.result = 0;
+    aResult.to_ip_addr_t(&(params.addr));
+
+    if (!aResult.fromString(aHostname)) {
         waitStatusBits(WIFI_DNS_IDLE_BIT, 16000);
         clearStatusBits(WIFI_DNS_IDLE_BIT | WIFI_DNS_DONE_BIT);
-        err_t err = esp_netif_tcpip_exec(wifi_gethostbyname_tcpip_ctx, &params);
-        if(err == ERR_OK && params.addr.u_addr.ip4.addr) {
-            aResult = params.addr.u_addr.ip4.addr;
-        } else if(err == ERR_INPROGRESS) {
+
+        err = esp_netif_tcpip_exec(wifi_gethostbyname_tcpip_ctx, &params);
+        if (err == ERR_OK) {
+            aResult.from_ip_addr_t(&(params.addr));
+        } else if (err == ERR_INPROGRESS) {
             waitStatusBits(WIFI_DNS_DONE_BIT, 15000);  //real internal timeout in lwip library is 14[s]
             clearStatusBits(WIFI_DNS_DONE_BIT);
+            if (params.result == 1) {
+                aResult.from_ip_addr_t(&(params.addr));
+                err = ERR_OK;
+            }
         }
         setStatusBits(WIFI_DNS_IDLE_BIT);
-        if((uint32_t)aResult == 0){
-            log_e("DNS Failed for %s", aHostname);
-        }
     }
-    return (uint32_t)aResult != 0;
-}
-
-/**
- * Resolve the given hostname to an IP6 address.
- * @param aHostname     Name to be resolved
- * @param aResult       IPv6Address structure to store the returned IP address
- * @return 1 if aHostname was successfully converted to an IP address,
- *          else error code
- */
-int WiFiGenericClass::hostByName6(const char* aHostname, ip_addr_t& aResult)
-{
-    ip_addr_t addr;
-    struct dns_api_msg6 arg;
-
-    memset(&arg, 0x0, sizeof(arg));
-    waitStatusBits(WIFI_DNS_IDLE_BIT, 16000);
-    clearStatusBits(WIFI_DNS_IDLE_BIT | WIFI_DNS_DONE_BIT);
-
-    err_t err = dns_gethostbyname_addrtype(aHostname, &addr, &wifi_dns6_found_callback,
-                &arg, LWIP_DNS_ADDRTYPE_IPV6_IPV4);
-    if(err == ERR_OK) {
-        aResult = addr;
-    } else if(err == ERR_INPROGRESS) {
-        waitStatusBits(WIFI_DNS_DONE_BIT, 15000);  //real internal timeout in lwip library is 14[s]
-        clearStatusBits(WIFI_DNS_DONE_BIT);
-        if (arg.result == 1) {
-            aResult = arg.ip_addr;
-        }
+    if (err == ERR_OK) {
+        return 1;
     }
-    setStatusBits(WIFI_DNS_IDLE_BIT);
-    return (uint32_t)err == ERR_OK || (err == ERR_INPROGRESS && arg.result == 1);
+    log_e("DNS Failed for '%s' with error '%d' and result '%d'", aHostname, err, params.result);
+    return err;
 }
 
 IPAddress WiFiGenericClass::calculateNetworkID(IPAddress ip, IPAddress subnet) {
diff --git a/libraries/WiFi/src/WiFiGeneric.h b/libraries/WiFi/src/WiFiGeneric.h
index 60b60fbef03..90db296d188 100644
--- a/libraries/WiFi/src/WiFiGeneric.h
+++ b/libraries/WiFi/src/WiFiGeneric.h
@@ -198,7 +198,6 @@ class WiFiGenericClass
     static const char * getHostname();
     static bool setHostname(const char * hostname);
     static bool hostname(const String& aHostname) { return setHostname(aHostname.c_str()); }
-    static int hostByName6(const char *aHostname, ip_addr_t& aResult);
 
     static esp_err_t _eventCallback(arduino_event_t *event);
     
@@ -219,7 +218,7 @@ class WiFiGenericClass
     static bool _isReconnectableReason(uint8_t reason);
 
   public:
-    static int hostByName(const char *aHostname, IPAddress &aResult);
+    static int hostByName(const char *aHostname, IPAddress &aResult, bool preferV6=false);
 
     static IPAddress calculateNetworkID(IPAddress ip, IPAddress subnet);
     static IPAddress calculateBroadcast(IPAddress ip, IPAddress subnet);

From e333afb0a1e86aa9bb2655543226c74c3d0f0cee Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Thu, 11 Jan 2024 12:26:34 +0200
Subject: [PATCH 08/21] implement logic to use v6 dns only when global v6
 address is assigned and remove IPv6Address

---
 CMakeLists.txt                      |  1 -
 cores/esp32/IPv6Address.cpp         | 90 ---------------------------
 cores/esp32/IPv6Address.h           | 94 -----------------------------
 docs/source/api/wifi.rst            |  4 +-
 libraries/AsyncUDP/src/AsyncUDP.cpp | 70 ++++-----------------
 libraries/AsyncUDP/src/AsyncUDP.h   | 12 +---
 libraries/ESPmDNS/src/ESPmDNS.cpp   |  8 +--
 libraries/ESPmDNS/src/ESPmDNS.h     |  3 +-
 libraries/Ethernet/src/ETH.cpp      | 49 +++++++++++++--
 libraries/Ethernet/src/ETH.h        |  5 +-
 libraries/WiFi/src/WiFi.h           |  1 -
 libraries/WiFi/src/WiFiAP.cpp       | 16 ++---
 libraries/WiFi/src/WiFiAP.h         |  2 +-
 libraries/WiFi/src/WiFiClient.cpp   |  2 +-
 libraries/WiFi/src/WiFiGeneric.cpp  | 51 +++++++++++++---
 libraries/WiFi/src/WiFiGeneric.h    | 10 ++-
 libraries/WiFi/src/WiFiMulti.cpp    |  4 +-
 libraries/WiFi/src/WiFiSTA.cpp      | 48 ++++++++-------
 libraries/WiFi/src/WiFiSTA.h        |  6 +-
 19 files changed, 166 insertions(+), 310 deletions(-)
 delete mode 100644 cores/esp32/IPv6Address.cpp
 delete mode 100644 cores/esp32/IPv6Address.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0834ae1ec34..e121aaf3e3c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -51,7 +51,6 @@ set(CORE_SRCS
   cores/esp32/FunctionalInterrupt.cpp
   cores/esp32/HardwareSerial.cpp
   cores/esp32/IPAddress.cpp
-  cores/esp32/IPv6Address.cpp
   cores/esp32/libb64/cdecode.c
   cores/esp32/libb64/cencode.c
   cores/esp32/main.cpp
diff --git a/cores/esp32/IPv6Address.cpp b/cores/esp32/IPv6Address.cpp
deleted file mode 100644
index 7d3c0de5f53..00000000000
--- a/cores/esp32/IPv6Address.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- IPv6Address.cpp - Base class that provides IPv6Address
- 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 <Arduino.h>
-#include <IPv6Address.h>
-#include <Print.h>
-
-IPv6Address::IPv6Address()
-{
-    memset(_address.bytes, 0, sizeof(_address.bytes));
-}
-
-IPv6Address::IPv6Address(const uint8_t *address)
-{
-    memcpy(_address.bytes, address, sizeof(_address.bytes));
-}
-
-IPv6Address::IPv6Address(const uint32_t *address)
-{
-    memcpy(_address.bytes, (const uint8_t *)address, sizeof(_address.bytes));
-}
-
-IPv6Address& IPv6Address::operator=(const uint8_t *address)
-{
-    memcpy(_address.bytes, address, sizeof(_address.bytes));
-    return *this;
-}
-
-bool IPv6Address::operator==(const uint8_t* addr) const
-{
-    return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0;
-}
-
-size_t IPv6Address::printTo(Print& p) const
-{
-    size_t n = 0;
-    for(int i = 0; i < 16; i+=2) {
-        if(i){
-            n += p.print(':');
-        }
-        n += p.printf("%02x", _address.bytes[i]);
-        n += p.printf("%02x", _address.bytes[i+1]);
-
-    }
-    return n;
-}
-
-String IPv6Address::toString() const
-{
-    char szRet[40];
-    sprintf(szRet,"%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
-            _address.bytes[0], _address.bytes[1], _address.bytes[2], _address.bytes[3],
-            _address.bytes[4], _address.bytes[5], _address.bytes[6], _address.bytes[7],
-            _address.bytes[8], _address.bytes[9], _address.bytes[10], _address.bytes[11],
-            _address.bytes[12], _address.bytes[13], _address.bytes[14], _address.bytes[15]);
-    return String(szRet);
-}
-
-bool IPv6Address::fromString(const char *address)
-{
-    //format 0011:2233:4455:6677:8899:aabb:ccdd:eeff
-    if(strlen(address) != 39){
-        return false;
-    }
-    char * pos = (char *)address;
-    size_t i = 0;
-    for(i = 0; i < 16; i+=2) {
-        if(!sscanf(pos, "%2hhx", &_address.bytes[i]) || !sscanf(pos+2, "%2hhx", &_address.bytes[i+1])){
-            return false;
-        }
-        pos += 5;
-    }
-    return true;
-}
diff --git a/cores/esp32/IPv6Address.h b/cores/esp32/IPv6Address.h
deleted file mode 100644
index e61d0e7b371..00000000000
--- a/cores/esp32/IPv6Address.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- IPv6Address.h - Base class that provides IPv6Address
- 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
- */
-
-#ifndef IPv6Address_h
-#define IPv6Address_h
-
-#include <stdint.h>
-#include <WString.h>
-#include <Printable.h>
-
-// A class to make it easier to handle and pass around IP addresses
-
-class IPv6Address: public Printable
-{
-private:
-    union {
-        uint8_t bytes[16];  // IPv4 address
-        uint32_t dword[4];
-    } _address;
-
-    // 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 _address.bytes;
-    }
-
-public:
-    // Constructors
-    IPv6Address();
-    IPv6Address(const uint8_t *address);
-    IPv6Address(const uint32_t *address);
-    virtual ~IPv6Address() {}
-
-    bool fromString(const char *address);
-    bool fromString(const String &address) { return fromString(address.c_str()); }
-
-    operator const uint8_t*() const
-    {
-        return _address.bytes;
-    }
-    operator const uint32_t*() const
-    {
-        return _address.dword;
-    }
-    bool operator==(const IPv6Address& addr) const
-    {
-        return (_address.dword[0] == addr._address.dword[0])
-            && (_address.dword[1] == addr._address.dword[1])
-            && (_address.dword[2] == addr._address.dword[2])
-            && (_address.dword[3] == addr._address.dword[3]);
-    }
-    bool operator==(const uint8_t* addr) const;
-
-    // Overloaded index operator to allow getting and setting individual octets of the address
-    uint8_t operator[](int index) const
-    {
-        return _address.bytes[index];
-    }
-    uint8_t& operator[](int index)
-    {
-        return _address.bytes[index];
-    }
-
-    // Overloaded copy operators to allow initialisation of IPv6Address objects from other types
-    IPv6Address& operator=(const uint8_t *address);
-
-    virtual size_t printTo(Print& p) const;
-    String toString() const;
-
-    friend class UDP;
-    friend class Client;
-    friend class Server;
-};
-
-#endif
diff --git a/docs/source/api/wifi.rst b/docs/source/api/wifi.rst
index c906cdaa588..f5f2fe03f2e 100644
--- a/docs/source/api/wifi.rst
+++ b/docs/source/api/wifi.rst
@@ -345,9 +345,9 @@ Function to get the IPv6 address.
 
 .. code-block:: arduino
 
-    IPv6Address softAPIPv6();
+    IPAddress softAPIPv6();
 
-The function will return the AP IPv6 address in ``IPv6Address`` format.
+The function will return the AP IPv6 address in ``IPAddress`` format.
 
 softAPgetHostname
 *****************
diff --git a/libraries/AsyncUDP/src/AsyncUDP.cpp b/libraries/AsyncUDP/src/AsyncUDP.cpp
index e533755da7a..1c650f1a721 100644
--- a/libraries/AsyncUDP/src/AsyncUDP.cpp
+++ b/libraries/AsyncUDP/src/AsyncUDP.cpp
@@ -424,12 +424,12 @@ IPAddress AsyncUDPPacket::localIP()
     return IPAddress(_localIp.u_addr.ip4.addr);
 }
 
-IPv6Address AsyncUDPPacket::localIPv6()
+IPAddress AsyncUDPPacket::localIPv6()
 {
     if(_localIp.type != IPADDR_TYPE_V6){
-        return IPv6Address();
+        return IPAddress(IPv6);
     }
-    return IPv6Address(_localIp.u_addr.ip6.addr);
+    return IPAddress(IPv6, (const uint8_t *)_localIp.u_addr.ip6.addr, _localIp.u_addr.ip6.zone);
 }
 
 uint16_t AsyncUDPPacket::localPort()
@@ -445,12 +445,12 @@ IPAddress AsyncUDPPacket::remoteIP()
     return IPAddress(_remoteIp.u_addr.ip4.addr);
 }
 
-IPv6Address AsyncUDPPacket::remoteIPv6()
+IPAddress AsyncUDPPacket::remoteIPv6()
 {
     if(_remoteIp.type != IPADDR_TYPE_V6){
-        return IPv6Address();
+        return IPAddress(IPv6);
     }
-    return IPv6Address(_remoteIp.u_addr.ip6.addr);
+    return IPAddress(IPv6, (const uint8_t *)_remoteIp.u_addr.ip6.addr, _remoteIp.u_addr.ip6.zone);
 }
 
 uint16_t AsyncUDPPacket::remotePort()
@@ -739,32 +739,28 @@ bool AsyncUDP::listen(uint16_t port)
 bool AsyncUDP::listen(const IPAddress addr, uint16_t port)
 {
     ip_addr_t laddr;
-    laddr.type = IPADDR_TYPE_V4;
-    laddr.u_addr.ip4.addr = addr;
+    addr.to_ip_addr_t(&laddr);
     return listen(&laddr, port);
 }
 
 bool AsyncUDP::listenMulticast(const IPAddress addr, uint16_t port, uint8_t ttl, tcpip_adapter_if_t tcpip_if)
 {
     ip_addr_t laddr;
-    laddr.type = IPADDR_TYPE_V4;
-    laddr.u_addr.ip4.addr = addr;
+    addr.to_ip_addr_t(&laddr);
     return listenMulticast(&laddr, port, ttl, tcpip_if);
 }
 
 bool AsyncUDP::connect(const IPAddress addr, uint16_t port)
 {
     ip_addr_t daddr;
-    daddr.type = IPADDR_TYPE_V4;
-    daddr.u_addr.ip4.addr = addr;
+    addr.to_ip_addr_t(&daddr);
     return connect(&daddr, port);
 }
 
 size_t AsyncUDP::writeTo(const uint8_t *data, size_t len, const IPAddress addr, uint16_t port, tcpip_adapter_if_t tcpip_if)
 {
     ip_addr_t daddr;
-    daddr.type = IPADDR_TYPE_V4;
-    daddr.u_addr.ip4.addr = addr;
+    addr.to_ip_addr_t(&daddr);
     return writeTo(data, len, &daddr, port, tcpip_if);
 }
 
@@ -776,44 +772,12 @@ IPAddress AsyncUDP::listenIP()
     return IPAddress(_pcb->remote_ip.u_addr.ip4.addr);
 }
 
-bool AsyncUDP::listen(const IPv6Address addr, uint16_t port)
-{
-    ip_addr_t laddr;
-    laddr.type = IPADDR_TYPE_V6;
-    memcpy((uint8_t*)(laddr.u_addr.ip6.addr), (const uint8_t*)addr, 16);
-    return listen(&laddr, port);
-}
-
-bool AsyncUDP::listenMulticast(const IPv6Address addr, uint16_t port, uint8_t ttl, tcpip_adapter_if_t tcpip_if)
-{
-    ip_addr_t laddr;
-    laddr.type = IPADDR_TYPE_V6;
-    memcpy((uint8_t*)(laddr.u_addr.ip6.addr), (const uint8_t*)addr, 16);
-    return listenMulticast(&laddr, port, ttl, tcpip_if);
-}
-
-bool AsyncUDP::connect(const IPv6Address addr, uint16_t port)
-{
-    ip_addr_t daddr;
-    daddr.type = IPADDR_TYPE_V6;
-    memcpy((uint8_t*)(daddr.u_addr.ip6.addr), (const uint8_t*)addr, 16);
-    return connect(&daddr, port);
-}
-
-size_t AsyncUDP::writeTo(const uint8_t *data, size_t len, const IPv6Address addr, uint16_t port, tcpip_adapter_if_t tcpip_if)
-{
-    ip_addr_t daddr;
-    daddr.type = IPADDR_TYPE_V6;
-    memcpy((uint8_t*)(daddr.u_addr.ip6.addr), (const uint8_t*)addr, 16);
-    return writeTo(data, len, &daddr, port, tcpip_if);
-}
-
-IPv6Address AsyncUDP::listenIPv6()
+IPAddress AsyncUDP::listenIPv6()
 {
     if(!_pcb || _pcb->remote_ip.type != IPADDR_TYPE_V6){
-        return IPv6Address();
+        return IPAddress(IPv6);
     }
-    return IPv6Address(_pcb->remote_ip.u_addr.ip6.addr);
+    return IPAddress(IPv6, (const uint8_t *)_pcb->remote_ip.u_addr.ip6.addr, _pcb->remote_ip.u_addr.ip6.zone);
 }
 
 size_t AsyncUDP::write(const uint8_t *data, size_t len)
@@ -866,14 +830,6 @@ size_t AsyncUDP::sendTo(AsyncUDPMessage &message, const IPAddress addr, uint16_t
     return writeTo(message.data(), message.length(), addr, port, tcpip_if);
 }
 
-size_t AsyncUDP::sendTo(AsyncUDPMessage &message, const IPv6Address addr, uint16_t port, tcpip_adapter_if_t tcpip_if)
-{
-    if(!message) {
-        return 0;
-    }
-    return writeTo(message.data(), message.length(), addr, port, tcpip_if);
-}
-
 size_t AsyncUDP::send(AsyncUDPMessage &message)
 {
     if(!message) {
diff --git a/libraries/AsyncUDP/src/AsyncUDP.h b/libraries/AsyncUDP/src/AsyncUDP.h
index c9f365d1197..31d075191df 100644
--- a/libraries/AsyncUDP/src/AsyncUDP.h
+++ b/libraries/AsyncUDP/src/AsyncUDP.h
@@ -2,7 +2,6 @@
 #define ESPASYNCUDP_H
 
 #include "IPAddress.h"
-#include "IPv6Address.h"
 #include "Print.h"
 #include "Stream.h"
 #include <functional>
@@ -81,10 +80,10 @@ class AsyncUDPPacket : public Stream
     tcpip_adapter_if_t interface();
 
     IPAddress localIP();
-    IPv6Address localIPv6();
+    IPAddress localIPv6();
     uint16_t localPort();
     IPAddress remoteIP();
-    IPv6Address remoteIPv6();
+    IPAddress remoteIPv6();
     uint16_t remotePort();
     void remoteMac(uint8_t * mac);
 
@@ -121,22 +120,18 @@ class AsyncUDP : public Print
 
     bool listen(const ip_addr_t *addr, uint16_t port);
     bool listen(const IPAddress addr, uint16_t port);
-    bool listen(const IPv6Address addr, uint16_t port);
     bool listen(uint16_t port);
 
     bool listenMulticast(const ip_addr_t *addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX);
     bool listenMulticast(const IPAddress addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX);
-    bool listenMulticast(const IPv6Address addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX);
 
     bool connect(const ip_addr_t *addr, uint16_t port);
     bool connect(const IPAddress addr, uint16_t port);
-    bool connect(const IPv6Address addr, uint16_t port);
 
     void close();
 
     size_t writeTo(const uint8_t *data, size_t len, const ip_addr_t *addr, uint16_t port, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX);
     size_t writeTo(const uint8_t *data, size_t len, const IPAddress addr, uint16_t port, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX);
-    size_t writeTo(const uint8_t *data, size_t len, const IPv6Address addr, uint16_t port, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX);
     size_t write(const uint8_t *data, size_t len);
     size_t write(uint8_t data);
 
@@ -147,14 +142,13 @@ class AsyncUDP : public Print
 
     size_t sendTo(AsyncUDPMessage &message, const ip_addr_t *addr, uint16_t port, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX);
     size_t sendTo(AsyncUDPMessage &message, const IPAddress addr, uint16_t port, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX);
-    size_t sendTo(AsyncUDPMessage &message, const IPv6Address addr, uint16_t port, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX);
     size_t send(AsyncUDPMessage &message);
 
     size_t broadcastTo(AsyncUDPMessage &message, uint16_t port, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX);
     size_t broadcast(AsyncUDPMessage &message);
 
     IPAddress listenIP();
-    IPv6Address listenIPv6();
+    IPAddress listenIPv6();
     bool connected();
 	esp_err_t lastErr();
     operator bool();
diff --git a/libraries/ESPmDNS/src/ESPmDNS.cpp b/libraries/ESPmDNS/src/ESPmDNS.cpp
index bccc4de5758..faef0427ea1 100644
--- a/libraries/ESPmDNS/src/ESPmDNS.cpp
+++ b/libraries/ESPmDNS/src/ESPmDNS.cpp
@@ -281,20 +281,20 @@ IPAddress MDNSResponder::IP(int idx) {
     return IPAddress();
 }
 
-IPv6Address MDNSResponder::IPv6(int idx) {
+IPAddress MDNSResponder::IPv6(int idx) {
     mdns_result_t * result = _getResult(idx);
     if(!result){
         log_e("Result %d not found", idx);
-        return IPv6Address();
+        return IPAddress(IPv6);
     }
     mdns_ip_addr_t * addr = result->addr;
     while(addr){
         if(addr->addr.type == MDNS_IP_PROTOCOL_V6){
-            return IPv6Address(addr->addr.u_addr.ip6.addr);
+            return IPAddress(IPv6, (const uint8_t *)addr->addr.u_addr.ip6.addr, addr->addr.u_addr.ip6.zone);
         }
         addr = addr->next;
     }
-    return IPv6Address();
+    return IPAddress(IPv6);
 }
 
 uint16_t MDNSResponder::port(int idx) {
diff --git a/libraries/ESPmDNS/src/ESPmDNS.h b/libraries/ESPmDNS/src/ESPmDNS.h
index 6a5cb56ffba..ea7ef14f6ef 100644
--- a/libraries/ESPmDNS/src/ESPmDNS.h
+++ b/libraries/ESPmDNS/src/ESPmDNS.h
@@ -42,7 +42,6 @@ License (MIT license):
 #define ESP32MDNS_H
 
 #include "Arduino.h"
-#include "IPv6Address.h"
 #include "mdns.h"
 #include "esp_interface.h"
 
@@ -109,7 +108,7 @@ class MDNSResponder {
 
   String hostname(int idx);
   IPAddress IP(int idx);
-  IPv6Address IPv6(int idx);
+  IPAddress IPv6(int idx);
   uint16_t port(int idx);
   int numTxt(int idx);
   bool hasTxt(int idx, const char * key);
diff --git a/libraries/Ethernet/src/ETH.cpp b/libraries/Ethernet/src/ETH.cpp
index 40c408ee69a..fa30735d9a4 100644
--- a/libraries/Ethernet/src/ETH.cpp
+++ b/libraries/Ethernet/src/ETH.cpp
@@ -861,24 +861,41 @@ bool ETHClass::setHostname(const char * hostname)
     return esp_netif_set_hostname(_esp_netif, hostname) == 0;
 }
 
-bool ETHClass::enableIpV6()
+bool ETHClass::enableIPv6(bool en)
 {
     if(_esp_netif == NULL){
         return false;
     }
     return esp_netif_create_ip6_linklocal(_esp_netif) == 0;
+   // if (en)
+   //     WiFiGenericClass::setStatusBits(ETH_WANT_IP6_BIT);
+   // else
+   //     WiFiGenericClass::clearStatusBits(ETH_WANT_IP6_BIT);
+   // return true;
 }
 
-IPv6Address ETHClass::localIPv6()
+IPAddress ETHClass::localIPv6()
 {
     if(_esp_netif == NULL){
-        return IPv6Address();
+        return IPAddress(IPv6);
     }
     static esp_ip6_addr_t addr;
     if(esp_netif_get_ip6_linklocal(_esp_netif, &addr)){
-        return IPv6Address();
+        return IPAddress(IPv6);
     }
-    return IPv6Address(addr.addr);
+    return IPAddress(IPv6, (const uint8_t *)addr.addr, addr.zone);
+}
+
+IPAddress ETHClass::globalIPv6()
+{
+    if(_esp_netif == NULL){
+        return IPAddress(IPv6);
+    }
+    static esp_ip6_addr_t addr;
+    if(esp_netif_get_ip6_global(_esp_netif, &addr)){
+        return IPAddress(IPv6);
+    }
+    return IPAddress(IPv6, (const uint8_t *)addr.addr, addr.zone);
 }
 
 const char * ETHClass::ifkey(void)
@@ -1031,6 +1048,28 @@ void ETHClass::printInfo(Print & out){
     out.print(dnsIP());
     out.println();
 
+    const char * types[] = { "UNKNOWN", "GLOBAL", "LINK_LOCAL", "SITE_LOCAL", "UNIQUE_LOCAL", "IPV4_MAPPED_IPV6" };
+    esp_ip6_addr_t if_ip6[5];
+    int v6addrs = esp_netif_get_all_ip6(_esp_netif, if_ip6);
+    for (int i = 0; i < v6addrs; ++i){
+        out.print("      ");
+        out.print("inet6 ");
+        IPAddress(IPv6, (const uint8_t *)if_ip6[i].addr, if_ip6[i].zone).printTo(out);
+        out.print(" type ");
+        out.print(types[esp_netif_ip6_get_addr_type(&if_ip6[i])]);
+        out.println();
+    }
+
+    // out.print("      ");
+    // out.print("inet6 ");
+    // localIPv6().printTo(out);
+    // out.println();
+
+    // out.print("      ");
+    // out.print("inet6 ");
+    // globalIPv6().printTo(out);
+    // out.println();
+
     out.println();
 }
 
diff --git a/libraries/Ethernet/src/ETH.h b/libraries/Ethernet/src/ETH.h
index 7ef39ef561f..7159ebd4574 100644
--- a/libraries/Ethernet/src/ETH.h
+++ b/libraries/Ethernet/src/ETH.h
@@ -145,8 +145,9 @@ class ETHClass {
         IPAddress broadcastIP();
         IPAddress networkID();
         uint8_t subnetCIDR();
-        bool enableIpV6();
-        IPv6Address localIPv6();
+        bool enableIPv6(bool en=true);
+        IPAddress localIPv6();
+        IPAddress globalIPv6();
         const char * ifkey(void);
         const char * desc(void);
         String impl_name(void);
diff --git a/libraries/WiFi/src/WiFi.h b/libraries/WiFi/src/WiFi.h
index 457f913ae8e..30b9e31f744 100644
--- a/libraries/WiFi/src/WiFi.h
+++ b/libraries/WiFi/src/WiFi.h
@@ -26,7 +26,6 @@
 
 #include "Print.h"
 #include "IPAddress.h"
-#include "IPv6Address.h"
 
 #include "WiFiType.h"
 #include "WiFiSTA.h"
diff --git a/libraries/WiFi/src/WiFiAP.cpp b/libraries/WiFi/src/WiFiAP.cpp
index a88547f9a89..84e508ee491 100644
--- a/libraries/WiFi/src/WiFiAP.cpp
+++ b/libraries/WiFi/src/WiFiAP.cpp
@@ -421,16 +421,18 @@ bool WiFiAPClass::softAPenableIpV6()
 
 /**
  * Get the softAP interface IPv6 address.
- * @return IPv6Address softAP IPv6
+ * @return IPAddress softAP IPv6
  */
-IPv6Address WiFiAPClass::softAPIPv6()
+
+IPAddress WiFiAPClass::softAPIPv6()
 {
-	esp_ip6_addr_t addr;
+    static esp_ip6_addr_t addr;
     if(WiFiGenericClass::getMode() == WIFI_MODE_NULL){
-        return IPv6Address();
+        return IPAddress(IPv6);
     }
-    if(esp_netif_get_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_AP), &addr)) {
-        return IPv6Address();
+    if(esp_netif_get_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_STA), &addr)){
+        return IPAddress(IPv6);
     }
-    return IPv6Address(addr.addr);
+    return IPAddress(IPv6, (const uint8_t *)addr.addr, addr.zone);
 }
+
diff --git a/libraries/WiFi/src/WiFiAP.h b/libraries/WiFi/src/WiFiAP.h
index 626a5b5b350..5e6e187a5c7 100644
--- a/libraries/WiFi/src/WiFiAP.h
+++ b/libraries/WiFi/src/WiFiAP.h
@@ -57,7 +57,7 @@ class WiFiAPClass
     uint8_t softAPSubnetCIDR();
 
     bool softAPenableIpV6();
-    IPv6Address softAPIPv6();
+    IPAddress softAPIPv6();
 
     const char * softAPgetHostname();
     bool softAPsetHostname(const char * hostname);
diff --git a/libraries/WiFi/src/WiFiClient.cpp b/libraries/WiFi/src/WiFiClient.cpp
index 99b153c7629..4127bcad3de 100644
--- a/libraries/WiFi/src/WiFiClient.cpp
+++ b/libraries/WiFi/src/WiFiClient.cpp
@@ -320,7 +320,7 @@ int WiFiClient::connect(const char *host, uint16_t port)
 int WiFiClient::connect(const char *host, uint16_t port, int32_t timeout_ms)
 {
     IPAddress srv((uint32_t)0);
-    if(!WiFiGenericClass::hostByName(host, srv, (WiFiGenericClass::getStatusBits() & WIFI_WANT_IP6_BIT))){
+    if(!WiFiGenericClass::hostByName(host, srv)){
         return 0;
     }
     return connect(srv, port, timeout_ms);
diff --git a/libraries/WiFi/src/WiFiGeneric.cpp b/libraries/WiFi/src/WiFiGeneric.cpp
index d0075170b76..3069b22f8af 100644
--- a/libraries/WiFi/src/WiFiGeneric.cpp
+++ b/libraries/WiFi/src/WiFiGeneric.cpp
@@ -1051,12 +1051,13 @@ esp_err_t WiFiGenericClass::_eventCallback(arduino_event_t *event)
         }
     } else if(event->event_id == ARDUINO_EVENT_WIFI_STA_STOP) {
         WiFiSTAClass::_setStatus(WL_STOPPED);
-        clearStatusBits(STA_STARTED_BIT | STA_CONNECTED_BIT | STA_HAS_IP_BIT | STA_HAS_IP6_BIT);
+        clearStatusBits(STA_STARTED_BIT | STA_CONNECTED_BIT | STA_HAS_IP_BIT | STA_HAS_IP6_BIT | STA_HAS_IP6_GLOBAL_BIT);
     } else if(event->event_id == ARDUINO_EVENT_WIFI_STA_CONNECTED) {
+        if (getStatusBits() & STA_WANT_IP6_BIT){
+            esp_netif_create_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_STA));
+        }
         WiFiSTAClass::_setStatus(WL_IDLE_STATUS);
         setStatusBits(STA_CONNECTED_BIT);
-        if (getStatusBits() & WIFI_WANT_IP6_BIT)
-            esp_netif_create_ip6_linklocal(esp_netifs[ESP_IF_WIFI_STA]);
     } else if(event->event_id == ARDUINO_EVENT_WIFI_STA_DISCONNECTED) {
         uint8_t reason = event->event_info.wifi_sta_disconnected.reason;
         // Reason 0 causes crash, use reason 1 (UNSPECIFIED) instead
@@ -1074,7 +1075,7 @@ esp_err_t WiFiGenericClass::_eventCallback(arduino_event_t *event)
         } else {
             WiFiSTAClass::_setStatus(WL_DISCONNECTED);
         }
-        clearStatusBits(STA_CONNECTED_BIT | STA_HAS_IP_BIT | STA_HAS_IP6_BIT);
+        clearStatusBits(STA_CONNECTED_BIT | STA_HAS_IP_BIT | STA_HAS_IP6_BIT | STA_HAS_IP6_GLOBAL_BIT);
 
         bool DoReconnect = false;
         if(reason == WIFI_REASON_ASSOC_LEAVE) {                                     //Voluntarily disconnected. Don't reconnect!
@@ -1126,11 +1127,14 @@ esp_err_t WiFiGenericClass::_eventCallback(arduino_event_t *event)
     } else if(event->event_id == ARDUINO_EVENT_ETH_START) {
         setStatusBits(ETH_STARTED_BIT);
     } else if(event->event_id == ARDUINO_EVENT_ETH_STOP) {
-        clearStatusBits(ETH_STARTED_BIT | ETH_CONNECTED_BIT | ETH_HAS_IP_BIT | ETH_HAS_IP6_BIT);
+        clearStatusBits(ETH_STARTED_BIT | ETH_CONNECTED_BIT | ETH_HAS_IP_BIT | ETH_HAS_IP6_BIT | ETH_HAS_IP6_GLOBAL_BIT);
     } else if(event->event_id == ARDUINO_EVENT_ETH_CONNECTED) {
+        if (getStatusBits() & ETH_WANT_IP6_BIT){
+            esp_netif_create_ip6_linklocal(get_esp_interface_netif(ESP_IF_ETH));
+        }
         setStatusBits(ETH_CONNECTED_BIT);
     } else if(event->event_id == ARDUINO_EVENT_ETH_DISCONNECTED) {
-        clearStatusBits(ETH_CONNECTED_BIT | ETH_HAS_IP_BIT | ETH_HAS_IP6_BIT);
+        clearStatusBits(ETH_CONNECTED_BIT | ETH_HAS_IP_BIT | ETH_HAS_IP6_BIT | ETH_HAS_IP6_GLOBAL_BIT);
     } else if(event->event_id == ARDUINO_EVENT_ETH_GOT_IP) {
 #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
         uint8_t * ip = (uint8_t *)&(event->event_info.got_ip.ip_info.ip.addr);
@@ -1147,10 +1151,16 @@ esp_err_t WiFiGenericClass::_eventCallback(arduino_event_t *event)
 
     } else if(event->event_id == ARDUINO_EVENT_WIFI_STA_GOT_IP6) {
     	setStatusBits(STA_CONNECTED_BIT | STA_HAS_IP6_BIT);
+        if(esp_netif_ip6_get_addr_type((esp_ip6_addr_t*)&(event->event_info.got_ip6.ip6_info.ip)) == ESP_IP6_ADDR_IS_GLOBAL){
+            setStatusBits(STA_HAS_IP6_GLOBAL_BIT);
+        }
     } else if(event->event_id == ARDUINO_EVENT_WIFI_AP_GOT_IP6) {
     	setStatusBits(AP_HAS_IP6_BIT);
     } else if(event->event_id == ARDUINO_EVENT_ETH_GOT_IP6) {
     	setStatusBits(ETH_CONNECTED_BIT | ETH_HAS_IP6_BIT);
+        if(esp_netif_ip6_get_addr_type((esp_ip6_addr_t*)&(event->event_info.got_ip6.ip6_info.ip)) == ESP_IP6_ADDR_IS_GLOBAL){
+            setStatusBits(ETH_HAS_IP6_GLOBAL_BIT);
+        }
     } else if(event->event_id == ARDUINO_EVENT_SC_GOT_SSID_PSWD) {
     	WiFi.begin(
 			(const char *)event->event_info.sc_got_ssid_pswd.ssid,
@@ -1567,6 +1577,15 @@ static void wifi_dns_found_callback(const char *name, const ip_addr_t *ipaddr, v
             memcpy(&(parameters->addr), ipaddr, sizeof(ip_addr_t));
             parameters->result = 1;
         }
+        IPAddress addr;
+        addr.from_ip_addr_t((ip_addr_t *)ipaddr);
+        Serial.print("dns_found ");
+        addr.printTo(Serial);
+        if(ipaddr->type == IPADDR_TYPE_V6){
+            Serial.print(" type ");
+            Serial.print((int)esp_netif_ip6_get_addr_type((esp_ip6_addr_t*)&(ipaddr->u_addr.ip6)));
+        }
+        Serial.println();
     } else {
         parameters->result = -1;
     }
@@ -1595,9 +1614,20 @@ int WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult, bool
     err_t err = ERR_OK;
     gethostbynameParameters_t params;
 
+    // This should generally check if we have a global address assigned to one of the interfaces.
+    // If such address is not assigned, there is no point in trying to get V6 from DNS as we will not be able to reach it.
+    // That is of course, if 'preferV6' is not set to true
+    static bool hasGlobalV6 = false;
+    bool hasGlobalV6Now = (getStatusBits() & NET_HAS_IP6_GLOBAL_BIT) != 0;
+    if(hasGlobalV6 != hasGlobalV6Now){
+        hasGlobalV6 = hasGlobalV6Now;
+        dns_clear_cache();
+        log_d("Clearing DNS cache");
+    }
+
     aResult = static_cast<uint32_t>(0);
     params.hostname = aHostname;
-    params.addr_type = preferV6?LWIP_DNS_ADDRTYPE_IPV6_IPV4:LWIP_DNS_ADDRTYPE_IPV4_IPV6;
+    params.addr_type = (preferV6 || hasGlobalV6)?LWIP_DNS_ADDRTYPE_IPV6_IPV4:LWIP_DNS_ADDRTYPE_IPV4;
     params.result = 0;
     aResult.to_ip_addr_t(&(params.addr));
 
@@ -1608,6 +1638,13 @@ int WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult, bool
         err = esp_netif_tcpip_exec(wifi_gethostbyname_tcpip_ctx, &params);
         if (err == ERR_OK) {
             aResult.from_ip_addr_t(&(params.addr));
+            Serial.print("dns_cache ");
+            aResult.printTo(Serial);
+            if(params.addr.type == IPADDR_TYPE_V6){
+                Serial.print(" type ");
+                Serial.print((int)esp_netif_ip6_get_addr_type((esp_ip6_addr_t*)&(params.addr.u_addr.ip6)));
+            }
+            Serial.println();
         } else if (err == ERR_INPROGRESS) {
             waitStatusBits(WIFI_DNS_DONE_BIT, 15000);  //real internal timeout in lwip library is 14[s]
             clearStatusBits(WIFI_DNS_DONE_BIT);
diff --git a/libraries/WiFi/src/WiFiGeneric.h b/libraries/WiFi/src/WiFiGeneric.h
index 90db296d188..c74c386a1eb 100644
--- a/libraries/WiFi/src/WiFiGeneric.h
+++ b/libraries/WiFi/src/WiFiGeneric.h
@@ -138,11 +138,18 @@ static const int ETH_STARTED_BIT   = BIT7;
 static const int ETH_CONNECTED_BIT = BIT8;
 static const int ETH_HAS_IP_BIT    = BIT9;
 static const int ETH_HAS_IP6_BIT   = BIT10;
+
 static const int WIFI_SCANNING_BIT = BIT11;
 static const int WIFI_SCAN_DONE_BIT= BIT12;
 static const int WIFI_DNS_IDLE_BIT = BIT13;
 static const int WIFI_DNS_DONE_BIT = BIT14;
-static const int WIFI_WANT_IP6_BIT = BIT15;
+
+static const int STA_WANT_IP6_BIT = BIT15;
+static const int ETH_WANT_IP6_BIT = BIT16;
+
+static const int STA_HAS_IP6_GLOBAL_BIT = BIT17;
+static const int ETH_HAS_IP6_GLOBAL_BIT = BIT18;
+static const int NET_HAS_IP6_GLOBAL_BIT = STA_HAS_IP6_GLOBAL_BIT | ETH_HAS_IP6_GLOBAL_BIT;
 
 typedef enum {
 	WIFI_RX_ANT0 = 0,
@@ -228,6 +235,7 @@ class WiFiGenericClass
     friend class WiFiSTAClass;
     friend class WiFiScanClass;
     friend class WiFiAPClass;
+    friend class ETHClass;
 };
 
 #endif /* ESP32WIFIGENERIC_H_ */
diff --git a/libraries/WiFi/src/WiFiMulti.cpp b/libraries/WiFi/src/WiFiMulti.cpp
index 9e7f03c6531..320c5c68c46 100644
--- a/libraries/WiFi/src/WiFiMulti.cpp
+++ b/libraries/WiFi/src/WiFiMulti.cpp
@@ -160,9 +160,9 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
         if(bestNetwork.ssid) {
             log_i("[WIFI] Connecting BSSID: %02X:%02X:%02X:%02X:%02X:%02X SSID: %s Channel: %d (%d)", bestBSSID[0], bestBSSID[1], bestBSSID[2], bestBSSID[3], bestBSSID[4], bestBSSID[5], bestNetwork.ssid, bestChannel, bestNetworkDb);
 
-            WiFi.begin(bestNetwork.ssid, bestNetwork.passphrase, bestChannel, bestBSSID);
             if (ipv6_support == true)
-                WiFi.IPv6(true);
+                WiFi.enableIPv6();
+            WiFi.begin(bestNetwork.ssid, bestNetwork.passphrase, bestChannel, bestBSSID);
             status = WiFi.status();
 
             auto startTime = millis();
diff --git a/libraries/WiFi/src/WiFiSTA.cpp b/libraries/WiFi/src/WiFiSTA.cpp
index ca3fb6b5e40..23f62b82cfa 100644
--- a/libraries/WiFi/src/WiFiSTA.cpp
+++ b/libraries/WiFi/src/WiFiSTA.cpp
@@ -806,43 +806,49 @@ int8_t WiFiSTAClass::RSSI(void)
 
 /**
  * Enable IPv6 on the station interface.
+ * Should be called before WiFi.begin()
+ * 
  * @return true on success
  */
-bool WiFiSTAClass::enableIpV6()
+bool WiFiSTAClass::enableIPv6(bool en)
 {
-    if(WiFiGenericClass::getMode() == WIFI_MODE_NULL){
-        return false;
-    }
-    return esp_netif_create_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_STA)) == ESP_OK;
+   if (en)
+       WiFiGenericClass::setStatusBits(STA_WANT_IP6_BIT);
+   else
+       WiFiGenericClass::clearStatusBits(STA_WANT_IP6_BIT);
+   return true;
 }
 
 /**
- * Enable IPv6 support on the station interface.
- * @return true on success
+ * Get the station interface link-local IPv6 address.
+ * @return IPAddress
  */
-bool WiFiSTAClass::IPv6(bool state)
+IPAddress WiFiSTAClass::localIPv6()
 {
-   if (state)
-       WiFiGenericClass::setStatusBits(WIFI_WANT_IP6_BIT);
-   else
-       WiFiGenericClass::clearStatusBits(WIFI_WANT_IP6_BIT);
-   return true;
+    static esp_ip6_addr_t addr;
+    if(WiFiGenericClass::getMode() == WIFI_MODE_NULL){
+        return IPAddress(IPv6);
+    }
+    if(esp_netif_get_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_STA), &addr)){
+        return IPAddress(IPv6);
+    }
+    return IPAddress(IPv6, (const uint8_t *)addr.addr, addr.zone);
 }
 
 /**
- * Get the station interface IPv6 address.
- * @return IPv6Address
+ * Get the station interface global IPv6 address.
+ * @return IPAddress
  */
-IPv6Address WiFiSTAClass::localIPv6()
+IPAddress WiFiSTAClass::globalIPv6()
 {
-	esp_ip6_addr_t addr;
+    static esp_ip6_addr_t addr;
     if(WiFiGenericClass::getMode() == WIFI_MODE_NULL){
-        return IPv6Address();
+        return IPAddress(IPv6);
     }
-    if(esp_netif_get_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_STA), &addr)) {
-        return IPv6Address();
+    if(esp_netif_get_ip6_global(get_esp_interface_netif(ESP_IF_WIFI_STA), &addr)){
+        return IPAddress(IPv6);
     }
-    return IPv6Address(addr.addr);
+    return IPAddress(IPv6, (const uint8_t *)addr.addr, addr.zone);
 }
 
 
diff --git a/libraries/WiFi/src/WiFiSTA.h b/libraries/WiFi/src/WiFiSTA.h
index 12e2f0bd4a4..4fbb789aa04 100644
--- a/libraries/WiFi/src/WiFiSTA.h
+++ b/libraries/WiFi/src/WiFiSTA.h
@@ -93,9 +93,9 @@ class WiFiSTAClass
     IPAddress networkID();
     uint8_t subnetCIDR();
     
-    bool enableIpV6();
-    bool IPv6(bool state);
-    IPv6Address localIPv6();
+    bool enableIPv6(bool en=true);
+    IPAddress localIPv6();
+    IPAddress globalIPv6();
 
     // STA WiFi info
     static wl_status_t status();

From c7e63e66e5199d38f592b4c702363a1f49bd682f Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Thu, 11 Jan 2024 12:31:25 +0200
Subject: [PATCH 09/21] Rename softAPenableIPv6

---
 docs/source/api/wifi.rst                                    | 4 ++--
 .../examples/WiFiBlueToothSwitch/WiFiBlueToothSwitch.ino    | 2 +-
 libraries/WiFi/examples/WiFiIPv6/WiFiIPv6.ino               | 6 +++---
 libraries/WiFi/src/WiFiAP.cpp                               | 2 +-
 libraries/WiFi/src/WiFiAP.h                                 | 2 +-
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/docs/source/api/wifi.rst b/docs/source/api/wifi.rst
index f5f2fe03f2e..86bf306a3f4 100644
--- a/docs/source/api/wifi.rst
+++ b/docs/source/api/wifi.rst
@@ -327,14 +327,14 @@ Get the softAP subnet mask.
 
     IPAddress softAPSubnetMask();
 
-softAPenableIpV6
+softAPenableIPv6
 ****************
 
 Function used to enable the IPv6 support.
 
 .. code-block:: arduino
 
-    bool softAPenableIpV6();
+    bool softAPenableIPv6();
 
 The function will return ``true`` if the configuration is successful.
 
diff --git a/libraries/WiFi/examples/WiFiBlueToothSwitch/WiFiBlueToothSwitch.ino b/libraries/WiFi/examples/WiFiBlueToothSwitch/WiFiBlueToothSwitch.ino
index 771a531a480..be9aa239627 100644
--- a/libraries/WiFi/examples/WiFiBlueToothSwitch/WiFiBlueToothSwitch.ino
+++ b/libraries/WiFi/examples/WiFiBlueToothSwitch/WiFiBlueToothSwitch.ino
@@ -89,7 +89,6 @@ void WiFiEvent(WiFiEvent_t event){
             break;
         case ARDUINO_EVENT_WIFI_STA_CONNECTED:
             Serial.println("STA Connected");
-            WiFi.enableIpV6();
             break;
         case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
             Serial.print("STA IPv6: ");
@@ -114,6 +113,7 @@ void setup() {
     Serial.begin(115200);
     pinMode(0, INPUT_PULLUP);
     WiFi.onEvent(WiFiEvent);  // Will call WiFiEvent() from another thread.
+    WiFi.enableIPv6();
     Serial.print("ESP32 SDK: ");
     Serial.println(ESP.getSdkVersion());
     Serial.println("Press the button to select the next mode");
diff --git a/libraries/WiFi/examples/WiFiIPv6/WiFiIPv6.ino b/libraries/WiFi/examples/WiFiIPv6/WiFiIPv6.ino
index 3dcba56eec2..2db8dabb5d1 100644
--- a/libraries/WiFi/examples/WiFiIPv6/WiFiIPv6.ino
+++ b/libraries/WiFi/examples/WiFiIPv6/WiFiIPv6.ino
@@ -71,15 +71,13 @@ void WiFiEvent(WiFiEvent_t event){
             //can set ap hostname here
             WiFi.softAPsetHostname(AP_SSID);
             //enable ap ipv6 here
-            WiFi.softAPenableIpV6();
+            WiFi.softAPenableIPv6();
             break;
         case ARDUINO_EVENT_WIFI_STA_START:
             //set sta hostname here
             WiFi.setHostname(AP_SSID);
             break;
         case ARDUINO_EVENT_WIFI_STA_CONNECTED:
-            //enable sta ipv6 here
-            WiFi.enableIpV6();
             break;
         case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
             Serial.print("STA IPv6: ");
@@ -108,6 +106,8 @@ void setup(){
     WiFi.onEvent(WiFiEvent);  // Will call WiFiEvent() from another thread.
     WiFi.mode(WIFI_MODE_APSTA);
     WiFi.softAP(AP_SSID);
+    //enable sta ipv6 here
+    WiFi.enableIPv6();
     WiFi.begin(STA_SSID, STA_PASS);
 }
 
diff --git a/libraries/WiFi/src/WiFiAP.cpp b/libraries/WiFi/src/WiFiAP.cpp
index 84e508ee491..79917daeb6e 100644
--- a/libraries/WiFi/src/WiFiAP.cpp
+++ b/libraries/WiFi/src/WiFiAP.cpp
@@ -411,7 +411,7 @@ bool WiFiAPClass::softAPsetHostname(const char * hostname)
  * Enable IPv6 on the softAP interface.
  * @return true on success
  */
-bool WiFiAPClass::softAPenableIpV6()
+bool WiFiAPClass::softAPenableIPv6()
 {
     if(WiFiGenericClass::getMode() == WIFI_MODE_NULL){
         return false;
diff --git a/libraries/WiFi/src/WiFiAP.h b/libraries/WiFi/src/WiFiAP.h
index 5e6e187a5c7..9c2d1b96aeb 100644
--- a/libraries/WiFi/src/WiFiAP.h
+++ b/libraries/WiFi/src/WiFiAP.h
@@ -56,7 +56,7 @@ class WiFiAPClass
     IPAddress softAPSubnetMask();
     uint8_t softAPSubnetCIDR();
 
-    bool softAPenableIpV6();
+    bool softAPenableIPv6();
     IPAddress softAPIPv6();
 
     const char * softAPgetHostname();

From 55aaa6f210a83ac73c79ed4c32df967e8d454f51 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Thu, 11 Jan 2024 12:37:22 +0200
Subject: [PATCH 10/21] Rename mDNS methods

---
 .../ESPmDNS/examples/mDNS-SD_Extended/mDNS-SD_Extended.ino    | 2 +-
 libraries/ESPmDNS/src/ESPmDNS.cpp                             | 4 ++--
 libraries/ESPmDNS/src/ESPmDNS.h                               | 4 ++--
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/libraries/ESPmDNS/examples/mDNS-SD_Extended/mDNS-SD_Extended.ino b/libraries/ESPmDNS/examples/mDNS-SD_Extended/mDNS-SD_Extended.ino
index 6f2dfddfb3b..9e582b3236a 100644
--- a/libraries/ESPmDNS/examples/mDNS-SD_Extended/mDNS-SD_Extended.ino
+++ b/libraries/ESPmDNS/examples/mDNS-SD_Extended/mDNS-SD_Extended.ino
@@ -70,7 +70,7 @@ void browseService(const char * service, const char * proto){
             Serial.print(": ");
             Serial.print(MDNS.hostname(i));
             Serial.print(" (");
-            Serial.print(MDNS.IP(i));
+            Serial.print(MDNS.address(i));
             Serial.print(":");
             Serial.print(MDNS.port(i));
             Serial.println(")");
diff --git a/libraries/ESPmDNS/src/ESPmDNS.cpp b/libraries/ESPmDNS/src/ESPmDNS.cpp
index faef0427ea1..21d93aa4e2a 100644
--- a/libraries/ESPmDNS/src/ESPmDNS.cpp
+++ b/libraries/ESPmDNS/src/ESPmDNS.cpp
@@ -265,7 +265,7 @@ String MDNSResponder::hostname(int idx) {
     return String(result->hostname);
 }
 
-IPAddress MDNSResponder::IP(int idx) {
+IPAddress MDNSResponder::address(int idx) {
     mdns_result_t * result = _getResult(idx);
     if(!result){
         log_e("Result %d not found", idx);
@@ -281,7 +281,7 @@ IPAddress MDNSResponder::IP(int idx) {
     return IPAddress();
 }
 
-IPAddress MDNSResponder::IPv6(int idx) {
+IPAddress MDNSResponder::addressV6(int idx) {
     mdns_result_t * result = _getResult(idx);
     if(!result){
         log_e("Result %d not found", idx);
diff --git a/libraries/ESPmDNS/src/ESPmDNS.h b/libraries/ESPmDNS/src/ESPmDNS.h
index ea7ef14f6ef..01ca7518d2f 100644
--- a/libraries/ESPmDNS/src/ESPmDNS.h
+++ b/libraries/ESPmDNS/src/ESPmDNS.h
@@ -107,8 +107,8 @@ class MDNSResponder {
   }
 
   String hostname(int idx);
-  IPAddress IP(int idx);
-  IPAddress IPv6(int idx);
+  IPAddress address(int idx);
+  IPAddress addressV6(int idx);
   uint16_t port(int idx);
   int numTxt(int idx);
   bool hasTxt(int idx, const char * key);

From c4f366c9667a6d2fb2e7f7869765fddf233970bf Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Thu, 11 Jan 2024 12:43:50 +0200
Subject: [PATCH 11/21] fix IPAddress method to work with const address

---
 cores/esp32/IPAddress.cpp | 2 +-
 cores/esp32/IPAddress.h   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/cores/esp32/IPAddress.cpp b/cores/esp32/IPAddress.cpp
index bd88a1a1096..13502ee05d3 100644
--- a/cores/esp32/IPAddress.cpp
+++ b/cores/esp32/IPAddress.cpp
@@ -374,7 +374,7 @@ size_t IPAddress::printTo(Print& p) const
     return n;
 }
 
-void IPAddress::to_ip_addr_t(ip_addr_t* addr){
+void IPAddress::to_ip_addr_t(ip_addr_t* addr) const {
     if(_type == IPv6){
         addr->type = IPADDR_TYPE_V6;
         addr->u_addr.ip6.addr[0] = _address.dword[0];
diff --git a/cores/esp32/IPAddress.h b/cores/esp32/IPAddress.h
index 2eedec83df5..88b1bf627f0 100644
--- a/cores/esp32/IPAddress.h
+++ b/cores/esp32/IPAddress.h
@@ -100,7 +100,7 @@ class IPAddress : public Printable {
     uint8_t zone() const { return (type() == IPv6)?_zone:0; }
 
     // LwIP conversions
-    void to_ip_addr_t(ip_addr_t* addr);
+    void to_ip_addr_t(ip_addr_t* addr) const;
     IPAddress& from_ip_addr_t(ip_addr_t* addr);
 
     friend class UDP;

From 701d35f20e587f1e874794b08bf630ba86eb636c Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Thu, 11 Jan 2024 16:02:45 +0200
Subject: [PATCH 12/21] Some cleanup and do not print zone in IPAddress

---
 cores/esp32/IPAddress.cpp                     | 12 ++--
 docs/source/api/wifi.rst                      |  2 +-
 libraries/Ethernet/src/ETH.cpp                | 47 ++++++++--------
 libraries/WiFi/examples/WiFiIPv6/WiFiIPv6.ino |  4 +-
 libraries/WiFi/src/WiFiAP.cpp                 | 15 +++--
 libraries/WiFi/src/WiFiAP.h                   |  2 +-
 libraries/WiFi/src/WiFiGeneric.cpp            | 55 +++++++++++++------
 libraries/WiFi/src/WiFiGeneric.h              | 52 ++++++++++--------
 libraries/WiFi/src/WiFiMulti.cpp              |  3 +-
 libraries/WiFi/src/WiFiSTA.cpp                | 11 ++--
 10 files changed, 120 insertions(+), 83 deletions(-)

diff --git a/cores/esp32/IPAddress.cpp b/cores/esp32/IPAddress.cpp
index 13502ee05d3..69ccc2ec881 100644
--- a/cores/esp32/IPAddress.cpp
+++ b/cores/esp32/IPAddress.cpp
@@ -355,12 +355,12 @@ size_t IPAddress::printTo(Print& p) const
             }
         }
         // add a zone if zone-id is non-zero
-        if(_zone > 0){
-            n += p.print('%');
-            char if_name[NETIF_NAMESIZE];
-            netif_index_to_name(_zone, if_name);
-            n += p.print(if_name);
-        }
+        // if(_zone > 0){
+        //     n += p.print('%');
+        //     char if_name[NETIF_NAMESIZE];
+        //     netif_index_to_name(_zone, if_name);
+        //     n += p.print(if_name);
+        // }
         return n;
     }
 
diff --git a/docs/source/api/wifi.rst b/docs/source/api/wifi.rst
index 86bf306a3f4..ea6e86cd3eb 100644
--- a/docs/source/api/wifi.rst
+++ b/docs/source/api/wifi.rst
@@ -334,7 +334,7 @@ Function used to enable the IPv6 support.
 
 .. code-block:: arduino
 
-    bool softAPenableIPv6();
+    bool softAPenableIPv6(bool enable=true);
 
 The function will return ``true`` if the configuration is successful.
 
diff --git a/libraries/Ethernet/src/ETH.cpp b/libraries/Ethernet/src/ETH.cpp
index fa30735d9a4..9587b22719d 100644
--- a/libraries/Ethernet/src/ETH.cpp
+++ b/libraries/Ethernet/src/ETH.cpp
@@ -401,13 +401,13 @@ bool ETHClass::beginSPI(eth_phy_type_t type, uint8_t phy_addr, int cs, int irq,
 
     // Init SPI bus
     if(_pin_sck >= 0 && _pin_miso >= 0 && _pin_mosi >= 0){
-        spi_bus_config_t buscfg = {
-            .mosi_io_num = _pin_mosi,
-            .miso_io_num = _pin_miso,
-            .sclk_io_num = _pin_sck,
-            .quadwp_io_num = -1,
-            .quadhd_io_num = -1,
-        };
+        spi_bus_config_t buscfg;
+        memset(&buscfg, 0, sizeof(spi_bus_config_t));
+        buscfg.mosi_io_num = _pin_mosi;
+        buscfg.miso_io_num = _pin_miso;
+        buscfg.sclk_io_num = _pin_sck;
+        buscfg.quadwp_io_num = -1;
+        buscfg.quadhd_io_num = -1;
         ret = spi_bus_initialize(spi_host, &buscfg, SPI_DMA_CH_AUTO);
         if(ret != ESP_OK){
             log_e("SPI bus initialize failed: %d", ret);
@@ -433,13 +433,13 @@ bool ETHClass::beginSPI(eth_phy_type_t type, uint8_t phy_addr, int cs, int irq,
     phy_config.reset_gpio_num = _pin_rst;
 
     // Configure SPI interface for specific SPI module
-    spi_device_interface_config_t spi_devcfg = {
-        .mode = 0,
-        .clock_speed_hz = _spi_freq_mhz * 1000 * 1000,
-        .input_delay_ns = 20,
-        .spics_io_num = _pin_cs,
-        .queue_size = 20,
-    };
+    spi_device_interface_config_t spi_devcfg;
+    memset(&spi_devcfg, 0, sizeof(spi_device_interface_config_t));
+    spi_devcfg.mode = 0;
+    spi_devcfg.clock_speed_hz = _spi_freq_mhz * 1000 * 1000;
+    spi_devcfg.input_delay_ns = 20;
+    spi_devcfg.spics_io_num = _pin_cs;
+    spi_devcfg.queue_size = 20;
 
     esp_eth_mac_t *mac = NULL;
     esp_eth_phy_t *phy = NULL;
@@ -863,15 +863,16 @@ bool ETHClass::setHostname(const char * hostname)
 
 bool ETHClass::enableIPv6(bool en)
 {
-    if(_esp_netif == NULL){
-        return false;
+    // if(_esp_netif == NULL){
+    //     return false;
+    // }
+    // return esp_netif_create_ip6_linklocal(_esp_netif) == 0;
+    if (en) {
+        WiFiGenericClass::setStatusBits(ETH_WANT_IP6_BIT);
+    } else {
+        WiFiGenericClass::clearStatusBits(ETH_WANT_IP6_BIT);
     }
-    return esp_netif_create_ip6_linklocal(_esp_netif) == 0;
-   // if (en)
-   //     WiFiGenericClass::setStatusBits(ETH_WANT_IP6_BIT);
-   // else
-   //     WiFiGenericClass::clearStatusBits(ETH_WANT_IP6_BIT);
-   // return true;
+    return true;
 }
 
 IPAddress ETHClass::localIPv6()
@@ -1048,7 +1049,7 @@ void ETHClass::printInfo(Print & out){
     out.print(dnsIP());
     out.println();
 
-    const char * types[] = { "UNKNOWN", "GLOBAL", "LINK_LOCAL", "SITE_LOCAL", "UNIQUE_LOCAL", "IPV4_MAPPED_IPV6" };
+    static const char * types[] = { "UNKNOWN", "GLOBAL", "LINK_LOCAL", "SITE_LOCAL", "UNIQUE_LOCAL", "IPV4_MAPPED_IPV6" };
     esp_ip6_addr_t if_ip6[5];
     int v6addrs = esp_netif_get_all_ip6(_esp_netif, if_ip6);
     for (int i = 0; i < v6addrs; ++i){
diff --git a/libraries/WiFi/examples/WiFiIPv6/WiFiIPv6.ino b/libraries/WiFi/examples/WiFiIPv6/WiFiIPv6.ino
index 2db8dabb5d1..c7edd384d82 100644
--- a/libraries/WiFi/examples/WiFiIPv6/WiFiIPv6.ino
+++ b/libraries/WiFi/examples/WiFiIPv6/WiFiIPv6.ino
@@ -70,8 +70,6 @@ void WiFiEvent(WiFiEvent_t event){
         case ARDUINO_EVENT_WIFI_AP_START:
             //can set ap hostname here
             WiFi.softAPsetHostname(AP_SSID);
-            //enable ap ipv6 here
-            WiFi.softAPenableIPv6();
             break;
         case ARDUINO_EVENT_WIFI_STA_START:
             //set sta hostname here
@@ -105,6 +103,8 @@ void setup(){
     WiFi.disconnect(true);
     WiFi.onEvent(WiFiEvent);  // Will call WiFiEvent() from another thread.
     WiFi.mode(WIFI_MODE_APSTA);
+    //enable ap ipv6 here
+    WiFi.softAPenableIPv6();
     WiFi.softAP(AP_SSID);
     //enable sta ipv6 here
     WiFi.enableIPv6();
diff --git a/libraries/WiFi/src/WiFiAP.cpp b/libraries/WiFi/src/WiFiAP.cpp
index 79917daeb6e..b02b929bce9 100644
--- a/libraries/WiFi/src/WiFiAP.cpp
+++ b/libraries/WiFi/src/WiFiAP.cpp
@@ -411,12 +411,18 @@ bool WiFiAPClass::softAPsetHostname(const char * hostname)
  * Enable IPv6 on the softAP interface.
  * @return true on success
  */
-bool WiFiAPClass::softAPenableIPv6()
+bool WiFiAPClass::softAPenableIPv6(bool enable)
 {
-    if(WiFiGenericClass::getMode() == WIFI_MODE_NULL){
-        return false;
+    if (enable) {
+        WiFiGenericClass::setStatusBits(AP_WANT_IP6_BIT);
+    } else {
+        WiFiGenericClass::clearStatusBits(AP_WANT_IP6_BIT);
     }
-    return esp_netif_create_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_AP)) == ESP_OK;
+    return true;
+    // if(WiFiGenericClass::getMode() == WIFI_MODE_NULL){
+    //     return false;
+    // }
+    // return esp_netif_create_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_AP)) == ESP_OK;
 }
 
 /**
@@ -430,6 +436,7 @@ IPAddress WiFiAPClass::softAPIPv6()
     if(WiFiGenericClass::getMode() == WIFI_MODE_NULL){
         return IPAddress(IPv6);
     }
+
     if(esp_netif_get_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_STA), &addr)){
         return IPAddress(IPv6);
     }
diff --git a/libraries/WiFi/src/WiFiAP.h b/libraries/WiFi/src/WiFiAP.h
index 9c2d1b96aeb..a07fab54335 100644
--- a/libraries/WiFi/src/WiFiAP.h
+++ b/libraries/WiFi/src/WiFiAP.h
@@ -56,7 +56,7 @@ class WiFiAPClass
     IPAddress softAPSubnetMask();
     uint8_t softAPSubnetCIDR();
 
-    bool softAPenableIPv6();
+    bool softAPenableIPv6(bool enable=true);
     IPAddress softAPIPv6();
 
     const char * softAPgetHostname();
diff --git a/libraries/WiFi/src/WiFiGeneric.cpp b/libraries/WiFi/src/WiFiGeneric.cpp
index 3069b22f8af..f79e6debee1 100644
--- a/libraries/WiFi/src/WiFiGeneric.cpp
+++ b/libraries/WiFi/src/WiFiGeneric.cpp
@@ -42,6 +42,7 @@ extern "C" {
 #include "lwip/opt.h"
 #include "lwip/err.h"
 #include "lwip/dns.h"
+#include "lwip/netif.h"
 #include "dhcpserver/dhcpserver.h"
 #include "dhcpserver/dhcpserver_options.h"
 
@@ -459,7 +460,13 @@ static void _arduino_event_cb(void* arg, esp_event_base_t event_base, int32_t ev
     } else if (event_base == IP_EVENT && event_id == IP_EVENT_GOT_IP6) {
     	ip_event_got_ip6_t * event = (ip_event_got_ip6_t*)event_data;
     	esp_interface_t iface = get_esp_netif_interface(event->esp_netif);
-    	log_v("IF[%d] Got IPv6: IP Index: %d, Zone: %d, " IPV6STR, iface, event->ip_index, event->ip6_info.ip.zone, IPV62STR(event->ip6_info.ip));
+#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE
+        char if_name[NETIF_NAMESIZE] = {0,};
+        netif_index_to_name(event->ip6_info.ip.zone, if_name);
+        esp_ip6_addr_type_t addr_type = esp_netif_ip6_get_addr_type(&event->ip6_info.ip);
+        static const char * addr_types[] = { "UNKNOWN", "GLOBAL", "LINK_LOCAL", "SITE_LOCAL", "UNIQUE_LOCAL", "IPV4_MAPPED_IPV6" };
+        log_v("IF %s Got IPv6: Interface: %d, IP Index: %d, Type: %s, Zone: %d (%s), Address: " IPV6STR, esp_netif_get_desc(event->esp_netif), iface, event->ip_index, addr_types[addr_type], event->ip6_info.ip.zone, if_name, IPV62STR(event->ip6_info.ip));
+#endif
     	memcpy(&arduino_event.event_info.got_ip6, event_data, sizeof(ip_event_got_ip6_t));
     	if(iface == ESP_IF_WIFI_STA){
         	arduino_event.event_id = ARDUINO_EVENT_WIFI_STA_GOT_IP6;
@@ -554,6 +561,8 @@ static void _arduino_event_cb(void* arg, esp_event_base_t event_base, int32_t ev
 	}
 }
 
+static uint32_t _initial_bits = NET_DNS_IDLE_BIT;
+
 static bool _start_network_event_task(){
     if(!_arduino_event_group){
         _arduino_event_group = xEventGroupCreate();
@@ -561,7 +570,7 @@ static bool _start_network_event_task(){
             log_e("Network Event Group Create Failed!");
             return false;
         }
-        xEventGroupSetBits(_arduino_event_group, WIFI_DNS_IDLE_BIT);
+        xEventGroupSetBits(_arduino_event_group, _initial_bits);
     }
     if(!_arduino_event_queue){
     	_arduino_event_queue = xQueueCreate(32, sizeof(arduino_event_t*));
@@ -909,21 +918,23 @@ bool WiFiGenericClass::setHostname(const char * hostname)
 
 int WiFiGenericClass::setStatusBits(int bits){
     if(!_arduino_event_group){
-        return 0;
+        _initial_bits |= bits;
+        return _initial_bits;
     }
     return xEventGroupSetBits(_arduino_event_group, bits);
 }
 
 int WiFiGenericClass::clearStatusBits(int bits){
     if(!_arduino_event_group){
-        return 0;
+        _initial_bits &= ~bits;
+        return _initial_bits;
     }
     return xEventGroupClearBits(_arduino_event_group, bits);
 }
 
 int WiFiGenericClass::getStatusBits(){
     if(!_arduino_event_group){
-        return 0;
+        return _initial_bits;
     }
     return xEventGroupGetBits(_arduino_event_group);
 }
@@ -1114,6 +1125,9 @@ esp_err_t WiFiGenericClass::_eventCallback(arduino_event_t *event)
 
     } else if(event->event_id == ARDUINO_EVENT_WIFI_AP_START) {
         setStatusBits(AP_STARTED_BIT);
+        if (getStatusBits() & AP_WANT_IP6_BIT){
+            esp_netif_create_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_AP));
+        }
     } else if(event->event_id == ARDUINO_EVENT_WIFI_AP_STOP) {
         clearStatusBits(AP_STARTED_BIT | AP_HAS_CLIENT_BIT);
     } else if(event->event_id == ARDUINO_EVENT_WIFI_AP_STACONNECTED) {
@@ -1150,16 +1164,25 @@ esp_err_t WiFiGenericClass::_eventCallback(arduino_event_t *event)
         clearStatusBits(ETH_HAS_IP_BIT);
 
     } else if(event->event_id == ARDUINO_EVENT_WIFI_STA_GOT_IP6) {
-    	setStatusBits(STA_CONNECTED_BIT | STA_HAS_IP6_BIT);
-        if(esp_netif_ip6_get_addr_type((esp_ip6_addr_t*)&(event->event_info.got_ip6.ip6_info.ip)) == ESP_IP6_ADDR_IS_GLOBAL){
+    	setStatusBits(STA_CONNECTED_BIT);
+        esp_ip6_addr_type_t addr_type = esp_netif_ip6_get_addr_type((esp_ip6_addr_t*)&(event->event_info.got_ip6.ip6_info.ip));
+        if(addr_type == ESP_IP6_ADDR_IS_GLOBAL){
             setStatusBits(STA_HAS_IP6_GLOBAL_BIT);
+        } else if(addr_type == ESP_IP6_ADDR_IS_LINK_LOCAL){
+            setStatusBits(STA_HAS_IP6_BIT);
         }
     } else if(event->event_id == ARDUINO_EVENT_WIFI_AP_GOT_IP6) {
-    	setStatusBits(AP_HAS_IP6_BIT);
+        esp_ip6_addr_type_t addr_type = esp_netif_ip6_get_addr_type((esp_ip6_addr_t*)&(event->event_info.got_ip6.ip6_info.ip));
+        if(addr_type == ESP_IP6_ADDR_IS_LINK_LOCAL){
+            setStatusBits(AP_HAS_IP6_BIT);
+        }
     } else if(event->event_id == ARDUINO_EVENT_ETH_GOT_IP6) {
-    	setStatusBits(ETH_CONNECTED_BIT | ETH_HAS_IP6_BIT);
-        if(esp_netif_ip6_get_addr_type((esp_ip6_addr_t*)&(event->event_info.got_ip6.ip6_info.ip)) == ESP_IP6_ADDR_IS_GLOBAL){
+    	setStatusBits(ETH_CONNECTED_BIT);
+        esp_ip6_addr_type_t addr_type = esp_netif_ip6_get_addr_type((esp_ip6_addr_t*)&(event->event_info.got_ip6.ip6_info.ip));
+        if(addr_type == ESP_IP6_ADDR_IS_GLOBAL){
             setStatusBits(ETH_HAS_IP6_GLOBAL_BIT);
+        } else if(addr_type == ESP_IP6_ADDR_IS_LINK_LOCAL){
+            setStatusBits(ETH_HAS_IP6_BIT);
         }
     } else if(event->event_id == ARDUINO_EVENT_SC_GOT_SSID_PSWD) {
     	WiFi.begin(
@@ -1589,7 +1612,7 @@ static void wifi_dns_found_callback(const char *name, const ip_addr_t *ipaddr, v
     } else {
         parameters->result = -1;
     }
-    xEventGroupSetBits(_arduino_event_group, WIFI_DNS_DONE_BIT);
+    xEventGroupSetBits(_arduino_event_group, NET_DNS_DONE_BIT);
 }
 
 /**
@@ -1632,8 +1655,8 @@ int WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult, bool
     aResult.to_ip_addr_t(&(params.addr));
 
     if (!aResult.fromString(aHostname)) {
-        waitStatusBits(WIFI_DNS_IDLE_BIT, 16000);
-        clearStatusBits(WIFI_DNS_IDLE_BIT | WIFI_DNS_DONE_BIT);
+        waitStatusBits(NET_DNS_IDLE_BIT, 16000);
+        clearStatusBits(NET_DNS_IDLE_BIT | NET_DNS_DONE_BIT);
 
         err = esp_netif_tcpip_exec(wifi_gethostbyname_tcpip_ctx, &params);
         if (err == ERR_OK) {
@@ -1646,14 +1669,14 @@ int WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult, bool
             }
             Serial.println();
         } else if (err == ERR_INPROGRESS) {
-            waitStatusBits(WIFI_DNS_DONE_BIT, 15000);  //real internal timeout in lwip library is 14[s]
-            clearStatusBits(WIFI_DNS_DONE_BIT);
+            waitStatusBits(NET_DNS_DONE_BIT, 15000);  //real internal timeout in lwip library is 14[s]
+            clearStatusBits(NET_DNS_DONE_BIT);
             if (params.result == 1) {
                 aResult.from_ip_addr_t(&(params.addr));
                 err = ERR_OK;
             }
         }
-        setStatusBits(WIFI_DNS_IDLE_BIT);
+        setStatusBits(NET_DNS_IDLE_BIT);
     }
     if (err == ERR_OK) {
         return 1;
diff --git a/libraries/WiFi/src/WiFiGeneric.h b/libraries/WiFi/src/WiFiGeneric.h
index c74c386a1eb..3de43905416 100644
--- a/libraries/WiFi/src/WiFiGeneric.h
+++ b/libraries/WiFi/src/WiFiGeneric.h
@@ -112,6 +112,34 @@ typedef void (*WiFiEventSysCb)(arduino_event_t *event);
 
 typedef size_t wifi_event_id_t;
 
+// General Flags
+static const int NET_DNS_IDLE_BIT       = BIT0;
+static const int NET_DNS_DONE_BIT       = BIT1;
+// WiFi Scan Flags
+static const int WIFI_SCANNING_BIT      = BIT2;
+static const int WIFI_SCAN_DONE_BIT     = BIT3;
+// AP Flags
+static const int AP_STARTED_BIT         = BIT4;
+static const int AP_HAS_IP6_BIT         = BIT5;
+static const int AP_HAS_CLIENT_BIT      = BIT6;
+static const int AP_WANT_IP6_BIT        = BIT7;
+// STA Flags
+static const int STA_STARTED_BIT        = BIT8;
+static const int STA_CONNECTED_BIT      = BIT9;
+static const int STA_HAS_IP_BIT         = BIT10;
+static const int STA_HAS_IP6_BIT        = BIT11;
+static const int STA_HAS_IP6_GLOBAL_BIT = BIT12;
+static const int STA_WANT_IP6_BIT       = BIT13;
+// ETH Flags
+static const int ETH_STARTED_BIT        = BIT14;
+static const int ETH_CONNECTED_BIT      = BIT15;
+static const int ETH_HAS_IP_BIT         = BIT16;
+static const int ETH_HAS_IP6_BIT        = BIT17;
+static const int ETH_HAS_IP6_GLOBAL_BIT = BIT18;
+static const int ETH_WANT_IP6_BIT       = BIT19;
+// Masks
+static const int NET_HAS_IP6_GLOBAL_BIT = STA_HAS_IP6_GLOBAL_BIT | ETH_HAS_IP6_GLOBAL_BIT;
+
 typedef enum {
     WIFI_POWER_19_5dBm = 78,// 19.5dBm
     WIFI_POWER_19dBm = 76,// 19dBm
@@ -127,30 +155,6 @@ typedef enum {
     WIFI_POWER_MINUS_1dBm = -4// -1dBm
 } wifi_power_t;
 
-static const int AP_STARTED_BIT    = BIT0;
-static const int AP_HAS_IP6_BIT    = BIT1;
-static const int AP_HAS_CLIENT_BIT = BIT2;
-static const int STA_STARTED_BIT   = BIT3;
-static const int STA_CONNECTED_BIT = BIT4;
-static const int STA_HAS_IP_BIT    = BIT5;
-static const int STA_HAS_IP6_BIT   = BIT6;
-static const int ETH_STARTED_BIT   = BIT7;
-static const int ETH_CONNECTED_BIT = BIT8;
-static const int ETH_HAS_IP_BIT    = BIT9;
-static const int ETH_HAS_IP6_BIT   = BIT10;
-
-static const int WIFI_SCANNING_BIT = BIT11;
-static const int WIFI_SCAN_DONE_BIT= BIT12;
-static const int WIFI_DNS_IDLE_BIT = BIT13;
-static const int WIFI_DNS_DONE_BIT = BIT14;
-
-static const int STA_WANT_IP6_BIT = BIT15;
-static const int ETH_WANT_IP6_BIT = BIT16;
-
-static const int STA_HAS_IP6_GLOBAL_BIT = BIT17;
-static const int ETH_HAS_IP6_GLOBAL_BIT = BIT18;
-static const int NET_HAS_IP6_GLOBAL_BIT = STA_HAS_IP6_GLOBAL_BIT | ETH_HAS_IP6_GLOBAL_BIT;
-
 typedef enum {
 	WIFI_RX_ANT0 = 0,
 	WIFI_RX_ANT1,
diff --git a/libraries/WiFi/src/WiFiMulti.cpp b/libraries/WiFi/src/WiFiMulti.cpp
index 320c5c68c46..908ec5fc331 100644
--- a/libraries/WiFi/src/WiFiMulti.cpp
+++ b/libraries/WiFi/src/WiFiMulti.cpp
@@ -160,8 +160,9 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
         if(bestNetwork.ssid) {
             log_i("[WIFI] Connecting BSSID: %02X:%02X:%02X:%02X:%02X:%02X SSID: %s Channel: %d (%d)", bestBSSID[0], bestBSSID[1], bestBSSID[2], bestBSSID[3], bestBSSID[4], bestBSSID[5], bestNetwork.ssid, bestChannel, bestNetworkDb);
 
-            if (ipv6_support == true)
+            if (ipv6_support == true) {
                 WiFi.enableIPv6();
+            }
             WiFi.begin(bestNetwork.ssid, bestNetwork.passphrase, bestChannel, bestBSSID);
             status = WiFi.status();
 
diff --git a/libraries/WiFi/src/WiFiSTA.cpp b/libraries/WiFi/src/WiFiSTA.cpp
index 23f62b82cfa..d58bd58da8e 100644
--- a/libraries/WiFi/src/WiFiSTA.cpp
+++ b/libraries/WiFi/src/WiFiSTA.cpp
@@ -812,11 +812,12 @@ int8_t WiFiSTAClass::RSSI(void)
  */
 bool WiFiSTAClass::enableIPv6(bool en)
 {
-   if (en)
-       WiFiGenericClass::setStatusBits(STA_WANT_IP6_BIT);
-   else
-       WiFiGenericClass::clearStatusBits(STA_WANT_IP6_BIT);
-   return true;
+    if (en) {
+        WiFiGenericClass::setStatusBits(STA_WANT_IP6_BIT);
+    } else {
+        WiFiGenericClass::clearStatusBits(STA_WANT_IP6_BIT);
+    }
+    return true;
 }
 
 /**

From cb381f2948dd474b800e3e9944d61638c08c84f9 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Thu, 11 Jan 2024 17:08:35 +0200
Subject: [PATCH 13/21] rename WiFiMulti method

---
 libraries/WiFi/src/WiFiMulti.cpp | 2 +-
 libraries/WiFi/src/WiFiMulti.h   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libraries/WiFi/src/WiFiMulti.cpp b/libraries/WiFi/src/WiFiMulti.cpp
index 908ec5fc331..22ec1ea46f9 100644
--- a/libraries/WiFi/src/WiFiMulti.cpp
+++ b/libraries/WiFi/src/WiFiMulti.cpp
@@ -207,6 +207,6 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
     return status;
 }
 
-void WiFiMulti::IPv6(bool state) {
+void WiFiMulti::enableIPv6(bool state) {
     ipv6_support = state;
 }
diff --git a/libraries/WiFi/src/WiFiMulti.h b/libraries/WiFi/src/WiFiMulti.h
index bbeb78dc860..d8551fab9dd 100644
--- a/libraries/WiFi/src/WiFiMulti.h
+++ b/libraries/WiFi/src/WiFiMulti.h
@@ -42,7 +42,7 @@ class WiFiMulti
 
     bool addAP(const char* ssid, const char *passphrase = NULL);
 
-    void IPv6(bool state);
+    void enableIPv6(bool state);
     uint8_t run(uint32_t connectTimeout=5000);
 
 private:

From 726496c176d048f1bbae8a79ac9b2faa723e53ce Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Thu, 11 Jan 2024 22:47:24 +0200
Subject: [PATCH 14/21] Fix AP DHCPS not properly working on recent IDF

---
 libraries/WiFi/src/WiFiGeneric.cpp | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/libraries/WiFi/src/WiFiGeneric.cpp b/libraries/WiFi/src/WiFiGeneric.cpp
index f79e6debee1..2a47094df36 100644
--- a/libraries/WiFi/src/WiFiGeneric.cpp
+++ b/libraries/WiFi/src/WiFiGeneric.cpp
@@ -192,16 +192,17 @@ esp_err_t set_esp_interface_ip(esp_interface_t interface, IPAddress local_ip=IPA
         lease.start_ip.addr = _byte_swap32(lease.start_ip.addr);
         lease.end_ip.addr = _byte_swap32(lease.end_ip.addr);
         log_v("DHCP Server Range: %s to %s", IPAddress(lease.start_ip.addr).toString().c_str(), IPAddress(lease.end_ip.addr).toString().c_str());
-        err = esp_netif_dhcps_option(
-            esp_netif,
-            ESP_NETIF_OP_SET,
-            ESP_NETIF_SUBNET_MASK,
-            (void*)&info.netmask.addr, sizeof(info.netmask.addr)
-        );
-		if(err){
-        	log_e("DHCPS Set Netmask Failed! 0x%04x", err);
-        	return err;
-        }
+        // Following block is commented because it breaks AP DHCPS on recent ESP-IDF
+        // err = esp_netif_dhcps_option(
+        //     esp_netif,
+        //     ESP_NETIF_OP_SET,
+        //     ESP_NETIF_SUBNET_MASK,
+        //     (void*)&info.netmask.addr, sizeof(info.netmask.addr)
+        // );
+		// if(err){
+        // 	log_e("DHCPS Set Netmask Failed! 0x%04x", err);
+        // 	return err;
+        // }
         err = esp_netif_dhcps_option(
             esp_netif,
             ESP_NETIF_OP_SET,

From 9012f647735647d29f5542fb5599fcbdc29bddb4 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Thu, 11 Jan 2024 23:19:57 +0200
Subject: [PATCH 15/21] Add option to print the zone at the end of IPv6

@TD-er
---
 cores/esp32/IPAddress.cpp      | 22 ++++++++++++++--------
 cores/esp32/IPAddress.h        |  3 ++-
 libraries/Ethernet/src/ETH.cpp |  2 +-
 3 files changed, 17 insertions(+), 10 deletions(-)

diff --git a/cores/esp32/IPAddress.cpp b/cores/esp32/IPAddress.cpp
index 69ccc2ec881..b701616826d 100644
--- a/cores/esp32/IPAddress.cpp
+++ b/cores/esp32/IPAddress.cpp
@@ -67,6 +67,7 @@ IPAddress::IPAddress(uint32_t address)
 {
     // IPv4 only
     _type = IPv4;
+    _zone = IP6_NO_ZONE;
     memset(_address.bytes, 0, sizeof(_address.bytes));
     _address.dword[IPADDRESS_V4_DWORD_INDEX] = address;
 
@@ -104,10 +105,10 @@ IPAddress::IPAddress(const IPAddress& address)
     *this = address;
 }
 
-String IPAddress::toString() const
+String IPAddress::toString(bool includeZone) const
 {
     StreamString s;
-    printTo(s);
+    printTo(s, includeZone);
     return String(s);
 }
 
@@ -303,6 +304,11 @@ uint8_t& IPAddress::operator[](int index) {
 }
 
 size_t IPAddress::printTo(Print& p) const
+{
+    return printTo(p, false);
+}
+
+size_t IPAddress::printTo(Print& p, bool includeZone) const
 {
     size_t n = 0;
 
@@ -355,12 +361,12 @@ size_t IPAddress::printTo(Print& p) const
             }
         }
         // add a zone if zone-id is non-zero
-        // if(_zone > 0){
-        //     n += p.print('%');
-        //     char if_name[NETIF_NAMESIZE];
-        //     netif_index_to_name(_zone, if_name);
-        //     n += p.print(if_name);
-        // }
+        if(_zone > 0 && includeZone){
+            n += p.print('%');
+            char if_name[NETIF_NAMESIZE];
+            netif_index_to_name(_zone, if_name);
+            n += p.print(if_name);
+        }
         return n;
     }
 
diff --git a/cores/esp32/IPAddress.h b/cores/esp32/IPAddress.h
index 88b1bf627f0..e8df9b488ea 100644
--- a/cores/esp32/IPAddress.h
+++ b/cores/esp32/IPAddress.h
@@ -93,7 +93,8 @@ class IPAddress : public Printable {
     IPAddress& operator=(const IPAddress& address);
 
     virtual size_t printTo(Print& p) const;
-    String toString() const;
+    size_t printTo(Print& p, bool includeZone) const;
+    String toString(bool includeZone = false) const;
 
     IPType type() const { return _type; }
 
diff --git a/libraries/Ethernet/src/ETH.cpp b/libraries/Ethernet/src/ETH.cpp
index 9587b22719d..0ace36c69d5 100644
--- a/libraries/Ethernet/src/ETH.cpp
+++ b/libraries/Ethernet/src/ETH.cpp
@@ -1055,7 +1055,7 @@ void ETHClass::printInfo(Print & out){
     for (int i = 0; i < v6addrs; ++i){
         out.print("      ");
         out.print("inet6 ");
-        IPAddress(IPv6, (const uint8_t *)if_ip6[i].addr, if_ip6[i].zone).printTo(out);
+        IPAddress(IPv6, (const uint8_t *)if_ip6[i].addr, if_ip6[i].zone).printTo(out, true);
         out.print(" type ");
         out.print(types[esp_netif_ip6_get_addr_type(&if_ip6[i])]);
         out.println();

From 58520a7e0f8940af0c36216de0a7d4de9bce7613 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Fri, 12 Jan 2024 00:20:45 +0200
Subject: [PATCH 16/21] remove log prints from hostByName

---
 libraries/WiFi/src/WiFiGeneric.cpp | 16 ----------------
 1 file changed, 16 deletions(-)

diff --git a/libraries/WiFi/src/WiFiGeneric.cpp b/libraries/WiFi/src/WiFiGeneric.cpp
index 2a47094df36..a7fa5957e33 100644
--- a/libraries/WiFi/src/WiFiGeneric.cpp
+++ b/libraries/WiFi/src/WiFiGeneric.cpp
@@ -1601,15 +1601,6 @@ static void wifi_dns_found_callback(const char *name, const ip_addr_t *ipaddr, v
             memcpy(&(parameters->addr), ipaddr, sizeof(ip_addr_t));
             parameters->result = 1;
         }
-        IPAddress addr;
-        addr.from_ip_addr_t((ip_addr_t *)ipaddr);
-        Serial.print("dns_found ");
-        addr.printTo(Serial);
-        if(ipaddr->type == IPADDR_TYPE_V6){
-            Serial.print(" type ");
-            Serial.print((int)esp_netif_ip6_get_addr_type((esp_ip6_addr_t*)&(ipaddr->u_addr.ip6)));
-        }
-        Serial.println();
     } else {
         parameters->result = -1;
     }
@@ -1662,13 +1653,6 @@ int WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult, bool
         err = esp_netif_tcpip_exec(wifi_gethostbyname_tcpip_ctx, &params);
         if (err == ERR_OK) {
             aResult.from_ip_addr_t(&(params.addr));
-            Serial.print("dns_cache ");
-            aResult.printTo(Serial);
-            if(params.addr.type == IPADDR_TYPE_V6){
-                Serial.print(" type ");
-                Serial.print((int)esp_netif_ip6_get_addr_type((esp_ip6_addr_t*)&(params.addr.u_addr.ip6)));
-            }
-            Serial.println();
         } else if (err == ERR_INPROGRESS) {
             waitStatusBits(NET_DNS_DONE_BIT, 15000);  //real internal timeout in lwip library is 14[s]
             clearStatusBits(NET_DNS_DONE_BIT);

From aad10411dea925d6429c310103c6d2f2d9fbce15 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Fri, 12 Jan 2024 12:26:54 +0200
Subject: [PATCH 17/21] Use correct array length for listing IPv6 addresses

---
 libraries/Ethernet/src/ETH.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/Ethernet/src/ETH.cpp b/libraries/Ethernet/src/ETH.cpp
index 0ace36c69d5..92d91b40a1a 100644
--- a/libraries/Ethernet/src/ETH.cpp
+++ b/libraries/Ethernet/src/ETH.cpp
@@ -1050,7 +1050,7 @@ void ETHClass::printInfo(Print & out){
     out.println();
 
     static const char * types[] = { "UNKNOWN", "GLOBAL", "LINK_LOCAL", "SITE_LOCAL", "UNIQUE_LOCAL", "IPV4_MAPPED_IPV6" };
-    esp_ip6_addr_t if_ip6[5];
+    esp_ip6_addr_t if_ip6[CONFIG_LWIP_IPV6_NUM_ADDRESSES];
     int v6addrs = esp_netif_get_all_ip6(_esp_netif, if_ip6);
     for (int i = 0; i < v6addrs; ++i){
         out.print("      ");

From a2a6bd80c1d19985ae631c334ba1c021a914ac52 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Sun, 14 Jan 2024 12:54:17 +0200
Subject: [PATCH 18/21] Implement some Tasmota requirements

Added constructor that takes `const ip_addr_t *`.
Added `addr_type()` getter
Organize header to highlight the Espressif additions to IPAddress
---
 cores/esp32/IPAddress.cpp | 15 ++++++++++++++-
 cores/esp32/IPAddress.h   | 12 +++++++-----
 2 files changed, 21 insertions(+), 6 deletions(-)

diff --git a/cores/esp32/IPAddress.cpp b/cores/esp32/IPAddress.cpp
index b701616826d..159d37b15cc 100644
--- a/cores/esp32/IPAddress.cpp
+++ b/cores/esp32/IPAddress.cpp
@@ -380,6 +380,10 @@ size_t IPAddress::printTo(Print& p, bool includeZone) const
     return n;
 }
 
+IPAddress::IPAddress(const ip_addr_t *addr){
+    from_ip_addr_t(addr);
+}
+
 void IPAddress::to_ip_addr_t(ip_addr_t* addr) const {
     if(_type == IPv6){
         addr->type = IPADDR_TYPE_V6;
@@ -396,7 +400,7 @@ void IPAddress::to_ip_addr_t(ip_addr_t* addr) const {
     }
 }
 
-IPAddress& IPAddress::from_ip_addr_t(ip_addr_t* addr){
+IPAddress& IPAddress::from_ip_addr_t(const ip_addr_t* addr){
     if(addr->type == IPADDR_TYPE_V6){
         _type = IPv6;
         _address.dword[0] = addr->u_addr.ip6.addr[0];
@@ -413,5 +417,14 @@ IPAddress& IPAddress::from_ip_addr_t(ip_addr_t* addr){
     return *this;
 }
 
+esp_ip6_addr_type_t IPAddress::addr_type(){
+    if(_type != IPv6){
+        return ESP_IP6_ADDR_IS_UNKNOWN;
+    }
+    ip_addr_t addr;
+    to_ip_addr_t(&addr);
+    return esp_netif_ip6_get_addr_type((esp_ip6_addr_t*)(&(addr.u_addr.ip6)));
+}
+
 const IPAddress IN6ADDR_ANY(IPv6);
 const IPAddress INADDR_NONE(0,0,0,0);
diff --git a/cores/esp32/IPAddress.h b/cores/esp32/IPAddress.h
index e8df9b488ea..b6851590c32 100644
--- a/cores/esp32/IPAddress.h
+++ b/cores/esp32/IPAddress.h
@@ -23,6 +23,7 @@
 #include "Printable.h"
 #include "WString.h"
 #include "lwip/ip_addr.h"
+#include "esp_netif_ip_addr.h"
 
 #define IPADDRESS_V4_BYTES_INDEX 12
 #define IPADDRESS_V4_DWORD_INDEX 3
@@ -93,16 +94,17 @@ class IPAddress : public Printable {
     IPAddress& operator=(const IPAddress& address);
 
     virtual size_t printTo(Print& p) const;
-    size_t printTo(Print& p, bool includeZone) const;
     String toString(bool includeZone = false) const;
 
     IPType type() const { return _type; }
 
-    uint8_t zone() const { return (type() == IPv6)?_zone:0; }
-
-    // LwIP conversions
+    // Espresif LwIP conversions
+    IPAddress(const ip_addr_t *addr);
     void to_ip_addr_t(ip_addr_t* addr) const;
-    IPAddress& from_ip_addr_t(ip_addr_t* addr);
+    IPAddress& from_ip_addr_t(const ip_addr_t* addr);
+    esp_ip6_addr_type_t addr_type();
+    uint8_t zone() const { return (type() == IPv6)?_zone:0; }
+    size_t printTo(Print& p, bool includeZone) const;
 
     friend class UDP;
     friend class Client;

From 1fb442dc5bc88f86d2850b9c1bf894bdb4fe7b98 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Mon, 15 Jan 2024 01:01:29 +0200
Subject: [PATCH 19/21] add 'const' to IPAddress::addr_type()

---
 cores/esp32/IPAddress.cpp | 2 +-
 cores/esp32/IPAddress.h   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/cores/esp32/IPAddress.cpp b/cores/esp32/IPAddress.cpp
index 159d37b15cc..0fa5affdea7 100644
--- a/cores/esp32/IPAddress.cpp
+++ b/cores/esp32/IPAddress.cpp
@@ -417,7 +417,7 @@ IPAddress& IPAddress::from_ip_addr_t(const ip_addr_t* addr){
     return *this;
 }
 
-esp_ip6_addr_type_t IPAddress::addr_type(){
+esp_ip6_addr_type_t IPAddress::addr_type() const {
     if(_type != IPv6){
         return ESP_IP6_ADDR_IS_UNKNOWN;
     }
diff --git a/cores/esp32/IPAddress.h b/cores/esp32/IPAddress.h
index b6851590c32..0a3efd01c8d 100644
--- a/cores/esp32/IPAddress.h
+++ b/cores/esp32/IPAddress.h
@@ -102,7 +102,7 @@ class IPAddress : public Printable {
     IPAddress(const ip_addr_t *addr);
     void to_ip_addr_t(ip_addr_t* addr) const;
     IPAddress& from_ip_addr_t(const ip_addr_t* addr);
-    esp_ip6_addr_type_t addr_type();
+    esp_ip6_addr_type_t addr_type() const;
     uint8_t zone() const { return (type() == IPv6)?_zone:0; }
     size_t printTo(Print& p, bool includeZone) const;
 

From 0df3aaad6c4e343547d82c8fba46e8dc276bbbba Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Mon, 15 Jan 2024 12:16:23 +0200
Subject: [PATCH 20/21] Fix WiFiUdp not updating mapped v4 address

---
 libraries/WiFi/src/WiFiGeneric.cpp | 15 ++++++++++++---
 libraries/WiFi/src/WiFiUdp.cpp     |  1 +
 2 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/libraries/WiFi/src/WiFiGeneric.cpp b/libraries/WiFi/src/WiFiGeneric.cpp
index a7fa5957e33..2fade00e450 100644
--- a/libraries/WiFi/src/WiFiGeneric.cpp
+++ b/libraries/WiFi/src/WiFiGeneric.cpp
@@ -1066,7 +1066,10 @@ esp_err_t WiFiGenericClass::_eventCallback(arduino_event_t *event)
         clearStatusBits(STA_STARTED_BIT | STA_CONNECTED_BIT | STA_HAS_IP_BIT | STA_HAS_IP6_BIT | STA_HAS_IP6_GLOBAL_BIT);
     } else if(event->event_id == ARDUINO_EVENT_WIFI_STA_CONNECTED) {
         if (getStatusBits() & STA_WANT_IP6_BIT){
-            esp_netif_create_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_STA));
+            esp_err_t err = esp_netif_create_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_STA));
+            if(err != ESP_OK){
+                log_e("Failed to enable IPv6 Link Local on STA: [%d] %s", err, esp_err_to_name(err));
+            }
         }
         WiFiSTAClass::_setStatus(WL_IDLE_STATUS);
         setStatusBits(STA_CONNECTED_BIT);
@@ -1127,7 +1130,10 @@ esp_err_t WiFiGenericClass::_eventCallback(arduino_event_t *event)
     } else if(event->event_id == ARDUINO_EVENT_WIFI_AP_START) {
         setStatusBits(AP_STARTED_BIT);
         if (getStatusBits() & AP_WANT_IP6_BIT){
-            esp_netif_create_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_AP));
+            esp_err_t err = esp_netif_create_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_AP));
+            if(err != ESP_OK){
+                log_e("Failed to enable IPv6 Link Local on AP: [%d] %s", err, esp_err_to_name(err));
+            }
         }
     } else if(event->event_id == ARDUINO_EVENT_WIFI_AP_STOP) {
         clearStatusBits(AP_STARTED_BIT | AP_HAS_CLIENT_BIT);
@@ -1145,7 +1151,10 @@ esp_err_t WiFiGenericClass::_eventCallback(arduino_event_t *event)
         clearStatusBits(ETH_STARTED_BIT | ETH_CONNECTED_BIT | ETH_HAS_IP_BIT | ETH_HAS_IP6_BIT | ETH_HAS_IP6_GLOBAL_BIT);
     } else if(event->event_id == ARDUINO_EVENT_ETH_CONNECTED) {
         if (getStatusBits() & ETH_WANT_IP6_BIT){
-            esp_netif_create_ip6_linklocal(get_esp_interface_netif(ESP_IF_ETH));
+            esp_err_t err = esp_netif_create_ip6_linklocal(get_esp_interface_netif(ESP_IF_ETH));
+            if(err != ESP_OK){
+                log_e("Failed to enable IPv6 Link Local on ETH: [%d] %s", err, esp_err_to_name(err));
+            }
         }
         setStatusBits(ETH_CONNECTED_BIT);
     } else if(event->event_id == ARDUINO_EVENT_ETH_DISCONNECTED) {
diff --git a/libraries/WiFi/src/WiFiUdp.cpp b/libraries/WiFi/src/WiFiUdp.cpp
index 683ff90067c..5d8b513a2d8 100644
--- a/libraries/WiFi/src/WiFiUdp.cpp
+++ b/libraries/WiFi/src/WiFiUdp.cpp
@@ -321,6 +321,7 @@ int WiFiUDP::parsePacket(){
     if (remote_ip.type() == IPv6 && ip6_addr_isipv4mappedipv6(ip_2_ip6(&addr))) {
       unmap_ipv4_mapped_ipv6(ip_2_ip4(&addr), ip_2_ip6(&addr));
       IP_SET_TYPE_VAL(addr, IPADDR_TYPE_V4);
+      remote_ip.from_ip_addr_t(&addr);
     }
     remote_port = ntohs(si_other.sin6_port);
   }

From 416187320c3cbaa28e5429245dff17999edfed96 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Mon, 15 Jan 2024 14:23:18 +0200
Subject: [PATCH 21/21] Update WiFiServer.cpp

---
 libraries/WiFi/src/WiFiServer.cpp | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libraries/WiFi/src/WiFiServer.cpp b/libraries/WiFi/src/WiFiServer.cpp
index d3f2b0b0120..9d79b5f7c46 100644
--- a/libraries/WiFi/src/WiFiServer.cpp
+++ b/libraries/WiFi/src/WiFiServer.cpp
@@ -87,12 +87,10 @@ void WiFiServer::begin(uint16_t port, int enable){
   setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
   server.sin6_family = AF_INET6;
   if (_addr.type() == IPv4) {
-    // log_e("---------------- IPv4");
     memcpy(server.sin6_addr.s6_addr+11, (uint8_t*)&_addr[0], 4);
     server.sin6_addr.s6_addr[10] = 0xFF;
     server.sin6_addr.s6_addr[11] = 0xFF;
   } else {
-    // log_e("---------------- IPv6");
     memcpy(server.sin6_addr.s6_addr, (uint8_t*)&_addr[0], 16);
   }
   memset(server.sin6_addr.s6_addr, 0x0, 16);