174 lines
6.4 KiB
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));
|
|
}
|