diff --git a/components/esp-tls/esp_tls.c b/components/esp-tls/esp_tls.c index e7afd25f8e..1ded53ef79 100644 --- a/components/esp-tls/esp_tls.c +++ b/components/esp-tls/esp_tls.c @@ -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; diff --git a/components/esp-tls/esp_tls.h b/components/esp-tls/esp_tls.h index 981379435c..72a78c5e7d 100644 --- a/components/esp-tls/esp_tls.h +++ b/components/esp-tls/esp_tls.h @@ -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 diff --git a/components/esp_http_client/esp_http_client.c b/components/esp_http_client/esp_http_client.c index 779033d427..33b928559a 100644 --- a/components/esp_http_client/esp_http_client.c +++ b/components/esp_http_client/esp_http_client.c @@ -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); diff --git a/components/esp_http_client/include/esp_http_client.h b/components/esp_http_client/include/esp_http_client.h index 4884a74e0f..ed4e4a3230 100644 --- a/components/esp_http_client/include/esp_http_client.h +++ b/components/esp_http_client/include/esp_http_client.h @@ -19,6 +19,7 @@ #include "http_parser.h" #include "sdkconfig.h" #include "esp_err.h" +#include #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; /** diff --git a/components/esp_websocket_client/esp_websocket_client.c b/components/esp_websocket_client/esp_websocket_client.c index 5055736d43..2f6405d47d 100644 --- a/components/esp_websocket_client/esp_websocket_client.c +++ b/components/esp_websocket_client/esp_websocket_client.c @@ -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); diff --git a/components/esp_websocket_client/include/esp_websocket_client.h b/components/esp_websocket_client/include/esp_websocket_client.h index 13f817685e..5ef08035d4 100644 --- a/components/esp_websocket_client/include/esp_websocket_client.h +++ b/components/esp_websocket_client/include/esp_websocket_client.h @@ -22,6 +22,7 @@ #include "freertos/FreeRTOS.h" #include "esp_err.h" #include "esp_event.h" +#include #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; /** diff --git a/components/tcp_transport/include/esp_transport_ssl.h b/components/tcp_transport/include/esp_transport_ssl.h index 2711abf330..d23a725c23 100644 --- a/components/tcp_transport/include/esp_transport_ssl.h +++ b/components/tcp_transport/include/esp_transport_ssl.h @@ -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 diff --git a/components/tcp_transport/include/esp_transport_tcp.h b/components/tcp_transport/include/esp_transport_tcp.h index 92b02fa789..6941820f0e 100644 --- a/components/tcp_transport/include/esp_transport_tcp.h +++ b/components/tcp_transport/include/esp_transport_tcp.h @@ -16,6 +16,7 @@ #define _ESP_TRANSPORT_TCP_H_ #include "esp_transport.h" +#include #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 * diff --git a/components/tcp_transport/test/test_transport.c b/components/tcp_transport/test/test_transport.c index 0395c0b6be..c67f5eed4b 100644 --- a/components/tcp_transport/test/test_transport.c +++ b/components/tcp_transport/test/test_transport.c @@ -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 diff --git a/components/tcp_transport/transport_ssl.c b/components/tcp_transport/transport_ssl.c index fd2795a4bd..5dfd1c2cb2 100644 --- a/components/tcp_transport/transport_ssl.c +++ b/components/tcp_transport/transport_ssl.c @@ -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); +} diff --git a/examples/system/ota/simple_ota_example/README.md b/examples/system/ota/simple_ota_example/README.md index a2629e1009..31a340974d 100644 --- a/examples/system/ota/simple_ota_example/README.md +++ b/examples/system/ota/simple_ota_example/README.md @@ -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`. diff --git a/examples/system/ota/simple_ota_example/main/Kconfig.projbuild b/examples/system/ota/simple_ota_example/main/Kconfig.projbuild index 2c2eb0c583..588d22b6c8 100644 --- a/examples/system/ota/simple_ota_example/main/Kconfig.projbuild +++ b/examples/system/ota/simple_ota_example/main/Kconfig.projbuild @@ -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 diff --git a/examples/system/ota/simple_ota_example/main/simple_ota_example.c b/examples/system/ota/simple_ota_example/main/simple_ota_example.c index 1f73b4d832..30730c3135 100644 --- a/examples/system/ota/simple_ota_example/main/simple_ota_example.c +++ b/examples/system/ota/simple_ota_example/main/simple_ota_example.c @@ -20,11 +20,20 @@ #include "nvs.h" #include "nvs_flash.h" #include "protocol_examples_common.h" - +#include #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 diff --git a/examples/system/ota/simple_ota_example/sdkconfig.ci b/examples/system/ota/simple_ota_example/sdkconfig.ci index 8367dac01f..278e33dd00 100644 --- a/examples/system/ota/simple_ota_example/sdkconfig.ci +++ b/examples/system/ota/simple_ota_example/sdkconfig.ci @@ -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