From 0fcdb2890be34dcde919bf42865acd0bf69a8ae6 Mon Sep 17 00:00:00 2001 From: Jiacheng Guo Date: Sat, 13 Nov 2021 03:29:07 +0800 Subject: [PATCH] esp32: support ICMPv6 RIO handling (#11412) * esp32: support ICMPv6 RIO handling * Add support for ICMPv6 RIO handling * Update ESP-IDF release to v4.4 for route hook support. * esp32: better rio message parsing * esp32: update v4.4 release branch README * fix review comments --- examples/all-clusters-app/esp32/README.md | 4 +- .../esp32/main/CMakeLists.txt | 1 + .../esp32/main/DeviceCallbacks.cpp | 7 + .../all-clusters-app/esp32/sdkconfig.defaults | 4 + .../esp32/sdkconfig_c3devkit.defaults | 3 + .../esp32/sdkconfig_m5stack.defaults | 4 + examples/bridge-app/esp32/README.md | 4 +- examples/bridge-app/esp32/main/CMakeLists.txt | 5 +- .../bridge-app/esp32/main/DeviceCallbacks.cpp | 4 + examples/bridge-app/esp32/sdkconfig.defaults | 3 + examples/ipv6only-app/esp32/README.md | 2 +- examples/lock-app/esp32/README.md | 4 +- examples/lock-app/esp32/main/CMakeLists.txt | 1 + .../lock-app/esp32/main/DeviceCallbacks.cpp | 8 + examples/lock-app/esp32/sdkconfig.defaults | 4 + examples/persistent-storage/esp32/README.md | 4 +- examples/pigweed-app/esp32/README.md | 4 +- .../esp32/route_hook/esp_route_hook.c | 206 ++++++++++++++++++ .../esp32/route_hook/esp_route_hook.h | 18 ++ .../esp32/route_hook/esp_route_table.c | 180 +++++++++++++++ .../esp32/route_hook/esp_route_table.h | 81 +++++++ .../esp32/README.md | 4 +- .../esp32/main/CMakeLists.txt | 8 +- .../esp32/main/DeviceCallbacks.cpp | 7 + .../esp32/sdkconfig.defaults | 4 + 25 files changed, 556 insertions(+), 18 deletions(-) create mode 100644 examples/platform/esp32/route_hook/esp_route_hook.c create mode 100644 examples/platform/esp32/route_hook/esp_route_hook.h create mode 100644 examples/platform/esp32/route_hook/esp_route_table.c create mode 100644 examples/platform/esp32/route_hook/esp_route_table.h diff --git a/examples/all-clusters-app/esp32/README.md b/examples/all-clusters-app/esp32/README.md index 65e4eff495c53f..0ca5d729eaa545 100644 --- a/examples/all-clusters-app/esp32/README.md +++ b/examples/all-clusters-app/esp32/README.md @@ -41,13 +41,13 @@ The VSCode devcontainer has these components pre-installed, so you can skip this step. To install these components manually, follow these steps: - Clone the Espressif ESP-IDF and checkout - [v4.3 tag](https://github.com/espressif/esp-idf/releases/v4.3) + [v4.4 tag](https://github.com/espressif/esp-idf/releases/v4.4) $ mkdir ${HOME}/tools $ cd ${HOME}/tools $ git clone https://github.com/espressif/esp-idf.git $ cd esp-idf - $ git checkout v4.3 + $ git checkout release/v4.4 $ git submodule update --init $ ./install.sh diff --git a/examples/all-clusters-app/esp32/main/CMakeLists.txt b/examples/all-clusters-app/esp32/main/CMakeLists.txt index 6728949965494a..6554df86908ddd 100644 --- a/examples/all-clusters-app/esp32/main/CMakeLists.txt +++ b/examples/all-clusters-app/esp32/main/CMakeLists.txt @@ -27,6 +27,7 @@ set(SRC_DIRS_LIST "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/zzz_generated/all-clusters-app/zap-generated" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/zzz_generated/app-common/app-common/zap-generated/attributes" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/zzz_generated/app-common/app-common/zap-generated" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32/route_hook" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32/shell_extension" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/util" diff --git a/examples/all-clusters-app/esp32/main/DeviceCallbacks.cpp b/examples/all-clusters-app/esp32/main/DeviceCallbacks.cpp index 9c6f54421bbe9b..6ec297cfb51f05 100644 --- a/examples/all-clusters-app/esp32/main/DeviceCallbacks.cpp +++ b/examples/all-clusters-app/esp32/main/DeviceCallbacks.cpp @@ -28,8 +28,11 @@ #include "Globals.h" #include "LEDWidget.h" #include "WiFiWidget.h" +#include "esp_check.h" +#include "esp_err.h" #include "esp_heap_caps.h" #include "esp_log.h" +#include "route_hook/esp_route_hook.h" #include #include #include @@ -83,6 +86,10 @@ void DeviceCallbacks::DeviceEventCallback(const ChipDeviceEvent * event, intptr_ // newly selected address. chip::app::DnssdServer::Instance().StartServer(); } + if (event->InterfaceIpAddressChanged.Type == InterfaceIpChangeType::kIpV6_Assigned) + { + ESP_ERROR_CHECK(esp_route_hook_init(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"))); + } break; } diff --git a/examples/all-clusters-app/esp32/sdkconfig.defaults b/examples/all-clusters-app/esp32/sdkconfig.defaults index 52d7e229e3337f..8cb1093fbfb606 100644 --- a/examples/all-clusters-app/esp32/sdkconfig.defaults +++ b/examples/all-clusters-app/esp32/sdkconfig.defaults @@ -44,3 +44,7 @@ CONFIG_DEVICE_PRODUCT_ID=0x4541 #enable debug shell CONFIG_ENABLE_CHIP_SHELL=y + +#enable lwIP route hooks +CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT=y +CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT=y diff --git a/examples/all-clusters-app/esp32/sdkconfig_c3devkit.defaults b/examples/all-clusters-app/esp32/sdkconfig_c3devkit.defaults index a636628c96567d..39f7907c458080 100644 --- a/examples/all-clusters-app/esp32/sdkconfig_c3devkit.defaults +++ b/examples/all-clusters-app/esp32/sdkconfig_c3devkit.defaults @@ -40,3 +40,6 @@ CONFIG_LWIP_IPV6_AUTOCONFIG=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +#enable lwIP route hooks +CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT=y +CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT=y diff --git a/examples/all-clusters-app/esp32/sdkconfig_m5stack.defaults b/examples/all-clusters-app/esp32/sdkconfig_m5stack.defaults index 8e79b8ea82df50..334cfe1b611cba 100644 --- a/examples/all-clusters-app/esp32/sdkconfig_m5stack.defaults +++ b/examples/all-clusters-app/esp32/sdkconfig_m5stack.defaults @@ -50,3 +50,7 @@ CONFIG_ESP_MAIN_TASK_STACK_SIZE=5120 #enable debug shell CONFIG_ENABLE_CHIP_SHELL=y + +#enable lwIP route hooks +CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT=y +CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT=y diff --git a/examples/bridge-app/esp32/README.md b/examples/bridge-app/esp32/README.md index add83d3346f770..6b83621d791d49 100644 --- a/examples/bridge-app/esp32/README.md +++ b/examples/bridge-app/esp32/README.md @@ -84,13 +84,13 @@ The VSCode devcontainer has these components pre-installed, so you can skip this step. To install these components manually, follow these steps: - Clone the Espressif ESP-IDF and checkout - [v4.3 tag](https://github.com/espressif/esp-idf/releases/v4.3) + [v4.4 tag](https://github.com/espressif/esp-idf/releases/v4.4) $ mkdir ${HOME}/tools $ cd ${HOME}/tools $ git clone https://github.com/espressif/esp-idf.git $ cd esp-idf - $ git checkout v4.3 + $ git checkout release/v4.4 $ git submodule update --init $ ./install.sh diff --git a/examples/bridge-app/esp32/main/CMakeLists.txt b/examples/bridge-app/esp32/main/CMakeLists.txt index 9d524d5d131721..f718d005201fb8 100644 --- a/examples/bridge-app/esp32/main/CMakeLists.txt +++ b/examples/bridge-app/esp32/main/CMakeLists.txt @@ -33,13 +33,14 @@ idf_component_register(PRIV_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/thread_network_diagnostics_server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/wifi_network_diagnostics_server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/software_diagnostics_server" - "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/general_diagnostics_server" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/general_diagnostics_server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/descriptor" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/network-commissioning" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/on-off-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/operational-credentials-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/general-commissioning-server" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32/route_hook" PRIV_REQUIRES chip QRCode bt) -set_property(TARGET ${COMPONENT_LIB} PROPERTY CXX_STANDARD 14) +set_property(TARGET ${COMPONENT_LIB} PROPERTY CXX_STANDARD 14) target_compile_options(${COMPONENT_LIB} PRIVATE "-DLWIP_IPV6_SCOPES=0" "-DCHIP_HAVE_CONFIG_H") diff --git a/examples/bridge-app/esp32/main/DeviceCallbacks.cpp b/examples/bridge-app/esp32/main/DeviceCallbacks.cpp index 48c3863feec7a3..a0491fc2df7af5 100644 --- a/examples/bridge-app/esp32/main/DeviceCallbacks.cpp +++ b/examples/bridge-app/esp32/main/DeviceCallbacks.cpp @@ -17,8 +17,11 @@ */ #include "DeviceCallbacks.h" +#include "esp_check.h" +#include "esp_err.h" #include "esp_heap_caps.h" #include "esp_log.h" +#include "route_hook/esp_route_hook.h" #include #include #include @@ -81,6 +84,7 @@ void DeviceCallbacks::OnInternetConnectivityChange(const ChipDeviceEvent * event { ESP_LOGI(TAG, "IPv6 Server ready..."); chip::app::DnssdServer::Instance().StartServer(); + ESP_ERROR_CHECK(esp_route_hook_init(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"))); } else if (event->InternetConnectivityChange.IPv6 == kConnectivity_Lost) { diff --git a/examples/bridge-app/esp32/sdkconfig.defaults b/examples/bridge-app/esp32/sdkconfig.defaults index 956dcfe0eecedd..6a6589e1040577 100644 --- a/examples/bridge-app/esp32/sdkconfig.defaults +++ b/examples/bridge-app/esp32/sdkconfig.defaults @@ -36,3 +36,6 @@ CONFIG_LWIP_IPV6_AUTOCONFIG=y # Use a custom partition table CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" + +CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT=y +CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT=y diff --git a/examples/ipv6only-app/esp32/README.md b/examples/ipv6only-app/esp32/README.md index 209db56e5a000d..e53262f096adcb 100644 --- a/examples/ipv6only-app/esp32/README.md +++ b/examples/ipv6only-app/esp32/README.md @@ -26,7 +26,7 @@ step. To install these components manually, follow these steps: $ cd ${HOME}/tools $ git clone https://github.com/espressif/esp-idf.git $ cd esp-idf - $ git checkout release/v4.3 + $ git checkout release/v4.4 $ git submodule update --init $ ./install.sh diff --git a/examples/lock-app/esp32/README.md b/examples/lock-app/esp32/README.md index ea9b03d7b1ee14..8b3daffbac7009 100644 --- a/examples/lock-app/esp32/README.md +++ b/examples/lock-app/esp32/README.md @@ -24,13 +24,13 @@ The VSCode devcontainer has these components pre-installed, so you can skip this step. To install these components manually, follow these steps: - Clone the Espressif ESP-IDF and checkout - [v4.3 tag](https://github.com/espressif/esp-idf/releases/v4.3) + [v4.4 tag](https://github.com/espressif/esp-idf/releases/v4.4) $ mkdir ${HOME}/tools $ cd ${HOME}/tools $ git clone https://github.com/espressif/esp-idf.git $ cd esp-idf - $ git checkout v4.3 + $ git checkout release/v4.4 $ git submodule update --init $ ./install.sh diff --git a/examples/lock-app/esp32/main/CMakeLists.txt b/examples/lock-app/esp32/main/CMakeLists.txt index ccc299805c384d..d4454e3f9ff9c5 100644 --- a/examples/lock-app/esp32/main/CMakeLists.txt +++ b/examples/lock-app/esp32/main/CMakeLists.txt @@ -127,6 +127,7 @@ idf_component_register(PRIV_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/zzz_generated/app-common/app-common/zap-generated/attributes" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/zzz_generated/app-common/app-common/zap-generated" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/zzz_generated/lock-app/zap-generated" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32/route_hook" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32/shell_extension" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/util" diff --git a/examples/lock-app/esp32/main/DeviceCallbacks.cpp b/examples/lock-app/esp32/main/DeviceCallbacks.cpp index 2804dea38a81af..00fe2a1d4928df 100644 --- a/examples/lock-app/esp32/main/DeviceCallbacks.cpp +++ b/examples/lock-app/esp32/main/DeviceCallbacks.cpp @@ -27,8 +27,11 @@ #include "AppConfig.h" #include "BoltLockManager.h" +#include "esp_check.h" +#include "esp_err.h" #include "esp_heap_caps.h" #include "esp_log.h" +#include "route_hook/esp_route_hook.h" #include #include #include @@ -63,11 +66,16 @@ void DeviceCallbacks::DeviceEventCallback(const ChipDeviceEvent * event, intptr_ // newly selected address. chip::app::DnssdServer::Instance().StartServer(); } + if (event->InterfaceIpAddressChanged.Type == InterfaceIpChangeType::kIpV6_Assigned) + { + ESP_ERROR_CHECK(esp_route_hook_init(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"))); + } break; } ESP_LOGI(TAG, "Current free heap: %d\n", heap_caps_get_free_size(MALLOC_CAP_8BIT)); } + void DeviceCallbacks::PostAttributeChangeCallback(EndpointId endpointId, ClusterId clusterId, AttributeId attributeId, uint8_t mask, uint8_t type, uint16_t size, uint8_t * value) { diff --git a/examples/lock-app/esp32/sdkconfig.defaults b/examples/lock-app/esp32/sdkconfig.defaults index e655288014f86d..0ac0ec53eed33f 100644 --- a/examples/lock-app/esp32/sdkconfig.defaults +++ b/examples/lock-app/esp32/sdkconfig.defaults @@ -36,3 +36,7 @@ CONFIG_LWIP_IPV6_AUTOCONFIG=y # Use a custom partition table CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" + +#enable lwIP route hooks +CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT=y +CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT=y diff --git a/examples/persistent-storage/esp32/README.md b/examples/persistent-storage/esp32/README.md index dc318548e49928..86525f46512a5a 100644 --- a/examples/persistent-storage/esp32/README.md +++ b/examples/persistent-storage/esp32/README.md @@ -43,13 +43,13 @@ The VSCode devcontainer has these components pre-installed, so you can skip this step. To install these components manually, follow these steps: - Clone the Espressif ESP-IDF and checkout - [v4.3 tag](https://github.com/espressif/esp-idf/releases/v4.3) + [v4.4 tag](https://github.com/espressif/esp-idf/releases/v4.4) $ mkdir ${HOME}/tools $ cd ${HOME}/tools $ git clone https://github.com/espressif/esp-idf.git $ cd esp-idf - $ git checkout v4.3 + $ git checkout release/v4.4 $ git submodule update --init $ ./install.sh diff --git a/examples/pigweed-app/esp32/README.md b/examples/pigweed-app/esp32/README.md index 4a2f54e25acad2..f9d8beea77f5f0 100644 --- a/examples/pigweed-app/esp32/README.md +++ b/examples/pigweed-app/esp32/README.md @@ -36,13 +36,13 @@ The VSCode devcontainer has these components pre-installed, so you can skip this step. To install these components manually, follow these steps: - Clone the Espressif ESP-IDF and checkout - [v4.3 tag](https://github.com/espressif/esp-idf/releases/v4.3) + [v4.4 tag](https://github.com/espressif/esp-idf/releases/v4.4) $ mkdir ${HOME}/tools $ cd ${HOME}/tools $ git clone https://github.com/espressif/esp-idf.git $ cd esp-idf - $ git checkout v4.3 + $ git checkout release/v4.4 $ git submodule update --init $ ./install.sh diff --git a/examples/platform/esp32/route_hook/esp_route_hook.c b/examples/platform/esp32/route_hook/esp_route_hook.c new file mode 100644 index 00000000000000..201619f301a574 --- /dev/null +++ b/examples/platform/esp32/route_hook/esp_route_hook.c @@ -0,0 +1,206 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_route_hook.h" + +#include +#include + +#include "esp_check.h" +#include "esp_err.h" +#include "esp_netif.h" +#include "esp_route_table.h" + +#include "lwip/icmp6.h" +#include "lwip/mld6.h" +#include "lwip/netif.h" +#include "lwip/prot/icmp6.h" +#include "lwip/prot/ip6.h" +#include "lwip/prot/nd6.h" +#include "lwip/raw.h" + +#define HOPLIM_MAX 255 +#define PIO_FLAG_ON_LINK (1 << 7) +#define PIO_FLAG_AUTO_CONFIG (1 << 6) + +#define TAG "ROUTE_HOOK" + +typedef struct esp_route_hook_t +{ + struct netif * netif; + struct raw_pcb * pcb; + struct esp_route_hook_t * next; +} esp_route_hook_t; + +PACK_STRUCT_BEGIN +struct rio_header_t +{ + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t length); + PACK_STRUCT_FLD_8(u8_t prefix_length); + PACK_STRUCT_FLD_8(u8_t preference); + PACK_STRUCT_FIELD(u32_t route_lifetime); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END + +typedef struct rio_header_t rio_header_t; + +static esp_route_hook_t * s_hooks; + +static bool is_self_address(struct netif * netif, const ip6_addr_t * addr) +{ + for (size_t i = 0; i < LWIP_ARRAYSIZE(netif->ip6_addr); i++) + { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + memcmp(addr->addr, netif_ip6_addr(netif, i)->addr, sizeof(addr->addr)) == 0) + { + return true; + } + } + return false; +} + +static void ra_recv_handler(struct netif * netif, const uint8_t * icmp_payload, uint16_t payload_len, const ip6_addr_t * src_addr) +{ + if (payload_len < sizeof(struct ra_header)) + { + return; + } + icmp_payload += sizeof(struct ra_header); + payload_len -= sizeof(struct ra_header); + + while (payload_len >= 2) + { + uint8_t opt_type = icmp_payload[0]; + uint8_t opt_len = icmp_payload[1] << 3; + + if (opt_type == ND6_OPTION_TYPE_ROUTE_INFO && opt_len >= sizeof(rio_header_t) && !is_self_address(netif, src_addr) && + payload_len >= opt_len) + { + rio_header_t rio_header; + memcpy(&rio_header, icmp_payload, sizeof(rio_header)); + + // skip if prefix is longer than IPv6 address. + if (rio_header.prefix_length > 128) + { + break; + } + uint8_t prefix_len_bytes = (rio_header.prefix_length + 7) / 8; + int8_t preference = -2 * ((rio_header.preference >> 4) & 1) + (((rio_header.preference) >> 3) & 1); + const uint8_t * rio_data = &icmp_payload[sizeof(rio_header_t)]; + uint8_t rio_data_len = opt_len - sizeof(rio_header_t); + + ESP_LOGI(TAG, "Received RIO"); + for (; rio_data_len >= prefix_len_bytes; rio_data_len -= prefix_len_bytes, rio_data += prefix_len_bytes) + { + ip6_addr_t prefix; + esp_route_entry_t route; + + memset(&prefix, 0, sizeof(prefix)); + memcpy(&prefix.addr, rio_data, prefix_len_bytes); + route.netif = netif; + route.gateway = *src_addr; + route.prefix_length = rio_header.prefix_length; + route.prefix = prefix; + route.preference = preference; + route.lifetime_seconds = lwip_ntohl(rio_header.route_lifetime); + ESP_LOGI(TAG, "prefix %s lifetime %u\n", ip6addr_ntoa(&prefix), route.lifetime_seconds); + if (esp_route_table_add_route_entry(&route) == NULL) + { + ESP_LOGI(TAG, "Failed to add route table entry\n"); + } + } + } + icmp_payload += opt_len; + payload_len -= opt_len; + } +} + +static uint8_t icmp6_raw_recv_handler(void * arg, struct raw_pcb * pcb, struct pbuf * p, const ip_addr_t * addr) +{ + uint8_t * icmp_payload = NULL; + uint16_t icmp_payload_len; + struct ip6_hdr * ip6_header = (struct ip6_hdr *) p->payload; + struct icmp6_hdr * icmp6_header; + ip6_addr_t src; + ip6_addr_t dest; + esp_route_hook_t * hook = (esp_route_hook_t *) arg; + + memcpy(src.addr, ip6_header->src.addr, sizeof(src.addr)); + memcpy(dest.addr, ip6_header->dest.addr, sizeof(dest.addr)); +#if LWIP_IPV6_SCOPES + src.zone = 0; +#endif + + if (p->tot_len != p->len) + { + ESP_LOGW(TAG, "Ignore segmented ICMP packet"); + return 0; + } + if (p->tot_len <= sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) + { + ESP_LOGW(TAG, "Ignore invalid ICMP packet"); + return 0; + } + if (!ip6_addr_islinklocal(&dest) && !ip6_addr_isallnodes_linklocal(&dest) && !ip6_addr_isallrouters_linklocal(&dest)) + { + return 0; + } + + icmp_payload_len = p->tot_len - sizeof(struct ip6_hdr); + icmp_payload = p->payload + sizeof(struct ip6_hdr); + + icmp6_header = (struct icmp6_hdr *) icmp_payload; + if (icmp6_header->type == ICMP6_TYPE_RA) + { + ra_recv_handler(hook->netif, icmp_payload, icmp_payload_len, &src); + } + return 0; +} + +esp_err_t esp_route_hook_init(esp_netif_t * netif) +{ + struct netif * lwip_netif; + ip_addr_t router_group = IPADDR6_INIT_HOST(0xFF020000, 0, 0, 0x02); + esp_route_hook_t * hook = NULL; + esp_err_t ret = ESP_OK; + + ESP_RETURN_ON_FALSE(netif != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid network interface"); + lwip_netif = netif_get_by_index(esp_netif_get_netif_impl_index(netif)); + ESP_RETURN_ON_FALSE(lwip_netif != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid network interface"); + + for (esp_route_hook_t * iter = s_hooks; iter != NULL; iter++) + { + if (iter->netif == lwip_netif) + { + ESP_LOGI(TAG, "Hook already installed on netif, skip..."); + return ESP_OK; + } + } + + hook = (esp_route_hook_t *) malloc(sizeof(esp_route_hook_t)); + ESP_RETURN_ON_FALSE(hook != NULL, ESP_ERR_NO_MEM, TAG, "Cannot allocate hook"); + + ESP_GOTO_ON_FALSE(mld6_joingroup_netif(lwip_netif, ip_2_ip6(&router_group)) == ESP_OK, ESP_FAIL, exit, TAG, + "Failed to join multicast group"); + hook->netif = lwip_netif; + hook->pcb = raw_new_ip_type(IPADDR_TYPE_V6, IP6_NEXTH_ICMP6); + hook->pcb->flags |= RAW_FLAGS_MULTICAST_LOOP; + hook->pcb->chksum_reqd = 1; + // The ICMPv6 header checksum offset + hook->pcb->chksum_offset = 2; + raw_bind_netif(hook->pcb, lwip_netif); + raw_recv(hook->pcb, icmp6_raw_recv_handler, hook); + hook->next = s_hooks; + s_hooks = hook; + +exit: + if (ret != ESP_OK && hook != NULL) + { + free(hook); + } + return ret; +} diff --git a/examples/platform/esp32/route_hook/esp_route_hook.h b/examples/platform/esp32/route_hook/esp_route_hook.h new file mode 100644 index 00000000000000..9f62fb9fa4743e --- /dev/null +++ b/examples/platform/esp32/route_hook/esp_route_hook.h @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_err.h" +#include "esp_netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +esp_err_t esp_route_hook_init(esp_netif_t * netif); + +#ifdef __cplusplus +} +#endif diff --git a/examples/platform/esp32/route_hook/esp_route_table.c b/examples/platform/esp32/route_hook/esp_route_table.c new file mode 100644 index 00000000000000..f7395306566e0e --- /dev/null +++ b/examples/platform/esp32/route_hook/esp_route_table.c @@ -0,0 +1,180 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_route_table.h" + +#include + +#include "esp_err.h" +#include "esp_log.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" +#include "lwip/timeouts.h" + +#define MAX_RIO_ROUTE 20 + +#define TAG "ROUTE_HOOK" + +static esp_route_entry_t s_route_entries[MAX_RIO_ROUTE]; + +static esp_route_entry_t * find_route_entry(const esp_route_entry_t * route_entry) +{ + for (size_t i = 0; i < LWIP_ARRAYSIZE(s_route_entries); i++) + { + if (s_route_entries[i].netif == NULL) + { + break; + } + if (s_route_entries[i].netif == route_entry->netif && s_route_entries[i].prefix_length == route_entry->prefix_length && + memcmp(s_route_entries[i].gateway.addr, route_entry->gateway.addr, sizeof(route_entry->gateway.addr)) == 0 && + memcmp(s_route_entries[i].prefix.addr, route_entry->prefix.addr, route_entry->prefix_length / 8) == 0) + { + return &s_route_entries[i]; + } + } + return NULL; +} + +static esp_route_entry_t * find_empty_route_entry(void) +{ + for (size_t i = 0; i < LWIP_ARRAYSIZE(s_route_entries); i++) + { + if (s_route_entries[i].netif == NULL) + { + return &s_route_entries[i]; + } + } + return NULL; +} + +static void route_timeout_handler(void * arg) +{ + esp_route_entry_t * route = (esp_route_entry_t *) arg; + + esp_route_table_remove_route_entry(route); +} + +esp_route_entry_t * esp_route_table_add_route_entry(const esp_route_entry_t * route_entry) +{ + if (route_entry == NULL) + { + return NULL; + } + + esp_route_entry_t * entry = find_route_entry(route_entry); + + if (entry == NULL) + { + entry = find_empty_route_entry(); + if (entry == NULL) + { + return NULL; + } + entry->netif = route_entry->netif; + entry->gateway = route_entry->gateway; + ip6_addr_assign_zone(&entry->gateway, IP6_UNICAST, entry->netif); + entry->prefix = route_entry->prefix; + entry->prefix_length = route_entry->prefix_length; + } + else + { + sys_untimeout(route_timeout_handler, entry); + } + entry->preference = route_entry->preference; + entry->lifetime_seconds = route_entry->lifetime_seconds; + sys_timeout(entry->lifetime_seconds * 1000, route_timeout_handler, entry); + return entry; +} + +esp_err_t esp_route_table_remove_route_entry(esp_route_entry_t * route_entry) +{ + if (route_entry < &s_route_entries[0] || route_entry > &s_route_entries[LWIP_ARRAYSIZE(s_route_entries)]) + { + return ESP_ERR_INVALID_ARG; + } + route_entry->netif = NULL; + for (esp_route_entry_t * moved = route_entry; moved < &s_route_entries[LWIP_ARRAYSIZE(s_route_entries) - 1]; moved++) + { + *moved = *(moved + 1); + if (moved->netif == NULL) + { + break; + } + } + return ESP_OK; +} + +static inline bool is_better_route(const esp_route_entry_t * lhs, const esp_route_entry_t * rhs) +{ + if (rhs == NULL) + { + return true; + } + if (lhs == NULL) + { + return false; + } + return (lhs->prefix_length > rhs->prefix_length) || + (lhs->prefix_length == rhs->prefix_length && lhs->preference > rhs->preference); +} + +static inline bool route_match(const esp_route_entry_t * route, const ip6_addr_t * dest) +{ + return memcmp(dest, route->prefix.addr, route->prefix_length / 8) == 0; +} + +struct netif * lwip_hook_ip6_route(const ip6_addr_t * src, const ip6_addr_t * dest) +{ + esp_route_entry_t * route = NULL; + + for (size_t i = 0; i < LWIP_ARRAYSIZE(s_route_entries); i++) + { + if (s_route_entries[i].netif == NULL) + { + break; + } + if (route_match(&s_route_entries[i], dest) && is_better_route(&s_route_entries[i], route)) + { + route = &s_route_entries[i]; + } + } + + if (route) + { + return route->netif; + } + else + { + return NULL; + } +} + +const ip6_addr_t * lwip_hook_nd6_get_gw(struct netif * netif, const ip6_addr_t * dest) +{ + esp_route_entry_t * route = NULL; + + for (size_t i = 0; i < LWIP_ARRAYSIZE(s_route_entries); i++) + { + if (s_route_entries[i].netif == NULL) + { + break; + } + if (s_route_entries[i].netif == netif && route_match(&s_route_entries[i], dest) && + is_better_route(&s_route_entries[i], route)) + { + route = &s_route_entries[i]; + } + } + + if (route) + { + return &route->gateway; + } + else + { + return NULL; + } +} diff --git a/examples/platform/esp32/route_hook/esp_route_table.h b/examples/platform/esp32/route_hook/esp_route_table.h new file mode 100644 index 00000000000000..e69fee1ad293bf --- /dev/null +++ b/examples/platform/esp32/route_hook/esp_route_table.h @@ -0,0 +1,81 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_err.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Route table entry + * + */ +typedef struct +{ + ip6_addr_t prefix; + uint8_t prefix_length; + ip6_addr_t gateway; + int8_t preference; + uint32_t lifetime_seconds; + struct netif * netif; +} esp_route_entry_t; + +/** + * @brief Adds an entry to the route table + * + * @param[in] route_entry The route entry to be added + * + * @return + * - The pointer to the added route entry on success + * - NULL on failure + * + */ +esp_route_entry_t * esp_route_table_add_route_entry(const esp_route_entry_t * route_entry); + +/** + * @brief Removes an entry from the route table + * + * @param[in] route_entry The route entry to be removed + * + * @return + * - ESP_OK + * - ESP_ERR_INVALID_ARG The provided route_entry is not in the route table. + * + */ +esp_err_t esp_route_table_remove_route_entry(esp_route_entry_t * route_entry); + +/** + * @brief The lwIP ip6 route hook, called by the lwIP function ip6_route when sending packets. + * + * @param[in] src The source address + * @param[in] dest The destination address + * + * @return + * - The target interface when route found + * - NULL when route not found + * + */ +struct netif * lwip_hook_ip6_route(const ip6_addr_t * src, const ip6_addr_t * dest); + +/** + * @brief The lwIP gateway hook, called by the lwIP when deciding next hop. + * + * @param[in] netif The output network interface + * @param[in] dest The destination address + * + * @return + * - The gateway address when route found + * - NULL when route not found + * + */ +const ip6_addr_t * lwip_hook_nd6_get_gw(struct netif * netif, const ip6_addr_t * dest); + +#ifdef __cplusplus +} +#endif diff --git a/examples/temperature-measurement-app/esp32/README.md b/examples/temperature-measurement-app/esp32/README.md index fe49ebced3167d..e1f74180bb3d51 100644 --- a/examples/temperature-measurement-app/esp32/README.md +++ b/examples/temperature-measurement-app/esp32/README.md @@ -24,13 +24,13 @@ The VSCode devcontainer has these components pre-installed, so you can skip this step. To install these components manually, follow these steps: - Clone the Espressif ESP-IDF and checkout - [v4.3 tag](https://github.com/espressif/esp-idf/releases/v4.3) + [v4.4 tag](https://github.com/espressif/esp-idf/releases/v4.4) $ mkdir ${HOME}/tools $ cd ${HOME}/tools $ git clone https://github.com/espressif/esp-idf.git $ cd esp-idf - $ git checkout v4.3 + $ git checkout release/v4.4 $ git submodule update --init $ ./install.sh diff --git a/examples/temperature-measurement-app/esp32/main/CMakeLists.txt b/examples/temperature-measurement-app/esp32/main/CMakeLists.txt index e6f66af4bbbe28..b41bc41e3c33dc 100644 --- a/examples/temperature-measurement-app/esp32/main/CMakeLists.txt +++ b/examples/temperature-measurement-app/esp32/main/CMakeLists.txt @@ -16,9 +16,10 @@ # # # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) -idf_component_register(PRIV_INCLUDE_DIRS +idf_component_register(PRIV_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/zzz_generated/temperature-measurement-app/" "${CMAKE_CURRENT_LIST_DIR}/include" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32" SRC_DIRS "${CMAKE_CURRENT_LIST_DIR}" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/zzz_generated/temperature-measurement-app/zap-generated" @@ -36,11 +37,12 @@ idf_component_register(PRIV_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/thread_network_diagnostics_server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/wifi_network_diagnostics_server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/software_diagnostics_server" - "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/general_diagnostics_server" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/general_diagnostics_server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/general-commissioning-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/network-commissioning" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/operational-credentials-server" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32/route_hook" PRIV_REQUIRES chip QRCode bt) -set_property(TARGET ${COMPONENT_LIB} PROPERTY CXX_STANDARD 14) +set_property(TARGET ${COMPONENT_LIB} PROPERTY CXX_STANDARD 14) target_compile_options(${COMPONENT_LIB} PRIVATE "-DLWIP_IPV6_SCOPES=0" "-DCHIP_HAVE_CONFIG_H") diff --git a/examples/temperature-measurement-app/esp32/main/DeviceCallbacks.cpp b/examples/temperature-measurement-app/esp32/main/DeviceCallbacks.cpp index 62251e904ab541..b5ddca53ade197 100644 --- a/examples/temperature-measurement-app/esp32/main/DeviceCallbacks.cpp +++ b/examples/temperature-measurement-app/esp32/main/DeviceCallbacks.cpp @@ -24,8 +24,11 @@ **/ #include "DeviceCallbacks.h" +#include "esp_check.h" +#include "esp_err.h" #include "esp_heap_caps.h" #include "esp_log.h" +#include "route_hook/esp_route_hook.h" #include #include #include @@ -59,6 +62,10 @@ void DeviceCallbacks::DeviceEventCallback(const ChipDeviceEvent * event, intptr_ // newly selected address. chip::app::DnssdServer::Instance().StartServer(); } + if (event->InterfaceIpAddressChanged.Type == InterfaceIpChangeType::kIpV6_Assigned) + { + ESP_ERROR_CHECK(esp_route_hook_init(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"))); + } break; } diff --git a/examples/temperature-measurement-app/esp32/sdkconfig.defaults b/examples/temperature-measurement-app/esp32/sdkconfig.defaults index a24175c5b32d76..0f43fb5079c635 100644 --- a/examples/temperature-measurement-app/esp32/sdkconfig.defaults +++ b/examples/temperature-measurement-app/esp32/sdkconfig.defaults @@ -79,3 +79,7 @@ CONFIG_DEVICE_PRODUCT_ID=0x4554 # Main task needs a bit more stack than the default # default is 3584, bump this up to 4k. CONFIG_ESP_MAIN_TASK_STACK_SIZE=4096 + +#enable lwIP route hooks +CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT=y +CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT=y