From 749ea9e8816cb09656f9e70192253748d919d100 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Tue, 24 Oct 2017 11:19:41 -0600 Subject: [PATCH] This commit removes the ping6 command from NSH and replaces it with the apps/system/ping6 built-in command. The NSH ping6 command had to be removed because it violated the portable POSIX OS interface. The apps/system/ping6 command uses the sem-standard IPPROTO_ICMP6 socket interface. Squashed commit of the following: apps/system ping and ping6: Fix a backward test. apps/system/ping6: ping6 logic now builds without error. apps/system/ping and ping6: Minor clean-up to configuration settings. apps/system/ping6: Minor clean-up to be able to configure CONFIG_SYSTEM_PING6 apps/nshlib: Remove support for ping6 from NSH. apps/system/ping6: Add IPPROTO_ICMPv6 ping6. Initial commit is simply a clone of the IPPROTO_ICMP ping logic. Rename CONFIG_NET_ICMPv6_PING to CONFIG_NET_ICMPv6_SOCKET. --- nshlib/Kconfig | 9 - nshlib/README.txt | 10 +- nshlib/nsh.h | 15 -- nshlib/nsh_command.c | 6 - nshlib/nsh_netcmds.c | 307 +-------------------- system/ping/Kconfig | 4 +- system/ping/ping.c | 29 +- system/ping6/.gitignore | 11 + system/ping6/Kconfig | 31 +++ system/ping6/Make.defs | 40 +++ system/ping6/Makefile | 147 ++++++++++ system/ping6/ping6.c | 577 ++++++++++++++++++++++++++++++++++++++++ 12 files changed, 826 insertions(+), 360 deletions(-) create mode 100644 system/ping6/.gitignore create mode 100644 system/ping6/Kconfig create mode 100644 system/ping6/Make.defs create mode 100644 system/ping6/Makefile create mode 100644 system/ping6/ping6.c diff --git a/nshlib/Kconfig b/nshlib/Kconfig index 6af994c16..1c64e955c 100644 --- a/nshlib/Kconfig +++ b/nshlib/Kconfig @@ -1544,15 +1544,6 @@ config NSH_WAPI_PASSPHRASE endmenu # WAPI Configuration endif # NSH_NETINIT - -config NSH_MAX_ROUNDTRIP - int "Max Ping6 Round-Trip (DSEC)" - default 20 - depends on NSH_LIBRARY && NET && !NSH_DISABLE_PING6 - ---help--- - This is the maximum round trip for a response to a ICMP ECHO request. - It is in units of deciseconds. The default is 20 (2 seconds). - endmenu # Networking Configuration" menu "Telnet Configuration" diff --git a/nshlib/README.txt b/nshlib/README.txt index 7ada2853a..1acf2e490 100644 --- a/nshlib/README.txt +++ b/nshlib/README.txt @@ -889,9 +889,6 @@ o passwd Set the password for the existing user to -o ping6 [-c ] [-i ] - ping6 differs from ping in that it uses IPv6 addressing. - o poweroff Shutdown and power off the system. This command depends on hardware @@ -1314,7 +1311,6 @@ Command Dependencies on Configuration Settings nfsmount !CONFIG_DISABLE_MOUNTPOINT && CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_NET && CONFIG_NFS nslookup CONFIG_LIBC_NETDB && CONFIG_NETDB_DNSCLIENT password !CONFIG_DISABLE_MOUNTPOINT && CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_FS_WRITABLE && CONFIG_NSH_LOGIN_PASSWD - ping6 CONFIG_NET && CONFIG_NET_ICMPv6 && CONFIG_NET_ICMPv6_PING && !CONFIG_DISABLE_SIGNALS poweroff CONFIG_BOARDCTL_POWEROFF ps CONFIG_FS_PROCFS && !CONFIG_FS_PROCFS_EXCLUDE_PROC put CONFIG_NET && CONFIG_NET_UDP && CONFIG_NFILE_DESCRIPTORS > 0 && MTU >= 558 (see note 1,2) @@ -1393,6 +1389,8 @@ All built-in applications require that support for NSH built-in applications has ----------- -------------------------- ping CONFIG_NET && CONFIG_NET_ICMP && CONFIG_NET_ICMP_SOCKET && CONFIG_SYSTEM_PING && !CONFIG_DISABLE_POLL && !CONFIG_DISABLE_SIGNALS + ping6 CONFIG_NET && CONFIG_NET_ICMPv6 && CONFIG_NET_ICMPv6_SOCKET && + CONFIG_SYSTEM_PING6 && !CONFIG_DISABLE_POLL && !CONFIG_DISABLE_SIGNALS NSH-Specific Configuration Settings ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1672,10 +1670,6 @@ NSH-Specific Configuration Settings Set if your ethernet hardware has no built-in MAC address. If set, a bogus MAC will be assigned. - * CONFIG_NSH_MAX_ROUNDTRIP - This is the maximum round trip for a response to a ICMP ECHO request. - It is in units of deciseconds. The default is 20 (2 seconds). - If you use DHCPC, then some special configuration network options are required. These include: diff --git a/nshlib/nsh.h b/nshlib/nsh.h index fb928d6ed..14470192c 100644 --- a/nshlib/nsh.h +++ b/nshlib/nsh.h @@ -400,15 +400,6 @@ #endif /* CONFIG_NSH_TELNET_LOGIN */ -/* CONFIG_NSH_MAX_ROUNDTRIP - This is the maximum round trip for a response to - * a ICMP ECHO request. It is in units of deciseconds. The default is 20 - * (2 seconds). - */ - -#ifndef CONFIG_NSH_MAX_ROUNDTRIP -# define CONFIG_NSH_MAX_ROUNDTRIP 20 -#endif - /* Verify support for ROMFS /etc directory support options */ #ifdef CONFIG_NSH_ROMFSETC @@ -1175,12 +1166,6 @@ int cmd_lsmod(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); int cmd_nfsmount(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); # endif # endif -# if defined(CONFIG_NET_ICMPv6) && defined(CONFIG_NET_ICMPv6_PING) && \ - !defined(CONFIG_DISABLE_SIGNALS) -# ifndef CONFIG_NSH_DISABLE_PING6 - int cmd_ping6(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); -# endif -# endif # if defined(CONFIG_NET_UDP) && CONFIG_NFILE_DESCRIPTORS > 0 # ifndef CONFIG_NSH_DISABLE_PUT int cmd_put(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); diff --git a/nshlib/nsh_command.c b/nshlib/nsh_command.c index b52e29fc8..66638ef5b 100644 --- a/nshlib/nsh_command.c +++ b/nshlib/nsh_command.c @@ -379,12 +379,6 @@ static const struct cmdmap_s g_cmdmap[] = # endif #endif -#if defined(CONFIG_NET) && defined(CONFIG_NET_ICMPv6) && defined(CONFIG_NET_ICMPv6_PING) && !defined(CONFIG_DISABLE_SIGNALS) -# ifndef CONFIG_NSH_DISABLE_PING6 - { "ping6", cmd_ping6, 2, 6, "[-c ] [-i ] " }, -# endif -#endif - #if defined(CONFIG_BOARDCTL_POWEROFF) && !defined(CONFIG_NSH_DISABLE_POWEROFF) { "poweroff", cmd_poweroff, 1, 1, NULL }, #endif diff --git a/nshlib/nsh_netcmds.c b/nshlib/nsh_netcmds.c index 61628108c..123d4674a 100644 --- a/nshlib/nsh_netcmds.c +++ b/nshlib/nsh_netcmds.c @@ -53,7 +53,6 @@ #include #include /* Needed for open */ #include -#include /* Needed for gethostbyname */ #include /* Needed for basename */ #include #include @@ -114,16 +113,10 @@ * Pre-processor Definitions ****************************************************************************/ -#undef HAVE_PING6 #undef HAVE_HWADDR #undef HAVE_EADDR #undef HAVE_RADIOADDR -#if defined(CONFIG_NET_ICMPv6) && defined(CONFIG_NET_ICMPv6_PING) && \ - !defined(CONFIG_DISABLE_SIGNALS) && !defined(CONFIG_NSH_DISABLE_PING6) -# define HAVE_PING6 1 -#endif - #if defined(CONFIG_NET_ETHERNET) # define HAVE_HWADDR 1 #elif defined(CONFIG_NET_6LOWPAN) @@ -136,10 +129,6 @@ # endif #endif -/* Size of the ECHO data */ - -#define DEFAULT_PING6_DATALEN 56 - /* Get the larger value */ #ifndef MAX @@ -180,125 +169,10 @@ typedef int (*nsh_netdev_callback_t)(FAR struct nsh_vtbl_s *vtbl, * Private Function Prototypes ****************************************************************************/ -/**************************************************************************** - * Private Data - ****************************************************************************/ - -#ifdef HAVE_PING6 -static uint16_t g_pingid = 0; -#endif - /**************************************************************************** * Private Functions ****************************************************************************/ -/**************************************************************************** - * Name: ping6_newid - ****************************************************************************/ - -#ifdef HAVE_PING6 -static uint16_t ping6_newid(void) -{ - irqstate_t save = enter_critical_section(); - uint16_t ret = ++g_pingid; - leave_critical_section(save); - return ret; -} -#endif /* HAVE_PIN6 */ - -/**************************************************************************** - * Name: ping6_options - ****************************************************************************/ - -#ifdef HAVE_PING6 -static int ping6_options(FAR struct nsh_vtbl_s *vtbl, - int argc, FAR char **argv, - FAR int *count, FAR uint32_t *dsec, FAR char **staddr) -{ - FAR const char *fmt = g_fmtarginvalid; - bool badarg = false; - int option; - int tmp; - - /* Get the ping6 options */ - - while ((option = getopt(argc, argv, ":c:i:")) != ERROR) - { - switch (option) - { - case 'c': - tmp = atoi(optarg); - if (tmp < 1 || tmp > 10000) - { - nsh_output(vtbl, g_fmtargrange, argv[0]); - badarg = true; - } - else - { - *count = tmp; - } - break; - - case 'i': - tmp = atoi(optarg); - if (tmp < 1 || tmp >= 4294) - { - nsh_output(vtbl, g_fmtargrange, argv[0]); - badarg = true; - } - else - { - *dsec = 10 * tmp; - } - break; - - case ':': - nsh_output(vtbl, g_fmtargrequired, argv[0]); - badarg = true; - break; - - case '?': - default: - nsh_output(vtbl, g_fmtarginvalid, argv[0]); - badarg = true; - break; - } - } - - /* If a bad argument was encountered, then return without processing the - * command - */ - - if (badarg) - { - return ERROR; - } - - /* There should be exactly on parameter left on the command-line */ - - if (optind == argc - 1) - { - *staddr = argv[optind]; - } - else if (optind >= argc) - { - fmt = g_fmttoomanyargs; - goto errout; - } - else - { - fmt = g_fmtargrequired; - goto errout; - } - - return OK; - -errout: - nsh_output(vtbl, fmt, argv[0]); - return ERROR; -} -#endif /* HAVE_PING6 */ - /**************************************************************************** * Name: net_statistics ****************************************************************************/ @@ -346,7 +220,7 @@ int tftpc_parseargs(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv, bool badarg = false; int option; - /* Get the ping6 options */ + /* Get the get/put options */ memset(args, 0, sizeof(struct tftpc_args_s)); while ((option = getopt(argc, argv, ":bnf:h:")) != ERROR) @@ -471,68 +345,6 @@ errout: } #endif -/**************************************************************************** - * Name: nsh_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. - * - ****************************************************************************/ - -#ifdef HAVE_PING6 -static int nsh_gethostip(FAR char *hostname, FAR union ip_addr_u *ipaddr) -{ -#ifdef CONFIG_LIBC_NETDB - - /* Netdb 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(ipaddr->ipv6, he->h_addr, sizeof(net_ipv6addr_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_INET6, hostname, ipaddr->ipv6); - - /* 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 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 */ -} -#endif - /**************************************************************************** * Name: wget_callback ****************************************************************************/ @@ -1316,123 +1128,6 @@ errout_invalid: } #endif -/**************************************************************************** - * Name: cmd_ping6 - ****************************************************************************/ - -#ifdef HAVE_PING6 -int cmd_ping6(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) -{ - FAR char *staddr; - struct in6_addr ipaddr; - systime_t start; - systime_t next; - int32_t elapsed; - uint32_t dsec = 10; - uint32_t maxwait; - uint16_t id; - int count = 10; - int seqno; - int replies = 0; - int ret; - int tmp; - int i; - - /* Get the ping6 options */ - - ret = ping6_options(vtbl, argc, argv, &count, &dsec, &staddr); - if (ret < 0) - { - return ERROR; - } - - /* Get the IP address in binary form */ - - ret = nsh_gethostip(staddr, (FAR union ip_addr_u *)&ipaddr); - if (ret < 0) - { - nsh_output(vtbl, g_fmtarginvalid, argv[0]); - return ERROR; - } - - /* Get the ID to use */ - - id = ping6_newid(); - - /* The maximum wait for a response will be the larger of the inter-ping - * time and the configured maximum round-trip time. - */ - - maxwait = MAX(dsec, CONFIG_NSH_MAX_ROUNDTRIP); - - /* Loop for the specified count */ - - nsh_output(vtbl, - "PING6 %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x %d bytes of data\n", - ntohs(ipaddr.s6_addr16[0]), ntohs(ipaddr.s6_addr16[1]), - ntohs(ipaddr.s6_addr16[2]), ntohs(ipaddr.s6_addr16[3]), - ntohs(ipaddr.s6_addr16[4]), ntohs(ipaddr.s6_addr16[5]), - ntohs(ipaddr.s6_addr16[6]), ntohs(ipaddr.s6_addr16[7]), - DEFAULT_PING6_DATALEN); - - start = clock_systimer(); - for (i = 1; i <= count; i++) - { - /* Send the ECHO request and wait for the response */ - - next = clock_systimer(); - seqno = icmpv6_ping(ipaddr.s6_addr16, id, i, DEFAULT_PING6_DATALEN, maxwait); - - /* Was any response returned? We can tell if a non-negative sequence - * number was returned. - */ - - if (seqno >= 0 && seqno <= i) - { - /* Get the elapsed time from the time that the request was - * sent until the response was received. If we got a response - * to an earlier request, then fudge the elapsed time. - */ - - elapsed = (int32_t)TICK2MSEC(clock_systimer() - next); - if (seqno < i) - { - elapsed += 100 * dsec * (i - seqno); - } - - /* Report the receipt of the reply */ - - nsh_output(vtbl, "%d bytes from %s: icmp_seq=%d time=%ld ms\n", - DEFAULT_PING6_DATALEN, staddr, seqno, (long)elapsed); - replies++; - } - - /* Wait for the remainder of the interval. If the last seqno> 1)) / count; - - nsh_output(vtbl, "%d packets transmitted, %d received, %d%% packet loss, time %ld ms\n", - count, replies, tmp, (long)elapsed); - return OK; -} -#endif /* CONFIG_NET_ICMPv6 && CONFIG_NET_ICMPv6_PING */ - /**************************************************************************** * Name: cmd_put ****************************************************************************/ diff --git a/system/ping/Kconfig b/system/ping/Kconfig index 07e339dc6..254a64f04 100644 --- a/system/ping/Kconfig +++ b/system/ping/Kconfig @@ -4,7 +4,7 @@ # config SYSTEM_PING - bool "ICMP Ping command" + bool "ICMP 'ping' command" default n depends on NET_ICMP_SOCKET ---help--- @@ -13,7 +13,7 @@ config SYSTEM_PING if SYSTEM_PING config SYSTEM_PING_PROGNAME string "Ping program name" - default "nxhello" + default "ping" depends on BUILD_KERNEL ---help--- This is the name of the program that will be use when the NSH ELF diff --git a/system/ping/ping.c b/system/ping/ping.c index 1ed84d781..a750bc8e1 100644 --- a/system/ping/ping.c +++ b/system/ping/ping.c @@ -160,9 +160,9 @@ static int ping_gethostip(FAR char *hostname, FAR struct ping_info_s *info) int ret = inet_pton(AF_INET, hostname, &info->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 a - * valid IPv6 address string, or -1 with errno set to EAFNOSUPPORT if - * the address family argument is unsupported. + * 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; @@ -171,10 +171,10 @@ static int ping_gethostip(FAR char *hostname, FAR struct ping_info_s *info) } /**************************************************************************** - * Name: do_ping + * Name: icmp_ping ****************************************************************************/ -void do_ping(FAR struct ping_info_s *info) +static void icmp_ping(FAR struct ping_info_s *info) { struct sockaddr_in destaddr; struct sockaddr_in fromaddr; @@ -194,13 +194,14 @@ void do_ping(FAR struct ping_info_s *info) int ch; int i; - memset(&destaddr, 0, sizeof destaddr); + memset(&destaddr, 0, sizeof(struct sockaddr_in)); destaddr.sin_family = AF_INET; + destaddr.sin_port = 0; destaddr.sin_addr.s_addr = info->dest.s_addr; - memset(&outhdr, 0, sizeof outhdr); + memset(&outhdr, 0, sizeof(struct icmp_hdr_s)); outhdr.type = ICMP_ECHO_REQUEST; - outhdr.icode = ping_newid(); + outhdr.id = ping_newid(); outhdr.seqno = 0; printf("PING %u.%u.%u.%u %d bytes of data\n", @@ -300,12 +301,12 @@ void do_ping(FAR struct ping_info_s *info) if (inhdr->type == ICMP_ECHO_REPLY) { - if (inhdr->icode != outhdr.icode) + if (inhdr->id != outhdr.id) { fprintf(stderr, "WARNING: Ignoring ICMP reply with ID %u. " "Expected %u\n", - inhdr->icode, outhdr.icode); + inhdr->id, outhdr.id); retry = true; } else if (inhdr->seqno > outhdr.seqno) @@ -419,7 +420,7 @@ static void show_usage(FAR const char *progname, int exitcode) printf("\nUsage: %s [-c ] [-i ] \n", progname); printf(" %s -h\n", progname); printf("\nWhere:\n"); - printf(" is the IP address request the ICMP ECHO replay.\n"); + printf(" is the IPv4 address request the ICMP ECHO reply.\n"); printf(" -c determines the number of pings. Default %u.\n", ICMP_NPINGS); printf(" -i is the default delay between pings (milliseconds).\n"); @@ -515,13 +516,13 @@ int ping_main(int argc, char **argv) show_usage(argv[0], EXIT_FAILURE); } - if (ping_gethostip(argv[optind], info) == 0) + if (ping_gethostip(argv[optind], info) < 0) { fprintf(stderr, "ERROR: ping_gethostip(%s) failed\n", argv[optind]); goto errout_with_info; } - info->sockfd = socket(AF_INET,SOCK_DGRAM,IPPROTO_ICMP); + info->sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); if (info->sockfd < 0) { fprintf(stderr, "ERROR: socket() failed: %d\n", errno); @@ -529,7 +530,7 @@ int ping_main(int argc, char **argv) } start = clock_systimer(); - do_ping(info); + icmp_ping(info); /* Get the total elapsed time */ diff --git a/system/ping6/.gitignore b/system/ping6/.gitignore new file mode 100644 index 000000000..83bd7b811 --- /dev/null +++ b/system/ping6/.gitignore @@ -0,0 +1,11 @@ +/Make.dep +/.depend +/.built +/*.asm +/*.rel +/*.lst +/*.sym +/*.adb +/*.lib +/*.src +/*.obj diff --git a/system/ping6/Kconfig b/system/ping6/Kconfig new file mode 100644 index 000000000..c6d50f6da --- /dev/null +++ b/system/ping6/Kconfig @@ -0,0 +1,31 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config SYSTEM_PING6 + bool "ICMPv6 'ping6' command" + default n + depends on NET_ICMPv6_SOCKET + ---help--- + Enable support for the ICMP 'ping' command. + +if SYSTEM_PING6 +config SYSTEM_PING6_PROGNAME + string "Ping program name" + default "ping6" + depends on BUILD_KERNEL + ---help--- + This is the name of the program that will be use when the NSH ELF + program is installed. + +config SYSTEM_PING6_PRIORITY + int "Ping task priority" + default 100 + +config SYSTEM_PING6_STACKSIZE + int "Ping stack size" + default 2048 + +endif + diff --git a/system/ping6/Make.defs b/system/ping6/Make.defs new file mode 100644 index 000000000..d25253e78 --- /dev/null +++ b/system/ping6/Make.defs @@ -0,0 +1,40 @@ +############################################################################ +# apps/system/ping6/Make.defs +# Adds selected applications to apps/ build +# +# Copyright (C) 2017 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. +# +############################################################################ + +ifeq ($(CONFIG_SYSTEM_PING6),y) +CONFIGURED_APPS += system/ping6 +endif + diff --git a/system/ping6/Makefile b/system/ping6/Makefile new file mode 100644 index 000000000..a4af8e46d --- /dev/null +++ b/system/ping6/Makefile @@ -0,0 +1,147 @@ +############################################################################ +# apps/system/ping6/Makefile +# +# Copyright (C) 2017 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 + +# ICMP ping6 command + +CONFIG_SYSTEM_PING6_PRIORITY ?= SCHED_PRIORITY_DEFAULT +CONFIG_SYSTEM_PING6_STACKSIZE ?= 2048 + +APPNAME = ping6 +PRIORITY = $(CONFIG_SYSTEM_PING6_PRIORITY) +STACKSIZE = $(CONFIG_SYSTEM_PING6_STACKSIZE) + +CONFIG_SYSTEM_PING6_PROGNAME ?= ping6$(EXEEXT) +PROGNAME = $(CONFIG_SYSTEM_PING6_PROGNAME) + +# Files + +ASRCS = +CSRCS = +MAINSRC = ping6.c + +AOBJS = $(ASRCS:.S=$(OBJEXT)) +COBJS = $(CSRCS:.c=$(OBJEXT)) +MAINOBJ = $(MAINSRC:.c=$(OBJEXT)) + +SRCS = $(ASRCS) $(CSRCS) $(MAINSRC) +OBJS = $(AOBJS) $(COBJS) + +ifneq ($(CONFIG_BUILD_KERNEL),y) + OBJS += $(MAINOBJ) +endif + +ifeq ($(CONFIG_WINDOWS_NATIVE),y) + BIN = ..\..\libapps$(LIBEXT) +else +ifeq ($(WINTOOL),y) + BIN = ..\\..\\libapps$(LIBEXT) +else + BIN = ../../libapps$(LIBEXT) +endif +endif + +ifeq ($(WINTOOL),y) + INSTALL_DIR = "${shell cygpath -w $(BIN_DIR)}" +else + INSTALL_DIR = $(BIN_DIR) +endif + +ROOTDEPPATH = --dep-path . + +# Common build + +VPATH = + +all: .built +.PHONY: context depend clean distclean preconfig +.PRECIOUS: ../../libapps$(LIBEXT) + +$(AOBJS): %$(OBJEXT): %.S + $(call ASSEMBLE, $<, $@) + +$(COBJS) $(MAINOBJ): %$(OBJEXT): %.c + $(call COMPILE, $<, $@) + +.built: $(OBJS) + $(call ARCHIVE, $(BIN), $(OBJS)) + $(Q) touch .built + +ifeq ($(CONFIG_BUILD_KERNEL),y) +$(BIN_DIR)$(DELIM)$(PROGNAME): $(OBJS) $(MAINOBJ) + @echo "LD: $(PROGNAME)" + $(Q) $(LD) $(LDELFFLAGS) $(LDLIBPATH) -o $(INSTALL_DIR)$(DELIM)$(PROGNAME) $(ARCHCRT0OBJ) $(MAINOBJ) $(LDLIBS) + $(Q) $(NM) -u $(INSTALL_DIR)$(DELIM)$(PROGNAME) + +install: $(BIN_DIR)$(DELIM)$(PROGNAME) + +else +install: + +endif + +# Register application + +ifeq ($(CONFIG_NSH_BUILTIN_APPS),y) +$(BUILTIN_REGISTRY)$(DELIM)$(APPNAME)_main.bdat: $(DEPCONFIG) Makefile + $(call REGISTER,$(APPNAME),$(PRIORITY),$(STACKSIZE),$(APPNAME)_main) + +context: $(BUILTIN_REGISTRY)$(DELIM)$(APPNAME)_main.bdat +else +context: +endif + +# Create dependencies + +.depend: Makefile $(SRCS) + $(Q) $(MKDEP) $(ROOTDEPPATH) "$(CC)" -- $(CFLAGS) -- $(SRCS) >Make.dep + $(Q) touch $@ + +depend: .depend + +clean: + $(call DELFILE, .built) + $(call CLEAN) + +distclean: clean + $(call DELFILE, Make.dep) + $(call DELFILE, .depend) + +preconfig: + +-include Make.dep diff --git a/system/ping6/ping6.c b/system/ping6/ping6.c new file mode 100644 index 000000000..3485fddf0 --- /dev/null +++ b/system/ping6/ping6.c @@ -0,0 +1,577 @@ +/**************************************************************************** + * apps/system/ping/ping.c + * + * Copyright (C) 2017 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 +#include + +#if defined(CONFIG_LIBC_NETDB) && defined(CONFIG_NETDB_DNSCLIENT) +# include +#endif + +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ICMPv6_PING6_DATALEN 56 +#define ICMPv6_IOBUFFER_SIZE \ + SIZEOF_ICMPV6_ECHO_REQUEST_S(0) + ICMPv6_PING6_DATALEN + +#define ICMPv6_NPINGS 10 /* Default number of pings */ +#define ICMPv6_POLL_DELAY 1000 /* 1 second in milliseconds */ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct ping6_info_s +{ + int sockfd; /* Open IPPROTO_ICMP6 socket */ + FAR struct in6_addr dest; /* Target address to ping */ + uint16_t count; /* Number of pings requested */ + uint16_t nrequests; /* Number of ICMP ECHO requests sent */ + uint16_t nreplies; /* Number of matching ICMP ECHO replies received */ + int16_t delay; /* Deciseconds to delay between pings */ + + /* I/O buffer for data transfers */ + + uint8_t iobuffer[ICMPv6_IOBUFFER_SIZE]; +}; + +/**************************************************************************** + * 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 char *hostname, FAR struct ping6_info_s *info) +{ +#if defined(CONFIG_LIBC_NETDB) && defined(CONFIG_NETDB_DNSCLIENT) + /* Netdb 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(&info->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, info->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: icmpv6_ping + ****************************************************************************/ + +static void icmpv6_ping(FAR struct ping6_info_s *info) +{ + 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 *ptr; + int32_t elapsed; + systime_t start; + socklen_t addrlen; + ssize_t nsent; + ssize_t nrecvd; + size_t outsize; + bool retry; + int delay; + int ret; + int ch; + int i; + + memset(&destaddr, 0, sizeof(struct sockaddr_in6)); + destaddr.sin6_family = AF_INET6; + destaddr.sin6_port = 0; + memcpy(&destaddr.sin6_addr, &info->dest, sizeof(struct in6_addr)); + + memset(&outhdr, 0, SIZEOF_ICMPV6_ECHO_REQUEST_S(0)); + outhdr.type = ICMPv6_ECHO_REQUEST; + outhdr.id = ping6_newid(); + outhdr.seqno = 0; + + printf("PING6 %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x %d bytes of data\n", + ntohs(info->dest.s6_addr16[0]), ntohs(info->dest.s6_addr16[1]), + ntohs(info->dest.s6_addr16[2]), ntohs(info->dest.s6_addr16[3]), + ntohs(info->dest.s6_addr16[4]), ntohs(info->dest.s6_addr16[5]), + ntohs(info->dest.s6_addr16[6]), ntohs(info->dest.s6_addr16[7]), + ICMPv6_PING6_DATALEN); + + while (info->nrequests < info->count) + { + /* Copy the ICMP header into the I/O buffer */ + + memcpy(info->iobuffer, &outhdr, SIZEOF_ICMPV6_ECHO_REQUEST_S(0)); + + /* Add some easily verifiable payload data */ + + ptr = &info->iobuffer[SIZEOF_ICMPV6_ECHO_REQUEST_S(0)]; + ch = 0x20; + + for (i = 0; i < ICMPv6_PING6_DATALEN; i++) + { + *ptr++ = ch; + if (++ch > 0x7e) + { + ch = 0x20; + } + } + + start = clock_systimer(); + outsize = SIZEOF_ICMPV6_ECHO_REPLY_S(0) + ICMPv6_PING6_DATALEN; + nsent = sendto(info->sockfd, info->iobuffer, outsize, 0, + (FAR struct sockaddr*)&destaddr, + sizeof(struct sockaddr_in6)); + if (nsent < 0) + { + fprintf(stderr, "ERROR: sendto failed at seqno %u: %d\n", + outhdr.seqno, errno); + return; + } + else if (nsent != outsize) + { + fprintf(stderr, "ERROR: sendto returned %ld, expected %lu\n", + (long)nsent, (unsigned long)outsize); + return; + } + + info->nrequests++; + + delay = info->delay; + do + { + /* Wait for a reply with a timeout */ + + retry = false; + + recvfd.fd = info->sockfd; + recvfd.events = POLLIN; + recvfd.revents = 0; + + ret = poll(&recvfd, 1, delay); + if (ret < 0) + { + fprintf(stderr, "ERROR: poll failed: %d\n", errno); + return; + } + else if (ret == 0) + { + printf("No response from " + "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x: " + "icmp_seq=%u time=%u ms\n", + ntohs(info->dest.s6_addr16[0]), + ntohs(info->dest.s6_addr16[1]), + ntohs(info->dest.s6_addr16[2]), + ntohs(info->dest.s6_addr16[3]), + ntohs(info->dest.s6_addr16[4]), + ntohs(info->dest.s6_addr16[5]), + ntohs(info->dest.s6_addr16[6]), + ntohs(info->dest.s6_addr16[7]), + outhdr.seqno, info->delay); + + continue; + } + + /* Get the ICMP response (ignoring the sender) */ + + addrlen = sizeof(struct sockaddr_in6); + nrecvd = recvfrom(info->sockfd, info->iobuffer, + ICMPv6_IOBUFFER_SIZE, 0, + (FAR struct sockaddr *)&fromaddr, &addrlen); + if (nrecvd < 0) + { + fprintf(stderr, "ERROR: recvfrom failed: %d\n", errno); + return; + } + else if (nrecvd < SIZEOF_ICMPV6_ECHO_REPLY_S(0)) + { + fprintf(stderr, "ERROR: short ICMP packet: %ld\n", (long)nrecvd); + return; + } + + elapsed = (unsigned int)TICK2MSEC(clock_systimer() - start); + inhdr = (FAR struct icmpv6_echo_reply_s *)info->iobuffer; + + if (inhdr->type == ICMPv6_ECHO_REPLY) + { + if (inhdr->id != outhdr.id) + { + fprintf(stderr, + "WARNING: Ignoring ICMP reply with ID %u. " + "Expected %u\n", + inhdr->id, outhdr.id); + retry = true; + } + else if (inhdr->seqno > outhdr.seqno) + { + fprintf(stderr, + "WARNING: Ignoring ICMP reply to sequence %u. " + "Expected <= &u\n", + inhdr->seqno, outhdr.seqno); + retry = true; + } + else + { + bool verified = true; + int32_t pktdelay = elapsed; + + if (inhdr->seqno < outhdr.seqno) + { + fprintf(stderr, "WARNING: Received after timeout\n"); + pktdelay += info->delay; + retry = true; + } + + printf("%ld bytes from " + "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x: " + "icmp_seq=%u time=%u ms\n", + nrecvd - SIZEOF_ICMPV6_ECHO_REPLY_S(0), + ntohs(info->dest.s6_addr16[0]), + ntohs(info->dest.s6_addr16[1]), + ntohs(info->dest.s6_addr16[2]), + ntohs(info->dest.s6_addr16[3]), + ntohs(info->dest.s6_addr16[4]), + ntohs(info->dest.s6_addr16[5]), + ntohs(info->dest.s6_addr16[6]), + ntohs(info->dest.s6_addr16[7]), + inhdr->seqno, pktdelay); + + /* Verify the payload data */ + + if (nrecvd != outsize) + { + fprintf(stderr, + "WARNING: Ignoring ICMP reply with different payload " + "size: %ld vs %lu\n", + (long)nrecvd, (unsigned long)outsize); + verified = false; + } + else + { + ptr = &info->iobuffer[SIZEOF_ICMPV6_ECHO_REPLY_S(0)]; + ch = 0x20; + + for (i = 0; i < ICMPv6_PING6_DATALEN; i++, ptr++) + { + if (*ptr != ch) + { + fprintf(stderr, "WARNING: Echoed data corrupted\n"); + verified = false; + break; + } + + if (++ch > 0x7e) + { + ch = 0x20; + } + } + } + + /* Only count the number of good replies */ + + if (verified) + { + info->nreplies++; + } + } + } + else + { + fprintf(stderr, "WARNING: ICMP packet with unknown type: %u\n", + inhdr->type); + } + + delay -= elapsed; + } + while (retry && delay > 0); + + /* Wait if necessary to preserved the requested ping rate */ + + elapsed = (unsigned int)TICK2MSEC(clock_systimer() - 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++; + } +} + +/**************************************************************************** + * 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) +{ + printf("\nUsage: %s [-c ] [-i ] \n", progname); + printf(" %s -h\n", progname); + printf("\nWhere:\n"); + printf(" is the IPv6 address request the ICMPv6 ECHO reply.\n"); + printf(" -c determines the number of pings. Default %u.\n", + ICMPv6_NPINGS); + printf(" -i is the default delay between pings (milliseconds).\n"); + printf(" Default %d.\n", ICMPv6_POLL_DELAY); + printf(" -h shows this text an exits.\n"); + exit(exitcode); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#ifdef CONFIG_BUILD_KERNEL +int main(int argc, FAR char *argv[]) +#else +int ping6_main(int argc, char **argv) +#endif +{ + FAR struct ping6_info_s *info; + FAR char *endptr; + systime_t start; + int32_t elapsed; + int exitcode; + int option; + + /* Allocate memory to hold ping information */ + + info = (FAR struct ping6_info_s *)zalloc(sizeof(struct ping6_info_s)); + if (info == NULL) + { + fprintf(stderr, "ERROR: Failed to allocate memory\n", argv[1]); + return EXIT_FAILURE; + } + + info->count = ICMPv6_NPINGS; + info->delay = ICMPv6_POLL_DELAY; + + /* Parse command line options */ + + exitcode = EXIT_FAILURE; + + while ((option = getopt(argc, argv, ":c:i: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 > INT16_MAX) + { + fprintf(stderr, "ERROR: out of range: %ld\n", delay); + goto errout_with_usage; + } + + info->delay = (int16_t)delay; + } + 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"); + free(info); + show_usage(argv[0], EXIT_FAILURE); + } + + if (ping6_gethostip(argv[optind], info) < 0) + { + fprintf(stderr, "ERROR: ping6_gethostip(%s) failed\n", argv[optind]); + goto errout_with_info; + } + + info->sockfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMP6); + if (info->sockfd < 0) + { + fprintf(stderr, "ERROR: socket() failed: %d\n", errno); + goto errout_with_info; + } + + start = clock_systimer(); + icmpv6_ping(info); + + /* Get the total elapsed time */ + + elapsed = (int32_t)TICK2MSEC(clock_systimer() - start); + + if (info->nrequests > 0) + { + unsigned int tmp; + + /* Calculate the percentage of lost packets */ + + tmp = (100 * (info->nrequests - info->nreplies) + (info->nrequests >> 1)) / + info->nrequests; + + printf("%u packets transmitted, %u received, %u%% packet loss, time %ld ms\n", + info->nrequests, info->nreplies, tmp, (long)elapsed); + } + + close(info->sockfd); + free(info); + return EXIT_SUCCESS; + +errout_with_usage: + free(info); + show_usage(argv[0], exitcode); + return exitcode; /* Not reachable */ + +errout_with_info: + free(info); + return EXIT_FAILURE; +}