diff --git a/include/netutils/icmp_ping.h b/include/netutils/icmp_ping.h new file mode 100644 index 000000000..64837342e --- /dev/null +++ b/include/netutils/icmp_ping.h @@ -0,0 +1,131 @@ +/**************************************************************************** + * apps/include/netutils/icmp_ping.h + * + * Copyright (C) 2018 Pinecone Inc. All rights reserved. + * Author: Guiding Li + * + * Extracted from logic originally written by: + * + * Copyright (C) 2017-2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 NuttX 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 OWNER 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. + * + ****************************************************************************/ + +#ifndef __APPS_INCLUDE_NETUTILS_ICMP_PING_H +#define __APPS_INCLUDE_NETUTILS_ICMP_PING_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Positive number represent information */ + +#define ICMP_I_BEGIN 0 /* extra: not used */ +#define ICMP_I_ROUNDTRIP 1 /* extra: packet delay */ +#define ICMP_I_FINISH 2 /* extra: elapsed time */ + +/* Negative odd number represent error(unrecoverable) */ + +#define ICMP_E_HOSTIP -1 /* extra: not used */ +#define ICMP_E_MEMORY -3 /* extra: not used */ +#define ICMP_E_SOCKET -5 /* extra: error code */ +#define ICMP_E_SENDTO -7 /* extra: error code */ +#define ICMP_E_SENDSMALL -9 /* extra: sent bytes */ +#define ICMP_E_POLL -11 /* extra: error code */ +#define ICMP_E_RECVFROM -13 /* extra: error code */ +#define ICMP_E_RECVSMALL -15 /* extra: recv bytes */ + +/* Negative even number represent warning(recoverable) */ + +#define ICMP_W_TIMEOUT -2 /* extra: timeout value */ +#define ICMP_W_IDDIFF -4 /* extra: recv id */ +#define ICMP_W_SEQNOBIG -6 /* extra: recv seqno */ +#define ICMP_W_SEQNOSMALL -8 /* extra: recv seqno */ +#define ICMP_W_RECVBIG -10 /* extra: recv bytes */ +#define ICMP_W_DATADIFF -12 /* extra: not used */ +#define ICMP_W_TYPE -14 /* extra: recv type */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct ping_result_s; + +struct ping_info_s +{ + FAR const char *hostname; /* Host name to ping */ + uint16_t count; /* Number of pings requested */ + uint16_t datalen; /* Number of bytes to be sent */ + uint16_t delay; /* Deciseconds to delay between pings */ + uint16_t timeout; /* Deciseconds to wait response before timeout */ + FAR void *priv; /* Private context for callback */ + void (*callback)(FAR const struct ping_result_s *result); +}; + +struct ping_result_s +{ + int code; /* Notice code ICMP_I/E/W_XXX */ + int extra; /* Extra information for code */ + struct in_addr dest; /* Target address to ping */ + uint16_t nrequests; /* Number of ICMP ECHO requests sent */ + uint16_t nreplies; /* Number of matching ICMP ECHO replies received */ + uint16_t outsize; /* Bytes(include ICMP header) to be sent */ + uint16_t id; /* ICMP_ECHO id */ + uint16_t seqno; /* ICMP_ECHO seqno */ + FAR const struct ping_info_s *info; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +void icmp_ping(FAR const struct ping_info_s *info); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __APPS_INCLUDE_NETUTILS_ICMP_PING_H */ diff --git a/include/netutils/icmpv6_ping.h b/include/netutils/icmpv6_ping.h new file mode 100644 index 000000000..96c213ce4 --- /dev/null +++ b/include/netutils/icmpv6_ping.h @@ -0,0 +1,131 @@ +/**************************************************************************** + * apps/include/netutils/icmpv6_ping.h + * + * Copyright (C) 2018 Pinecone Inc. All rights reserved. + * Author: Guiding Li + * + * Extracted from logic originally written by: + * + * Copyright (C) 2017-2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 NuttX 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 OWNER 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. + * + ****************************************************************************/ + +#ifndef __APPS_INCLUDE_NETUTILS_ICMPV6_PING_H +#define __APPS_INCLUDE_NETUTILS_ICMPV6_PING_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Positive number represent information */ + +#define ICMPv6_I_BEGIN 0 /* extra: not used */ +#define ICMPv6_I_ROUNDTRIP 1 /* extra: packet delay */ +#define ICMPv6_I_FINISH 2 /* extra: elapsed time */ + +/* Negative odd number represent error(unrecoverable) */ + +#define ICMPv6_E_HOSTIP -1 /* extra: not used */ +#define ICMPv6_E_MEMORY -3 /* extra: not used */ +#define ICMPv6_E_SOCKET -5 /* extra: error code */ +#define ICMPv6_E_SENDTO -7 /* extra: error code */ +#define ICMPv6_E_SENDSMALL -9 /* extra: sent bytes */ +#define ICMPv6_E_POLL -11 /* extra: error code */ +#define ICMPv6_E_RECVFROM -13 /* extra: error code */ +#define ICMPv6_E_RECVSMALL -15 /* extra: recv bytes */ + +/* Negative even number represent warning(recoverable) */ + +#define ICMPv6_W_TIMEOUT -2 /* extra: timeout value */ +#define ICMPv6_W_IDDIFF -4 /* extra: recv id */ +#define ICMPv6_W_SEQNOBIG -6 /* extra: recv seqno */ +#define ICMPv6_W_SEQNOSMALL -8 /* extra: recv seqno */ +#define ICMPv6_W_RECVBIG -10 /* extra: recv bytes */ +#define ICMPv6_W_DATADIFF -12 /* extra: not used */ +#define ICMPv6_W_TYPE -14 /* extra: recv type */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct ping6_result_s; + +struct ping6_info_s +{ + FAR const char *hostname; /* Host name to ping */ + uint16_t count; /* Number of pings requested */ + uint16_t datalen; /* Number of bytes to be sent */ + uint16_t delay; /* Deciseconds to delay between pings */ + uint16_t timeout; /* Deciseconds to wait response before timeout */ + FAR void *priv; /* Private context for callback */ + void (*callback)(FAR const struct ping6_result_s *result); +}; + +struct ping6_result_s +{ + int code; /* Notice code ICMPv6_I/E/W_XXX */ + int extra; /* Extra information for code */ + struct in6_addr dest; /* Target address to ping */ + uint16_t nrequests; /* Number of ICMP ECHO requests sent */ + uint16_t nreplies; /* Number of matching ICMP ECHO replies received */ + uint16_t outsize; /* Bytes(include ICMP header) to be sent */ + uint16_t id; /* ICMPv6_ECHO id */ + uint16_t seqno; /* ICMPv6_ECHO seqno */ + FAR const struct ping6_info_s *info; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +void icmp6_ping(FAR const struct ping6_info_s *info); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __APPS_INCLUDE_NETUTILS_ICMP_PING_H */ diff --git a/netutils/ping/Kconfig b/netutils/ping/Kconfig new file mode 100644 index 000000000..3fcd96f80 --- /dev/null +++ b/netutils/ping/Kconfig @@ -0,0 +1,22 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config NETUTILS_PING + bool "ICMP ping support" + default n + depends on NET_ICMP + help + Build in support for a IPv4 ping command. This command ping will + send the ICMP ECHO_REQUEST and wait for the ICMP ECHO_RESPONSE from + the remote peer. + + config NETUTILS_PING6 + bool "ICMPv6 ping support" + default n + depends on NET_ICMPv6 + help + Build in support for a IPv6 ping command. This command ping will + send the ICMPv6 ECHO_REQUEST and wait for the ICMPv6 ECHO_RESPONSE + from the remote peer. diff --git a/netutils/ping/Make.defs b/netutils/ping/Make.defs new file mode 100644 index 000000000..edf1bb2d6 --- /dev/null +++ b/netutils/ping/Make.defs @@ -0,0 +1,37 @@ +############################################################################ +# apps/netutils/ping/Make.defs +# Adds selected applications to apps/ build +# +# Copyright (C) 2016 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# 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 NuttX 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 OWNER 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. +# +############################################################################ + +CONFIGURED_APPS += netutils/ping diff --git a/netutils/ping/Makefile b/netutils/ping/Makefile new file mode 100644 index 000000000..6f8482b5f --- /dev/null +++ b/netutils/ping/Makefile @@ -0,0 +1,52 @@ +############################################################################ +# apps/netutils/ping/Makefile +# +# Copyright (C) 2015 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# 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 NuttX 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 OWNER 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 $(TOPDIR)/.config +-include $(TOPDIR)/Make.defs +include $(APPDIR)/Make.defs + +CSRCS = + +ifeq ($(CONFIG_NETUTILS_PING),y) + CSRCS += icmp_ping.c +endif + +ifeq ($(CONFIG_NETUTILS_PING6),y) + CSRCS += icmpv6_ping.c +endif + +include $(APPDIR)/Application.mk + + diff --git a/netutils/ping/icmp_ping.c b/netutils/ping/icmp_ping.c new file mode 100644 index 000000000..3b67cf04d --- /dev/null +++ b/netutils/ping/icmp_ping.c @@ -0,0 +1,407 @@ +/**************************************************************************** + * apps/netutils/ping/icmp_ping.c + * + * Copyright (C) 2018 Pinecone Inc. All rights reserved. + * Author: Guiding Li + * + * Extracted from logic originally written by: + * + * Copyright (C) 2017-2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 NuttX 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 OWNER 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 + +#include + +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_LIBC_NETDB) && defined(CONFIG_NETDB_DNSCLIENT) +# include +#endif + +#include + +#include +#include + +#include "netutils/icmp_ping.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ICMP_IOBUFFER_SIZE(x) (sizeof(struct icmp_hdr_s) + (x)) + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* NOTE: This will not work in the kernel build where there will be a + * separate instance of g_pingid in every process space. + */ + +static uint16_t g_pingid = 0; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ping_newid + ****************************************************************************/ + +static inline uint16_t ping_newid(void) +{ + /* Revisit: No thread safe */ + + return ++g_pingid; +} + +/**************************************************************************** + * Name: ping_gethostip + * + * Description: + * Call gethostbyname() to get the IP address associated with a hostname. + * + * Input Parameters + * hostname - The host name to use in the nslookup. + * ipv4addr - The location to return the IPv4 address. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int ping_gethostip(FAR const char *hostname, FAR struct in_addr *dest) +{ +#if defined(CONFIG_LIBC_NETDB) && defined(CONFIG_NETDB_DNSCLIENT) + /* Netdb DNS client support is enabled */ + + FAR struct hostent *he; + + he = gethostbyname(hostname); + if (he == NULL) + { + return -ENOENT; + } + else if (he->h_addrtype == AF_INET) + { + memcpy(dest, he->h_addr, sizeof(in_addr_t)); + } + else + { + return -ENOEXEC; + } + + return OK; + +#else /* CONFIG_LIBC_NETDB */ + + /* No host name support */ + /* Convert strings to numeric IPv6 address */ + + int ret = inet_pton(AF_INET, hostname, dest); + + /* The inet_pton() function returns 1 if the conversion succeeds. It will + * return 0 if the input is not a valid IPv4 dotted-decimal string or -1 + * with errno set to EAFNOSUPPORT if the address family argument is + * unsupported. + */ + + return (ret > 0) ? OK : ERROR; + +#endif /* CONFIG_LIBC_NETDB */ +} + +/**************************************************************************** + * Name: icmp_callback + ****************************************************************************/ + +static void icmp_callback(FAR struct ping_result_s *result, int code, int extra) +{ + result->code = code; + result->extra = extra; + result->info->callback(result); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: icmp_ping + ****************************************************************************/ + +void icmp_ping(FAR const struct ping_info_s *info) +{ + struct ping_result_s result; + struct sockaddr_in destaddr; + struct sockaddr_in fromaddr; + struct icmp_hdr_s outhdr; + FAR struct icmp_hdr_s *inhdr; + struct pollfd recvfd; + FAR uint8_t *iobuffer; + FAR uint8_t *ptr; + int32_t elapsed; + clock_t kickoff; + clock_t start; + socklen_t addrlen; + ssize_t nsent; + ssize_t nrecvd; + bool retry; + int sockfd; + int ret; + int ch; + int i; + + /* Initialize result structure */ + + memset(&result, 0, sizeof(result)); + result.info = info; + result.id = ping_newid(); + result.outsize = ICMP_IOBUFFER_SIZE(info->datalen); + if (ping_gethostip(info->hostname, &result.dest) < 0) + { + icmp_callback(&result, ICMP_E_HOSTIP, 0); + return; + } + + /* Allocate memory to hold ping buffer */ + + iobuffer = (FAR uint8_t *)malloc(result.outsize); + if (iobuffer == NULL) + { + icmp_callback(&result, ICMP_E_MEMORY, 0); + return; + } + + sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); + if (sockfd < 0) + { + icmp_callback(&result, ICMP_E_SOCKET, errno); + free(iobuffer); + return; + } + + kickoff = clock(); + + memset(&destaddr, 0, sizeof(struct sockaddr_in)); + destaddr.sin_family = AF_INET; + destaddr.sin_port = 0; + destaddr.sin_addr.s_addr = result.dest.s_addr; + + memset(&outhdr, 0, sizeof(struct icmp_hdr_s)); + outhdr.type = ICMP_ECHO_REQUEST; + outhdr.id = htons(result.id); + outhdr.seqno = htons(result.seqno); + + icmp_callback(&result, ICMP_I_BEGIN, 0); + + while (result.nrequests < info->count) + { + /* Copy the ICMP header into the I/O buffer */ + + memcpy(iobuffer, &outhdr, sizeof(struct icmp_hdr_s)); + + /* Add some easily verifiable payload data */ + + ptr = &iobuffer[sizeof(struct icmp_hdr_s)]; + ch = 0x20; + + for (i = 0; i < info->datalen; i++) + { + *ptr++ = ch; + if (++ch > 0x7e) + { + ch = 0x20; + } + } + + start = clock(); + nsent = sendto(sockfd, iobuffer, result.outsize, 0, + (FAR struct sockaddr*)&destaddr, + sizeof(struct sockaddr_in)); + if (nsent < 0) + { + icmp_callback(&result, ICMP_E_SENDTO, errno); + goto done; + } + else if (nsent != result.outsize) + { + icmp_callback(&result, ICMP_E_SENDSMALL, nsent); + goto done; + } + + result.nrequests++; + + elapsed = 0; + do + { + retry = false; + + recvfd.fd = sockfd; + recvfd.events = POLLIN; + recvfd.revents = 0; + + ret = poll(&recvfd, 1, info->timeout - elapsed); + if (ret < 0) + { + icmp_callback(&result, ICMP_E_POLL, errno); + goto done; + } + else if (ret == 0) + { + icmp_callback(&result, ICMP_W_TIMEOUT, info->timeout); + continue; + } + + /* Get the ICMP response (ignoring the sender) */ + + addrlen = sizeof(struct sockaddr_in); + nrecvd = recvfrom(sockfd, iobuffer, result.outsize, 0, + (FAR struct sockaddr *)&fromaddr, &addrlen); + if (nrecvd < 0) + { + icmp_callback(&result, ICMP_E_RECVFROM, errno); + goto done; + } + else if (nrecvd < sizeof(struct icmp_hdr_s)) + { + icmp_callback(&result, ICMP_E_RECVSMALL, nrecvd); + goto done; + } + + elapsed = (unsigned int)TICK2MSEC(clock() - start); + inhdr = (FAR struct icmp_hdr_s *)iobuffer; + + if (inhdr->type == ICMP_ECHO_REPLY) + { + if (ntohs(inhdr->id) != result.id) + { + icmp_callback(&result, ICMP_W_IDDIFF, ntohs(inhdr->id)); + retry = true; + } + else if (ntohs(inhdr->seqno) > result.seqno) + { + icmp_callback(&result, ICMP_W_SEQNOBIG, ntohs(inhdr->seqno)); + retry = true; + } + else + { + bool verified = true; + int32_t pktdelay = elapsed; + + if (ntohs(inhdr->seqno) < result.seqno) + { + icmp_callback(&result, ICMP_W_SEQNOSMALL, ntohs(inhdr->seqno)); + pktdelay += info->delay; + retry = true; + } + + icmp_callback(&result, ICMP_I_ROUNDTRIP, pktdelay); + + /* Verify the payload data */ + + if (nrecvd != result.outsize) + { + icmp_callback(&result, ICMP_W_RECVBIG, nrecvd); + verified = false; + } + else + { + ptr = &iobuffer[sizeof(struct icmp_hdr_s)]; + ch = 0x20; + + for (i = 0; i < info->datalen; i++, ptr++) + { + if (*ptr != ch) + { + icmp_callback(&result, ICMP_W_DATADIFF, 0); + verified = false; + break; + } + + if (++ch > 0x7e) + { + ch = 0x20; + } + } + } + + /* Only count the number of good replies */ + + if (verified) + { + result.nreplies++; + } + } + } + else + { + icmp_callback(&result, ICMP_W_TYPE, inhdr->type); + } + } + while (retry && info->delay > elapsed && info->timeout > elapsed); + + /* Wait if necessary to preserved the requested ping rate */ + + elapsed = (unsigned int)TICK2MSEC(clock() - start); + if (elapsed < info->delay) + { + struct timespec rqt; + unsigned int remaining; + unsigned int sec; + unsigned int frac; /* In deciseconds */ + + remaining = info->delay - elapsed; + sec = remaining / MSEC_PER_SEC; + frac = remaining - MSEC_PER_SEC * sec; + + rqt.tv_sec = sec; + rqt.tv_nsec = frac * NSEC_PER_MSEC; + + (void)nanosleep(&rqt, NULL); + } + + outhdr.seqno = htons(++result.seqno); + } + +done: + icmp_callback(&result, ICMP_I_FINISH, TICK2MSEC(clock() - kickoff)); + close(sockfd); + free(iobuffer); +} diff --git a/netutils/ping/icmpv6_ping.c b/netutils/ping/icmpv6_ping.c new file mode 100644 index 000000000..aea59eed8 --- /dev/null +++ b/netutils/ping/icmpv6_ping.c @@ -0,0 +1,409 @@ +/**************************************************************************** + * apps/netutils/ping/icmpv6_ping.c + * + * Copyright (C) 2018 Pinecone Inc. All rights reserved. + * Author: Guiding Li + * + * Extracted from logic originally written by: + * + * Copyright (C) 2017-2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 NuttX 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 OWNER 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_LIBC_NETDB) && defined(CONFIG_NETDB_DNSCLIENT) +# include +#endif + +#include + +#include +#include +#include "netutils/icmpv6_ping.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ICMPv6_IOBUFFER_SIZE(x) (SIZEOF_ICMPV6_ECHO_REQUEST_S(0) + (x)) + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* NOTE: This will not work in the kernel build where there will be a + * separate instance of g_ping6_id in every process space. + */ + +static uint16_t g_ping6_id = 0; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ping6_newid + ****************************************************************************/ + +static inline uint16_t ping6_newid(void) +{ + /* Revisit: No thread safe */ + + return ++g_ping6_id; +} + +/**************************************************************************** + * Name: ping6_gethostip + * + * Description: + * Call gethostbyname() to get the IP address associated with a hostname. + * + * Input Parameters + * hostname - The host name to use in the nslookup. + * ipv4addr - The location to return the IPv4 address. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int ping6_gethostip(FAR const char *hostname, FAR struct in6_addr *dest) +{ +#if defined(CONFIG_LIBC_NETDB) && defined(CONFIG_NETDB_DNSCLIENT) + /* Netdb DNS client support is enabled */ + + FAR struct hostent *he; + + he = gethostbyname(hostname); + if (he == NULL) + { + return -ENOENT; + } + else if (he->h_addrtype == AF_INET6) + { + memcpy(dest, he->h_addr, sizeof(struct in6_addr)); + } + else + { + return -ENOEXEC; + } + + return OK; + +#else /* CONFIG_LIBC_NETDB */ + + /* No host name support */ + /* Convert strings to numeric IPv6 address */ + + int ret = inet_pton(AF_INET6, hostname, dest->s6_addr16); + + /* The inet_pton() function returns 1 if the conversion succeeds. It will + * return 0 if the input is not a valid IPv6 address string, or -1 with + * errno set to EAFNOSUPPORT if the address family argument is unsupported. + */ + + return (ret > 0) ? OK : ERROR; + +#endif /* CONFIG_LIBC_NETDB */ +} + +/**************************************************************************** + * Name: icmp6_callback + ****************************************************************************/ + +static void icmp6_callback(FAR struct ping6_result_s *result, int code, int extra) +{ + result->code = code; + result->extra = extra; + result->info->callback(result); +} + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Name: icmp6_ping + ****************************************************************************/ + +void icmp6_ping(FAR const struct ping6_info_s *info) +{ + struct ping6_result_s result; + struct sockaddr_in6 destaddr; + struct sockaddr_in6 fromaddr; + struct icmpv6_echo_request_s outhdr; + FAR struct icmpv6_echo_reply_s *inhdr; + struct pollfd recvfd; + FAR uint8_t *iobuffer; + FAR uint8_t *ptr; + int32_t elapsed; + clock_t kickoff; + clock_t start; + socklen_t addrlen; + ssize_t nsent; + ssize_t nrecvd; + bool retry; + int sockfd; + int ret; + int ch; + int i; + + /* Initialize result structure */ + + memset(&result, 0, sizeof(result)); + result.info = info; + result.id = ping6_newid(); + result.outsize = ICMPv6_IOBUFFER_SIZE(info->datalen); + if (ping6_gethostip(info->hostname, &result.dest) < 0) + { + icmp6_callback(&result, ICMPv6_E_HOSTIP, 0); + return; + } + + /* Allocate memory to hold ping buffer */ + + iobuffer = (FAR uint8_t *)malloc(result.outsize); + if (iobuffer == NULL) + { + icmp6_callback(&result, ICMPv6_E_MEMORY, 0); + return; + } + + sockfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMP6); + if (sockfd < 0) + { + icmp6_callback(&result, ICMPv6_E_SOCKET, errno); + free(iobuffer); + return; + } + + kickoff = clock(); + + memset(&destaddr, 0, sizeof(struct sockaddr_in6)); + destaddr.sin6_family = AF_INET6; + destaddr.sin6_port = 0; + memcpy(&destaddr.sin6_addr, &result.dest, sizeof(struct in6_addr)); + + memset(&outhdr, 0, SIZEOF_ICMPV6_ECHO_REQUEST_S(0)); + outhdr.type = ICMPv6_ECHO_REQUEST; + outhdr.id = htons(result.id); + outhdr.seqno = htons(result.seqno); + + icmp6_callback(&result, ICMPv6_I_BEGIN, 0); + + while (result.nrequests < info->count) + { + /* Copy the ICMP header into the I/O buffer */ + + memcpy(iobuffer, &outhdr, SIZEOF_ICMPV6_ECHO_REQUEST_S(0)); + + /* Add some easily verifiable payload data */ + + ptr = &iobuffer[SIZEOF_ICMPV6_ECHO_REQUEST_S(0)]; + ch = 0x20; + + for (i = 0; i < info->datalen; i++) + { + *ptr++ = ch; + if (++ch > 0x7e) + { + ch = 0x20; + } + } + + start = clock(); + nsent = sendto(sockfd, iobuffer, result.outsize, 0, + (FAR struct sockaddr*)&destaddr, + sizeof(struct sockaddr_in6)); + if (nsent < 0) + { + icmp6_callback(&result, ICMPv6_E_SENDTO, errno); + goto done; + } + else if (nsent != result.outsize) + { + icmp6_callback(&result, ICMPv6_E_SENDSMALL, nsent); + goto done; + } + + result.nrequests++; + + elapsed = 0; + do + { + retry = false; + + recvfd.fd = sockfd; + recvfd.events = POLLIN; + recvfd.revents = 0; + + ret = poll(&recvfd, 1, info->timeout - elapsed); + if (ret < 0) + { + icmp6_callback(&result, ICMPv6_E_POLL, errno); + goto done; + } + else if (ret == 0) + { + icmp6_callback(&result, ICMPv6_W_TIMEOUT, info->timeout); + continue; + } + + /* Get the ICMP response (ignoring the sender) */ + + addrlen = sizeof(struct sockaddr_in6); + nrecvd = recvfrom(sockfd, iobuffer, result.outsize, 0, + (FAR struct sockaddr *)&fromaddr, &addrlen); + if (nrecvd < 0) + { + icmp6_callback(&result, ICMPv6_E_RECVFROM, errno); + goto done; + } + else if (nrecvd < SIZEOF_ICMPV6_ECHO_REPLY_S(0)) + { + icmp6_callback(&result, ICMPv6_E_RECVSMALL, nrecvd); + goto done; + } + + elapsed = (unsigned int)TICK2MSEC(clock() - start); + inhdr = (FAR struct icmpv6_echo_reply_s *)iobuffer; + + if (inhdr->type == ICMPv6_ECHO_REPLY) + { + if (ntohs(inhdr->id) != result.id) + { + icmp6_callback(&result, ICMPv6_W_IDDIFF, ntohs(inhdr->id)); + retry = true; + } + else if (ntohs(inhdr->seqno) > result.seqno) + { + icmp6_callback(&result, ICMPv6_W_SEQNOBIG, ntohs(inhdr->seqno)); + retry = true; + } + else + { + bool verified = true; + int32_t pktdelay = elapsed; + + if (ntohs(inhdr->seqno) < result.seqno) + { + icmp6_callback(&result, ICMPv6_W_SEQNOSMALL, ntohs(inhdr->seqno)); + pktdelay += info->delay; + retry = true; + } + + icmp6_callback(&result, ICMPv6_I_ROUNDTRIP, pktdelay); + + /* Verify the payload data */ + + if (nrecvd != result.outsize) + { + icmp6_callback(&result, ICMPv6_W_RECVBIG, nrecvd); + verified = false; + } + else + { + ptr = &iobuffer[SIZEOF_ICMPV6_ECHO_REPLY_S(0)]; + ch = 0x20; + + for (i = 0; i < info->datalen; i++, ptr++) + { + if (*ptr != ch) + { + icmp6_callback(&result, ICMPv6_W_DATADIFF, 0); + verified = false; + break; + } + + if (++ch > 0x7e) + { + ch = 0x20; + } + } + } + + /* Only count the number of good replies */ + + if (verified) + { + result.nreplies++; + } + } + } + else + { + icmp6_callback(&result, ICMPv6_W_TYPE, inhdr->type); + } + } + while (retry && info->delay > elapsed && info->timeout > elapsed); + + /* Wait if necessary to preserved the requested ping rate */ + + elapsed = (unsigned int)TICK2MSEC(clock() - start); + if (elapsed < info->delay) + { + struct timespec rqt; + unsigned int remaining; + unsigned int sec; + unsigned int frac; /* In deciseconds */ + + remaining = info->delay - elapsed; + sec = remaining / MSEC_PER_SEC; + frac = remaining - MSEC_PER_SEC * sec; + + rqt.tv_sec = sec; + rqt.tv_nsec = frac * NSEC_PER_MSEC; + + (void)nanosleep(&rqt, NULL); + } + + outhdr.seqno = htons(++result.seqno); + } + +done: + icmp6_callback(&result, ICMPv6_I_FINISH, TICK2MSEC(clock() - kickoff)); + close(sockfd); + free(iobuffer); +} diff --git a/system/ping/Kconfig b/system/ping/Kconfig index 3283bdc1e..41dc0427b 100644 --- a/system/ping/Kconfig +++ b/system/ping/Kconfig @@ -6,7 +6,8 @@ config SYSTEM_PING tristate "ICMP 'ping' command" default n - depends on NET_ICMP_SOCKET || NET_USRSOCK + depends on NET_ICMP + select NETUTILS_PING ---help--- Enable support for the ICMP 'ping' command. diff --git a/system/ping/ping.c b/system/ping/ping.c index 2fece4ce7..3fc278f02 100644 --- a/system/ping/ping.c +++ b/system/ping/ping.c @@ -39,435 +39,27 @@ #include -#include -#include - #include #include #include -#include -#include #include #include -#if defined(CONFIG_LIBC_NETDB) && defined(CONFIG_NETDB_DNSCLIENT) -# include -#endif - -#include -#include - -#include +#include "netutils/icmp_ping.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ -/* Positive number represent information */ - -#define ICMP_I_BEGIN 0 /* extra: not used */ -#define ICMP_I_ROUNDTRIP 1 /* extra: packet delay */ -#define ICMP_I_FINISH 2 /* extra: elapsed time */ - -/* Negative odd number represent error(unrecoverable) */ - -#define ICMP_E_HOSTIP -1 /* extra: not used */ -#define ICMP_E_MEMORY -3 /* extra: not used */ -#define ICMP_E_SOCKET -5 /* extra: error code */ -#define ICMP_E_SENDTO -7 /* extra: error code */ -#define ICMP_E_SENDSMALL -9 /* extra: sent bytes */ -#define ICMP_E_POLL -11 /* extra: error code */ -#define ICMP_E_RECVFROM -13 /* extra: error code */ -#define ICMP_E_RECVSMALL -15 /* extra: recv bytes */ - -/* Negative even number represent warning(recoverable) */ - -#define ICMP_W_TIMEOUT -2 /* extra: timeout value */ -#define ICMP_W_IDDIFF -4 /* extra: recv id */ -#define ICMP_W_SEQNOBIG -6 /* extra: recv seqno */ -#define ICMP_W_SEQNOSMALL -8 /* extra: recv seqno */ -#define ICMP_W_RECVBIG -10 /* extra: recv bytes */ -#define ICMP_W_DATADIFF -12 /* extra: not used */ -#define ICMP_W_TYPE -14 /* extra: recv type */ - #define ICMP_PING_DATALEN 56 -#define ICMP_IOBUFFER_SIZE(x) (sizeof(struct icmp_hdr_s) + (x)) #define ICMP_NPINGS 10 /* Default number of pings */ #define ICMP_POLL_DELAY 1000 /* 1 second in milliseconds */ -/**************************************************************************** - * Private Types - ****************************************************************************/ - -struct ping_result_s; - -struct ping_info_s -{ - FAR const char *hostname; /* Host name to ping */ - uint16_t count; /* Number of pings requested */ - uint16_t datalen; /* Number of bytes to be sent */ - uint16_t nrequests; /* Number of ICMP ECHO requests sent */ - uint16_t nreplies; /* Number of matching ICMP ECHO replies received */ - uint16_t delay; /* Deciseconds to delay between pings */ - uint16_t timeout; /* Deciseconds to wait response before timeout */ - FAR void *priv; /* Private context for callback */ - void (*callback)(FAR const struct ping_result_s *result); -}; - -struct ping_result_s -{ - int code; /* Notice code ICMP_I/E/W_XXX */ - int extra; /* Extra information for code */ - struct in_addr dest; /* Target address to ping */ - uint16_t nrequests; /* Number of ICMP ECHO requests sent */ - uint16_t nreplies; /* Number of matching ICMP ECHO replies received */ - uint16_t outsize; /* Bytes(include ICMP header) to be sent */ - uint16_t id; /* ICMP_ECHO id */ - uint16_t seqno; /* ICMP_ECHO seqno */ - FAR const struct ping_info_s *info; -}; - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/* NOTE: This will not work in the kernel build where there will be a - * separate instance of g_pingid in every process space. - */ - -static uint16_t g_pingid = 0; - /**************************************************************************** * Private Functions ****************************************************************************/ -/**************************************************************************** - * Name: ping_newid - ****************************************************************************/ - -static inline uint16_t ping_newid(void) -{ - /* Revisit: No thread safe */ - - return ++g_pingid; -} - -/**************************************************************************** - * Name: ping_gethostip - * - * Description: - * Call gethostbyname() to get the IP address associated with a hostname. - * - * Input Parameters - * hostname - The host name to use in the nslookup. - * ipv4addr - The location to return the IPv4 address. - * - * Returned Value: - * Zero (OK) on success; a negated errno value on failure. - * - ****************************************************************************/ - -static int ping_gethostip(FAR const char *hostname, FAR struct in_addr *dest) -{ -#if defined(CONFIG_LIBC_NETDB) && defined(CONFIG_NETDB_DNSCLIENT) - /* Netdb DNS client support is enabled */ - - FAR struct hostent *he; - - he = gethostbyname(hostname); - if (he == NULL) - { - nerr("ERROR: gethostbyname failed: %d\n", h_errno); - return -ENOENT; - } - else if (he->h_addrtype == AF_INET) - { - memcpy(dest, he->h_addr, sizeof(in_addr_t)); - } - else - { - nerr("ERROR: gethostbyname returned an address of type: %d\n", - he->h_addrtype); - return -ENOEXEC; - } - - return OK; - -#else /* CONFIG_LIBC_NETDB */ - - /* No host name support */ - /* Convert strings to numeric IPv6 address */ - - int ret = inet_pton(AF_INET, hostname, dest); - - /* The inet_pton() function returns 1 if the conversion succeeds. It will - * return 0 if the input is not a valid IPv4 dotted-decimal string or -1 - * with errno set to EAFNOSUPPORT if the address family argument is - * unsupported. - */ - - return (ret > 0) ? OK : ERROR; - -#endif /* CONFIG_LIBC_NETDB */ -} - -/**************************************************************************** - * Name: icmp_callback - ****************************************************************************/ - -static void icmp_callback(FAR struct ping_result_s *result, int code, - int extra) -{ - result->code = code; - result->extra = extra; - result->info->callback(result); -} - -/**************************************************************************** - * Name: icmp_ping - ****************************************************************************/ - -static void icmp_ping(FAR const struct ping_info_s *info) -{ - struct ping_result_s result; - struct sockaddr_in destaddr; - struct sockaddr_in fromaddr; - struct icmp_hdr_s outhdr; - FAR struct icmp_hdr_s *inhdr; - struct pollfd recvfd; - FAR uint8_t *iobuffer; - FAR uint8_t *ptr; - int32_t elapsed; - clock_t kickoff; - clock_t start; - socklen_t addrlen; - ssize_t nsent; - ssize_t nrecvd; - bool retry; - int sockfd; - int ret; - int ch; - int i; - - /* Initialize result structure */ - - memset(&result, 0, sizeof(result)); - result.info = info; - result.id = ping_newid(); - result.outsize = ICMP_IOBUFFER_SIZE(info->datalen); - if (ping_gethostip(info->hostname, &result.dest) < 0) - { - icmp_callback(&result, ICMP_E_HOSTIP, 0); - return; - } - - /* Allocate memory to hold ping buffer */ - - iobuffer = (FAR uint8_t *)malloc(result.outsize); - if (iobuffer == NULL) - { - icmp_callback(&result, ICMP_E_MEMORY, 0); - return; - } - - sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); - if (sockfd < 0) - { - icmp_callback(&result, ICMP_E_SOCKET, errno); - free(iobuffer); - return; - } - - kickoff = clock(); - - memset(&destaddr, 0, sizeof(struct sockaddr_in)); - destaddr.sin_family = AF_INET; - destaddr.sin_port = 0; - destaddr.sin_addr.s_addr = result.dest.s_addr; - - memset(&outhdr, 0, sizeof(struct icmp_hdr_s)); - outhdr.type = ICMP_ECHO_REQUEST; - outhdr.id = htons(result.id); - outhdr.seqno = htons(result.seqno); - - icmp_callback(&result, ICMP_I_BEGIN, 0); - - while (result.nrequests < info->count) - { - /* Copy the ICMP header into the I/O buffer */ - - memcpy(iobuffer, &outhdr, sizeof(struct icmp_hdr_s)); - - /* Add some easily verifiable payload data */ - - ptr = &iobuffer[sizeof(struct icmp_hdr_s)]; - ch = 0x20; - - for (i = 0; i < info->datalen; i++) - { - *ptr++ = ch; - if (++ch > 0x7e) - { - ch = 0x20; - } - } - - start = clock(); - nsent = sendto(sockfd, iobuffer, result.outsize, 0, - (FAR struct sockaddr*)&destaddr, - sizeof(struct sockaddr_in)); - if (nsent < 0) - { - icmp_callback(&result, ICMP_E_SENDTO, errno); - goto done; - } - else if (nsent != result.outsize) - { - icmp_callback(&result, ICMP_E_SENDSMALL, nsent); - goto done; - } - - result.nrequests++; - - elapsed = 0; - do - { - retry = false; - - recvfd.fd = sockfd; - recvfd.events = POLLIN; - recvfd.revents = 0; - - ret = poll(&recvfd, 1, info->timeout - elapsed); - if (ret < 0) - { - icmp_callback(&result, ICMP_E_POLL, errno); - goto done; - } - else if (ret == 0) - { - icmp_callback(&result, ICMP_W_TIMEOUT, info->timeout); - continue; - } - - /* Get the ICMP response (ignoring the sender) */ - - addrlen = sizeof(struct sockaddr_in); - nrecvd = recvfrom(sockfd, iobuffer, result.outsize, 0, - (FAR struct sockaddr *)&fromaddr, &addrlen); - if (nrecvd < 0) - { - icmp_callback(&result, ICMP_E_RECVFROM, errno); - goto done; - } - else if (nrecvd < sizeof(struct icmp_hdr_s)) - { - icmp_callback(&result, ICMP_E_RECVSMALL, nrecvd); - goto done; - } - - elapsed = (unsigned int)TICK2MSEC(clock() - start); - inhdr = (FAR struct icmp_hdr_s *)iobuffer; - - if (inhdr->type == ICMP_ECHO_REPLY) - { - if (ntohs(inhdr->id) != result.id) - { - icmp_callback(&result, ICMP_W_IDDIFF, ntohs(inhdr->id)); - retry = true; - } - else if (ntohs(inhdr->seqno) > result.seqno) - { - icmp_callback(&result, ICMP_W_SEQNOBIG, ntohs(inhdr->seqno)); - retry = true; - } - else - { - bool verified = true; - int32_t pktdelay = elapsed; - - if (ntohs(inhdr->seqno) < result.seqno) - { - icmp_callback(&result, ICMP_W_SEQNOSMALL, ntohs(inhdr->seqno)); - pktdelay += info->delay; - retry = true; - } - - icmp_callback(&result, ICMP_I_ROUNDTRIP, pktdelay); - - /* Verify the payload data */ - - if (nrecvd != result.outsize) - { - icmp_callback(&result, ICMP_W_RECVBIG, nrecvd); - verified = false; - } - else - { - ptr = &iobuffer[sizeof(struct icmp_hdr_s)]; - ch = 0x20; - - for (i = 0; i < info->datalen; i++, ptr++) - { - if (*ptr != ch) - { - icmp_callback(&result, ICMP_W_DATADIFF, 0); - verified = false; - break; - } - - if (++ch > 0x7e) - { - ch = 0x20; - } - } - } - - /* Only count the number of good replies */ - - if (verified) - { - result.nreplies++; - } - } - } - else - { - icmp_callback(&result, ICMP_W_TYPE, inhdr->type); - } - } - while (retry && info->delay > elapsed && info->timeout > elapsed); - - /* Wait if necessary to preserved the requested ping rate */ - - elapsed = (unsigned int)TICK2MSEC(clock() - start); - if (elapsed < info->delay) - { - struct timespec rqt; - unsigned int remaining; - unsigned int sec; - unsigned int frac; /* In deciseconds */ - - remaining = info->delay - elapsed; - sec = remaining / MSEC_PER_SEC; - frac = remaining - MSEC_PER_SEC * sec; - - rqt.tv_sec = sec; - rqt.tv_nsec = frac * NSEC_PER_MSEC; - - (void)nanosleep(&rqt, NULL); - } - - outhdr.seqno = htons(++result.seqno); - } - -done: - icmp_callback(&result, ICMP_I_FINISH, TICK2MSEC(clock() - kickoff)); - close(sockfd); - free(iobuffer); -} - /**************************************************************************** * Name: show_usage ****************************************************************************/ diff --git a/system/ping6/ping6.c b/system/ping6/ping6.c index fe89662d7..8d7ddcda2 100644 --- a/system/ping6/ping6.c +++ b/system/ping6/ping6.c @@ -39,434 +39,29 @@ #include -#include -#include - #include #include #include -#include -#include #include #include -#if defined(CONFIG_LIBC_NETDB) && defined(CONFIG_NETDB_DNSCLIENT) -# include -#endif - -#include #include -#include +#include "netutils/icmpv6_ping.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ -/* Positive number represent information */ - -#define ICMPv6_I_BEGIN 0 /* extra: not used */ -#define ICMPv6_I_ROUNDTRIP 1 /* extra: packet delay */ -#define ICMPv6_I_FINISH 2 /* extra: elapsed time */ - -/* Negative odd number represent error(unrecoverable) */ - -#define ICMPv6_E_HOSTIP -1 /* extra: not used */ -#define ICMPv6_E_MEMORY -3 /* extra: not used */ -#define ICMPv6_E_SOCKET -5 /* extra: error code */ -#define ICMPv6_E_SENDTO -7 /* extra: error code */ -#define ICMPv6_E_SENDSMALL -9 /* extra: sent bytes */ -#define ICMPv6_E_POLL -11 /* extra: error code */ -#define ICMPv6_E_RECVFROM -13 /* extra: error code */ -#define ICMPv6_E_RECVSMALL -15 /* extra: recv bytes */ - -/* Negative even number represent warning(recoverable) */ - -#define ICMPv6_W_TIMEOUT -2 /* extra: timeout value */ -#define ICMPv6_W_IDDIFF -4 /* extra: recv id */ -#define ICMPv6_W_SEQNOBIG -6 /* extra: recv seqno */ -#define ICMPv6_W_SEQNOSMALL -8 /* extra: recv seqno */ -#define ICMPv6_W_RECVBIG -10 /* extra: recv bytes */ -#define ICMPv6_W_DATADIFF -12 /* extra: not used */ -#define ICMPv6_W_TYPE -14 /* extra: recv type */ - #define ICMPv6_PING6_DATALEN 56 -#define ICMPv6_IOBUFFER_SIZE(x) (SIZEOF_ICMPV6_ECHO_REQUEST_S(0) + (x)) #define ICMPv6_NPINGS 10 /* Default number of pings */ #define ICMPv6_POLL_DELAY 1000 /* 1 second in milliseconds */ -/**************************************************************************** - * Private Types - ****************************************************************************/ - -struct ping6_result_s; - -struct ping6_info_s -{ - FAR const char *hostname; /* Host name to ping */ - uint16_t count; /* Number of pings requested */ - uint16_t datalen; /* Number of bytes to be sent */ - uint16_t nrequests; /* Number of ICMP ECHO requests sent */ - uint16_t nreplies; /* Number of matching ICMP ECHO replies received */ - uint16_t delay; /* Deciseconds to delay between pings */ - uint16_t timeout; /* Deciseconds to wait response before timeout */ - FAR void *priv; /* Private context for callback */ - void (*callback)(FAR const struct ping6_result_s *result); -}; - -struct ping6_result_s -{ - int code; /* Notice code ICMPv6_I/E/W_XXX */ - int extra; /* Extra information for code */ - struct in6_addr dest; /* Target address to ping */ - uint16_t nrequests; /* Number of ICMP ECHO requests sent */ - uint16_t nreplies; /* Number of matching ICMP ECHO replies received */ - uint16_t outsize; /* Bytes(include ICMP header) to be sent */ - uint16_t id; /* ICMPv6_ECHO id */ - uint16_t seqno; /* ICMPv6_ECHO seqno */ - FAR const struct ping6_info_s *info; -}; - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/* NOTE: This will not work in the kernel build where there will be a - * separate instance of g_ping6_id in every process space. - */ - -static uint16_t g_ping6_id = 0; - /**************************************************************************** * Private Functions ****************************************************************************/ -/**************************************************************************** - * Name: ping6_newid - ****************************************************************************/ - -static inline uint16_t ping6_newid(void) -{ - /* Revisit: No thread safe */ - - return ++g_ping6_id; -} - -/**************************************************************************** - * Name: ping6_gethostip - * - * Description: - * Call gethostbyname() to get the IP address associated with a hostname. - * - * Input Parameters - * hostname - The host name to use in the nslookup. - * ipv4addr - The location to return the IPv4 address. - * - * Returned Value: - * Zero (OK) on success; a negated errno value on failure. - * - ****************************************************************************/ - -static int ping6_gethostip(FAR const char *hostname, FAR struct in6_addr *dest) -{ -#if defined(CONFIG_LIBC_NETDB) && defined(CONFIG_NETDB_DNSCLIENT) - /* Netdb DNS client support is enabled */ - - FAR struct hostent *he; - - he = gethostbyname(hostname); - if (he == NULL) - { - nerr("ERROR: gethostbyname failed: %d\n", h_errno); - return -ENOENT; - } - else if (he->h_addrtype == AF_INET6) - { - memcpy(dest, he->h_addr, sizeof(struct in6_addr)); - } - else - { - nerr("ERROR: gethostbyname returned an address of type: %d\n", - he->h_addrtype); - return -ENOEXEC; - } - - return OK; - -#else /* CONFIG_LIBC_NETDB */ - - /* No host name support */ - /* Convert strings to numeric IPv6 address */ - - int ret = inet_pton(AF_INET6, hostname, dest->s6_addr16); - - /* The inet_pton() function returns 1 if the conversion succeeds. It will - * return 0 if the input is not a valid IPv6 address string, or -1 with - * errno set to EAFNOSUPPORT if the address family argument is unsupported. - */ - - return (ret > 0) ? OK : ERROR; - -#endif /* CONFIG_LIBC_NETDB */ -} - -/**************************************************************************** - * Name: icmp6_callback - ****************************************************************************/ - -static void icmp6_callback(FAR struct ping6_result_s *result, int code, - int extra) -{ - result->code = code; - result->extra = extra; - result->info->callback(result); -} - -/**************************************************************************** - * Name: icmp6_ping - ****************************************************************************/ - -static void icmp6_ping(FAR const struct ping6_info_s *info) -{ - struct ping6_result_s result; - struct sockaddr_in6 destaddr; - struct sockaddr_in6 fromaddr; - struct icmpv6_echo_request_s outhdr; - FAR struct icmpv6_echo_reply_s *inhdr; - struct pollfd recvfd; - FAR uint8_t *iobuffer; - FAR uint8_t *ptr; - int32_t elapsed; - clock_t kickoff; - clock_t start; - socklen_t addrlen; - ssize_t nsent; - ssize_t nrecvd; - bool retry; - int sockfd; - int ret; - int ch; - int i; - - /* Initialize result structure */ - - memset(&result, 0, sizeof(result)); - result.info = info; - result.id = ping6_newid(); - result.outsize = ICMPv6_IOBUFFER_SIZE(info->datalen); - if (ping6_gethostip(info->hostname, &result.dest) < 0) - { - icmp6_callback(&result, ICMPv6_E_HOSTIP, 0); - return; - } - - /* Allocate memory to hold ping buffer */ - - iobuffer = (FAR uint8_t *)malloc(result.outsize); - if (iobuffer == NULL) - { - icmp6_callback(&result, ICMPv6_E_MEMORY, 0); - return; - } - - sockfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMP6); - if (sockfd < 0) - { - icmp6_callback(&result, ICMPv6_E_SOCKET, errno); - free(iobuffer); - return; - } - - kickoff = clock(); - - memset(&destaddr, 0, sizeof(struct sockaddr_in6)); - destaddr.sin6_family = AF_INET6; - destaddr.sin6_port = 0; - memcpy(&destaddr.sin6_addr, &result.dest, sizeof(struct in6_addr)); - - memset(&outhdr, 0, SIZEOF_ICMPV6_ECHO_REQUEST_S(0)); - outhdr.type = ICMPv6_ECHO_REQUEST; - outhdr.id = htons(result.id); - outhdr.seqno = htons(result.seqno); - - icmp6_callback(&result, ICMPv6_I_BEGIN, 0); - - while (result.nrequests < info->count) - { - /* Copy the ICMP header into the I/O buffer */ - - memcpy(iobuffer, &outhdr, SIZEOF_ICMPV6_ECHO_REQUEST_S(0)); - - /* Add some easily verifiable payload data */ - - ptr = &iobuffer[SIZEOF_ICMPV6_ECHO_REQUEST_S(0)]; - ch = 0x20; - - for (i = 0; i < info->datalen; i++) - { - *ptr++ = ch; - if (++ch > 0x7e) - { - ch = 0x20; - } - } - - start = clock(); - nsent = sendto(sockfd, iobuffer, result.outsize, 0, - (FAR struct sockaddr*)&destaddr, - sizeof(struct sockaddr_in6)); - if (nsent < 0) - { - icmp6_callback(&result, ICMPv6_E_SENDTO, errno); - goto done; - } - else if (nsent != result.outsize) - { - icmp6_callback(&result, ICMPv6_E_SENDSMALL, nsent); - goto done; - } - - result.nrequests++; - - elapsed = 0; - do - { - retry = false; - - recvfd.fd = sockfd; - recvfd.events = POLLIN; - recvfd.revents = 0; - - ret = poll(&recvfd, 1, info->timeout - elapsed); - if (ret < 0) - { - icmp6_callback(&result, ICMPv6_E_POLL, errno); - goto done; - } - else if (ret == 0) - { - icmp6_callback(&result, ICMPv6_W_TIMEOUT, info->timeout); - continue; - } - - /* Get the ICMP response (ignoring the sender) */ - - addrlen = sizeof(struct sockaddr_in6); - nrecvd = recvfrom(sockfd, iobuffer, result.outsize, 0, - (FAR struct sockaddr *)&fromaddr, &addrlen); - if (nrecvd < 0) - { - icmp6_callback(&result, ICMPv6_E_RECVFROM, errno); - goto done; - } - else if (nrecvd < SIZEOF_ICMPV6_ECHO_REPLY_S(0)) - { - icmp6_callback(&result, ICMPv6_E_RECVSMALL, nrecvd); - goto done; - } - - elapsed = (unsigned int)TICK2MSEC(clock() - start); - inhdr = (FAR struct icmpv6_echo_reply_s *)iobuffer; - - if (inhdr->type == ICMPv6_ECHO_REPLY) - { - if (ntohs(inhdr->id) != result.id) - { - icmp6_callback(&result, ICMPv6_W_IDDIFF, ntohs(inhdr->id)); - retry = true; - } - else if (ntohs(inhdr->seqno) > result.seqno) - { - icmp6_callback(&result, ICMPv6_W_SEQNOBIG, ntohs(inhdr->seqno)); - retry = true; - } - else - { - bool verified = true; - int32_t pktdelay = elapsed; - - if (ntohs(inhdr->seqno) < result.seqno) - { - icmp6_callback(&result, ICMPv6_W_SEQNOSMALL, ntohs(inhdr->seqno)); - pktdelay += info->delay; - retry = true; - } - - icmp6_callback(&result, ICMPv6_I_ROUNDTRIP, pktdelay); - - /* Verify the payload data */ - - if (nrecvd != result.outsize) - { - icmp6_callback(&result, ICMPv6_W_RECVBIG, nrecvd); - verified = false; - } - else - { - ptr = &iobuffer[SIZEOF_ICMPV6_ECHO_REPLY_S(0)]; - ch = 0x20; - - for (i = 0; i < info->datalen; i++, ptr++) - { - if (*ptr != ch) - { - icmp6_callback(&result, ICMPv6_W_DATADIFF, 0); - verified = false; - break; - } - - if (++ch > 0x7e) - { - ch = 0x20; - } - } - } - - /* Only count the number of good replies */ - - if (verified) - { - result.nreplies++; - } - } - } - else - { - icmp6_callback(&result, ICMPv6_W_TYPE, inhdr->type); - } - } - while (retry && info->delay > elapsed && info->timeout > elapsed); - - /* Wait if necessary to preserved the requested ping rate */ - - elapsed = (unsigned int)TICK2MSEC(clock() - start); - if (elapsed < info->delay) - { - struct timespec rqt; - unsigned int remaining; - unsigned int sec; - unsigned int frac; /* In deciseconds */ - - remaining = info->delay - elapsed; - sec = remaining / MSEC_PER_SEC; - frac = remaining - MSEC_PER_SEC * sec; - - rqt.tv_sec = sec; - rqt.tv_nsec = frac * NSEC_PER_MSEC; - - (void)nanosleep(&rqt, NULL); - } - - outhdr.seqno = htons(++result.seqno); - } - -done: - icmp6_callback(&result, ICMPv6_I_FINISH, TICK2MSEC(clock() - kickoff)); - close(sockfd); - free(iobuffer); -} - /**************************************************************************** * Name: show_usage ****************************************************************************/