/* * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "esp_openthread_netif_glue.h" #include #include #include #include "esp_check.h" #include "esp_err.h" #include "esp_event.h" #include "esp_log.h" #include "esp_netif.h" #include "esp_openthread.h" #include "esp_openthread_common_macro.h" #include "esp_openthread_lock.h" #include "esp_openthread_netif_glue_priv.h" #include "esp_openthread_platform.h" #include "esp_openthread_types.h" #include "esp_vfs_eventfd.h" #include "sdkconfig.h" #include "common/code_utils.hpp" #include "config/link_quality.h" #include "freertos/FreeRTOS.h" #include "openthread/error.h" #include "openthread/icmp6.h" #include "openthread/instance.h" #include "openthread/ip6.h" #include "openthread/link.h" #include "openthread/message.h" #include "openthread/thread.h" typedef struct { esp_netif_driver_base_t base; int event_fd; } esp_openthread_netif_glue_t; static esp_openthread_netif_glue_t s_openthread_netif_glue = { .event_fd = -1, }; ESP_EVENT_DEFINE_BASE(OPENTHREAD_EVENT); static QueueHandle_t s_packet_queue; static esp_netif_t *s_openthread_netif; static const char *netif_glue_workflow = "netif_glue"; const esp_netif_inherent_config_t g_esp_netif_inherent_openthread_config = ESP_NETIF_INHERENT_DEFAULT_OPENTHREAD(); #define NETIF_OUTPUT_SIGNAL 1 static bool is_link_local_addr(const otIp6Address *address) { return address->mFields.m32[0] == 0xfe800000 && address->mFields.m32[1] == 0; } static bool is_mesh_local_addr(const otIp6Address *address) { return memcmp(address->mFields.m8, otThreadGetMeshLocalPrefix(esp_openthread_get_instance())->m8, sizeof(otThreadGetMeshLocalPrefix(esp_openthread_get_instance())->m8)) == 0; } static bool is_addr_locator(const otIp6Address *address) { return address->mFields.m32[2] == 0xff000000 && address->mFields.m16[6] == 0xfe; } static bool is_openthread_internal_mesh_local_addr(const otIp6AddressInfo *address_info) { return is_addr_locator(address_info->mAddress) && is_mesh_local_addr(address_info->mAddress); } static esp_err_t notify_packets_pending(void) { uint64_t signal = NETIF_OUTPUT_SIGNAL; ssize_t ret = write(s_openthread_netif_glue.event_fd, &signal, sizeof(signal)); if (ret != sizeof(signal)) { ESP_LOGW(OT_PLAT_LOG_TAG, "Thread netif failed to notify eventfd"); return ESP_FAIL; } return ESP_OK; } static void process_thread_address(const otIp6AddressInfo *address_info, bool is_added, void *context) { bool is_multicast = address_info->mAddress->mFields.m8[0] == 0xff; esp_ip6_addr_t addr; if (is_openthread_internal_mesh_local_addr(address_info)) { return; } memcpy(addr.addr, address_info->mAddress->mFields.m8, sizeof(addr.addr)); if (is_added) { if (is_multicast) { if (esp_event_post(OPENTHREAD_EVENT, OPENTHREAD_EVENT_MULTICAST_GROUP_JOIN, &addr, sizeof(addr), 0) != ESP_OK) { ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to post OpenThread join multicast group event"); } } else { ip_event_add_ip6_t add_addr; add_addr.addr = addr; // if an address is not mesh local or link local, we set preferred for this address. add_addr.preferred = is_mesh_local_addr(address_info->mAddress) || is_link_local_addr(address_info->mAddress) ? 0 : 1; if (esp_event_post(OPENTHREAD_EVENT, OPENTHREAD_EVENT_GOT_IP6, &add_addr, sizeof(add_addr), 0) != ESP_OK) { ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to post OpenThread got ip6 address event"); } } } else { if (is_multicast) { if (esp_event_post(OPENTHREAD_EVENT, OPENTHREAD_EVENT_MULTICAST_GROUP_LEAVE, &addr, sizeof(addr), 0) != ESP_OK) { ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to post OpenThread leave multicast group event"); } } else { if (esp_event_post(OPENTHREAD_EVENT, OPENTHREAD_EVENT_LOST_IP6, &addr, sizeof(addr), 0) != ESP_OK) { ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to post OpenThread lost ip6 address event"); } } } } static void process_thread_receive(otMessage *message, void *context) { esp_err_t error; uint16_t length = otMessageGetLength(message); error = esp_netif_receive(s_openthread_netif_glue.base.netif, message, length, NULL); if (error != ESP_OK) { ESP_LOGW(OT_PLAT_LOG_TAG, "process_thread_receive failed: %s", esp_err_to_name(error)); } otMessageFree(message); } static esp_err_t process_thread_transmit(otInstance *instance) { otMessage *msg = NULL; esp_err_t error = ESP_OK; uint64_t event; int ret = read(s_openthread_netif_glue.event_fd, &event, sizeof(event)); assert(ret == sizeof(event)); while (xQueueReceive(s_packet_queue, &msg, 0) == pdTRUE) { if (msg) { otError ot_error = otIp6Send(esp_openthread_get_instance(), msg); if (ot_error != OT_ERROR_NONE && ot_error != OT_ERROR_DROP) { ESP_LOGW(OT_PLAT_LOG_TAG, "ThreadNetif Failed to send OpenThread IP6 message: %s", otThreadErrorToString(ot_error)); } if (ot_error == OT_ERROR_DROP) { // OpenThread will intentionally drop some multicast and ICMPv6 packets // which are not required for the Thread network. ESP_LOGD(OT_PLAT_LOG_TAG, "OpenThread stack filtered netif packet"); } if (ot_error != OT_ERROR_NONE) { break; } } } if (uxQueueMessagesWaiting(s_packet_queue) > 0) { error = notify_packets_pending(); } return error; } void esp_openthread_netif_glue_state_callback(otChangedFlags changed_flags, void *ctx) { otInstance *instance = esp_openthread_get_instance(); esp_err_t err = ESP_OK; if (s_packet_queue != NULL && (OT_CHANGED_THREAD_NETIF_STATE & changed_flags)) { if (otLinkIsEnabled(instance)) { ESP_LOGI(OT_PLAT_LOG_TAG, "netif up"); if (esp_event_post(OPENTHREAD_EVENT, OPENTHREAD_EVENT_IF_UP, NULL, 0, 0) != ESP_OK) { ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to post OpenThread if up event"); } } else { ESP_LOGI(OT_PLAT_LOG_TAG, "netif down"); if (esp_event_post(OPENTHREAD_EVENT, OPENTHREAD_EVENT_IF_DOWN, NULL, 0, 0) != ESP_OK) { ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to post OpenThread if down event"); } } } if (err != ESP_OK) { ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to configure netif state"); } } static esp_err_t openthread_netif_transmit(void *handle, void *buffer, size_t len) { esp_err_t error = ESP_OK; otError ot_error = OT_ERROR_NONE; esp_openthread_task_switching_lock_acquire(portMAX_DELAY); otMessage *message = otIp6NewMessage(esp_openthread_get_instance(), NULL); if (message == NULL) { ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to allocate OpenThread message"); ExitNow(error = ESP_ERR_NO_MEM); } ot_error = otMessageAppend(message, buffer, len); if (ot_error != OT_ERROR_NONE) { ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to copy to OpenThread message: %s", otThreadErrorToString(ot_error)); ExitNow(error = ESP_ERR_NO_MEM); } if (xQueueSend(s_packet_queue, &message, 0) != pdTRUE) { ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to send to Thread netif: packet queue full"); ExitNow(error = ESP_ERR_NO_MEM, ot_error = OT_ERROR_NO_BUFS); } VerifyOrExit(notify_packets_pending() == ESP_OK, error = ESP_FAIL); exit: if (ot_error != OT_ERROR_NONE && message != NULL) { otMessageFree(message); } esp_openthread_task_switching_lock_release(); return error; } static esp_err_t register_openthread_event_handlers(esp_netif_t *esp_netif) { ESP_RETURN_ON_ERROR( esp_event_handler_register(OPENTHREAD_EVENT, OPENTHREAD_EVENT_START, esp_netif_action_start, esp_netif), OT_PLAT_LOG_TAG, "OpenThread start event register failed"); ESP_RETURN_ON_ERROR( esp_event_handler_register(OPENTHREAD_EVENT, OPENTHREAD_EVENT_STOP, esp_netif_action_stop, esp_netif), OT_PLAT_LOG_TAG, "OpenThread stop event register failed"); ESP_RETURN_ON_ERROR( esp_event_handler_register(OPENTHREAD_EVENT, OPENTHREAD_EVENT_IF_UP, esp_netif_action_connected, esp_netif), OT_PLAT_LOG_TAG, "OpenThread interface up event register failed"); ESP_RETURN_ON_ERROR(esp_event_handler_register(OPENTHREAD_EVENT, OPENTHREAD_EVENT_IF_DOWN, esp_netif_action_disconnected, esp_netif), OT_PLAT_LOG_TAG, "OpenThread interface down event register failed"); ESP_RETURN_ON_ERROR(esp_event_handler_register(OPENTHREAD_EVENT, OPENTHREAD_EVENT_GOT_IP6, esp_netif_action_add_ip6_address, esp_netif), OT_PLAT_LOG_TAG, "OpenThread interface got ip6 event register failed"); ESP_RETURN_ON_ERROR(esp_event_handler_register(OPENTHREAD_EVENT, OPENTHREAD_EVENT_LOST_IP6, esp_netif_action_remove_ip6_address, esp_netif), OT_PLAT_LOG_TAG, "OpenThread interface remove ip6 event register failed"); ESP_RETURN_ON_ERROR(esp_event_handler_register(OPENTHREAD_EVENT, OPENTHREAD_EVENT_MULTICAST_GROUP_JOIN, esp_netif_action_join_ip6_multicast_group, esp_netif), OT_PLAT_LOG_TAG, "OpenThread interface join ip6 multicast group event register failed"); ESP_RETURN_ON_ERROR(esp_event_handler_register(OPENTHREAD_EVENT, OPENTHREAD_EVENT_MULTICAST_GROUP_LEAVE, esp_netif_action_leave_ip6_multicast_group, esp_netif), OT_PLAT_LOG_TAG, "OpenThread interface leave ip6 multicast group event register failed"); return ESP_OK; } static void unregister_openthread_event_handlers(void) { esp_event_handler_unregister(OPENTHREAD_EVENT, OPENTHREAD_EVENT_START, esp_netif_action_start); esp_event_handler_unregister(OPENTHREAD_EVENT, OPENTHREAD_EVENT_STOP, esp_netif_action_stop); esp_event_handler_unregister(OPENTHREAD_EVENT, OPENTHREAD_EVENT_IF_UP, esp_netif_action_connected); esp_event_handler_unregister(OPENTHREAD_EVENT, OPENTHREAD_EVENT_IF_DOWN, esp_netif_action_disconnected); esp_event_handler_unregister(OPENTHREAD_EVENT, OPENTHREAD_EVENT_GOT_IP6, esp_netif_action_add_ip6_address); esp_event_handler_unregister(OPENTHREAD_EVENT, OPENTHREAD_EVENT_LOST_IP6, esp_netif_action_remove_ip6_address); esp_event_handler_unregister(OPENTHREAD_EVENT, OPENTHREAD_EVENT_MULTICAST_GROUP_JOIN, esp_netif_action_join_ip6_multicast_group); esp_event_handler_unregister(OPENTHREAD_EVENT, OPENTHREAD_EVENT_MULTICAST_GROUP_LEAVE, esp_netif_action_leave_ip6_multicast_group); } static esp_err_t openthread_netif_post_attach(esp_netif_t *esp_netif, void *args) { esp_netif_driver_base_t *base = (esp_netif_driver_base_t *)args; base->netif = esp_netif; // set driver related config to esp-netif esp_netif_driver_ifconfig_t driver_ifconfig = { .handle = &s_openthread_netif_glue, .transmit = openthread_netif_transmit, .driver_free_rx_buffer = NULL}; ESP_ERROR_CHECK(esp_netif_set_driver_config(esp_netif, &driver_ifconfig)); ESP_LOGI(OT_PLAT_LOG_TAG, "OpenThread attached to netif"); esp_err_t error = register_openthread_event_handlers(esp_netif); s_openthread_netif = esp_netif; if (error == ESP_OK) { error = esp_event_post(OPENTHREAD_EVENT, OPENTHREAD_EVENT_START, NULL, 0, 0); } return error; } void *esp_openthread_netif_glue_init(const esp_openthread_platform_config_t *config) { otInstance *instance = esp_openthread_get_instance(); esp_err_t error = ESP_OK; if (instance == NULL || s_packet_queue || s_openthread_netif_glue.event_fd >= 0) { return NULL; } ESP_RETURN_ON_FALSE(otSetStateChangedCallback(instance, esp_openthread_netif_glue_state_callback, NULL) == OT_ERROR_NONE, NULL, OT_PLAT_LOG_TAG, "Failed to install netif glue state callback"); s_packet_queue = xQueueCreate(config->port_config.netif_queue_size, sizeof(otMessage *)); if (s_packet_queue == NULL) { ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to allocate Thread netif packet queue"); ExitNow(error = ESP_ERR_NO_MEM); } otIp6SetAddressCallback(instance, process_thread_address, instance); otIp6SetReceiveCallback(instance, process_thread_receive, instance); otIp6SetReceiveFilterEnabled(instance, true); otIcmp6SetEchoMode(instance, OT_ICMP6_ECHO_HANDLER_DISABLED); s_openthread_netif_glue.event_fd = eventfd(0, 0); if (s_openthread_netif_glue.event_fd < 0) { ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to create event fd for Thread netif"); ExitNow(error = ESP_FAIL); } s_openthread_netif_glue.base.post_attach = openthread_netif_post_attach; error = esp_openthread_platform_workflow_register(&esp_openthread_netif_glue_update, &esp_openthread_netif_glue_process, netif_glue_workflow); exit: if (error != ESP_OK) { return NULL; } return &s_openthread_netif_glue.base; } void esp_openthread_netif_glue_deinit(void) { otInstance *instance = esp_openthread_get_instance(); otIp6SetAddressCallback(instance, NULL, NULL); otIp6SetReceiveCallback(instance, NULL, NULL); if (s_packet_queue) { vQueueDelete(s_packet_queue); s_packet_queue = NULL; } if (s_openthread_netif_glue.event_fd >= 0) { close(s_openthread_netif_glue.event_fd); s_openthread_netif_glue.event_fd = -1; } if (esp_event_post(OPENTHREAD_EVENT, OPENTHREAD_EVENT_STOP, NULL, 0, 0) != ESP_OK) { ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to stop OpenThread netif"); } s_openthread_netif = NULL; unregister_openthread_event_handlers(); esp_openthread_platform_workflow_unregister(netif_glue_workflow); } void esp_openthread_netif_glue_update(esp_openthread_mainloop_context_t *mainloop) { if (s_openthread_netif_glue.event_fd >= 0) { FD_SET(s_openthread_netif_glue.event_fd, &mainloop->read_fds); if (s_openthread_netif_glue.event_fd > mainloop->max_fd) { mainloop->max_fd = s_openthread_netif_glue.event_fd; } } } esp_err_t esp_openthread_netif_glue_process(otInstance *instance, const esp_openthread_mainloop_context_t *context) { if (s_openthread_netif_glue.event_fd >= 0 && FD_ISSET(s_openthread_netif_glue.event_fd, &context->read_fds)) { return process_thread_transmit(instance); } return ESP_OK; } esp_netif_t *esp_openthread_get_netif(void) { return s_openthread_netif; }