static uint16_t ping_interrupt(FAR struct net_driver_s *dev, FAR void *conn, FAR void *pvpriv, uint16_t flags) { FAR struct icmpv6_ping_s *pstate = (struct icmpv6_ping_s *)pvpriv; nllvdbg("flags: %04x\n", flags); if (pstate) { /* Check if this is a ICMPv6 ECHO reply. If so, return the sequence * number to the caller. NOTE: We may not even have sent the * requested ECHO request; this could have been the delayed ECHO * response from a previous ping. */ if ((flags & ICMPv6_ECHOREPLY) != 0 && conn != NULL) { FAR struct icmpv6_echo_reply_s *reply = ICMPv6ECHOREPLY; nllvdbg("ECHO reply: id=%d seqno=%d\n", ntohs(reply->id), ntohs(reply->seqno)); if (ntohs(reply->id) == pstate->png_id) { /* Consume the ECHOREPLY */ flags &= ~ICMPv6_ECHOREPLY; dev->d_len = 0; /* Return the result to the caller */ pstate->png_result = OK; pstate->png_seqno = ntohs(reply->seqno); goto end_wait; } } /* Check: * If the outgoing packet is available (it may have been claimed * by a sendto interrupt serving a different thread) * -OR- * If the output buffer currently contains unprocessed incoming * data. * -OR- * If we have already sent the ECHO request * * In the first two cases, we will just have to wait for the next * polling cycle. */ if (dev->d_sndlen <= 0 && /* Packet available */ (flags & ICMPv6_NEWDATA) == 0 && /* No incoming data */ !pstate->png_sent) /* Request not sent */ { /* Send the ECHO request now. */ icmpv6_echo_request(dev, pstate); pstate->png_sent = true; return flags; } /* Check if the selected timeout has elapsed */ if (ping_timeout(pstate)) { int failcode; /* Check if this device is on the same network as the destination * device. */ if (!net_ipv6addr_maskcmp(pstate->png_addr, dev->d_ipv6addr, dev->d_ipv6netmask)) { /* Destination address was not on the local network served by * this device. If a timeout occurs, then the most likely * reason is that the destination address is not reachable. */ nlldbg("Not reachable\n"); failcode = -ENETUNREACH; } else { nlldbg("Ping timeout\n"); failcode = -ETIMEDOUT; } /* Report the failure */ pstate->png_result = failcode; goto end_wait; } /* Continue waiting */ } return flags; end_wait: nllvdbg("Resuming\n"); /* Do not allow any further callbacks */ pstate->png_cb->flags = 0; pstate->png_cb->priv = NULL; pstate->png_cb->event = NULL; /* Wake up the waiting thread */ sem_post(&pstate->png_sem); return flags; }
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; }