From: Andro Nooh Date: Thu, 19 Sep 2019 05:13:02 +0000 (-0700) Subject: lwip: add icmp echo example X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a77e69c40820873654b288107b60cc685f93227b;p=esp-idf lwip: add icmp echo example It piggybacks on the console example to add ping support and CLI. Merges https://github.com/espressif/esp-idf/pull/4093 --- diff --git a/components/lwip/apps/ping/esp_ping.c b/components/lwip/apps/ping/esp_ping.c index 75b669412d..0ffd2c0af4 100644 --- a/components/lwip/apps/ping/esp_ping.c +++ b/components/lwip/apps/ping/esp_ping.c @@ -128,7 +128,7 @@ esp_err_t esp_ping_get_target(ping_target_id_t opt_id, void *opt_val, uint32_t o return ret; } -esp_err_t esp_ping_result(uint8_t res_val, uint16_t ping_len, uint32_t ping_time) +esp_err_t esp_ping_result(uint8_t res_val, uint16_t ping_len, uint32_t ping_time, uint32_t seqno) { esp_err_t ret = ESP_OK; @@ -154,6 +154,7 @@ esp_err_t esp_ping_result(uint8_t res_val, uint16_t ping_len, uint32_t ping_time ping_option_info->ping_res.total_time += ping_time; ping_option_info->ping_res.recv_count ++; + ping_option_info->ping_res.ping_seqno = seqno; } } diff --git a/components/lwip/apps/ping/ping.c b/components/lwip/apps/ping/ping.c index 8b8d5512f6..e77885e5fd 100644 --- a/components/lwip/apps/ping/ping.c +++ b/components/lwip/apps/ping/ping.c @@ -208,7 +208,7 @@ ping_recv(int s) if ((iecho->id == PING_ID) && (iecho->seqno == htons(ping_seq_num))) { /* do some ping result processing */ #ifdef ESP_PING - esp_ping_result((ICMPH_TYPE(iecho) == ICMP_ER), len, PING_TIME_DIFF_MS(now, ping_time)); + esp_ping_result((ICMPH_TYPE(iecho) == ICMP_ER), len, PING_TIME_DIFF_MS(now, ping_time), ntohs(iecho->seqno)); #else PING_RESULT((ICMPH_TYPE(iecho) == ICMP_ER)); #endif @@ -228,7 +228,7 @@ ping_recv(int s) /* do some ping result processing */ #ifdef ESP_PING - esp_ping_result(0, len, PING_TIME_DIFF_MS(now, ping_time)); + esp_ping_result(0, len, PING_TIME_DIFF_MS(now, ping_time),0); #else PING_RESULT(0); #endif @@ -317,7 +317,7 @@ _exit: close(s); _exit_new_socket_failed: - esp_ping_result(PING_RES_FINISH, 0, 0); + esp_ping_result(PING_RES_FINISH, 0, 0, 0); SYS_ARCH_PROTECT(lev); if (ping_init_flag) { /* Ping closed by this thread */ LWIP_DEBUGF( PING_DEBUG, ("ping: closed by self ")); diff --git a/components/lwip/include/apps/esp_ping.h b/components/lwip/include/apps/esp_ping.h index 2386795283..1258d4c08c 100644 --- a/components/lwip/include/apps/esp_ping.h +++ b/components/lwip/include/apps/esp_ping.h @@ -42,6 +42,7 @@ typedef struct _ping_found { uint32_t total_time; uint32_t min_time; uint32_t max_time; + uint32_t ping_seqno; int8_t ping_err; } esp_ping_found; @@ -102,7 +103,7 @@ esp_err_t esp_ping_get_target(ping_target_id_t opt_id, void *opt_val, uint32_t o * - ESP_OK * - ESP_ERR_PING_INVALID_PARAMS */ -esp_err_t esp_ping_result(uint8_t res_val, uint16_t res_len, uint32_t res_time); +esp_err_t esp_ping_result(uint8_t res_val, uint16_t res_len, uint32_t res_time, uint32_t seqno); #ifdef __cplusplus } diff --git a/examples/system/console/README.md b/examples/system/console/README.md index 695d319ef9..80a8aea48e 100644 --- a/examples/system/console/README.md +++ b/examples/system/console/README.md @@ -100,6 +100,30 @@ Type 'help' to get the list of commands. Use UP/DOWN arrows to navigate through command history. Press TAB when typing command name to auto-complete. [esp32]> +ping +Send an ICMP message to an IPv4/IPv6 address + Target IP Address +-c, --count= Number of messages +-t, --timeout= Connection timeout, ms +-d, --delay= Delay between messges, ms +-s, --size= Packet data size, bytes +--tos= Type of service + +esp32> +esp32> ping 192.168.1.1 +PING 192.168.1.1 (192.168.1.1) 32(60) bytes of data. +60 bytes from 192.168.1.1: icmp_seq=1 time=13 ms +60 bytes from 192.168.1.1: icmp_seq=2 time=11 ms +60 bytes from 192.168.1.1: icmp_seq=3 time=7 ms +60 bytes from 192.168.1.1: icmp_seq=4 time=13 ms +60 bytes from 192.168.1.1: icmp_seq=5 time=25 ms + +--- 192.168.1.1 ping statistics --- +5 packets transmitted, 5 received, 0% packet loss, time 69ms +rtt min/avg/max = 7/13.80/25 ms +esp32> + + ``` @@ -148,4 +172,4 @@ Several commands implemented in `cmd_wifi.c` and `cmd_system.c` use the Argtable ### Command history -Each time a new command line is obtained from `linenoise`, it is written into history and the history is saved into a file in flash memory. On reset, history is initialized from that file. \ No newline at end of file +Each time a new command line is obtained from `linenoise`, it is written into history and the history is saved into a file in flash memory. On reset, history is initialized from that file. diff --git a/examples/system/console/main/CMakeLists.txt b/examples/system/console/main/CMakeLists.txt index 5afdb190ad..055046f9b2 100644 --- a/examples/system/console/main/CMakeLists.txt +++ b/examples/system/console/main/CMakeLists.txt @@ -1,3 +1,4 @@ idf_component_register(SRCS "cmd_wifi.c" "console_example_main.c" - INCLUDE_DIRS ".") \ No newline at end of file + "cmd_ping.c" + INCLUDE_DIRS ".") diff --git a/examples/system/console/main/cmd_decl.h b/examples/system/console/main/cmd_decl.h index 983b6e974c..d1446ff757 100644 --- a/examples/system/console/main/cmd_decl.h +++ b/examples/system/console/main/cmd_decl.h @@ -14,6 +14,7 @@ extern "C" { #include "cmd_system.h" #include "cmd_wifi.h" +#include "cmd_ping.h" #include "cmd_nvs.h" #ifdef __cplusplus diff --git a/examples/system/console/main/cmd_ping.c b/examples/system/console/main/cmd_ping.c new file mode 100644 index 0000000000..3be6862273 --- /dev/null +++ b/examples/system/console/main/cmd_ping.c @@ -0,0 +1,194 @@ +/* Console example — WiFi commands + + 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. +*/ + +#include +#include +#include "esp_log.h" +#include "esp_console.h" +#include "argtable3/argtable3.h" +#include "cmd_decl.h" +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "esp_wifi.h" +#include "tcpip_adapter.h" +#include "esp_event.h" +#include "cmd_wifi.h" +#include "cmd_ping.h" +#include "lwip/inet.h" +#include +#include "esp_ping.h" +#include "ping/ping.h" + +#define DEFAULT_COUNT (5) +#define DEFAULT_TIMEOUT (1000) // unit = ms +#define DEFAULT_DELAY (500) // unit = ms +#define DEFAULT_DATA_SIZE (32) // bytes +#define FAILURE (-1) + +extern volatile bool is_connected; + +/** Arguments used by 'send_icmp' function */ +static struct { + struct arg_str *ip_address; + struct arg_int *count; + struct arg_int *timeout; + struct arg_int *delay; + struct arg_int *packet_size; + struct arg_int *tos; + struct arg_end *end; +} ping_args; + +static bool parse_ip_address(const char *address, ip4_addr_t *ipv4, ip6_addr_t *ipv6) +{ + // Determine if the address is IPv4 or IPv6 + char *indicator = (char *) malloc(strlen(address)); + strcpy(indicator, address); + while(*indicator++) + { + if (*indicator == '.') { + return inet_aton(address, ipv4); + } + else if (*indicator == ':') { + return inet6_aton(address, &ipv6); + } + } + return false; +} + +static esp_err_t parse_args(ip4_addr_t *ip4, ip6_addr_t *ip6, uint32_t *count, uint32_t *timeout, + uint32_t *delay, uint32_t *packet_size, uint32_t *tos) +{ + if (ping_args.ip_address->count > 0) + { + // Parse IP address (Handle v4 and v6) + bool rc = parse_ip_address(ping_args.ip_address->sval[0], ip4, ip6); + if (!rc) { + ESP_LOGE("ping","Error parsing provided IP address...aborting!\n"); + return ESP_FAIL; + } + } + + if (ping_args.count->count > 0) + *count= *ping_args.count->ival; + + if (ping_args.timeout->count > 0) + *timeout = *ping_args.timeout->ival; + + if (ping_args.delay->count > 0) + *delay = *ping_args.delay->ival; + + if (ping_args.packet_size->count > 0) + *packet_size = *ping_args.packet_size->ival; + if (ping_args.tos->count > 0) + *tos = *ping_args.tos->ival; + + return ESP_OK; +} + +esp_err_t ping_results(ping_target_id_t message_type, esp_ping_found *found_val) +{ + static int ctr; + ip4_addr_t target_ipv4; + uint32_t ping_count = 0; + esp_ping_get_target(PING_TARGET_IP_ADDRESS, &target_ipv4.addr, sizeof(uint32_t)); + esp_ping_get_target(PING_TARGET_IP_ADDRESS_COUNT, &ping_count, sizeof(uint32_t)); + + if (found_val->ping_err != 2) + { + fprintf(stdout, "%d bytes from %s: icmp_seq=%d time=%d ms\n", + found_val->bytes, inet_ntoa(target_ipv4), found_val->ping_seqno, found_val->resp_time); + } else { + fprintf(stdout, "."); + fflush(stdout); + } + + + if (++ctr >= ping_count) { + int32_t packet_loss = ( 1-((found_val->recv_count * 1.0) / found_val->send_count)) * 100; + fprintf(stdout, "\n--- %s ping statistics ---\n", inet_ntoa(target_ipv4)); + fprintf(stdout, "%d packets transmitted, %d received, %d%% packet loss, time %dms\n", + found_val->send_count, found_val->recv_count, packet_loss, found_val->total_time); + fprintf(stdout, "rtt min/avg/max = %d/%1.2f/%d ms\n", found_val->min_time, + (found_val->total_time * 1.0) / found_val->recv_count, found_val->max_time); + // clean up time + memset(found_val, 0, sizeof(esp_ping_found)); + ctr = 0; + } + ping_deinit(); + + return ESP_OK; +} +static esp_err_t send_icmp(int argc, char **argv) +{ + if (!is_connected) + { + ESP_LOGE("ping", "Device is not connected to any AP. Cannot ping host without an IP address\n" + "Please associate to an SSID and then use ping!\n"); + return ESP_FAIL; + } + esp_err_t ret; + int counter; + ip4_addr_t target_ipv4; + // initialize optional arguments to default values + uint32_t ping_count = DEFAULT_COUNT; + uint32_t ping_timeout = DEFAULT_TIMEOUT; + uint32_t ping_delay = DEFAULT_DELAY; + uint32_t packet_size = DEFAULT_DATA_SIZE; + uint32_t ping_tos = -1; + // TO DO: IPv6 + ip6_addr_t target_ipv6; + + int nerrors = arg_parse(argc, argv, (void **) &ping_args); + if (nerrors != 0) { + arg_print_errors(stderr, ping_args.end, argv[0]); + return FAILURE; + } + + ret = parse_args(&target_ipv4, &target_ipv6, &ping_count, &ping_timeout, &ping_delay, + &packet_size, &ping_tos); + + if (ret == ESP_OK) { + printf("PING %s (%s) %d(%d) bytes of data.\n", inet_ntoa(target_ipv4), + inet_ntoa(target_ipv4), packet_size, (packet_size + 28)); // ICMP header length is 28 bytes + for (counter = 0; counter < ping_count; counter++) + { + vTaskDelay(1000 / portTICK_PERIOD_MS); + esp_ping_set_target(PING_TARGET_IP_ADDRESS_COUNT, &ping_count, sizeof(uint32_t)); + esp_ping_set_target(PING_TARGET_RCV_TIMEO, &ping_timeout, sizeof(uint32_t)); + esp_ping_set_target(PING_TARGET_DELAY_TIME, &ping_delay, sizeof(uint32_t)); + esp_ping_set_target(PING_TARGET_IP_ADDRESS, &target_ipv4.addr, sizeof(uint32_t)); + esp_ping_set_target(PING_TARGET_RES_FN, &ping_results, sizeof(ping_results)); + esp_ping_set_target(PING_TARGET_DATA_LEN, &packet_size, sizeof(uint32_t)); + if (ping_tos != -1) + esp_ping_set_target(PING_TARGET_IP_TOS, &ping_tos, sizeof(uint32_t)); + ping_init(); + } + } else + return ESP_FAIL; + return ESP_OK; +} + +void register_ping(void) +{ + ping_args.ip_address = arg_str0(NULL, NULL, "", "Target IP Address"); + ping_args.timeout = arg_int0("t", "timeout", "", "Connection timeout, ms"); + ping_args.count = arg_int0("c", "count", "", "Number of messages"); + ping_args.delay= arg_int0("d", "delay", "", "Delay between messges, ms"); + ping_args.packet_size = arg_int0("s", "size", "", "Packet data size, bytes"); + ping_args.tos = arg_int0(NULL, "tos", "", "Type of service"); + ping_args.end = arg_end(1); + const esp_console_cmd_t ping_cmd = { + .command = "ping", + .help = "Send an ICMP message to an IPv4/IPv6 address", + .hint = "", + .func = &send_icmp, + .argtable = &ping_args + }; + ESP_ERROR_CHECK( esp_console_cmd_register(&ping_cmd) ); +} diff --git a/examples/system/console/main/cmd_ping.h b/examples/system/console/main/cmd_ping.h new file mode 100644 index 0000000000..7b03e3a041 --- /dev/null +++ b/examples/system/console/main/cmd_ping.h @@ -0,0 +1,21 @@ +/* Console example — declarations of command registration functions. + + 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. +*/ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +// Register Ping functions +void register_ping(void); + +#ifdef __cplusplus +} +#endif + diff --git a/examples/system/console/main/cmd_wifi.c b/examples/system/console/main/cmd_wifi.c index b9c24f0cd1..7ae47e3e60 100644 --- a/examples/system/console/main/cmd_wifi.c +++ b/examples/system/console/main/cmd_wifi.c @@ -24,6 +24,7 @@ static EventGroupHandle_t wifi_event_group; const int CONNECTED_BIT = BIT0; +volatile bool is_connected = false; static void event_handler(void* arg, esp_event_base_t event_base, @@ -106,6 +107,7 @@ static int connect(int argc, char **argv) return 1; } ESP_LOGI(__func__, "Connected"); + is_connected = true; return 0; } diff --git a/examples/system/console/main/cmd_wifi.h b/examples/system/console/main/cmd_wifi.h index 5259dd72ff..f5f5dc3c20 100644 --- a/examples/system/console/main/cmd_wifi.h +++ b/examples/system/console/main/cmd_wifi.h @@ -12,6 +12,7 @@ extern "C" { #endif +extern volatile bool is_connected; // Register WiFi functions void register_wifi(void); diff --git a/examples/system/console/main/console_example_main.c b/examples/system/console/main/console_example_main.c index 2348ac12e7..6fc2aebb87 100644 --- a/examples/system/console/main/console_example_main.c +++ b/examples/system/console/main/console_example_main.c @@ -130,6 +130,7 @@ void app_main(void) register_system(); register_wifi(); register_nvs(); + register_ping(); /* Prompt to be printed before each line. * This can be customized, made dynamic, etc.