From 933a2a69fec7172f45828cbbb3e3e29675fd5373 Mon Sep 17 00:00:00 2001 From: Abhik Roy Date: Sat, 23 Jul 2022 00:01:50 +0530 Subject: [PATCH] tcp_client example: support for Linux target. --- examples/protocols/.build-test-rules.yml | 2 + .../sockets/tcp_client/CMakeLists.txt | 14 +- .../protocols/sockets/tcp_client/README.md | 64 +++-- .../components/esp_stubs/CMakeLists.txt | 6 + .../esp_stubs/esp_stubs/esp_stubs.c | 45 ++++ .../esp_stubs/include/stubs/esp_event.h | 8 + .../esp_stubs/include/stubs/esp_netif.h | 11 + .../esp_stubs/include/stubs/nvs_flash.h | 8 + .../include/stubs/protocol_examples_common.h | 8 + .../sockets/tcp_client/main/CMakeLists.txt | 15 +- .../sockets/tcp_client/main/Kconfig.projbuild | 20 ++ .../sockets/tcp_client/main/tcp_client_main.c | 27 ++ .../main/{tcp_client.c => tcp_client_v4.c} | 73 ++--- .../sockets/tcp_client/main/tcp_client_v6.c | 255 ++++++++++++++++++ .../sockets/tcp_client/sdkconfig.defaults | 4 + 15 files changed, 475 insertions(+), 85 deletions(-) create mode 100644 examples/protocols/sockets/tcp_client/components/esp_stubs/CMakeLists.txt create mode 100644 examples/protocols/sockets/tcp_client/components/esp_stubs/esp_stubs/esp_stubs.c create mode 100644 examples/protocols/sockets/tcp_client/components/esp_stubs/include/stubs/esp_event.h create mode 100644 examples/protocols/sockets/tcp_client/components/esp_stubs/include/stubs/esp_netif.h create mode 100644 examples/protocols/sockets/tcp_client/components/esp_stubs/include/stubs/nvs_flash.h create mode 100644 examples/protocols/sockets/tcp_client/components/esp_stubs/include/stubs/protocol_examples_common.h create mode 100644 examples/protocols/sockets/tcp_client/main/tcp_client_main.c rename examples/protocols/sockets/tcp_client/main/{tcp_client.c => tcp_client_v4.c} (56%) create mode 100644 examples/protocols/sockets/tcp_client/main/tcp_client_v6.c create mode 100644 examples/protocols/sockets/tcp_client/sdkconfig.defaults diff --git a/examples/protocols/.build-test-rules.yml b/examples/protocols/.build-test-rules.yml index 5348b49f01d..47315cab0e6 100644 --- a/examples/protocols/.build-test-rules.yml +++ b/examples/protocols/.build-test-rules.yml @@ -229,6 +229,8 @@ examples/protocols/sockets/tcp_client: - if: IDF_TARGET != "esp32" temporary: true reason: lack of runners + enable: + - if: INCLUDE_DEFAULT == 1 or IDF_TARGET == "linux" examples/protocols/sockets/tcp_client_multi_net: disable: diff --git a/examples/protocols/sockets/tcp_client/CMakeLists.txt b/examples/protocols/sockets/tcp_client/CMakeLists.txt index 7015df7e4b7..0d19ff56311 100644 --- a/examples/protocols/sockets/tcp_client/CMakeLists.txt +++ b/examples/protocols/sockets/tcp_client/CMakeLists.txt @@ -1,10 +1,14 @@ -# The following five lines of boilerplate have to be in your project's +# The following lines of boilerplate have to be in your project's # CMakeLists in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.16) -# (Not part of the boilerplate) -# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection. -set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common) +if(${IDF_TARGET} STREQUAL "linux") + list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/freertos/") + set(COMPONENTS main) + include($ENV{IDF_PATH}/tools/cmake/project.cmake) +else() + set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common) + include($ENV{IDF_PATH}/tools/cmake/project.cmake) +endif() -include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(tcp_client) diff --git a/examples/protocols/sockets/tcp_client/README.md b/examples/protocols/sockets/tcp_client/README.md index 0ac216d4a1c..0d90afe16c2 100644 --- a/examples/protocols/sockets/tcp_client/README.md +++ b/examples/protocols/sockets/tcp_client/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S2 | ESP32-S3 | Linux | +| ----------------- | ----- | -------- | -------- | -------- | -------- | ----- | # TCP Client example @@ -8,13 +8,38 @@ The application creates a TCP socket and tries to connect to the server with predefined IP address and port number. When a connection is successfully established, the application sends message and waits for the answer. After the server's reply, application prints received reply as ASCII text, waits for 2 seconds and sends another message. -## How to use example +## Configure the project +This example can be configured to run on ESP32 and Linux target to communicate over IPv4 and IPv6. + +``` +idf.py menuconfig +``` + +Set following parameters under ```Example Configuration``` Options: + +* Set `IP version` of example to be IPV4 or IPV6. + +* Set `IPV4 Address` in case your chose IP version IPV4 above. + +* Set `IPV6 Address` in case your chose IP version IPV6 above. + * For IPv6 there's an additional option for ```Interface selection```. + * Enter the name of the interface to explicitely establish communication over a specific interface. + * On selecting ```Auto``` the example will find the first interface with an IPv6 address and use it. + +* Set `Port` number that represents remote port the example will connect to. + +Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details. + + +Note: please replace `192.168.0.167 3333` with desired IPV4/IPV6 address (displayed in monitor console) and port number in the following command. + + +## How to use example: In order to create TCP server that communicates with TCP Client example, choose one of the following options. There are many host-side tools which can be used to interact with the UDP/TCP server/client. One command line tool is [netcat](http://netcat.sourceforge.net) which can send and receive many kinds of packets. -Note: please replace `192.168.0.167 3333` with desired IPV4/IPV6 address (displayed in monitor console) and port number in the following command. In addition to those tools, simple Python scripts can be found under sockets/scripts directory. Every script is designed to interact with one of the examples. @@ -24,35 +49,24 @@ nc -l 192.168.0.167 3333 ``` ### Python scripts -Script example_test.py could be used as a counter part to the tcp-client project, ip protocol name (IPv4 or IPv6) shall be stated as argument. Example: +Script example_test.py could be used as a counter part to the tcp-client project, ip protocol name (IPv4 or IPv6) shall be stated as argument. +Note that this script is used in automated tests, as well, so the IDF test framework packages need to be imported. +Please run the following commands to configure the terminal to execute the script. ``` -python example_test.py IPv4 +export PYTHONPATH="$IDF_PATH/tools:$IDF_PATH/tools/ci/python_packages" +python -m pip install -r $IDF_PATH/tools/ci/python_packages/ttfw_idf/requirements.txt ``` -Note that this script is used in automated tests, as well, so the IDF test framework packages need to be imported; -please add `$IDF_PATH/tools/ci/python_packages` to `PYTHONPATH`. - -## Hardware Required - -This example can be run on any commonly available ESP32 development board. - -## Configure the project +Example: ``` -idf.py menuconfig +python example_test.py IPv4 ``` +## Hardware Required -Set following parameters under Example Configuration Options: - -* Set `IP version` of example to be IPV4 or IPV6. - -* Set `IPV4 Address` in case your chose IP version IPV4 above. - -* Set `IPV6 Address` in case your chose IP version IPV6 above. - -* Set `Port` number that represents remote port the example will connect to. +This example can be run on any commonly available ESP32 development board. +This example can also run on any Linux environment. -Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details. ## Build and Flash diff --git a/examples/protocols/sockets/tcp_client/components/esp_stubs/CMakeLists.txt b/examples/protocols/sockets/tcp_client/components/esp_stubs/CMakeLists.txt new file mode 100644 index 00000000000..74f75ec2a8a --- /dev/null +++ b/examples/protocols/sockets/tcp_client/components/esp_stubs/CMakeLists.txt @@ -0,0 +1,6 @@ +if(${IDF_TARGET} STREQUAL "linux") + idf_component_register(SRCS + esp_stubs/esp_stubs.c + INCLUDE_DIRS . include/stubs + REQUIRES main) +endif() diff --git a/examples/protocols/sockets/tcp_client/components/esp_stubs/esp_stubs/esp_stubs.c b/examples/protocols/sockets/tcp_client/components/esp_stubs/esp_stubs/esp_stubs.c new file mode 100644 index 00000000000..42ef914aaff --- /dev/null +++ b/examples/protocols/sockets/tcp_client/components/esp_stubs/esp_stubs/esp_stubs.c @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include +#include "esp_err.h" +#include "esp_log.h" + +extern void app_main(void); + +void _esp_error_check_failed(esp_err_t rc, const char *file, int line, const char *function, const char *expression) +{ + ESP_LOGE("ESP_ERROR_CHECK", "Failed with esp_err_t: 0x%x", rc); + ESP_LOGE("ESP_ERROR_CHECK", "Expression: %s", expression); + ESP_LOGE("ESP_ERROR_CHECK", "Functions: %s %s(%d)", function, file, line); + abort(); +} + +esp_err_t esp_event_loop_create_default(void) +{ + return ESP_OK; +} + +esp_err_t esp_netif_init(void) +{ + return ESP_OK; +} + +esp_err_t example_connect(void) +{ + return ESP_OK; +} + +esp_err_t nvs_flash_init(void) +{ + return ESP_OK; +} + +int main() +{ + app_main(); + + return 0; +} diff --git a/examples/protocols/sockets/tcp_client/components/esp_stubs/include/stubs/esp_event.h b/examples/protocols/sockets/tcp_client/components/esp_stubs/include/stubs/esp_event.h new file mode 100644 index 00000000000..79887049cfb --- /dev/null +++ b/examples/protocols/sockets/tcp_client/components/esp_stubs/include/stubs/esp_event.h @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include "esp_err.h" + +esp_err_t esp_event_loop_create_default(void); diff --git a/examples/protocols/sockets/tcp_client/components/esp_stubs/include/stubs/esp_netif.h b/examples/protocols/sockets/tcp_client/components/esp_stubs/include/stubs/esp_netif.h new file mode 100644 index 00000000000..860d7a620b1 --- /dev/null +++ b/examples/protocols/sockets/tcp_client/components/esp_stubs/include/stubs/esp_netif.h @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include +#include +#include +#include "esp_err.h" + +esp_err_t esp_netif_init(void); diff --git a/examples/protocols/sockets/tcp_client/components/esp_stubs/include/stubs/nvs_flash.h b/examples/protocols/sockets/tcp_client/components/esp_stubs/include/stubs/nvs_flash.h new file mode 100644 index 00000000000..9d138a838a5 --- /dev/null +++ b/examples/protocols/sockets/tcp_client/components/esp_stubs/include/stubs/nvs_flash.h @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include "esp_err.h" + +esp_err_t nvs_flash_init(void); diff --git a/examples/protocols/sockets/tcp_client/components/esp_stubs/include/stubs/protocol_examples_common.h b/examples/protocols/sockets/tcp_client/components/esp_stubs/include/stubs/protocol_examples_common.h new file mode 100644 index 00000000000..6f8bf0706fd --- /dev/null +++ b/examples/protocols/sockets/tcp_client/components/esp_stubs/include/stubs/protocol_examples_common.h @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include "esp_err.h" + +esp_err_t example_connect(void); diff --git a/examples/protocols/sockets/tcp_client/main/CMakeLists.txt b/examples/protocols/sockets/tcp_client/main/CMakeLists.txt index 9ff632b1e04..acea8ee344a 100644 --- a/examples/protocols/sockets/tcp_client/main/CMakeLists.txt +++ b/examples/protocols/sockets/tcp_client/main/CMakeLists.txt @@ -1,2 +1,13 @@ -idf_component_register(SRCS "tcp_client.c" - INCLUDE_DIRS ".") +if(${IDF_TARGET} STREQUAL "linux") + set(requires esp_stubs) +endif() + +if("${CONFIG_EXAMPLE_IPV4}" STREQUAL y) + set(tcp_client_ip tcp_client_v4.c) +else() + set(tcp_client_ip tcp_client_v6.c) +endif() + +idf_component_register(SRCS "tcp_client_main.c" "${tcp_client_ip}" + INCLUDE_DIRS "." + REQUIRES ${requires}) diff --git a/examples/protocols/sockets/tcp_client/main/Kconfig.projbuild b/examples/protocols/sockets/tcp_client/main/Kconfig.projbuild index a1f5cea4a3f..ca0de49c393 100644 --- a/examples/protocols/sockets/tcp_client/main/Kconfig.projbuild +++ b/examples/protocols/sockets/tcp_client/main/Kconfig.projbuild @@ -35,6 +35,26 @@ menu "Example Configuration" help The remote port to which the client example will connect to. + choice EXAMPLE_INTERFACE + prompt "Interface selection" + depends on EXAMPLE_IPV6 + help + Example can use either "Auto" or "User specified". + + config EXAMPLE_IFACE_AUTO + bool "Auto" + + config EXAMPLE_USER_SPECIFIED_IFACE + bool "User specified interface" + endchoice + + config EXAMPLE_USER_SPECIFIED_IFACE_NAME + string "User specified interface name" + default "st1" + depends on EXAMPLE_USER_SPECIFIED_IFACE + help + This interface will be used for communication. + choice EXAMPLE_SOCKET_IP_INPUT prompt "Socket example source" default EXAMPLE_SOCKET_IP_INPUT_STRING diff --git a/examples/protocols/sockets/tcp_client/main/tcp_client_main.c b/examples/protocols/sockets/tcp_client/main/tcp_client_main.c new file mode 100644 index 00000000000..2b2a27d4207 --- /dev/null +++ b/examples/protocols/sockets/tcp_client/main/tcp_client_main.c @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include "nvs_flash.h" +#include "esp_netif.h" +#include "protocol_examples_common.h" +#include "esp_event.h" + + +extern void tcp_client(void); + +void app_main(void) +{ + ESP_ERROR_CHECK(nvs_flash_init()); + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. + * Read "Establishing Wi-Fi or Ethernet Connection" section in + * examples/protocols/README.md for more information about this function. + */ + ESP_ERROR_CHECK(example_connect()); + + tcp_client(); +} diff --git a/examples/protocols/sockets/tcp_client/main/tcp_client.c b/examples/protocols/sockets/tcp_client/main/tcp_client_v4.c similarity index 56% rename from examples/protocols/sockets/tcp_client/main/tcp_client.c rename to examples/protocols/sockets/tcp_client/main/tcp_client_v4.c index a1977c49d63..e50e974895d 100644 --- a/examples/protocols/sockets/tcp_client/main/tcp_client.c +++ b/examples/protocols/sockets/tcp_client/main/tcp_client_v4.c @@ -1,33 +1,24 @@ -/* BSD Socket API Example - - This example code is in the Public Domain (or CC0 licensed, at your option.) - - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. -*/ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include "sdkconfig.h" #include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" -#include "esp_system.h" -#include "esp_wifi.h" -#include "esp_event.h" -#include "esp_log.h" -#include "nvs_flash.h" +#include +#include +#include +#include // struct addrinfo +#include #include "esp_netif.h" -#include "protocol_examples_common.h" +#include "esp_log.h" +#if defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN) #include "addr_from_stdin.h" -#include "lwip/err.h" -#include "lwip/sockets.h" - +#endif #if defined(CONFIG_EXAMPLE_IPV4) #define HOST_IP_ADDR CONFIG_EXAMPLE_IPV4_ADDR -#elif defined(CONFIG_EXAMPLE_IPV6) -#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV6_ADDR -#else +#elif defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN) #define HOST_IP_ADDR "" #endif @@ -36,7 +27,8 @@ static const char *TAG = "example"; static const char *payload = "Message from ESP32 "; -static void tcp_client_task(void *pvParameters) + +void tcp_client(void) { char rx_buffer[128]; char host_ip[] = HOST_IP_ADDR; @@ -46,23 +38,16 @@ static void tcp_client_task(void *pvParameters) while (1) { #if defined(CONFIG_EXAMPLE_IPV4) struct sockaddr_in dest_addr; - dest_addr.sin_addr.s_addr = inet_addr(host_ip); + inet_pton(AF_INET, host_ip, &dest_addr.sin_addr); dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(PORT); addr_family = AF_INET; ip_protocol = IPPROTO_IP; -#elif defined(CONFIG_EXAMPLE_IPV6) - struct sockaddr_in6 dest_addr = { 0 }; - inet6_aton(host_ip, &dest_addr.sin6_addr); - dest_addr.sin6_family = AF_INET6; - dest_addr.sin6_port = htons(PORT); - dest_addr.sin6_scope_id = esp_netif_get_netif_impl_index(EXAMPLE_INTERFACE); - addr_family = AF_INET6; - ip_protocol = IPPROTO_IPV6; #elif defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN) struct sockaddr_storage dest_addr = { 0 }; ESP_ERROR_CHECK(get_addr_from_stdin(PORT, SOCK_STREAM, &ip_protocol, &addr_family, &dest_addr)); #endif + int sock = socket(addr_family, SOCK_STREAM, ip_protocol); if (sock < 0) { ESP_LOGE(TAG, "Unable to create socket: errno %d", errno); @@ -70,7 +55,7 @@ static void tcp_client_task(void *pvParameters) } ESP_LOGI(TAG, "Socket created, connecting to %s:%d", host_ip, PORT); - int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in6)); + int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); if (err != 0) { ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno); break; @@ -96,8 +81,6 @@ static void tcp_client_task(void *pvParameters) ESP_LOGI(TAG, "Received %d bytes from %s:", len, host_ip); ESP_LOGI(TAG, "%s", rx_buffer); } - - vTaskDelay(2000 / portTICK_PERIOD_MS); } if (sock != -1) { @@ -106,20 +89,4 @@ static void tcp_client_task(void *pvParameters) close(sock); } } - vTaskDelete(NULL); -} - -void app_main(void) -{ - ESP_ERROR_CHECK(nvs_flash_init()); - ESP_ERROR_CHECK(esp_netif_init()); - ESP_ERROR_CHECK(esp_event_loop_create_default()); - - /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. - * Read "Establishing Wi-Fi or Ethernet Connection" section in - * examples/protocols/README.md for more information about this function. - */ - ESP_ERROR_CHECK(example_connect()); - - xTaskCreate(tcp_client_task, "tcp_client", 4096, NULL, 5, NULL); } diff --git a/examples/protocols/sockets/tcp_client/main/tcp_client_v6.c b/examples/protocols/sockets/tcp_client/main/tcp_client_v6.c new file mode 100644 index 00000000000..73acd117e23 --- /dev/null +++ b/examples/protocols/sockets/tcp_client/main/tcp_client_v6.c @@ -0,0 +1,255 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include "sdkconfig.h" +#include +#include +#include +#include +#include // struct addrinfo +#include +#include "esp_netif.h" +#include "esp_log.h" +#if defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN) +#include "addr_from_stdin.h" +#endif + +#if defined(CONFIG_EXAMPLE_IPV6_ADDR) +#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV6_ADDR +#else +#define HOST_IP_ADDR "" +#endif + +#define PORT CONFIG_EXAMPLE_PORT + +static const char *TAG = "example"; +static const char *payload = "Message from ESP32 "; + +#if defined(CONFIG_IDF_TARGET_LINUX) +// Checks for Global address, Unique Unicast(RFC4193) and link-local address. +#define ip6_addr_isglobal(ip6addr) ((((ip6addr)->sin6_addr.s6_addr[0] & htonl(0xe0000000UL)) & htonl(0x20000000UL)) || \ + (((ip6addr)->sin6_addr.s6_addr[0] & htonl(0xff000000UL)) & htonl(0xfc000000UL)) || \ + (((ip6addr)->sin6_addr.s6_addr[0] & htonl(0xff000000UL)) & htonl(0xfe800000UL))) + + +/** + * @brief In case of Auto mode returns the interface name with a valid IPv6 address or + * In case the user has specified interface, validates and returns the interface name. + * + * @param[out] interface Name of the interface in as a string. + * + * @return 0 incase of success. + */ +static int get_src_iface(char *interface) +{ + struct ifaddrs *ifap, *ifa; + char src_addr_str[INET6_ADDRSTRLEN]; + + if (getifaddrs(&ifap) == -1) { + ESP_LOGE(TAG, "getifaddrs failed"); + return -1; + } + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6) { +#if defined(CONFIG_EXAMPLE_USER_SPECIFIED_IFACE) + if (0 == strcmp(CONFIG_EXAMPLE_USER_SPECIFIED_IFACE_NAME, ifa->ifa_name)) { + strcpy(interface, ifa->ifa_name); + freeifaddrs(ifap); + ESP_LOGI(TAG, "Interface: %s", interface); + return 0; + } +#else + strcpy(interface, ifa->ifa_name); + getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), src_addr_str, + sizeof(src_addr_str), NULL, 0, NI_NUMERICHOST); + + struct sockaddr_in6 *src_addr = (struct sockaddr_in6 *) ifa->ifa_addr; + inet_ntop(AF_INET6, &(src_addr->sin6_addr), src_addr_str, INET6_ADDRSTRLEN); + + if (ip6_addr_isglobal(src_addr)) { + //Return as we have the source address + freeifaddrs(ifap); + ESP_LOGI(TAG, "Interface: %s", interface); + return 0; + } +#endif // #if defined(CONFIG_EXAMPLE_USER_SPECIFIED_IFACE) + } + } + + freeifaddrs(ifap); + return -1; +} +#else + +static esp_netif_t *get_esp_netif_from_iface(char *interface_i) +{ + esp_netif_t *netif = NULL; + esp_err_t ret = ESP_FAIL; + char iface[10]; + + // 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, iface); + if ((ESP_FAIL == ret) || (NULL == netif)) { + ESP_LOGE(TAG, "No interface available"); + return NULL; + } + + if (0 == strcmp(interface_i, iface)) { + return netif; + } + } + + return NULL; +} + + +/** + * @brief In case of Auto mode returns the interface name with a valid IPv6 address or + * In case the user has specified interface, validates and returns the interface name. + * + * @param[out] interface Name of the interface in as a string. + * + * @return 0 incase of success. + */ +static int get_src_iface(char *interface) +{ + esp_netif_t *netif = NULL; + esp_err_t ret = ESP_FAIL; + int ip6_addrs_count = 0; + esp_ip6_addr_t ip6[LWIP_IPV6_NUM_ADDRESSES]; + + // 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); + + if ((ESP_FAIL == ret) || (NULL == netif)) { + ESP_LOGE(TAG, "No interface available"); + return -1; + } + +#if defined(CONFIG_EXAMPLE_USER_SPECIFIED_IFACE) + if (!strcmp(CONFIG_EXAMPLE_USER_SPECIFIED_IFACE_NAME, interface)) { + ESP_LOGI(TAG, "Interface: %s", interface); + return 0; + } +#else + ip6_addrs_count = esp_netif_get_all_ip6(netif, ip6); + for (int j = 0; j < ip6_addrs_count; ++j) { + esp_ip6_addr_type_t ipv6_type = esp_netif_ip6_get_addr_type(&(ip6[j])); + + if ((ESP_IP6_ADDR_IS_GLOBAL == ipv6_type) || + (ESP_IP6_ADDR_IS_UNIQUE_LOCAL == ipv6_type) || + (ESP_IP6_ADDR_IS_LINK_LOCAL == ipv6_type)) { + // Break as we have the source address + ESP_LOGI(TAG, "Interface: %s", interface); + return 0; + } + } +#endif // #if defined(CONFIG_EXAMPLE_USER_SPECIFIED_IFACE) + } + + return -1; +} +#endif // #if defined(CONFIG_IDF_TARGET_LINUX) + + +void tcp_client(void) +{ + char rx_buffer[128]; + char host_ip[] = HOST_IP_ADDR; + int addr_family = 0; + int ip_protocol = 0; + char interface[10]; +#if defined(CONFIG_IDF_TARGET_LINUX) + struct ifreq ifr; +#else + esp_netif_t *netif = NULL; +#endif + + while (1) { +#if defined(CONFIG_EXAMPLE_IPV6) + struct sockaddr_in6 dest_addr = { 0 }; + inet_pton(AF_INET6, host_ip, &dest_addr.sin6_addr); + dest_addr.sin6_family = AF_INET6; + dest_addr.sin6_port = htons(PORT); + addr_family = AF_INET6; + ip_protocol = IPPROTO_TCP; +#elif defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN) + struct sockaddr_storage dest_addr = { 0 }; + ESP_ERROR_CHECK(get_addr_from_stdin(PORT, SOCK_STREAM, &ip_protocol, &addr_family, &dest_addr)); +#endif + int sock = socket(addr_family, SOCK_STREAM, ip_protocol); + if (sock < 0) { + ESP_LOGE(TAG, "Unable to create socket: errno %d", errno); + break; + } + ESP_LOGI(TAG, "Socket created, connecting to %s:%d", host_ip, PORT); + + if (0 != get_src_iface(interface)) { + ESP_LOGE(TAG, "Interface: Unavailable\n"); + break; + } + +#if defined(CONFIG_IDF_TARGET_LINUX) + memset (&ifr, 0, sizeof(ifr)); + snprintf (ifr.ifr_name, sizeof (ifr.ifr_name), "%s", interface); + if (ioctl (sock, SIOCGIFINDEX, &ifr) < 0) { + ESP_LOGE(TAG, "ioctl() failed to find interface "); + break; + } +#if defined(CONFIG_EXAMPLE_IPV6) + dest_addr.sin6_scope_id = ifr.ifr_ifindex; + ESP_LOGI(TAG, "Interface index: %d\n", dest_addr.sin6_scope_id); +#endif +#else + if (NULL == (netif = get_esp_netif_from_iface(interface))) { + ESP_LOGE(TAG, "Failed to find interface "); + break; + } +#if defined(CONFIG_EXAMPLE_IPV6) + dest_addr.sin6_scope_id = esp_netif_get_netif_impl_index(netif); + ESP_LOGI(TAG, "Interface index: %d\n", dest_addr.sin6_scope_id); +#endif +#endif + + int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); + if (err != 0) { + ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno); + break; + } + ESP_LOGI(TAG, "Successfully connected"); + + while (1) { + int err = send(sock, payload, strlen(payload), 0); + if (err < 0) { + ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); + break; + } + + int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0); + // Error occurred during receiving + if (len < 0) { + ESP_LOGE(TAG, "recv failed: errno %d", errno); + break; + } + // Data received + else { + rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string + ESP_LOGI(TAG, "Received %d bytes from %s:", len, host_ip); + ESP_LOGI(TAG, "%s", rx_buffer); + } + } + + if (sock != -1) { + ESP_LOGE(TAG, "Shutting down socket and restarting..."); + shutdown(sock, 0); + close(sock); + } + } +} diff --git a/examples/protocols/sockets/tcp_client/sdkconfig.defaults b/examples/protocols/sockets/tcp_client/sdkconfig.defaults new file mode 100644 index 00000000000..d4a9198544d --- /dev/null +++ b/examples/protocols/sockets/tcp_client/sdkconfig.defaults @@ -0,0 +1,4 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +# CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER is not set