/* * ESP32 Ping library * * All rights reserved. * * Permission to use, copy, modify, and distribute this software * and its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and this * permission notice and warranty disclaimer appear in supporting * documentation, and that the name of the author not be used in * advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * The author disclaim all warranties with regard to this * software, including all implied warranties of merchantability * and fitness. In no event shall the author be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether * in an action of contract, negligence or other tortious action, * arising out of or in connection with the use or performance of * this software. * * -------------------------------------------------------------------------------- * Ping Library is based on the following source code: * * Lua RTOS, ping utility * * * Author: Jaume Oliv� (jolive@iberoxarxa.com / jolive@whitecatboard.org) * * -------------------------------------------------------------------------------- * * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. * * This file is part of the lwIP TCP/IP stack. * */ #include #include #include #include #include #include #include #include "ping.h" #include "lwip/inet_chksum.h" #include "lwip/ip.h" #include "lwip/ip4.h" #include "lwip/err.h" #include "lwip/icmp.h" #include "lwip/sockets.h" #include "lwip/sys.h" #include "lwip/netdb.h" #include "lwip/dns.h" static uint16_t ping_seq_num; static uint8_t stopped = 0; /* * Statistics */ static uint32_t transmitted = 0; static uint32_t received = 0; static float min_time = 0; static float max_time = 0; static float mean_time = 0; static float last_mean_time = 0; static float var_time = 0; #define PING_ID 0xAFAF #define PING_DEFAULT_COUNT 10 #define PING_DEFAULT_INTERVAL 1 #define PING_DEFAULT_SIZE 32 #define PING_DEFAULT_TIMEOUT 1 /* * Helper functions * */ static void ping_prepare_echo(struct icmp_echo_hdr *iecho, uint16_t len) { size_t i; size_t data_len = len - sizeof(struct icmp_echo_hdr); ICMPH_TYPE_SET(iecho, ICMP_ECHO); ICMPH_CODE_SET(iecho, 0); iecho->chksum = 0; iecho->id = PING_ID; iecho->seqno = htons(++ping_seq_num); /* fill the additional data buffer with some data */ for (i = 0; i < data_len; i++) { ((char*)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i; } iecho->chksum = inet_chksum(iecho, len); } static err_t ping_send(int s, ip4_addr_t *addr, int size) { struct icmp_echo_hdr *iecho; struct sockaddr_in to; size_t ping_size = sizeof(struct icmp_echo_hdr) + size; int err; iecho = (struct icmp_echo_hdr *)mem_malloc((mem_size_t)ping_size); if (!iecho) { mem_free(iecho); return ERR_MEM; } ping_prepare_echo(iecho, (uint16_t)ping_size); to.sin_len = sizeof(to); to.sin_family = AF_INET; inet_addr_from_ip4addr(&to.sin_addr, addr); if ((err = sendto(s, iecho, ping_size, 0, (struct sockaddr*)&to, sizeof(to)))) { transmitted++; } mem_free(iecho); return (err ? ERR_OK : ERR_VAL); } static void ping_recv(int s) { char buf[64]; int fromlen, len; struct sockaddr_in from; struct ip_hdr *iphdr; struct icmp_echo_hdr *iecho = NULL; char ipa[16]; struct timeval begin; struct timeval end; uint64_t micros_begin; uint64_t micros_end; float elapsed; // Register begin time gettimeofday(&begin, NULL); // Send while ((len = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr*)&from, (socklen_t*)&fromlen)) > 0) { if (len >= (int)(sizeof(struct ip_hdr) + sizeof(struct icmp_echo_hdr))) { // Register end time gettimeofday(&end, NULL); /// Get from IP address ip4_addr_t fromaddr; inet_addr_to_ip4addr(&fromaddr, &from.sin_addr); strcpy(ipa, inet_ntoa(fromaddr)); // Get echo iphdr = (struct ip_hdr *)buf; iecho = (struct icmp_echo_hdr *)(buf + (IPH_HL(iphdr) * 4)); // Print .... if ((iecho->id == PING_ID) && (iecho->seqno == htons(ping_seq_num))) { received++; // Get elapsed time in milliseconds micros_begin = begin.tv_sec * 1000000; micros_begin += begin.tv_usec; micros_end = end.tv_sec * 1000000; micros_end += end.tv_usec; elapsed = (float)(micros_end - micros_begin) / (float)1000.0; // Update statistics // Mean and variance are computed in an incremental way if (elapsed < min_time) { min_time = elapsed; } if (elapsed > max_time) { max_time = elapsed; } last_mean_time = mean_time; mean_time = (((received - 1) * mean_time) + elapsed) / received; if (received > 1) { var_time = var_time + ((elapsed - last_mean_time) * (elapsed - mean_time)); } // Print ... log_d("%d bytes from %s: icmp_seq=%d time=%.3f ms\r\n", len, ipa, ntohs(iecho->seqno), elapsed ); return; } else { // TODO } } } if (len < 0) { log_d("Request timeout for icmp_seq %d\r\n", ping_seq_num); } } /* static void stop_action(int i) { signal(i, SIG_DFL); stopped = 1; } */ /* * Operation functions * */ void ping(const char *name, int count, int interval, int size, int timeout) { // Resolve name hostent * target = gethostbyname(name); IPAddress adr = *target->h_addr_list[0]; if (target->h_length == 0) { // TODO: error not found target????? return; } ping_start(adr, count, interval, size, timeout); } bool ping_start(struct ping_option *ping_o) { return ping_start(ping_o->ip,ping_o->count,0,0,0,ping_o); } bool ping_start(IPAddress adr, int count=0, int interval=0, int size=0, int timeout=0, struct ping_option *ping_o) { // driver_error_t *error; struct sockaddr_in address; ip4_addr_t ping_target; int s; // Get default values if argument are not provided if (count == 0) { count = PING_DEFAULT_COUNT; } if (interval == 0) { interval = PING_DEFAULT_INTERVAL; } if (size == 0) { size = PING_DEFAULT_SIZE; } if (timeout == 0) { timeout = PING_DEFAULT_TIMEOUT; } // Create socket if ((s = socket(AF_INET, SOCK_RAW, IP_PROTO_ICMP)) < 0) { // TODO: error return false; } address.sin_addr.s_addr = adr; ping_target.addr = address.sin_addr.s_addr; // Setup socket struct timeval tout; // Timeout tout.tv_sec = timeout; tout.tv_usec = 0; if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tout, sizeof(tout)) < 0) { closesocket(s); // TODO: error return false; } stopped = 0; transmitted = 0; received = 0; min_time = 1.E+9;// FLT_MAX; max_time = 0.0; mean_time = 0.0; var_time = 0.0; // Register signal for stop ping //signal(SIGINT, stop_action); // Begin ping ... char ipa[16]; strcpy(ipa, inet_ntoa(ping_target)); log_i("PING %s: %d data bytes\r\n", ipa, size); ping_seq_num = 0; unsigned long ping_started_time = millis(); while ((ping_seq_num < count) && (!stopped)) { if (ping_send(s, &ping_target, size) == ERR_OK) { ping_recv(s); } if(ping_seq_num < count){ delay( interval*1000L); } } closesocket(s); log_i("%d packets transmitted, %d packets received, %.1f%% packet loss\r\n", transmitted, received, ((((float)transmitted - (float)received) / (float)transmitted) * 100.0) ); if (ping_o) { ping_resp pingresp; log_i("round-trip min/avg/max/stddev = %.3f/%.3f/%.3f/%.3f ms\r\n", min_time, mean_time, max_time, sqrt(var_time / received)); pingresp.total_count = count; //Number of pings pingresp.resp_time = mean_time; //Average time for the pings pingresp.seqno = 0; //not relevant pingresp.timeout_count = transmitted - received; //number of pings which failed pingresp.bytes = size; //number of bytes received for 1 ping pingresp.total_bytes = size * count; //number of bytes for all pings pingresp.total_time = (millis() - ping_started_time) / 1000.0; //Time consumed for all pings; it takes into account also timeout pings pingresp.ping_err = transmitted - received; //number of pings failed // Call the callback function ping_o->recv_function(ping_o, &pingresp); } // Return true if at least one ping had a successfull "pong" return (received > 0); } bool ping_regist_recv(struct ping_option *ping_opt, ping_recv_function ping_recv) { if (ping_opt == NULL) return false; ping_opt->recv_function = ping_recv; return true; } bool ping_regist_sent(struct ping_option *ping_opt, ping_sent_function ping_sent) { if (ping_opt == NULL) return false; ping_opt->sent_function = ping_sent; return true; }