mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 06:33:51 +00:00 
			
		
		
		
	Add IPv6 support for ESP-IDF framework (#2953)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
This commit is contained in:
		| @@ -24,7 +24,7 @@ static const char *const TAG = "api"; | |||||||
| void APIServer::setup() { | void APIServer::setup() { | ||||||
|   ESP_LOGCONFIG(TAG, "Setting up Home Assistant API server..."); |   ESP_LOGCONFIG(TAG, "Setting up Home Assistant API server..."); | ||||||
|   this->setup_controller(); |   this->setup_controller(); | ||||||
|   socket_ = socket::socket(AF_INET, SOCK_STREAM, 0); |   socket_ = socket::socket_ip(SOCK_STREAM, 0); | ||||||
|   if (socket_ == nullptr) { |   if (socket_ == nullptr) { | ||||||
|     ESP_LOGW(TAG, "Could not create socket."); |     ESP_LOGW(TAG, "Could not create socket."); | ||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
| @@ -43,13 +43,16 @@ void APIServer::setup() { | |||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   struct sockaddr_in server; |   struct sockaddr_storage server; | ||||||
|   memset(&server, 0, sizeof(server)); |  | ||||||
|   server.sin_family = AF_INET; |  | ||||||
|   server.sin_addr.s_addr = ESPHOME_INADDR_ANY; |  | ||||||
|   server.sin_port = htons(this->port_); |  | ||||||
|  |  | ||||||
|   err = socket_->bind((struct sockaddr *) &server, sizeof(server)); |   socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), htons(this->port_)); | ||||||
|  |   if (sl == 0) { | ||||||
|  |     ESP_LOGW(TAG, "Socket unable to set sockaddr: errno %d", errno); | ||||||
|  |     this->mark_failed(); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   err = socket_->bind((struct sockaddr *) &server, sl); | ||||||
|   if (err != 0) { |   if (err != 0) { | ||||||
|     ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno); |     ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno); | ||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
|   | |||||||
| @@ -1,7 +1,27 @@ | |||||||
| import esphome.codegen as cg | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components.esp32 import add_idf_sdkconfig_option | ||||||
|  |  | ||||||
|  | from esphome.const import ( | ||||||
|  |     CONF_ENABLE_IPV6, | ||||||
|  | ) | ||||||
|  |  | ||||||
| CODEOWNERS = ["@esphome/core"] | CODEOWNERS = ["@esphome/core"] | ||||||
| AUTO_LOAD = ["mdns"] | AUTO_LOAD = ["mdns"] | ||||||
|  |  | ||||||
| network_ns = cg.esphome_ns.namespace("network") | network_ns = cg.esphome_ns.namespace("network") | ||||||
| IPAddress = network_ns.class_("IPAddress") | IPAddress = network_ns.class_("IPAddress") | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = cv.Schema( | ||||||
|  |     { | ||||||
|  |         cv.SplitDefault(CONF_ENABLE_IPV6, esp32_idf=False): cv.All( | ||||||
|  |             cv.only_with_esp_idf, cv.boolean | ||||||
|  |         ), | ||||||
|  |     } | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     if CONF_ENABLE_IPV6 in config and config[CONF_ENABLE_IPV6]: | ||||||
|  |         add_idf_sdkconfig_option("CONFIG_LWIP_IPV6", True) | ||||||
|  |         add_idf_sdkconfig_option("CONFIG_LWIP_IPV6_AUTOCONFIG", True) | ||||||
|   | |||||||
| @@ -36,7 +36,7 @@ std::unique_ptr<OTABackend> make_ota_backend() { | |||||||
| } | } | ||||||
|  |  | ||||||
| void OTAComponent::setup() { | void OTAComponent::setup() { | ||||||
|   server_ = socket::socket(AF_INET, SOCK_STREAM, 0); |   server_ = socket::socket_ip(SOCK_STREAM, 0); | ||||||
|   if (server_ == nullptr) { |   if (server_ == nullptr) { | ||||||
|     ESP_LOGW(TAG, "Could not create socket."); |     ESP_LOGW(TAG, "Could not create socket."); | ||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
| @@ -55,11 +55,14 @@ void OTAComponent::setup() { | |||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   struct sockaddr_in server; |   struct sockaddr_storage server; | ||||||
|   memset(&server, 0, sizeof(server)); |  | ||||||
|   server.sin_family = AF_INET; |   socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), htons(this->port_)); | ||||||
|   server.sin_addr.s_addr = ESPHOME_INADDR_ANY; |   if (sl == 0) { | ||||||
|   server.sin_port = htons(this->port_); |     ESP_LOGW(TAG, "Socket unable to set sockaddr: errno %d", errno); | ||||||
|  |     this->mark_failed(); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   err = server_->bind((struct sockaddr *) &server, sizeof(server)); |   err = server_->bind((struct sockaddr *) &server, sizeof(server)); | ||||||
|   if (err != 0) { |   if (err != 0) { | ||||||
|   | |||||||
							
								
								
									
										43
									
								
								esphome/components/socket/socket.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								esphome/components/socket/socket.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  | #include "socket.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  | #include <cstring> | ||||||
|  | #include <cerrno> | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace socket { | ||||||
|  |  | ||||||
|  | std::unique_ptr<Socket> socket_ip(int type, int protocol) { | ||||||
|  | #if LWIP_IPV6 | ||||||
|  |   return socket(AF_INET6, type, protocol); | ||||||
|  | #else | ||||||
|  |   return socket(AF_INET, type, protocol); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t port) { | ||||||
|  | #if LWIP_IPV6 | ||||||
|  |   if (addrlen < sizeof(sockaddr_in6)) { | ||||||
|  |     errno = EINVAL; | ||||||
|  |     return 0; | ||||||
|  |   } | ||||||
|  |   auto *server = reinterpret_cast<sockaddr_in6 *>(addr); | ||||||
|  |   memset(server, 0, sizeof(sockaddr_in6)); | ||||||
|  |   server->sin6_family = AF_INET6; | ||||||
|  |   server->sin6_port = port; | ||||||
|  |   server->sin6_addr = in6addr_any; | ||||||
|  |   return sizeof(sockaddr_in6); | ||||||
|  | #else | ||||||
|  |   if (addrlen < sizeof(sockaddr_in)) { | ||||||
|  |     errno = EINVAL; | ||||||
|  |     return 0; | ||||||
|  |   } | ||||||
|  |   auto *server = reinterpret_cast<sockaddr_in *>(addr); | ||||||
|  |   memset(server, 0, sizeof(sockaddr_in)); | ||||||
|  |   server->sin_family = AF_INET; | ||||||
|  |   server->sin_addr.s_addr = ESPHOME_INADDR_ANY; | ||||||
|  |   server->sin_port = port; | ||||||
|  |   return sizeof(sockaddr_in); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | }  // namespace socket | ||||||
|  | }  // namespace esphome | ||||||
| @@ -38,7 +38,14 @@ class Socket { | |||||||
|   virtual int loop() { return 0; }; |   virtual int loop() { return 0; }; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | /// Create a socket of the given domain, type and protocol. | ||||||
| std::unique_ptr<Socket> socket(int domain, int type, int protocol); | std::unique_ptr<Socket> socket(int domain, int type, int protocol); | ||||||
|  |  | ||||||
|  | /// Create a socket in the newest available IP domain (IPv6 or IPv4) of the given type and protocol. | ||||||
|  | std::unique_ptr<Socket> socket_ip(int type, int protocol); | ||||||
|  |  | ||||||
|  | /// Set a sockaddr to the any address for the IP version used by socket_ip(). | ||||||
|  | socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t port); | ||||||
|  |  | ||||||
| }  // namespace socket | }  // namespace socket | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|   | |||||||
| @@ -56,6 +56,7 @@ struct IDFWiFiEvent { | |||||||
|     wifi_event_ap_probe_req_rx_t ap_probe_req_rx; |     wifi_event_ap_probe_req_rx_t ap_probe_req_rx; | ||||||
|     wifi_event_bss_rssi_low_t bss_rssi_low; |     wifi_event_bss_rssi_low_t bss_rssi_low; | ||||||
|     ip_event_got_ip_t ip_got_ip; |     ip_event_got_ip_t ip_got_ip; | ||||||
|  |     ip_event_got_ip6_t ip_got_ip6; | ||||||
|     ip_event_ap_staipassigned_t ip_ap_staipassigned; |     ip_event_ap_staipassigned_t ip_ap_staipassigned; | ||||||
|   } data; |   } data; | ||||||
| }; | }; | ||||||
| @@ -79,6 +80,8 @@ void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, voi | |||||||
|     memcpy(&event.data.sta_disconnected, event_data, sizeof(wifi_event_sta_disconnected_t)); |     memcpy(&event.data.sta_disconnected, event_data, sizeof(wifi_event_sta_disconnected_t)); | ||||||
|   } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { |   } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { | ||||||
|     memcpy(&event.data.ip_got_ip, event_data, sizeof(ip_event_got_ip_t)); |     memcpy(&event.data.ip_got_ip, event_data, sizeof(ip_event_got_ip_t)); | ||||||
|  |   } else if (event_base == IP_EVENT && event_id == IP_EVENT_GOT_IP6) { | ||||||
|  |     memcpy(&event.data.ip_got_ip6, event_data, sizeof(ip_event_got_ip6_t)); | ||||||
|   } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_LOST_IP) {  // NOLINT(bugprone-branch-clone) |   } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_LOST_IP) {  // NOLINT(bugprone-branch-clone) | ||||||
|     // no data |     // no data | ||||||
|   } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE) { |   } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE) { | ||||||
| @@ -497,12 +500,8 @@ const char *get_auth_mode_str(uint8_t mode) { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| std::string format_ip4_addr(const esp_ip4_addr_t &ip) { | std::string format_ip4_addr(const esp_ip4_addr_t &ip) { return str_snprintf(IPSTR, 15, IP2STR(&ip)); } | ||||||
|   char buf[20]; | std::string format_ip6_addr(const esp_ip6_addr_t &ip) { return str_snprintf(IPV6STR, 39, IPV62STR(ip)); } | ||||||
|   snprintf(buf, sizeof(buf), "%u.%u.%u.%u", uint8_t(ip.addr >> 0), uint8_t(ip.addr >> 8), uint8_t(ip.addr >> 16), |  | ||||||
|            uint8_t(ip.addr >> 24)); |  | ||||||
|   return buf; |  | ||||||
| } |  | ||||||
| const char *get_disconnect_reason_str(uint8_t reason) { | const char *get_disconnect_reason_str(uint8_t reason) { | ||||||
|   switch (reason) { |   switch (reason) { | ||||||
|     case WIFI_REASON_AUTH_EXPIRE: |     case WIFI_REASON_AUTH_EXPIRE: | ||||||
| @@ -635,10 +634,17 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) { | |||||||
|  |  | ||||||
|   } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_STA_GOT_IP) { |   } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_STA_GOT_IP) { | ||||||
|     const auto &it = data->data.ip_got_ip; |     const auto &it = data->data.ip_got_ip; | ||||||
|  | #ifdef LWIP_IPV6_AUTOCONFIG | ||||||
|  |     tcpip_adapter_create_ip6_linklocal(TCPIP_ADAPTER_IF_STA); | ||||||
|  | #endif | ||||||
|     ESP_LOGV(TAG, "Event: Got IP static_ip=%s gateway=%s", format_ip4_addr(it.ip_info.ip).c_str(), |     ESP_LOGV(TAG, "Event: Got IP static_ip=%s gateway=%s", format_ip4_addr(it.ip_info.ip).c_str(), | ||||||
|              format_ip4_addr(it.ip_info.gw).c_str()); |              format_ip4_addr(it.ip_info.gw).c_str()); | ||||||
|     s_sta_got_ip = true; |     s_sta_got_ip = true; | ||||||
|  |  | ||||||
|  |   } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_GOT_IP6) { | ||||||
|  |     const auto &it = data->data.ip_got_ip6; | ||||||
|  |     ESP_LOGV(TAG, "Event: Got IPv6 address=%s", format_ip6_addr(it.ip6_info.ip).c_str()); | ||||||
|  |  | ||||||
|   } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_STA_LOST_IP) { |   } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_STA_LOST_IP) { | ||||||
|     ESP_LOGV(TAG, "Event: Lost IP"); |     ESP_LOGV(TAG, "Event: Lost IP"); | ||||||
|     s_sta_got_ip = false; |     s_sta_got_ip = false; | ||||||
|   | |||||||
| @@ -188,6 +188,7 @@ CONF_ECO2 = "eco2" | |||||||
| CONF_EFFECT = "effect" | CONF_EFFECT = "effect" | ||||||
| CONF_EFFECTS = "effects" | CONF_EFFECTS = "effects" | ||||||
| CONF_ELSE = "else" | CONF_ELSE = "else" | ||||||
|  | CONF_ENABLE_IPV6 = "enable_ipv6" | ||||||
| CONF_ENABLE_PIN = "enable_pin" | CONF_ENABLE_PIN = "enable_pin" | ||||||
| CONF_ENABLE_TIME = "enable_time" | CONF_ENABLE_TIME = "enable_time" | ||||||
| CONF_ENERGY = "energy" | CONF_ENERGY = "energy" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user