Add IPv6 support for ESP-IDF framework (#2953)

Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
This commit is contained in:
Jimmy Hedman 2022-01-25 09:55:33 +01:00 committed by GitHub
parent 7a0827e3d0
commit d92f297bc0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 102 additions and 19 deletions

View file

@ -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();

View file

@ -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)

View file

@ -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) {

View 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

View file

@ -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

View file

@ -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;

View file

@ -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"