/**************************************************************************** * apps/system/ping/ping.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "netutils/icmp_ping.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define ICMP_PING_DATALEN 56 #define ICMP_NPINGS 10 /* Default number of pings */ #define ICMP_POLL_DELAY 1000 /* 1 second in milliseconds */ /**************************************************************************** * Private Types ****************************************************************************/ struct ping_priv_s { int code; /* Notice code ICMP_I/E/W_XXX */ long tmin; /* Minimum round trip time */ long tmax; /* Maximum round trip time */ long long tsum; /* Sum of all times, for doing average */ long long tsum2; /* Sum2 is the sum of the squares of sum ,for doing mean deviation */ }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: show_usage ****************************************************************************/ static void show_usage(FAR const char *progname, int exitcode) noreturn_function; static void show_usage(FAR const char *progname, int exitcode) { #if defined(CONFIG_LIBC_NETDB) && defined(CONFIG_NETDB_DNSCLIENT) printf("\nUsage: %s [-c ] [-i ] [-W ] " "[-s ] \n", progname); printf(" %s -h\n", progname); printf("\nWhere:\n"); printf(" is either an IPv4 address or the name of " "the remote host\n"); printf(" that is requested the ICMPv4 ECHO reply.\n"); #else printf("\nUsage: %s [-c ] [-i ] [-W ] " "[-s ] \n", progname); printf(" %s -h\n", progname); printf("\nWhere:\n"); printf(" is the IPv4 address request the ICMP " "ECHO reply.\n"); #endif printf(" -c determines the number of pings. Default %u.\n", ICMP_NPINGS); printf(" -i is the default delay between pings " "(milliseconds).\n"); printf(" Default %d.\n", ICMP_POLL_DELAY); printf(" -W is the timeout for wait response " "(milliseconds).\n"); printf(" Default %d.\n", ICMP_POLL_DELAY); printf(" -s specifies the number of data bytes to be sent. " "Default %u.\n", ICMP_PING_DATALEN); printf(" -h shows this text and exits.\n"); exit(exitcode); } /**************************************************************************** * Name: ping_result ****************************************************************************/ static void ping_result(FAR const struct ping_result_s *result) { FAR struct ping_priv_s *priv = result->info->priv; if (result->code < 0) { priv->code = result->code; } switch (result->code) { case ICMP_E_HOSTIP: fprintf(stderr, "ERROR: ping_gethostip(%s) failed\n", result->info->hostname); break; case ICMP_E_MEMORY: fprintf(stderr, "ERROR: Failed to allocate memory\n"); break; case ICMP_E_SOCKET: fprintf(stderr, "ERROR: socket() failed: %ld\n", result->extra); break; case ICMP_I_BEGIN: printf("PING %u.%u.%u.%u %u bytes of data\n", ip4_addr1(result->dest.s_addr), ip4_addr2(result->dest.s_addr), ip4_addr3(result->dest.s_addr), ip4_addr4(result->dest.s_addr), result->info->datalen); break; case ICMP_E_SENDTO: fprintf(stderr, "ERROR: sendto failed at seqno %u: %ld\n", result->seqno, result->extra); break; case ICMP_E_SENDSMALL: fprintf(stderr, "ERROR: sendto returned %ld, expected %u\n", result->extra, result->outsize); break; case ICMP_E_POLL: fprintf(stderr, "ERROR: poll failed: %ld\n", result->extra); break; case ICMP_W_TIMEOUT: printf("No response from %u.%u.%u.%u: icmp_seq=%u time=%ld ms\n", ip4_addr1(result->dest.s_addr), ip4_addr2(result->dest.s_addr), ip4_addr3(result->dest.s_addr), ip4_addr4(result->dest.s_addr), result->seqno, result->extra); break; case ICMP_E_RECVFROM: fprintf(stderr, "ERROR: recvfrom failed: %ld\n", result->extra); break; case ICMP_E_RECVSMALL: fprintf(stderr, "ERROR: short ICMP packet: %ld\n", result->extra); break; case ICMP_W_IDDIFF: fprintf(stderr, "WARNING: Ignoring ICMP reply with ID %ld. " "Expected %u\n", result->extra, result->id); break; case ICMP_W_SEQNOBIG: fprintf(stderr, "WARNING: Ignoring ICMP reply to sequence %ld. " "Expected <= %u\n", result->extra, result->seqno); break; case ICMP_W_SEQNOSMALL: fprintf(stderr, "WARNING: Received after timeout\n"); break; case ICMP_I_ROUNDTRIP: priv->tsum += result->extra; priv->tsum2 += (long long)result->extra * result->extra; if (result->extra < priv->tmin) { priv->tmin = result->extra; } if (result->extra > priv->tmax) { priv->tmax = result->extra; } printf("%u bytes from %u.%u.%u.%u: icmp_seq=%u time=%ld.%ld ms\n", result->info->datalen, ip4_addr1(result->dest.s_addr), ip4_addr2(result->dest.s_addr), ip4_addr3(result->dest.s_addr), ip4_addr4(result->dest.s_addr), result->seqno, result->extra / USEC_PER_MSEC, result->extra % USEC_PER_MSEC / MSEC_PER_DSEC); break; case ICMP_W_RECVBIG: fprintf(stderr, "WARNING: Ignoring ICMP reply with different payload " "size: %ld vs %u\n", result->extra, result->outsize); break; case ICMP_W_DATADIFF: fprintf(stderr, "WARNING: Echoed data corrupted\n"); break; case ICMP_W_TYPE: fprintf(stderr, "WARNING: ICMP packet with unknown type: %ld\n", result->extra); break; case ICMP_I_FINISH: if (result->nrequests > 0) { unsigned int tmp; /* Calculate the percentage of lost packets */ tmp = (100 * (result->nrequests - result->nreplies) + (result->nrequests >> 1)) / result->nrequests; printf("%u packets transmitted, %u received, %u%% packet loss, " "time %ld ms\n", result->nrequests, result->nreplies, tmp, result->extra / USEC_PER_MSEC); if (result->nreplies > 0) { long avg = 0; long long tempnum = 0; long tmdev = 0; if (priv->tsum > 0) { avg = priv->tsum / result->nreplies; tempnum = priv->tsum2 / result->nreplies - (long long)avg * avg; tmdev = ub16toi(ub32sqrtub16(uitoub32(tempnum))); } printf("rtt min/avg/max/mdev = %ld.%03ld/%ld.%03ld/" "%ld.%03ld/%ld.%03ld ms\n", priv->tmin / USEC_PER_MSEC, priv->tmin % USEC_PER_MSEC, avg / USEC_PER_MSEC, avg % USEC_PER_MSEC, priv->tmax / USEC_PER_MSEC, priv->tmax % USEC_PER_MSEC, tmdev / USEC_PER_MSEC, tmdev % USEC_PER_MSEC); } } break; } } /**************************************************************************** * Public Functions ****************************************************************************/ int main(int argc, FAR char *argv[]) { struct ping_info_s info; struct ping_priv_s priv; FAR char *endptr; int exitcode; int option; info.count = ICMP_NPINGS; info.datalen = ICMP_PING_DATALEN; info.delay = ICMP_POLL_DELAY; info.timeout = ICMP_POLL_DELAY; info.callback = ping_result; info.priv = &priv; priv.code = ICMP_I_OK; priv.tmin = LONG_MAX; priv.tmax = 0; priv.tsum = 0; priv.tsum2 = 0; /* Parse command line options */ exitcode = EXIT_FAILURE; while ((option = getopt(argc, argv, ":c:i:W:s:h")) != ERROR) { switch (option) { case 'c': { long count = strtol(optarg, &endptr, 10); if (count < 1 || count > UINT16_MAX) { fprintf(stderr, "ERROR: out of range: %ld\n", count); goto errout_with_usage; } info.count = (uint16_t)count; } break; case 'i': { long delay = strtol(optarg, &endptr, 10); if (delay < 1 || delay > UINT16_MAX) { fprintf(stderr, "ERROR: out of range: %ld\n", delay); goto errout_with_usage; } info.delay = (int16_t)delay; } break; case 'W': { long timeout = strtol(optarg, &endptr, 10); if (timeout < 1 || timeout > UINT16_MAX) { fprintf(stderr, "ERROR: out of range: %ld\n", timeout); goto errout_with_usage; } info.timeout = (int16_t)timeout; } break; case 's': { long datalen = strtol(optarg, &endptr, 10); if (datalen < 1 || datalen > UINT16_MAX) { fprintf(stderr, "ERROR: out of range: %ld\n", datalen); goto errout_with_usage; } info.datalen = (uint16_t)datalen; } break; case 'h': exitcode = EXIT_SUCCESS; goto errout_with_usage; case ':': fprintf(stderr, "ERROR: Missing required argument\n"); goto errout_with_usage; case '?': default: fprintf(stderr, "ERROR: Unrecognized option\n"); goto errout_with_usage; } } /* There should be one final parameters remaining on the command line */ if (optind >= argc) { printf("ERROR: Missing required argument\n"); goto errout_with_usage; } info.hostname = argv[optind]; icmp_ping(&info); return priv.code < 0 ? EXIT_FAILURE: EXIT_SUCCESS; errout_with_usage: optind = 0; show_usage(argv[0], exitcode); return exitcode; /* Not reachable */ }