mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/transport_support_dev_bind' into 'master'
transport: Support bind socket to specified interface Closes IDFGH-4232 See merge request espressif/esp-idf!11961
This commit is contained in:
commit
b8cd8cc2df
@ -196,16 +196,18 @@ static void ms_to_timeval(int timeout_ms, struct timeval *tv)
|
||||
|
||||
static esp_err_t esp_tls_set_socket_options(int fd, const esp_tls_cfg_t *cfg)
|
||||
{
|
||||
if (cfg && cfg->timeout_ms >= 0) {
|
||||
struct timeval tv;
|
||||
ms_to_timeval(cfg->timeout_ms, &tv);
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) {
|
||||
ESP_LOGE(TAG, "Fail to setsockopt SO_RCVTIMEO");
|
||||
return ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED;
|
||||
}
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) != 0) {
|
||||
ESP_LOGE(TAG, "Fail to setsockopt SO_SNDTIMEO");
|
||||
return ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED;
|
||||
if (cfg) {
|
||||
if (cfg->timeout_ms >= 0) {
|
||||
struct timeval tv;
|
||||
ms_to_timeval(cfg->timeout_ms, &tv);
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) {
|
||||
ESP_LOGE(TAG, "Fail to setsockopt SO_RCVTIMEO");
|
||||
return ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED;
|
||||
}
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) != 0) {
|
||||
ESP_LOGE(TAG, "Fail to setsockopt SO_SNDTIMEO");
|
||||
return ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED;
|
||||
}
|
||||
}
|
||||
if (cfg->keep_alive_cfg && cfg->keep_alive_cfg->keep_alive_enable) {
|
||||
int keep_alive_enable = 1;
|
||||
@ -231,6 +233,15 @@ static esp_err_t esp_tls_set_socket_options(int fd, const esp_tls_cfg_t *cfg)
|
||||
return ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED;
|
||||
}
|
||||
}
|
||||
if (cfg->if_name) {
|
||||
if (cfg->if_name->ifr_name[0] != 0) {
|
||||
ESP_LOGD(TAG, "Bind [sock=%d] to interface %s", fd, cfg->if_name->ifr_name);
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, cfg->if_name, sizeof(struct ifreq)) != 0) {
|
||||
ESP_LOGE(TAG, "Bind [sock=%d] to interface %s fail", fd, cfg->if_name->ifr_name);
|
||||
return ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
@ -266,7 +277,7 @@ static esp_err_t esp_tcp_connect(const char *host, int hostlen, int port, int *s
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Set timeout options and keep-alive options if configured
|
||||
// Set timeout options, keep-alive options and bind device options if configured
|
||||
ret = esp_tls_set_socket_options(fd, cfg);
|
||||
if (ret != ESP_OK) {
|
||||
goto err;
|
||||
|
@ -172,6 +172,7 @@ typedef struct esp_tls_cfg {
|
||||
void *ds_data; /*!< Pointer for digital signature peripheral context */
|
||||
bool is_plain_tcp; /*!< Use non-TLS connection: When set to true, the esp-tls uses
|
||||
plain TCP transport rather then TLS/SSL connection */
|
||||
struct ifreq *if_name; /*!< The name of interface for data to go through. Use the default interface without setting */
|
||||
} esp_tls_cfg_t;
|
||||
|
||||
#ifdef CONFIG_ESP_TLS_SERVER
|
||||
|
@ -122,6 +122,7 @@ struct esp_http_client {
|
||||
int header_index;
|
||||
bool is_async;
|
||||
esp_transport_keep_alive_t keep_alive_cfg;
|
||||
struct ifreq *if_name;
|
||||
};
|
||||
|
||||
typedef struct esp_http_client esp_http_client_t;
|
||||
@ -576,6 +577,7 @@ esp_http_client_handle_t esp_http_client_init(const esp_http_client_config_t *co
|
||||
ESP_LOGE(TAG, "Error initialize transport");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (config->keep_alive_enable == true) {
|
||||
client->keep_alive_cfg.keep_alive_enable = true;
|
||||
client->keep_alive_cfg.keep_alive_idle = (config->keep_alive_idle == 0) ? DEFAULT_KEEP_ALIVE_IDLE : config->keep_alive_idle;
|
||||
@ -583,6 +585,14 @@ esp_http_client_handle_t esp_http_client_init(const esp_http_client_config_t *co
|
||||
client->keep_alive_cfg.keep_alive_count = (config->keep_alive_count == 0) ? DEFAULT_KEEP_ALIVE_COUNT : config->keep_alive_count;
|
||||
esp_transport_tcp_set_keep_alive(tcp, &client->keep_alive_cfg);
|
||||
}
|
||||
|
||||
if (config->if_name) {
|
||||
client->if_name = calloc(1, sizeof(struct ifreq) + 1);
|
||||
HTTP_MEM_CHECK(TAG, client->if_name, goto error);
|
||||
memcpy(client->if_name, config->if_name, sizeof(struct ifreq));
|
||||
esp_transport_tcp_set_interface_name(tcp, client->if_name);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS
|
||||
esp_transport_handle_t ssl = NULL;
|
||||
_success = (
|
||||
@ -613,10 +623,6 @@ esp_http_client_handle_t esp_http_client_init(const esp_http_client_config_t *co
|
||||
if (config->skip_cert_common_name_check) {
|
||||
esp_transport_ssl_skip_common_name_check(ssl);
|
||||
}
|
||||
|
||||
if (config->keep_alive_enable == true) {
|
||||
esp_transport_ssl_set_keep_alive(ssl, &client->keep_alive_cfg);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (_set_config(client, config) != ESP_OK) {
|
||||
@ -719,7 +725,9 @@ esp_err_t esp_http_client_cleanup(esp_http_client_handle_t client)
|
||||
free(client->response->buffer);
|
||||
free(client->response);
|
||||
}
|
||||
|
||||
if (client->if_name) {
|
||||
free(client->if_name);
|
||||
}
|
||||
free(client->parser);
|
||||
free(client->parser_settings);
|
||||
_clear_connection_info(client);
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "http_parser.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_err.h"
|
||||
#include <sys/socket.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -135,6 +136,7 @@ typedef struct {
|
||||
int keep_alive_idle; /*!< Keep-alive idle time. Default is 5 (second) */
|
||||
int keep_alive_interval; /*!< Keep-alive interval time. Default is 5 (second) */
|
||||
int keep_alive_count; /*!< Keep-alive packet retry send count. Default is 3 counts */
|
||||
struct ifreq *if_name; /*!< The name of interface for data to go through. Use the default interface without setting */
|
||||
} esp_http_client_config_t;
|
||||
|
||||
/**
|
||||
|
@ -121,6 +121,7 @@ struct esp_websocket_client {
|
||||
int payload_len;
|
||||
int payload_offset;
|
||||
esp_transport_keep_alive_t keep_alive_cfg;
|
||||
struct ifreq *if_name;
|
||||
};
|
||||
|
||||
static uint64_t _tick_get_ms(void)
|
||||
@ -316,6 +317,12 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie
|
||||
client->keep_alive_cfg.keep_alive_count = (config->keep_alive_count == 0) ? WEBSOCKET_KEEP_ALIVE_COUNT : config->keep_alive_count;
|
||||
}
|
||||
|
||||
if (config->if_name) {
|
||||
client->if_name = calloc(1, sizeof(struct ifreq) + 1);
|
||||
ESP_WS_CLIENT_MEM_CHECK(TAG, client->if_name, goto _websocket_init_fail);
|
||||
memcpy(client->if_name, config->if_name, sizeof(struct ifreq));
|
||||
}
|
||||
|
||||
client->lock = xSemaphoreCreateRecursiveMutex();
|
||||
ESP_WS_CLIENT_MEM_CHECK(TAG, client->lock, goto _websocket_init_fail);
|
||||
|
||||
@ -331,7 +338,7 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie
|
||||
esp_transport_set_default_port(tcp, WEBSOCKET_TCP_DEFAULT_PORT);
|
||||
esp_transport_list_add(client->transport_list, tcp, "_tcp"); // need to save to transport list, for cleanup
|
||||
esp_transport_tcp_set_keep_alive(tcp, &client->keep_alive_cfg);
|
||||
|
||||
esp_transport_tcp_set_interface_name(tcp, client->if_name);
|
||||
|
||||
esp_transport_handle_t ws = esp_transport_ws_init(tcp);
|
||||
ESP_WS_CLIENT_MEM_CHECK(TAG, ws, goto _websocket_init_fail);
|
||||
@ -374,7 +381,6 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie
|
||||
if (config->skip_cert_common_name_check) {
|
||||
esp_transport_ssl_skip_common_name_check(ssl);
|
||||
}
|
||||
esp_transport_ssl_set_keep_alive(ssl, &client->keep_alive_cfg);
|
||||
|
||||
esp_transport_handle_t wss = esp_transport_ws_init(ssl);
|
||||
ESP_WS_CLIENT_MEM_CHECK(TAG, wss, goto _websocket_init_fail);
|
||||
@ -448,6 +454,9 @@ esp_err_t esp_websocket_client_destroy(esp_websocket_client_handle_t client)
|
||||
if (client->event_handle) {
|
||||
esp_event_loop_delete(client->event_handle);
|
||||
}
|
||||
if (client->if_name) {
|
||||
free(client->if_name);
|
||||
}
|
||||
esp_websocket_client_destroy_config(client);
|
||||
esp_transport_list_destroy(client->transport_list);
|
||||
vQueueDelete(client->lock);
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_event.h"
|
||||
#include <sys/socket.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -100,6 +101,7 @@ typedef struct {
|
||||
int keep_alive_interval; /*!< Keep-alive interval time. Default is 5 (second) */
|
||||
int keep_alive_count; /*!< Keep-alive packet retry send count. Default is 3 counts */
|
||||
size_t ping_interval_sec; /*!< Websocket ping interval, defaults to 10 seconds if not set */
|
||||
struct ifreq *if_name; /*!< The name of interface for data to go through. Use the default interface without setting */
|
||||
} esp_websocket_client_config_t;
|
||||
|
||||
/**
|
||||
|
@ -173,6 +173,15 @@ void esp_transport_ssl_set_psk_key_hint(esp_transport_handle_t t, const psk_hint
|
||||
*/
|
||||
void esp_transport_ssl_set_keep_alive(esp_transport_handle_t t, esp_transport_keep_alive_t *keep_alive_cfg);
|
||||
|
||||
/**
|
||||
* @brief Set name of interface that socket can be binded on
|
||||
* So the data can transport on this interface
|
||||
*
|
||||
* @param[in] t The transport handle
|
||||
* @param[in] if_name The interface name
|
||||
*/
|
||||
void esp_transport_ssl_set_interface_name(esp_transport_handle_t t, struct ifreq *if_name);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -16,6 +16,7 @@
|
||||
#define _ESP_TRANSPORT_TCP_H_
|
||||
|
||||
#include "esp_transport.h"
|
||||
#include <sys/socket.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -30,6 +31,15 @@ extern "C" {
|
||||
*/
|
||||
void esp_transport_tcp_set_keep_alive(esp_transport_handle_t t, esp_transport_keep_alive_t *keep_alive_cfg);
|
||||
|
||||
/**
|
||||
* @brief Set name of interface that socket can be binded on
|
||||
* So the data can transport on this interface
|
||||
*
|
||||
* @param[in] t The transport handle
|
||||
* @param[in] if_name The interface name
|
||||
*/
|
||||
void esp_transport_tcp_set_interface_name(esp_transport_handle_t t, struct ifreq *if_name);
|
||||
|
||||
/**
|
||||
* @brief Create TCP transport, the transport handle must be release esp_transport_destroy callback
|
||||
*
|
||||
|
@ -32,6 +32,12 @@ struct tcp_connect_task_params {
|
||||
bool consume_sock_backlog;
|
||||
};
|
||||
|
||||
#define TEST_TRANSPORT_BIND_IFNAME() \
|
||||
struct ifreq ifr; \
|
||||
ifr.ifr_name[0] = 'l'; \
|
||||
ifr.ifr_name[1] = 'o'; \
|
||||
ifr.ifr_name[2] = '\0';
|
||||
|
||||
/**
|
||||
* @brief Recursively connects with a new socket to loopback interface until the last one blocks.
|
||||
* The last socket is closed upon test teardown, that initiates recursive cleanup (close) for all
|
||||
@ -350,6 +356,10 @@ TEST_CASE("tcp_transport: Keep alive test", "[tcp_transport]")
|
||||
.keep_alive_count = 3 };
|
||||
esp_transport_tcp_set_keep_alive(tcp, &keep_alive_cfg);
|
||||
|
||||
// Bind device interface to loopback
|
||||
TEST_TRANSPORT_BIND_IFNAME();
|
||||
esp_transport_tcp_set_interface_name(tcp, &ifr);
|
||||
|
||||
tcp_transport_keepalive_test(tcp, &keep_alive_cfg);
|
||||
|
||||
// Cleanup
|
||||
@ -374,6 +384,10 @@ TEST_CASE("ssl_transport: Keep alive test", "[tcp_transport]")
|
||||
.keep_alive_count = 4 };
|
||||
esp_transport_ssl_set_keep_alive(ssl, &keep_alive_cfg);
|
||||
|
||||
// Bind device interface to loopback
|
||||
TEST_TRANSPORT_BIND_IFNAME();
|
||||
esp_transport_ssl_set_interface_name(ssl, &ifr);
|
||||
|
||||
tcp_transport_keepalive_test(ssl, &keep_alive_cfg);
|
||||
|
||||
// Cleanup
|
||||
@ -400,6 +414,10 @@ TEST_CASE("ws_transport: Keep alive test", "[tcp_transport]")
|
||||
.keep_alive_count = 3 };
|
||||
esp_transport_tcp_set_keep_alive(ssl, &keep_alive_cfg);
|
||||
|
||||
// Bind device interface to loopback
|
||||
TEST_TRANSPORT_BIND_IFNAME();
|
||||
esp_transport_ssl_set_interface_name(ws, &ifr);
|
||||
|
||||
tcp_transport_keepalive_test(ws, &keep_alive_cfg);
|
||||
|
||||
// Cleanup
|
||||
@ -424,6 +442,10 @@ TEST_CASE("ssl_transport: Check that parameters (keepalive) are set independentl
|
||||
.keep_alive_count = 3 };
|
||||
esp_transport_ssl_set_keep_alive(ssl, &keep_alive_cfg);
|
||||
|
||||
// Bind device interface to loopback
|
||||
TEST_TRANSPORT_BIND_IFNAME();
|
||||
esp_transport_ssl_set_interface_name(ssl, &ifr);
|
||||
|
||||
tcp_transport_keepalive_test(ssl, &keep_alive_cfg);
|
||||
|
||||
// Cleanup
|
||||
|
@ -345,6 +345,12 @@ void esp_transport_ssl_set_keep_alive(esp_transport_handle_t t, esp_transport_ke
|
||||
ssl->cfg.keep_alive_cfg = (tls_keep_alive_cfg_t *) keep_alive_cfg;
|
||||
}
|
||||
|
||||
void esp_transport_ssl_set_interface_name(esp_transport_handle_t t, struct ifreq *if_name)
|
||||
{
|
||||
GET_SSL_FROM_TRANSPORT_OR_RETURN(ssl, t);
|
||||
ssl->cfg.if_name = if_name;
|
||||
}
|
||||
|
||||
esp_transport_handle_t esp_transport_ssl_init(void)
|
||||
{
|
||||
esp_transport_handle_t t = esp_transport_init();
|
||||
@ -378,3 +384,8 @@ void esp_transport_tcp_set_keep_alive(esp_transport_handle_t t, esp_transport_ke
|
||||
{
|
||||
return esp_transport_ssl_set_keep_alive(t, keep_alive_cfg);
|
||||
}
|
||||
|
||||
void esp_transport_tcp_set_interface_name(esp_transport_handle_t t, struct ifreq *if_name)
|
||||
{
|
||||
return esp_transport_ssl_set_interface_name(t, if_name);
|
||||
}
|
||||
|
@ -4,4 +4,6 @@ This example is based on `esp_https_ota` component's APIs.
|
||||
|
||||
## Configuration
|
||||
|
||||
Refer README.md in the parent directory for setup details
|
||||
Refer README.md in the parent directory for setup details.
|
||||
|
||||
Example also supports binding to specific interface (either "Ethernet" or "WiFi Station"), which will allow firmware upgrade to happen through specific interface (in case multiple networking interfaces are enabled). Please see more on this through example configuration in `idf.py menuconfig -> Example Configuration -> Support firmware upgrade bind specificied interface->Choose OTA data bind interface`.
|
||||
|
@ -17,4 +17,29 @@ menu "Example Configuration"
|
||||
help
|
||||
This allows you to skip the validation of OTA server certificate CN field.
|
||||
|
||||
config EXAMPLE_FIRMWARE_UPGRADE_BIND_IF
|
||||
bool "Support firmware upgrade bind specified interface"
|
||||
default n
|
||||
help
|
||||
This allows you to bind specified interface in OTA example.
|
||||
|
||||
choice EXAMPLE_FIRMWARE_UPGRADE_BIND_IF_TYPE
|
||||
prompt "Choose OTA data bind interface"
|
||||
default EXAMPLE_FIRMWARE_UPGRADE_BIND_IF_STA
|
||||
depends on EXAMPLE_FIRMWARE_UPGRADE_BIND_IF
|
||||
help
|
||||
Select which interface type of OTA data go through.
|
||||
|
||||
config EXAMPLE_FIRMWARE_UPGRADE_BIND_IF_STA
|
||||
bool "Bind wifi station interface"
|
||||
depends on EXAMPLE_CONNECT_WIFI
|
||||
help
|
||||
Select wifi station interface to pass the OTA data.
|
||||
|
||||
config EXAMPLE_FIRMWARE_UPGRADE_BIND_IF_ETH
|
||||
bool "Bind ethernet interface"
|
||||
depends on EXAMPLE_CONNECT_ETHERNET
|
||||
help
|
||||
Select ethernet interface to pass the OTA data.
|
||||
endchoice
|
||||
endmenu
|
||||
|
@ -20,11 +20,20 @@
|
||||
#include "nvs.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "protocol_examples_common.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#if CONFIG_EXAMPLE_CONNECT_WIFI
|
||||
#include "esp_wifi.h"
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_FIRMWARE_UPGRADE_BIND_IF
|
||||
/* The interface name value can refer to if_desc in esp_netif_defaults.h */
|
||||
#if CONFIG_EXAMPLE_FIRMWARE_UPGRADE_BIND_IF_ETH
|
||||
static const char *bind_interface_name = "eth";
|
||||
#elif CONFIG_EXAMPLE_FIRMWARE_UPGRADE_BIND_IF_STA
|
||||
static const char *bind_interface_name = "sta";
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static const char *TAG = "simple_ota_example";
|
||||
extern const uint8_t server_cert_pem_start[] asm("_binary_ca_cert_pem_start");
|
||||
extern const uint8_t server_cert_pem_end[] asm("_binary_ca_cert_pem_end");
|
||||
@ -62,12 +71,24 @@ esp_err_t _http_event_handler(esp_http_client_event_t *evt)
|
||||
void simple_ota_example_task(void *pvParameter)
|
||||
{
|
||||
ESP_LOGI(TAG, "Starting OTA example");
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_FIRMWARE_UPGRADE_BIND_IF
|
||||
esp_netif_t *netif = get_example_netif_from_desc(bind_interface_name);
|
||||
if (netif == NULL) {
|
||||
ESP_LOGE(TAG, "Can't find netif from interface description");
|
||||
abort();
|
||||
}
|
||||
struct ifreq ifr;
|
||||
esp_netif_get_netif_impl_name(netif, ifr.ifr_name);
|
||||
ESP_LOGI(TAG, "Bind interface name is %s", ifr.ifr_name);
|
||||
#endif
|
||||
esp_http_client_config_t config = {
|
||||
.url = CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL,
|
||||
.cert_pem = (char *)server_cert_pem_start,
|
||||
.event_handler = _http_event_handler,
|
||||
.keep_alive_enable = true,
|
||||
#ifdef CONFIG_EXAMPLE_FIRMWARE_UPGRADE_BIND_IF
|
||||
.if_name = &ifr,
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL_FROM_STDIN
|
||||
|
@ -1,2 +1,3 @@
|
||||
CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL="FROM_STDIN"
|
||||
CONFIG_EXAMPLE_SKIP_COMMON_NAME_CHECK=y
|
||||
CONFIG_EXAMPLE_FIRMWARE_UPGRADE_BIND_IF=y
|
||||
|
Loading…
Reference in New Issue
Block a user