diff --git a/components/esp_netif/esp_netif_objects.c b/components/esp_netif/esp_netif_objects.c index 490a91c7e0..b953885c4f 100644 --- a/components/esp_netif/esp_netif_objects.c +++ b/components/esp_netif/esp_netif_objects.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -32,25 +32,41 @@ static SemaphoreHandle_t s_list_lock = NULL; ESP_EVENT_DEFINE_BASE(IP_EVENT); +esp_err_t esp_netif_objects_init(void) +{ + if (s_list_lock != NULL) { + // already initialized + return ESP_OK; + } + s_list_lock = xSemaphoreCreateMutex(); + if (s_list_lock == NULL) { + return ESP_ERR_NO_MEM; + } + return ESP_OK; +} + +void esp_netif_objects_deinit(void) +{ + vSemaphoreDelete(s_list_lock); + s_list_lock = NULL; +} + esp_err_t esp_netif_list_lock(void) { - if (s_list_lock == NULL) { - s_list_lock = xSemaphoreCreateMutex(); - if (s_list_lock == NULL) { - return ESP_ERR_NO_MEM; - } + if (s_list_lock) { + xSemaphoreTake(s_list_lock, portMAX_DELAY); + } else { + ESP_LOGD(TAG, "%s list not locked (s_list_lock not initialized)", __func__); } - xSemaphoreTake(s_list_lock, portMAX_DELAY); return ESP_OK; } void esp_netif_list_unlock(void) { - assert(s_list_lock); - xSemaphoreGive(s_list_lock); - if (s_esp_netif_counter == 0) { - vQueueDelete(s_list_lock); - s_list_lock = NULL; + if (s_list_lock) { + xSemaphoreGive(s_list_lock); + } else { + ESP_LOGD(TAG, "%s list not unlocked (s_list_lock not initialized)", __func__); } } diff --git a/components/esp_netif/include/esp_netif.h b/components/esp_netif/include/esp_netif.h index aeb5ffdd6a..8037cf0bcb 100644 --- a/components/esp_netif/include/esp_netif.h +++ b/components/esp_netif/include/esp_netif.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -939,12 +939,49 @@ int32_t esp_netif_get_event_id(esp_netif_t *esp_netif, esp_netif_ip_event_type_t /** * @brief Iterates over list of interfaces. Returns first netif if NULL given as parameter * + * @note This API doesn't lock the list, nor the TCPIP context, as this it's usually required + * to get atomic access between iteration steps rather that within a single iteration. + * Therefore it is recommended to iterate over the interfaces inside esp_netif_tcpip_exec() + * + * You can use esp_netif_next_unsafe() directly if all the system + * interfaces are under your control and you can safely iterate over them. + * Otherwise, iterate over interfaces using esp_netif_tcpip_exec(), or use esp_netif_find_if() + * to search in the list of netifs with defined predicate. + * * @param[in] esp_netif Handle to esp-netif instance * * @return First netif from the list if supplied parameter is NULL, next one otherwise */ esp_netif_t *esp_netif_next(esp_netif_t *esp_netif); +/** + * @brief Iterates over list of interfaces without list locking. Returns first netif if NULL given as parameter + * + * Used for bulk search loops within TCPIP context, e.g. using esp_netif_tcpip_exec(), or if we're sure + * that the iteration is safe from our application perspective (e.g. no interface is removed between iterations) + * + * @param[in] esp_netif Handle to esp-netif instance + * + * @return First netif from the list if supplied parameter is NULL, next one otherwise + */ +esp_netif_t* esp_netif_next_unsafe(esp_netif_t* esp_netif); + +/** + * @brief Predicate callback for esp_netif_find_if() used to find interface + * which meets defined criteria + */ +typedef bool (*esp_netif_find_predicate_t)(esp_netif_t *netif, void *ctx); + +/** + * @brief Return a netif pointer for the first interface that meets criteria defined + * by the callback + * + * @param fn Predicate function returning true for the desired interface + * @param ctx Context pointer passed to the predicate, typically a descriptor to compare with + * @return valid netif pointer if found, NULL if not + */ +esp_netif_t *esp_netif_find_if(esp_netif_find_predicate_t fn, void *ctx); + /** * @brief Returns number of registered esp_netif objects * diff --git a/components/esp_netif/loopback/esp_netif_loopback.c b/components/esp_netif/loopback/esp_netif_loopback.c index e3fbb71921..0721fcce58 100644 --- a/components/esp_netif/loopback/esp_netif_loopback.c +++ b/components/esp_netif/loopback/esp_netif_loopback.c @@ -187,7 +187,7 @@ esp_netif_t *esp_netif_new(const esp_netif_config_t *esp_netif_config) } esp_netif->ip_info_old = ip_info; - esp_netif_add_to_list(esp_netif); + esp_netif_add_to_list_unsafe(esp_netif); // Configure the created object with provided configuration esp_err_t ret = esp_netif_init_configuration(esp_netif, esp_netif_config); @@ -203,7 +203,7 @@ esp_netif_t *esp_netif_new(const esp_netif_config_t *esp_netif_config) void esp_netif_destroy(esp_netif_t *esp_netif) { if (esp_netif) { - esp_netif_remove_from_list(esp_netif); + esp_netif_remove_from_list_unsafe(esp_netif); free(esp_netif->ip_info); free(esp_netif->ip_info_old); free(esp_netif->if_key); diff --git a/components/esp_netif/lwip/esp_netif_lwip.c b/components/esp_netif/lwip/esp_netif_lwip.c index 38c40d39ae..da4557c203 100644 --- a/components/esp_netif/lwip/esp_netif_lwip.c +++ b/components/esp_netif/lwip/esp_netif_lwip.c @@ -148,14 +148,6 @@ static esp_err_t set_lwip_netif_callback(struct esp_netif_api_msg_s *msg) return ESP_OK; } -static esp_err_t remove_lwip_netif_callback(struct esp_netif_api_msg_s *msg) -{ - (void)msg; - netif_remove_ext_callback(&netif_callback); - memset(&netif_callback, 0, sizeof(netif_callback)); - return ESP_OK; -} - static void dns_clear_servers(bool keep_fallback) { u8_t numdns = 0; @@ -259,6 +251,16 @@ static inline esp_err_t esp_netif_lwip_ipc_call_fn(esp_netif_api_fn fn, esp_neti return esp_netif_lwip_ipc_call_msg(&msg); } +static inline esp_err_t esp_netif_lwip_ipc_call_get_netif(esp_netif_api_fn fn, esp_netif_t **netif, void *ctx) +{ + esp_netif_api_msg_t msg = { + .p_esp_netif = netif, + .data = ctx, + .api_fn = fn + }; + return esp_netif_lwip_ipc_call_msg(&msg); +} + static inline esp_err_t esp_netif_lwip_ipc_no_args(esp_netif_api_fn fn) { esp_netif_api_msg_t msg = { @@ -502,6 +504,10 @@ static void tcpip_init_done(void *arg) esp_err_t esp_netif_init(void) { + if (esp_netif_objects_init() != ESP_OK) { + ESP_LOGE(TAG, "esp_netif_objects_init() failed"); + return ESP_FAIL; + } if (!sys_thread_tcpip(LWIP_CORE_IS_TCPIP_INITIALIZED)) { #if CONFIG_LWIP_HOOK_TCP_ISN_DEFAULT uint8_t rand_buf[16]; @@ -559,6 +565,11 @@ esp_err_t esp_netif_init(void) esp_err_t esp_netif_deinit(void) { + /* esp_netif_deinit() is not supported (as lwIP deinit isn't suported either) + * Once it's supported, we need to de-initialize: + * - netif objects calling esp_netif_objects_deinit() + * - other lwIP specific objects (see the comment after tcpip_initialized) + */ if (sys_thread_tcpip(LWIP_CORE_IS_TCPIP_INITIALIZED)) { /* deinit of LwIP not supported: * do not deinit semaphores and states, @@ -566,6 +577,7 @@ esp_err_t esp_netif_deinit(void) * sys_sem_free(&api_sync_sem); sys_sem_free(&api_lock_sem); + netif_remove_ext_callback(); (in lwip context) */ return ESP_ERR_NOT_SUPPORTED; @@ -736,8 +748,6 @@ esp_netif_t *esp_netif_new(const esp_netif_config_t *esp_netif_config) esp_netif->lwip_netif = lwip_netif; - esp_netif_add_to_list(esp_netif); - #if ESP_DHCPS // Create DHCP server structure if (esp_netif_config->base->flags & ESP_NETIF_DHCP_SERVER) { @@ -763,9 +773,39 @@ esp_netif_t *esp_netif_new(const esp_netif_config_t *esp_netif_config) esp_netif_lwip_ipc_no_args(set_lwip_netif_callback); } + esp_netif_add_to_list(esp_netif); + return esp_netif; } +typedef struct find_if_api { + esp_netif_find_predicate_t fn; + void *ctx; +} find_if_api_t; + +static esp_err_t esp_netif_find_if_api(esp_netif_api_msg_t *msg) +{ + find_if_api_t *find_if_api = msg->data; + esp_netif_t *esp_netif = NULL; + while ((esp_netif = esp_netif_next_unsafe(esp_netif)) != NULL) { + if (find_if_api->fn(esp_netif, find_if_api->ctx)) { + *msg->p_esp_netif = esp_netif; + return ESP_OK; + } + } + return ESP_FAIL; +} + +esp_netif_t *esp_netif_find_if(esp_netif_find_predicate_t fn, void *ctx) +{ + esp_netif_t *netif = NULL; + find_if_api_t find_if_api = { .fn = fn, .ctx = ctx }; + if (esp_netif_lwip_ipc_call_get_netif(esp_netif_find_if_api, &netif, &find_if_api) == ESP_OK) { + return netif; + } + return NULL; +} + static void esp_netif_lwip_remove(esp_netif_t *esp_netif) { if (esp_netif->lwip_netif) { @@ -858,9 +898,11 @@ void esp_netif_destroy(esp_netif_t *esp_netif) { if (esp_netif) { esp_netif_remove_from_list(esp_netif); - if (esp_netif_get_nr_of_ifs() == 0) { - esp_netif_lwip_ipc_no_args(remove_lwip_netif_callback); - } + // not calling `netif_remove_ext_callback()` if number of netifs is 0 + // since it's unsafe. + // It is expected to be called globally in `esp_netif_deinit()` + // once it's supported. + // } free(esp_netif->ip_info); free(esp_netif->ip_info_old); free(esp_netif->if_key); diff --git a/components/esp_netif/lwip/esp_netif_lwip_internal.h b/components/esp_netif/lwip/esp_netif_lwip_internal.h index 5b154d4c8d..715005ba60 100644 --- a/components/esp_netif/lwip/esp_netif_lwip_internal.h +++ b/components/esp_netif/lwip/esp_netif_lwip_internal.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -22,9 +22,10 @@ typedef struct esp_netif_api_msg_s { int ret; esp_netif_api_fn api_fn; union { - esp_netif_t *esp_netif; - esp_netif_callback_fn user_fn; - }; + esp_netif_t *esp_netif; /* esp_netif as input param */ + esp_netif_t **p_esp_netif; /* esp_netif as output */ + esp_netif_callback_fn user_fn; /* user callback */ + }; /* Commonly used parameters what calling api_fn */ void *data; } esp_netif_api_msg_t; diff --git a/components/esp_netif/private_include/esp_netif_private.h b/components/esp_netif/private_include/esp_netif_private.h index b7fcfa6ef1..ba0909d917 100644 --- a/components/esp_netif/private_include/esp_netif_private.h +++ b/components/esp_netif/private_include/esp_netif_private.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -103,23 +103,12 @@ esp_err_t esp_netif_add_to_list(esp_netif_t* netif); */ esp_err_t esp_netif_remove_from_list(esp_netif_t* netif); -/** - * @brief Iterates over list of interfaces without list locking. Returns first netif if NULL given as parameter - * - * Used for bulk search loops to avoid locking and unlocking every iteration. esp_netif_list_lock and esp_netif_list_unlock - * must be used to guard the search loop - * - * @param[in] esp_netif Handle to esp-netif instance - * - * @return First netif from the list if supplied parameter is NULL, next one otherwise - */ -esp_netif_t* esp_netif_next_unsafe(esp_netif_t* netif); - /** * @brief Locking network interface list. Use only in connection with esp_netif_next_unsafe * * @return ESP_OK on success, specific mutex error if failed to lock */ + esp_err_t esp_netif_list_lock(void); /** @@ -165,4 +154,17 @@ esp_err_t esp_netif_add_ip6_address(esp_netif_t *esp_netif, const ip_event_add_i */ esp_err_t esp_netif_remove_ip6_address(esp_netif_t *esp_netif, const esp_ip6_addr_t *addr); +/** + * @brief Initialize netif objects for handling lists of interfaces one esp_netif level + * + * @return esp_err_t ESP_OK on success + */ +esp_err_t esp_netif_objects_init(void); + +/** + * @brief Deinitialize netif objects + * + */ +void esp_netif_objects_deinit(void); + #endif //_ESP_NETIF_PRIVATE_H_ diff --git a/components/esp_netif/test_apps/main/esp_netif_test.c b/components/esp_netif/test_apps/main/esp_netif_test.c index 12bef72e57..2eeece32fb 100644 --- a/components/esp_netif/test_apps/main/esp_netif_test.c +++ b/components/esp_netif/test_apps/main/esp_netif_test.c @@ -77,6 +77,44 @@ TEST(esp_netif, create_delete_multiple_netifs) } +static bool desc_matches_with(esp_netif_t *netif, void *ctx) +{ + return strcmp(ctx, esp_netif_get_desc(netif)) == 0; +} + +TEST(esp_netif, find_netifs) +{ + // Create some interfaces + const char* if_keys[] = { "if1", "if2", "if3", "if4", "if5"}; + const int nr_of_netifs = sizeof(if_keys)/sizeof(char*); + esp_netif_t *netifs[nr_of_netifs]; + + for (int i=0; i0 : Successfully found an interface with Global/Unique local IPv6 address. - * -1 : Unable to to find a valid interface with Global/Unique local IPv6 address. + * ESP_OK : Successfully found an interface with Global/Unique local IPv6 address. + * ESP_FAIL : Unable to to find a valid interface with Global/Unique local IPv6 address. */ -bool get_src_iface(char *interface, char *src_addr_str) +static esp_err_t get_src_iface(void* ctx) { + src_iface_api_t *api = ctx; esp_netif_t *netif = NULL; int ip6_addrs_count = 0; esp_ip6_addr_t ip6[LWIP_IPV6_NUM_ADDRESSES]; - esp_err_t ret = ESP_FAIL; // Get interface details and own global ipv6 address - for (int i = 0; i < esp_netif_get_nr_of_ifs(); ++i) { - netif = esp_netif_next(netif); - ret = esp_netif_get_netif_impl_name(netif, interface); + while ((netif = esp_netif_next_unsafe(netif)) != NULL) { + esp_err_t ret = esp_netif_get_netif_impl_name(netif, api->interface); if ((ESP_FAIL == ret) || (NULL == netif)) { ESP_LOGE(TAG, "No interface available"); - return false; + return ESP_FAIL; } - ESP_LOGI(TAG, "Interface: %s", interface); + ESP_LOGI(TAG, "Interface: %s", api->interface); ip6_addrs_count = esp_netif_get_all_ip6(netif, ip6); for (int j = 0; j < ip6_addrs_count; ++j) { @@ -252,13 +259,13 @@ bool get_src_iface(char *interface, char *src_addr_str) if ((ESP_IP6_ADDR_IS_GLOBAL == ipv6_type) || (ESP_IP6_ADDR_IS_UNIQUE_LOCAL == ipv6_type)) { // Break as we have the source address - sprintf(src_addr_str, IPV6STR, IPV62STR(ip6[j])); - return true; + sprintf(api->src_addr_str, IPV6STR, IPV62STR(ip6[j])); + return ESP_OK; } } } - return false; + return ESP_FAIL; } @@ -268,7 +275,8 @@ static void ping6_test_task(void *pvParameters) char interface[10]; char dst_addr_str[] = CONFIG_EXAMPLE_DST_IPV6_ADDR; - if (true == get_src_iface(interface, src_addr_str)) { + src_iface_api_t api = { .interface = interface, .src_addr_str = src_addr_str }; + if (esp_netif_tcpip_exec(get_src_iface, &api) == ESP_OK) { ESP_LOGI(TAG, "Source address: %s", src_addr_str); ESP_LOGI(TAG, "Destination address: %s", dst_addr_str); ESP_LOGI(TAG, "Interface name: %s", interface);