ESP32/components/app_console/cmd_ping.c

174 lines
6.4 KiB
C

/*
* cmd_ping.c
*
* Copyright (c) 2025 Umber Networks & Robert McMahon
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <inttypes.h>
#include "esp_log.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "ping/ping_sock.h"
#include "lwip/inet.h"
#include "lwip/netdb.h"
#include "app_console.h"
// ============================================================================
// COMMAND: ping (ICMP Echo)
// ============================================================================
static struct {
struct arg_str *host;
struct arg_int *count;
struct arg_int *interval;
struct arg_end *end;
} ping_args;
static void cmd_ping_on_ping_success(esp_ping_handle_t hdl, void *args) {
uint8_t ttl;
uint16_t seqno;
uint32_t elapsed_time, recv_len;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
printf("%" PRIu32 " bytes from %s: icmp_seq=%u ttl=%u time=%" PRIu32 " ms\n",
recv_len, inet_ntoa(target_addr.u_addr.ip4), seqno, ttl, elapsed_time);
}
static void cmd_ping_on_ping_timeout(esp_ping_handle_t hdl, void *args) {
uint16_t seqno;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
printf("From %s: icmp_seq=%u timeout\n", inet_ntoa(target_addr.u_addr.ip4), seqno);
}
static void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args) {
uint32_t transmitted;
uint32_t received;
uint32_t total_time_ms;
esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted));
esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received));
esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms));
printf("\n--- ping statistics ---\n");
printf("%" PRIu32 " packets transmitted, %" PRIu32 " received, %" PRIu32 "%% packet loss, time %" PRIu32 "ms\n",
transmitted, received, (transmitted - received) * 100 / transmitted, total_time_ms);
esp_ping_delete_session(hdl);
}
static int cmd_ping(int argc, char **argv) {
int nerrors = arg_parse(argc, argv, (void **)&ping_args);
if (nerrors != 0) {
arg_print_errors(stderr, ping_args.end, argv[0]);
return 1;
}
esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG();
// Parse Args
if (ping_args.count->count > 0) {
config.count = ping_args.count->ival[0];
}
if (ping_args.interval->count > 0) {
config.interval_ms = ping_args.interval->ival[0] * 1000;
}
// Parse Target IP or Hostname
ip_addr_t target_addr;
struct addrinfo hint;
struct addrinfo *res = NULL;
memset(&hint, 0, sizeof(hint));
memset(&target_addr, 0, sizeof(target_addr));
// Check if simple IP string
if (inet_aton(ping_args.host->sval[0], &target_addr.u_addr.ip4)) {
target_addr.type = IPADDR_TYPE_V4;
} else {
// Resolve Hostname
printf("Resolving %s...\n", ping_args.host->sval[0]);
// Set hint to prefer IPv4 if desired, or leave 0 for ANY
hint.ai_family = AF_INET;
if (getaddrinfo(ping_args.host->sval[0], NULL, &hint, &res) != 0) {
printf("ping: unknown host %s\n", ping_args.host->sval[0]);
return 1;
}
// Convert struct sockaddr_in to ip_addr_t
struct sockaddr_in *sa = (struct sockaddr_in *)res->ai_addr;
inet_addr_to_ip4addr(ip_2_ip4(&target_addr), &sa->sin_addr);
target_addr.type = IPADDR_TYPE_V4;
freeaddrinfo(res);
}
config.target_addr = target_addr;
config.task_stack_size = 4096; // Ensure enough stack for callbacks
esp_ping_callbacks_t cbs = {
.on_ping_success = cmd_ping_on_ping_success,
.on_ping_timeout = cmd_ping_on_ping_timeout,
.on_ping_end = cmd_ping_on_ping_end,
.cb_args = NULL
};
esp_ping_handle_t ping;
esp_ping_new_session(&config, &cbs, &ping);
esp_ping_start(ping);
return 0;
}
void register_ping_cmd(void) {
ping_args.host = arg_str1(NULL, NULL, "<host>", "Host address or name");
ping_args.count = arg_int0("c", "count", "<n>", "Stop after <n> replies");
ping_args.interval = arg_int0("i", "interval", "<seconds>", "Wait interval");
ping_args.end = arg_end(1);
const esp_console_cmd_t cmd = {
.command = "ping",
.help = "Send ICMP ECHO_REQUEST to network hosts",
.hint = NULL,
.func = &cmd_ping,
.argtable = &ping_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}