diff --git a/CHANGELOG.md b/CHANGELOG.md index 22ed71f35..5a019f47e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. ### Added - Serial Modbus transmit enable GPIOs to all modbus energy drivers and modbus bridge (#17247) - Berry crypto module, with AES_GCM by default and EC_CC25519 optional +- IPv6 support for Ethernet (ESP32) ### Breaking Changed diff --git a/tasmota/include/i18n.h b/tasmota/include/i18n.h index 4028b33d2..4294c3297 100644 --- a/tasmota/include/i18n.h +++ b/tasmota/include/i18n.h @@ -808,6 +808,7 @@ #define D_LOG_UPLOAD "UPL: " // Upload #define D_LOG_UPNP "UPP: " // UPnP #define D_LOG_WIFI "WIF: " // Wifi +#define D_LOG_ETH "ETH: " // Ethernet #define D_LOG_ZIGBEE "ZIG: " // Zigbee #define D_LOG_TCP "TCP: " // TCP bridge #define D_LOG_BERRY "BRY: " // Berry scripting language diff --git a/tasmota/tasmota_support/support_command.ino b/tasmota/tasmota_support/support_command.ino index ce66bffe1..7144351ed 100644 --- a/tasmota/tasmota_support/support_command.ino +++ b/tasmota/tasmota_support/support_command.ino @@ -828,11 +828,20 @@ void CmndStatus(void) ResponseAppend_P(PSTR(",\"Ethernet\":{\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%_I\",\"" D_JSON_GATEWAY "\":\"%_I\",\"" D_JSON_SUBNETMASK "\":\"%_I\",\"" D_JSON_DNSSERVER "1\":\"%_I\",\"" D_JSON_DNSSERVER "2\":\"%_I\",\"" - D_JSON_MAC "\":\"%s\"}"), + D_JSON_MAC "\":\"%s\"" + +#if LWIP_IPV6 + ",\"" D_JSON_IP6_GLOBAL "\":\"%s\",\"" D_JSON_IP6_LOCAL "\":\"%s\"" +#endif // LWIP_IPV6 + "}"), EthernetHostname(), (uint32_t)EthernetLocalIP(), Settings->eth_ipv4_address[1], Settings->eth_ipv4_address[2], Settings->eth_ipv4_address[3], Settings->eth_ipv4_address[4], - EthernetMacAddress().c_str()); + EthernetMacAddress().c_str() +#if LWIP_IPV6 + ,EthernetGetIPv6().c_str(), EthernetGetIPv6LinkLocal().c_str() +#endif // LWIP_IPV6 + ); #endif // USE_ETHERNET ResponseAppend_P(PSTR(",\"" D_CMND_WEBSERVER "\":%d,\"HTTP_API\":%d,\"" D_CMND_WIFICONFIG "\":%d,\"" D_CMND_WIFIPOWER "\":%s}}"), Settings->webserver, Settings->flag5.disable_referer_chk, Settings->sta_config, WifiGetOutputPower().c_str()); diff --git a/tasmota/tasmota_support/support_wifi.ino b/tasmota/tasmota_support/support_wifi.ino index 439eb752e..6c2914d8a 100644 --- a/tasmota/tasmota_support/support_wifi.ino +++ b/tasmota/tasmota_support/support_wifi.ino @@ -466,21 +466,34 @@ void WifiSetState(uint8_t state) } #if LWIP_IPV6 +// +// Scan through all interfaces to find a global or local IPv6 address +// Arg: +// is_local: is the address Link-Local (true) or Global (false) +// if_type: possible values are "st" for Wifi STA, "en" for Ethernet, "lo" for localhost (not useful) +static String WifiFindIPv6(bool is_local, const char * if_type = "st") { + for (netif* intf = netif_list; intf != nullptr; intf = intf->next) { + if (intf->name[0] == if_type[0] && intf->name[1] == if_type[1]) { + for (uint32_t i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + ip_addr_t *ipv6 = &intf->ip6_addr[i]; + if (IP_IS_V6_VAL(*ipv6) && !ip_addr_isloopback(ipv6) && ((bool)ip_addr_islinklocal(ipv6) == is_local)) { + return IPAddress46(ipv6).toString(); + } + } + } + } + return String(); +} + // Returns only IPv6 global address (no loopback and no link-local) String WifiGetIPv6(void) { - for (auto a : addrList) { - if(!ip_addr_isloopback((ip_addr_t*)a.addr()) && !a.isLocal() && a.isV6()) return a.toString(); - } - return ""; + return WifiFindIPv6(false, "st"); } String WifiGetIPv6LinkLocal(void) { - for (auto a : addrList) { - if(!ip_addr_isloopback((ip_addr_t*)a.addr()) && a.isLocal() && a.isV6()) return a.toString(); - } - return ""; + return WifiFindIPv6(true, "st"); } // add an IPv6 link-local address to all netif @@ -493,16 +506,18 @@ void CreateLinkLocalIPv6(void) #endif // ESP32 } +// void WifiDumpAddressesIPv6(void) { - for (auto a: addrList) - AddLog(LOG_LEVEL_DEBUG, PSTR("IF='%s' index=%d legacy=%d IPv4=%d local=%d addr='%s'"), - a.ifname().c_str(), - a.ifnumber(), - a.isLegacy(), - a.addr().isV4(), - a.addr().isLocal(), - a.toString().c_str()); + for (netif* intf = netif_list; intf != nullptr; intf = intf->next) { + if (!ip_addr_isany_val(intf->ip_addr)) AddLog(LOG_LEVEL_DEBUG, "WIF: '%c%c' IPv4 %s", intf->name[0], intf->name[1], IPAddress46(intf->ip_addr).toString().c_str()); + for (uint32_t i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (!ip_addr_isany_val(intf->ip6_addr[i])) + AddLog(LOG_LEVEL_DEBUG, "WIF: '%c%c' IPv6 %s %s", intf->name[0], intf->name[1], + IPAddress46(intf->ip6_addr[i]).toString().c_str(), + ip_addr_islinklocal(&intf->ip6_addr[i]) ? "local" : ""); + } + } } #endif // LWIP_IPV6=1 @@ -738,7 +753,7 @@ void WifiConnect(void) { if (!Settings->flag4.network_wifi) { return; } -#ifdef ESP32 +#if defined(ESP32) && !defined(FIRMWARE_MINIMAL) static bool wifi_event_registered = false; if (!wifi_event_registered) { WiFi.onEvent(WifiEvents); // register event listener only once @@ -895,6 +910,7 @@ bool WifiHostByName(const char* aHostname, IPAddress& aResult) { if (WiFi.hostByName(aHostname, aResult)) { // Host name resolved if (0xFFFFFFFF != (uint32_t)aResult) { + AddLog(LOG_LEVEL_DEBUG, "WIF: Resolving '%s' (%s)", aHostname, aResult.toString().c_str()); return true; } } @@ -903,6 +919,7 @@ bool WifiHostByName(const char* aHostname, IPAddress& aResult) { uint32_t dns_address = (!TasmotaGlobal.global_state.eth_down) ? Settings->eth_ipv4_address[3] : Settings->ipv4_address[3]; DnsClient.begin((IPAddress)dns_address); if (1 == DnsClient.getHostByName(aHostname, aResult)) { + AddLog(LOG_LEVEL_DEBUG, "WIF: Resolving '%s' (%s)", aHostname, aResult.toString().c_str()); return true; } } @@ -1076,16 +1093,19 @@ void WifiEvents(arduino_event_t *event) { ip_addr_t ip_addr6; ip_addr_copy_from_ip6(ip_addr6, event->event_info.got_ip6.ip6_info.ip); IPAddress46 addr(ip_addr6); - AddLog(LOG_LEVEL_DEBUG, PSTR("WIF: IPv6 %s %s"), addr.isLocal() ? PSTR("Link-Local") : PSTR("Global"), addr.toString().c_str()); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: IPv6 %s %s"), + event->event_id == ARDUINO_EVENT_ETH_GOT_IP6 ? "ETH" : "WIF", + addr.isLocal() ? PSTR("Local") : PSTR("Global"), addr.toString().c_str()); } break; #endif // LWIP_IPV6 - case ARDUINO_EVENT_ETH_GOT_IP: case ARDUINO_EVENT_WIFI_STA_GOT_IP: + case ARDUINO_EVENT_ETH_GOT_IP: { ip_addr_t ip_addr4; ip_addr_copy_from_ip4(ip_addr4, event->event_info.got_ip.ip_info.ip); - AddLog(LOG_LEVEL_DEBUG, PSTR("WIF: IPv4 %_I, mask %_I, gateway %_I"), + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: IPv4 %_I, mask %_I, gateway %_I"), + event->event_id == ARDUINO_EVENT_ETH_GOT_IP ? "ETH" : "WIF", event->event_info.got_ip.ip_info.ip.addr, event->event_info.got_ip.ip_info.netmask.addr, event->event_info.got_ip.ip_info.gw.addr); diff --git a/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino b/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino index a681b3603..ffc691eca 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino @@ -2362,11 +2362,11 @@ void HandleInformation(void) #if LWIP_IPV6 String ipv6_addr = WifiGetIPv6(); if (ipv6_addr != "") { - WSContentSend_P(PSTR("}1 IPv6 Global }2%s"), ipv6_addr.c_str()); + WSContentSend_P(PSTR("}1 IPv6 Global (wifi)}2%s"), ipv6_addr.c_str()); } ipv6_addr = WifiGetIPv6LinkLocal(); if (ipv6_addr != "") { - WSContentSend_P(PSTR("}1 IPv6 Link-Local }2%s"), ipv6_addr.c_str()); + WSContentSend_P(PSTR("}1 IPv6 Local (wifi)}2%s"), ipv6_addr.c_str()); } #endif // LWIP_IPV6 = 1 if (static_cast(WiFi.localIP()) != 0) { @@ -2387,6 +2387,16 @@ void HandleInformation(void) WSContentSend_P(PSTR("}1
}2
")); } WSContentSend_P(PSTR("}1" D_HOSTNAME "}2%s%s"), EthernetHostname(), (Mdns.begun) ? PSTR(".local") : ""); +#if LWIP_IPV6 + String ipv6_eth_addr = EthernetGetIPv6(); + if (ipv6_eth_addr != "") { + WSContentSend_P(PSTR("}1 IPv6 Global (eth)}2%s"), ipv6_eth_addr.c_str()); + } + ipv6_eth_addr = EthernetGetIPv6LinkLocal(); + if (ipv6_eth_addr != "") { + WSContentSend_P(PSTR("}1 IPv6 Local (eth)}2%s"), ipv6_eth_addr.c_str()); + } +#endif // LWIP_IPV6 = 1 WSContentSend_P(PSTR("}1" D_MAC_ADDRESS "}2%s"), EthernetMacAddress().c_str()); WSContentSend_P(PSTR("}1" D_IP_ADDRESS " (eth)}2%_I"), (uint32_t)EthernetLocalIP()); } diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tasmota.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tasmota.ino index c6246514e..c2327b73f 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tasmota.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tasmota.ino @@ -228,7 +228,7 @@ extern "C" { #endif if (static_cast(WiFi.localIP()) != 0) { be_map_insert_str(vm, "mac", WiFi.macAddress().c_str()); - be_map_insert_str(vm, "ip", WiFi.localIP().toString().c_str()); + be_map_insert_str(vm, "ip", IPAddress46((uint32_t)WiFi.localIP()).toString().c_str()); // quick fix for IPAddress bug show_rssi = true; } if (show_rssi) { @@ -252,8 +252,18 @@ extern "C" { #ifdef USE_ETHERNET if (static_cast(EthernetLocalIP()) != 0) { be_map_insert_str(vm, "mac", EthernetMacAddress().c_str()); - be_map_insert_str(vm, "ip", EthernetLocalIP().toString().c_str()); + be_map_insert_str(vm, "ip", IPAddress46((uint32_t)EthernetLocalIP()).toString().c_str()); // quick fix for IPAddress bug } +#if LWIP_IPV6 + String ipv6_addr = EthernetGetIPv6(); + if (ipv6_addr != "") { + be_map_insert_str(vm, "ip6", ipv6_addr.c_str()); + } + ipv6_addr = EthernetGetIPv6LinkLocal(); + if (ipv6_addr != "") { + be_map_insert_str(vm, "ip6local", ipv6_addr.c_str()); + } +#endif #endif be_pop(vm, 1); be_return(vm); diff --git a/tasmota/tasmota_xdrv_driver/xdrv_82_esp32_ethernet.ino b/tasmota/tasmota_xdrv_driver/xdrv_82_esp32_ethernet.ino index 922d44abc..4b6b142be 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_82_esp32_ethernet.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_82_esp32_ethernet.ino @@ -85,19 +85,28 @@ char eth_hostname[sizeof(TasmotaGlobal.hostname)]; uint8_t eth_config_change; -void EthernetEvent(WiFiEvent_t event) { - switch (event) { +void EthernetEvent(arduino_event_t *event) { + switch (event->event_id) { case ARDUINO_EVENT_ETH_START: - AddLog(LOG_LEVEL_DEBUG, PSTR("ETH: " D_ATTEMPTING_CONNECTION)); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ETH D_ATTEMPTING_CONNECTION)); ETH.setHostname(eth_hostname); break; + case ARDUINO_EVENT_ETH_CONNECTED: - AddLog(LOG_LEVEL_INFO, PSTR("ETH: " D_CONNECTED " at %dMbps%s"), - ETH.linkSpeed(), (ETH.fullDuplex()) ? " Full Duplex" : ""); +#if LWIP_IPV6 + ETH.enableIpV6(); // enable Link-Local +#endif + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ETH D_CONNECTED " at %dMbps%s, Mac %s, Hostname %s"), + ETH.linkSpeed(), (ETH.fullDuplex()) ? " Full Duplex" : "", + ETH.macAddress().c_str(), eth_hostname + ); + + // AddLog(LOG_LEVEL_DEBUG, D_LOG_ETH "ETH.enableIpV6() -> %i", ETH.enableIpV6()); break; + case ARDUINO_EVENT_ETH_GOT_IP: - AddLog(LOG_LEVEL_DEBUG, PSTR("ETH: Mac %s, IPAddress %_I, Hostname %s"), - ETH.macAddress().c_str(), (uint32_t)ETH.localIP(), eth_hostname); + // AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ETH "Mac %s, IPAddress %_I, Hostname %s"), + // ETH.macAddress().c_str(), (uint32_t)ETH.localIP(), eth_hostname); Settings->eth_ipv4_address[1] = (uint32_t)ETH.gatewayIP(); Settings->eth_ipv4_address[2] = (uint32_t)ETH.subnetMask(); if (0 == Settings->eth_ipv4_address[0]) { // At this point ETH.dnsIP() are NOT correct unless DHCP @@ -107,15 +116,18 @@ void EthernetEvent(WiFiEvent_t event) { TasmotaGlobal.rules_flag.eth_connected = 1; TasmotaGlobal.global_state.eth_down = 0; break; + case ARDUINO_EVENT_ETH_DISCONNECTED: - AddLog(LOG_LEVEL_INFO, PSTR("ETH: Disconnected")); + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ETH "Disconnected")); TasmotaGlobal.rules_flag.eth_disconnected = 1; TasmotaGlobal.global_state.eth_down = 1; break; + case ARDUINO_EVENT_ETH_STOP: - AddLog(LOG_LEVEL_DEBUG, PSTR("ETH: Stopped")); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ETH "Stopped")); TasmotaGlobal.global_state.eth_down = 1; break; + default: break; } @@ -130,10 +142,21 @@ void EthernetSetIp(void) { Settings->eth_ipv4_address[4]); // IPAddress dns2 } +// Returns only IPv6 global address (no loopback and no link-local) +String EthernetGetIPv6(void) +{ + return WifiFindIPv6(false, "en"); +} + +String EthernetGetIPv6LinkLocal(void) +{ + return WifiFindIPv6(true, "en"); +} + void EthernetInit(void) { if (!Settings->flag4.network_ethernet) { return; } if (!PinUsed(GPIO_ETH_PHY_MDC) && !PinUsed(GPIO_ETH_PHY_MDIO)) { - AddLog(LOG_LEVEL_DEBUG, PSTR("ETH: No ETH MDC and/or ETH MDIO GPIO defined")); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ETH "No ETH MDC and/or ETH MDIO GPIO defined")); return; } @@ -180,7 +203,7 @@ void EthernetInit(void) { delay(1); #endif // CONFIG_IDF_TARGET_ESP32 if (!ETH.begin(Settings->eth_address, eth_power, eth_mdc, eth_mdio, (eth_phy_type_t)Settings->eth_type, (eth_clock_mode_t)Settings->eth_clk_mode)) { - AddLog(LOG_LEVEL_DEBUG, PSTR("ETH: Bad PHY type or init error")); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ETH "Bad PHY type or init error")); return; };