From cfcd396861d0fb31b01bfdda72e677229817b7d0 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Fri, 11 Dec 2015 09:09:38 -0600 Subject: [PATCH] apps/netutils/ping: This is an unfinished implementation of ping/ping6 using raw packets. Not yet even hooked into the build and configuration systems --- ChangeLog.txt | 2 + netutils/README.txt | 10 ++ netutils/ping/Kconfig | 30 ++++ netutils/ping/Makefile | 106 ++++++++++++ netutils/ping/icmp_ping.c | 315 ++++++++++++++++++++++++++++++++++++ netutils/ping/icmpv6_ping.c | 308 +++++++++++++++++++++++++++++++++++ 6 files changed, 771 insertions(+) create mode 100644 netutils/ping/Kconfig create mode 100644 netutils/ping/Makefile create mode 100644 netutils/ping/icmp_ping.c create mode 100644 netutils/ping/icmpv6_ping.c diff --git a/ChangeLog.txt b/ChangeLog.txt index 672e2541f..aa1b8a62b 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1496,4 +1496,6 @@ creates Telnet sessions by open a new factory device at /dev/telnet and then using ioctl calls to create the session character drivers at /dev/telnetN (2015-12-07). + * netutils/ping: Unfinished implementation of ping/ping6 using raw + sockets (2015-12-11). diff --git a/netutils/README.txt b/netutils/README.txt index 0eb3a5875..88be41dd4 100644 --- a/netutils/README.txt +++ b/netutils/README.txt @@ -79,6 +79,16 @@ highly influenced by uIP) include: xmlrpc - The Embeddable Lightweight XML-RPC Server discussed at http://www.drdobbs.com/web-development/an-embeddable-lightweight-xml-rpc-server/184405364 + ping - This is an unfinished implementation of ping and ping6 using + raw sockets. It is not yet hooked into the configuration or + build systems. + + Current ping/ping6 logic in NSH makes illegal calls into the + OS in order to implement ping/ping6. One correct + implementation would be to use raw sockets to implement ping/ + ping6 as a user application. This is a first cut at such an + implementation. + Tips for Using Telnetd ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/netutils/ping/Kconfig b/netutils/ping/Kconfig new file mode 100644 index 000000000..81efef2bb --- /dev/null +++ b/netutils/ping/Kconfig @@ -0,0 +1,30 @@ +# +# 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_IPv4 && NET_ICMP && NET_PKT + 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_IPv6 && NET_ICMPv6 && NET_PKT + 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. + +if NETUTILS_PING || NETUTILS_PING6 + +config NETUTILS_PING_SIGNO + int "Ping timeout signal" + default 13 + +endif # NETUTILS_PING || NETUTILS_PING6 \ No newline at end of file diff --git a/netutils/ping/Makefile b/netutils/ping/Makefile new file mode 100644 index 000000000..d195ef1fc --- /dev/null +++ b/netutils/ping/Makefile @@ -0,0 +1,106 @@ +############################################################################ +# 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 + +# ICMP/ICMPv6 ping support + +ASRCS = +CSRCS = + +ifeq ($(CONFIG_NETUTILS_PING),y) +CSRCS += icmp_ping.c +endif + +ifeq ($(CONFIG_NETUTILS_PING6),y) +CSRCS += icmpv6_ping.c +endif + +AOBJS = $(ASRCS:.S=$(OBJEXT)) +COBJS = $(CSRCS:.c=$(OBJEXT)) + +SRCS = $(ASRCS) $(CSRCS) +OBJS = $(AOBJS) $(COBJS) + +ifeq ($(CONFIG_WINDOWS_NATIVE),y) + BIN = ..\..\libapps$(LIBEXT) +else +ifeq ($(WINTOOL),y) + BIN = ..\\..\\libapps$(LIBEXT) +else + BIN = ../../libapps$(LIBEXT) +endif +endif + +ROOTDEPPATH = --dep-path . + +# Common build + +VPATH = + +all: .built +.PHONY: context depend clean distclean + +$(AOBJS): %$(OBJEXT): %.S + $(call ASSEMBLE, $<, $@) + +$(COBJS): %$(OBJEXT): %.c + $(call COMPILE, $<, $@) + +.built: $(OBJS) + $(call ARCHIVE, $(BIN), $(OBJS)) + $(Q) touch .built + +install: + +context: + +.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) + +-include Make.dep diff --git a/netutils/ping/icmp_ping.c b/netutils/ping/icmp_ping.c new file mode 100644 index 000000000..92667bfa6 --- /dev/null +++ b/netutils/ping/icmp_ping.c @@ -0,0 +1,315 @@ +/**************************************************************************** + * netutils/ping/icmp_ping.c + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ICMPBUF ((struct icmp_iphdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) +#define ICMPDAT (&dev->d_buf[NET_LL_HDRLEN(dev) + sizeof(struct icmp_iphdr_s)]) + +/* Use the monotonic clock if it is available */ + +#ifdef CONFIG_CLOCK_MONOTONIC +# define PING_CLOCK CLOCK_MONOTONIC +#else +# define PING_CLOCK CLOCK_REALTIME +#endif + +#ifndef CONFIG_NETUTILS_PING_SIGNO +# define CONFIG_NETUTILS_PING_SIGNO 13 +#endif + +#define ICMP_DATALEN 56 +#define IPv4_PAYLOAD_SIZE (ICMP_HDRLEN + ICMP_DATALEN) +#define PING_BUFFER_SIZE (IPv4_HDRLEN + IPv4_PAYLOAD_SIZE) + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: icmpv6_echo_request + * + * Description: + * Setup to send an ICMP packet + * + * Input Parameters: + * + * + * Return: + * None + * + ****************************************************************************/ + +static void icmp_echo_request(FAR struct net_driver_s *dev, + FAR in_addr_t *destaddr) +{ + FAR struct icmp_iphdr_s *picmp = ICMPBUF; + FAR uint8_t *ptr; + size_t sendlen; + size_t icmplen; + size_t pktlen; + int i; + + /* The total length to send is the size of the application data plus the + * IPv4 and ICMP headers (and, eventually, the Ethernet header) + */ + + pktlen = IPv4_PAYLOAD_SIZE + IPICMP_HDRLEN; + + /* The total size of the data (for ICMP checksum calculation) includes the + * size of the ICMP header + */ + + icmplen = IPv4_PAYLOAD_SIZE + ICMP_HDRLEN; + + /* Initialize the IP header. */ + + picmp->vhl = 0x45; + picmp->tos = 0; + picmp->len[0] = (pktlen >> 8); + picmp->len[1] = (pktlen & 0xff); + ++g_ipid; + picmp->ipid[0] = g_ipid >> 8; + picmp->ipid[1] = g_ipid & 0xff; + picmp->ipoffset[0] = IP_FLAG_DONTFRAG >> 8; + picmp->ipoffset[1] = IP_FLAG_DONTFRAG & 0xff; + picmp->ttl = IP_TTL; + picmp->proto = IP_PROTO_ICMP; + + net_ipv4addr_hdrcopy(picmp->srcipaddr, &dev->d_ipaddr); + net_ipv4addr_hdrcopy(picmp->destipaddr, destaddr); + + /* Format the ICMP ECHO request packet */ + + picmp->type = ICMP_ECHO_REQUEST; + picmp->icode = 0; + picmp->id = htons(pstate->png_id); + picmp->seqno = htons(pstate->png_seqno); + + /* Insert some recognizable data into the packet */ + + for (i = 0, ptr = ICMPDAT; i < ICMP_DATALEN; i++) + { + *ptr++ = i; + } + + /* Calculate IP checksum. */ + + picmp->ipchksum = 0; + picmp->ipchksum = ~(ipv4_chksum(dev)); + + /* Calculate the ICMP checksum. */ + + picmp->icmpchksum = 0; + picmp->icmpchksum = ~(icmp_chksum(dev, icmplen)); + if (picmp->icmpchksum == 0) + { + picmp->icmpchksum = 0xffff; + } +} + +static void ping_timeout(int signo, FAR siginfo_t *info, FAR void *arg) +{ + FAR bool *timeout = (FAR bool *)arg; + *timeout = true; +} + +int ipv4_ping(FAR struct sockaddr_in *raddr, + FAR const struct timespec *timeout, + FAR struct timespec *roundtrip) +{ + FAR struct icmp *icmpv4; + struct sigevent notify; + struct sigaction act; + struct sigaction oact; + struct itimerspec value; + struct itimerspec ovalue; + char buffer[PING_BUFFER_SIZE]; /* Kind of big to be on the stack */ + timer_t timerid; + ssize_t nrecvd; + bool timeout; + int errcode; + int ret; + int sd; + + DEBUGASSERT(raddr != NULL); + + /* Allocate a POSIX timer */ + + timeout = false; + notify.sigev_notify = SIGEV_SIGNAL; + notify.sigev_signo = CONFIG_NETUTILS_PING_SIGNO; + notify.sigev_value.sival_ptr = (FAR void *)&timeout; + + ret = timer_create(PING_CLOCK, ¬ify, &timerid); + if (ret < 0) + { + errcode = errno; + DEBUGASSERT(errno > 0); + return -errcode; + } + + /* Attach a signal handler */ + + act.sa_sigaction = ping_timeout; + act.sa_flags = SA_SIGINFO; + + (void)sigfillset(&act.sa_mask); + (void)sigdelset(&act.sa_mask, CONFIG_NETUTILS_PING_SIGNO); + + ret = sigaction(CONFIG_NETUTILS_PING_SIGNO, &act, &oact); + if (ret < 0) + { + errcode = errno; + DEBUGASSERT(errno > 0); + ret = -errcode; + goto errout_with_timer; + } + + /* Create the raw socket */ + + sd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + if (sd < 0) + { + errcode = errno; + DEBUGASSERT(errno > 0); + ret = -errcode; + goto errout_with_sigaction; + } + + /* Format and send the ECHO request */ + + icmpv6_echo_request(dev, destaddr); + + ret = sendto(sd, buffer, PING6_BUFFER_SIZE, &raddr, + sizeof(struct sockaddr_in)); + + if (ret < 0) + { + errcode = errno; + DEBUGASSERT(errno > 0); + ret = -errcode; + goto errout_with_socket; + } + + /* Start the timer */ + + value.it_value.tv_sec = timeout->tv_sec; + value.it_value.tv_nsec = timeout->tv_nsec; + value.it_interval.tv_sec = 0; + value.it_interval.tv_nsec = 0; + + ret = timer_settime(timerid, 0, &value, NULL); + if (ret < 0) + { + errcode = errno; + DEBUGASSERT(errno > 0); + ret = -errcode; + goto errout_with_socket; + } + + /* Wait for the echo reply */ + + for (; ; ) + { + nrecvd = recv(sd, buffer, PING_BUFFER_SIZE, 0); + if (nrecvd < 0) + { + if (errno != EINTR) + { + int errcode = errno; + DEBUGASSERT(errno > 0); + + ndbg("ERROR: recv failed: %d\n", errcode); + ret = -errcode; + break; + } + } + else if (nrecvd >= PING_BUFFER_SIZE) + { + FAR struct iphdr *iphdr = (FAR struct iphdr *)buffer; + + icmpv4 = (FAR struct icmp *)(buffer + (iphdr->ihl << 2)); /* skip ip hdr */ + if (icmpv4->icmp_type == ICMP_ECHO_REPLY) + { + ret = OK; + break; + } + } + } + + /* Stop the timer */ + + value.it_value.tv_sec = 0; + value.it_value.tv_nsec = 0; + value.it_interval.tv_sec = 0; + value.it_interval.tv_nsec = 0; + + (void)timer_settime(timerid, 0, &value, &ovalue); + if (ret < 0) + { + errcode = errno; + DEBUGASSERT(errno > 0); + ret = -errcode; + } + else + { + rountrip->tv_sec = ovalue.it_value.tv_sec; + rountrip->tv_nsec = ovalue.it_value.tv_nsec; + ret = OK; + } + +errout_with_socket: + close(sd); + +errout_with_sigaction: + (void) sigaction(CONFIG_NETUTILS_PING_SIGNO, &oact, NULL); + +errout_with_timer: + timer_delete(timerid); + return ret; +} diff --git a/netutils/ping/icmpv6_ping.c b/netutils/ping/icmpv6_ping.c new file mode 100644 index 000000000..c7bfefccd --- /dev/null +++ b/netutils/ping/icmpv6_ping.c @@ -0,0 +1,308 @@ +/**************************************************************************** + * netutils/ping/icmp_ping.c + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ICMPv6BUF ((struct icmpv6_iphdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) +#define ICMPv6ECHOREQ \ + ((struct icmpv6_echo_request_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) +#define ICMPv6ECHOREPLY \ + ((struct icmpv6_echo_reply_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) + +/* Use the monotonic clock if it is available */ + +#ifdef CONFIG_CLOCK_MONOTONIC +# define PING_CLOCK CLOCK_MONOTONIC +#else +# define PING_CLOCK CLOCK_REALTIME +#endif + +#ifndef CONFIG_NETUTILS_PING_SIGNO +# define CONFIG_NETUTILS_PING_SIGNO 13 +#endif + +#define ICMPv6_DATALEN 56 +#define IPv6_PAYLOAD_SIZE (ICMPv6_HDRLEN + ICMPv6_DATALEN) +#define PING6_BUFFER_SIZE (IPv6_HDRLEN + IPv6_PAYLOAD_SIZE) + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: icmpv6_echo_request + * + * Description: + * Format an ICMPv6 Echo Request message. + * + * Parameters: + * + * + * Return: + * None + * + ****************************************************************************/ + +static void icmpv6_echo_request(FAR struct net_driver_s *dev, + FAR struct icmpv6_ping_s *pstate) +{ + FAR struct icmpv6_iphdr_s *icmp; + FAR struct icmpv6_echo_request_s *req; + uint16_t reqlen; + int i; + + nllvdbg("Send ECHO request: seqno=%d\n", pstate->png_seqno); + + /* Set up the IPv6 header (most is probably already in place) */ + + icmp = ICMPv6BUF; + icmp->vtc = 0x60; /* Version/traffic class (MS) */ + icmp->tcf = 0; /* Traffic class (LS)/Flow label (MS) */ + icmp->flow = 0; /* Flow label (LS) */ + + /* Length excludes the IPv6 header */ + + reqlen = SIZEOF_ICMPV6_ECHO_REQUEST_S(pstate->png_datlen); + icmp->len[0] = (reqlen >> 8); + icmp->len[1] = (reqlen & 0xff); + + icmp->proto = IP_PROTO_ICMP6; /* Next header */ + icmp->ttl = IP_TTL; /* Hop limit */ + + /* Set the multicast destination IP address */ + + net_ipv6addr_copy(icmp->destipaddr, pstate->png_addr); + + /* Add out IPv6 address as the source address */ + + net_ipv6addr_copy(icmp->srcipaddr, dev->d_ipv6addr); + + /* Set up the ICMPv6 Echo Request message */ + + req = ICMPv6ECHOREQ; + req->type = ICMPv6_ECHO_REQUEST; /* Message type */ + req->code = 0; /* Message qualifier */ + req->id = htons(pstate->png_id); + req->seqno = htons(pstate->png_seqno); + + /* Add some easily verifiable data */ + + for (i = 0; i < pstate->png_datlen; i++) + { + req->data[i] = i; + } + + /* Calculate the checksum over both the ICMP header and payload */ + + icmp->chksum = 0; + icmp->chksum = ~icmpv6_chksum(dev); + + /* Set the size to the size of the IPv6 header and the payload size */ + + IFF_SET_IPv6(dev->d_flags); + + dev->d_sndlen = reqlen; + dev->d_len = reqlen + IPv6_HDRLEN; +} + +static void ping6_timeout(int signo, FAR siginfo_t *info, FAR void *arg) +{ + FAR bool *timeout = (FAR bool *)arg; + *timeout = true; +} + +int ipv6_ping(FAR struct sockaddr_in6 *raddr, + FAR const struct timespec *timeout, + FAR struct timespec *roundtrip) +{ + FAR struct icmp6_hdr *icmpv6; + struct sigevent notify; + struct sigaction act; + struct sigaction oact; + struct itimerspec value; + struct itimerspec ovalue; + char buffer[PING6_BUFFER_SIZE]; + timer_t timerid; + ssize_t nrecvd; + bool timeout; + int errcode; + int ret; + int sd; + + DEBUGASSERT(raddr != NULL); + + /* Allocate a POSIX timer */ + + timeout = false; + notify.sigev_notify = SIGEV_SIGNAL; + notify.sigev_signo = CONFIG_NETUTILS_PING_SIGNO; + notify.sigev_value.sival_ptr = (FAR void *)&timeout; + + ret = timer_create(PING_CLOCK, ¬ify, &timerid); + if (ret < 0) + { + errcode = errno; + DEBUGASSERT(errno > 0); + return -errcode; + } + + /* Attach a signal handler */ + + act.sa_sigaction = ping_timeout; + act.sa_flags = SA_SIGINFO; + + (void)sigfillset(&act.sa_mask); + (void)sigdelset(&act.sa_mask, CONFIG_NETUTILS_PING_SIGNO); + + ret = sigaction(CONFIG_NETUTILS_PING_SIGNO, &act, &oact); + if (ret < 0) + { + errcode = errno; + DEBUGASSERT(errno > 0); + ret = -errcode; + goto errout_with_timer; + } + + /* Create the raw socket */ + + sd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); + if (sd < 0) + { + errcode = errno; + DEBUGASSERT(errno > 0); + ret = -errcode; + goto errout_with_sigaction; + } + + /* Format and send the ECHO request */ + + icmpv6_echo_request(FAR struct net_driver_s *dev, + FAR struct icmpv6_ping_s *pstate); + + ret = sendto(sd, buffer, PING6_BUFFER_SIZE, &raddr, + sizeof(struct sockaddr_in6)); + + if (ret < 0) + { + errcode = errno; + DEBUGASSERT(errno > 0); + ret = -errcode; + goto errout_with_socket; + } + + /* Start the timer */ + + value.it_value.tv_sec = timeout->tv_sec; + value.it_value.tv_nsec = timeout->tv_nsec; + value.it_interval.tv_sec = 0; + value.it_interval.tv_nsec = 0; + + ret = timer_settime(timerid, 0, &value, NULL); + if (ret < 0) + { + errcode = errno; + DEBUGASSERT(errno > 0); + ret = -errcode; + goto errout_with_socket; + } + + /* Wait for the echo reply */ + + for (; ; ) + { + nrecvd = recv(sd, buffer, PING6_BUFFER_SIZE, 0); + if (nrecvd < 0) + { + if (errno != EINTR) + { + int errcode = errno; + DEBUGASSERT(errno > 0); + + ndbg("ERROR: recv failed: %d\n", errcode); + ret = -errcode; + break; + } + } + else if (nrecvd >= PING6_BUFFER_SIZE && + icmpv6->icmp6_type == ICMPv6_ECHO_REPLY) + { + ret = OK; + break; + } + } + + /* Stop the timer */ + + value.it_value.tv_sec = 0; + value.it_value.tv_nsec = 0; + value.it_interval.tv_sec = 0; + value.it_interval.tv_nsec = 0; + + (void)timer_settime(timerid, 0, &value, &ovalue); + if (ret < 0) + { + errcode = errno; + DEBUGASSERT(errno > 0); + ret = -errcode; + } + else + { + rountrip->tv_sec = ovalue.it_value.tv_sec; + rountrip->tv_nsec = ovalue.it_value.tv_nsec; + ret = OK; + } + +errout_with_socket: + close(sd); + +errout_with_sigaction: + (void) sigaction(CONFIG_NETUTILS_PING_SIGNO, &oact, NULL); + +errout_with_timer: + timer_delete(timerid); + return ret; +}