mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #9523 from miri64/gnrc_icmpv6_echo/enh/new-shell-cmd
shell_commands: adapt ping6 to original-ping-like implementation
This commit is contained in:
commit
e8f89b9d3f
@ -37,7 +37,7 @@ ifneq (,$(filter gnrc_ipv6_blacklist,$(USEMODULE)))
|
|||||||
endif
|
endif
|
||||||
ifneq (,$(filter gnrc_icmpv6_echo,$(USEMODULE)))
|
ifneq (,$(filter gnrc_icmpv6_echo,$(USEMODULE)))
|
||||||
ifneq (,$(filter xtimer,$(USEMODULE)))
|
ifneq (,$(filter xtimer,$(USEMODULE)))
|
||||||
SRC += sc_icmpv6_echo.c
|
SRC += sc_gnrc_icmpv6_echo.c
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
ifneq (,$(filter gnrc_pktbuf_cmd,$(USEMODULE)))
|
ifneq (,$(filter gnrc_pktbuf_cmd,$(USEMODULE)))
|
||||||
|
425
sys/shell/commands/sc_gnrc_icmpv6_echo.c
Normal file
425
sys/shell/commands/sc_gnrc_icmpv6_echo.c
Normal file
@ -0,0 +1,425 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Freie Universität Berlin
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser
|
||||||
|
* General Public License v2.1. See the file LICENSE in the top level
|
||||||
|
* directory for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||||
|
*
|
||||||
|
* This implementation oriented itself on the [version by Mike
|
||||||
|
* Muuss](http://ftp.arl.army.mil/~mike/ping.html) which was published under
|
||||||
|
* public domain. The state-handling and duplicate detection was inspired by the
|
||||||
|
* ping version of [inetutils](://www.gnu.org/software/inetutils/), which was
|
||||||
|
* published under GPLv3
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef MODULE_GNRC_ICMPV6
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "bitfield.h"
|
||||||
|
#include "byteorder.h"
|
||||||
|
#include "kernel_types.h"
|
||||||
|
#ifdef MODULE_LUID
|
||||||
|
#include "luid.h"
|
||||||
|
#endif
|
||||||
|
#include "msg.h"
|
||||||
|
#include "net/gnrc.h"
|
||||||
|
#include "net/gnrc/icmpv6.h"
|
||||||
|
#ifdef MODULE_GNRC_IPV6_NIB
|
||||||
|
#include "net/gnrc/ipv6/nib/nc.h"
|
||||||
|
#endif
|
||||||
|
#include "net/icmpv6.h"
|
||||||
|
#include "net/ipv6.h"
|
||||||
|
#include "timex.h"
|
||||||
|
#include "utlist.h"
|
||||||
|
#include "xtimer.h"
|
||||||
|
|
||||||
|
#define _SEND_NEXT_PING (0xEF48)
|
||||||
|
#define _PING_FINISH (0xEF49)
|
||||||
|
|
||||||
|
#define CKTAB_SIZE (64U * 8) /* 64 byte * 8 bit/byte */
|
||||||
|
|
||||||
|
#define DEFAULT_COUNT (3U)
|
||||||
|
#define DEFAULT_DATALEN (sizeof(uint32_t))
|
||||||
|
#define DEFAULT_ID (0x53)
|
||||||
|
#define DEFAULT_INTERVAL_USEC (1U * US_PER_SEC)
|
||||||
|
#define DEFAULT_TIMEOUT_USEC (1U * US_PER_SEC)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
gnrc_netreg_entry_t netreg;
|
||||||
|
xtimer_t sched_timer;
|
||||||
|
msg_t sched_msg;
|
||||||
|
ipv6_addr_t host;
|
||||||
|
char *hostname;
|
||||||
|
unsigned long num_sent, num_recv, num_rept;
|
||||||
|
unsigned long long tsum;
|
||||||
|
unsigned tmin, tmax;
|
||||||
|
unsigned count;
|
||||||
|
size_t datalen;
|
||||||
|
BITFIELD(cktab, CKTAB_SIZE);
|
||||||
|
uint32_t timeout;
|
||||||
|
uint32_t interval;
|
||||||
|
kernel_pid_t iface;
|
||||||
|
uint16_t id;
|
||||||
|
uint8_t hoplimit;
|
||||||
|
uint8_t pattern;
|
||||||
|
} _ping_data_t;
|
||||||
|
|
||||||
|
static void _usage(char *cmdname);
|
||||||
|
static int _configure(int argc, char **argv, _ping_data_t *data);
|
||||||
|
static void _pinger(_ping_data_t *data);
|
||||||
|
static void _print_reply(_ping_data_t *data, gnrc_pktsnip_t *icmpv6,
|
||||||
|
ipv6_addr_t *from, unsigned hoplimit);
|
||||||
|
static void _handle_reply(_ping_data_t *data, gnrc_pktsnip_t *pkt);
|
||||||
|
static int _finish(_ping_data_t *data);
|
||||||
|
|
||||||
|
int _gnrc_icmpv6_ping(int argc, char **argv)
|
||||||
|
{
|
||||||
|
_ping_data_t data = {
|
||||||
|
.netreg = GNRC_NETREG_ENTRY_INIT_PID(ICMPV6_ECHO_REP,
|
||||||
|
sched_active_pid),
|
||||||
|
.count = DEFAULT_COUNT,
|
||||||
|
.tmin = UINT_MAX,
|
||||||
|
.datalen = DEFAULT_DATALEN,
|
||||||
|
.timeout = DEFAULT_TIMEOUT_USEC,
|
||||||
|
.interval = DEFAULT_INTERVAL_USEC,
|
||||||
|
.id = DEFAULT_ID,
|
||||||
|
.pattern = DEFAULT_ID,
|
||||||
|
};
|
||||||
|
int res;
|
||||||
|
|
||||||
|
if ((res = _configure(argc, argv, &data)) != 0) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
gnrc_netreg_register(GNRC_NETTYPE_ICMPV6, &data.netreg);
|
||||||
|
_pinger(&data);
|
||||||
|
do {
|
||||||
|
msg_t msg;
|
||||||
|
|
||||||
|
msg_receive(&msg);
|
||||||
|
switch (msg.type) {
|
||||||
|
case GNRC_NETAPI_MSG_TYPE_RCV: {
|
||||||
|
_handle_reply(&data, msg.content.ptr);
|
||||||
|
gnrc_pktbuf_release(msg.content.ptr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case _SEND_NEXT_PING:
|
||||||
|
_pinger(&data);
|
||||||
|
break;
|
||||||
|
case _PING_FINISH:
|
||||||
|
goto finish;
|
||||||
|
default:
|
||||||
|
/* requeue wrong packets */
|
||||||
|
msg_send(&msg, sched_active_pid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (data.num_recv < data.count);
|
||||||
|
finish:
|
||||||
|
xtimer_remove(&data.sched_timer);
|
||||||
|
res = _finish(&data);
|
||||||
|
gnrc_netreg_unregister(GNRC_NETTYPE_ICMPV6, &data.netreg);
|
||||||
|
for (unsigned i = 0;
|
||||||
|
i < cib_avail((cib_t *)&sched_active_thread->msg_queue);
|
||||||
|
i++) {
|
||||||
|
msg_t msg;
|
||||||
|
|
||||||
|
/* remove all remaining messages (likely caused by duplicates) */
|
||||||
|
if ((msg_try_receive(&msg) > 0) &&
|
||||||
|
(msg.type == GNRC_NETAPI_MSG_TYPE_RCV) &&
|
||||||
|
(((gnrc_pktsnip_t *)msg.content.ptr)->type == GNRC_NETTYPE_ICMPV6)) {
|
||||||
|
gnrc_pktbuf_release(msg.content.ptr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* requeue other packets */
|
||||||
|
msg_send(&msg, sched_active_pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _usage(char *cmdname)
|
||||||
|
{
|
||||||
|
printf("%s [-c <count>] [-h] [-i <ms interval>] [-s <packetsize>]\n",
|
||||||
|
cmdname);
|
||||||
|
puts(" [-t hoplimit] [-W <ms timeout>] <host>[%<interface>]");
|
||||||
|
puts(" count: number of pings (default: 3)");
|
||||||
|
puts(" ms interval: wait interval milliseconds between sending "
|
||||||
|
"(default: 1000)");
|
||||||
|
puts(" packetsize: number of bytes in echo payload; must be >= 4 to "
|
||||||
|
"measure round trip time (default: 4)");
|
||||||
|
puts(" hoplimit: Set the IP time to life/hoplimit "
|
||||||
|
"(default: interface config)");
|
||||||
|
puts(" ms timeout: Time to wait for a resonse in milliseconds "
|
||||||
|
"(default: 1000). The option affects only timeout in absence "
|
||||||
|
"of any responses, otherwise wait for two RTTs");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _configure(int argc, char **argv, _ping_data_t *data)
|
||||||
|
{
|
||||||
|
char *cmdname = argv[0];
|
||||||
|
int res = 1;
|
||||||
|
|
||||||
|
/* parse command line arguments */
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
char *arg = argv[i];
|
||||||
|
if (arg[0] != '-') {
|
||||||
|
data->hostname = arg;
|
||||||
|
#ifdef MODULE_SOCK_DNS
|
||||||
|
if (sock_dns_query(data->hostname, &data->host, AF_INET6) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
data->iface = ipv6_addr_split_iface(data->hostname);
|
||||||
|
if (data->iface < KERNEL_PID_UNDEF) {
|
||||||
|
#if GNRC_NETIF_NUMOF == 1
|
||||||
|
gnrc_netif_t *netif = gnrc_netif_iter(NULL);
|
||||||
|
if (netif != NULL) {
|
||||||
|
data->iface = netif->pid;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (ipv6_addr_from_str(&data->host, data->hostname) == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
res = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch (arg[1]) {
|
||||||
|
case 'c':
|
||||||
|
if (((i++) + 1) < argc) {
|
||||||
|
data->count = atoi(argv[i]);
|
||||||
|
if (data->count > 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* intentionally falls through */
|
||||||
|
case 'h':
|
||||||
|
res = 1;
|
||||||
|
continue;
|
||||||
|
/* intentionally falls through */
|
||||||
|
case 'i':
|
||||||
|
if ((++i) < argc) {
|
||||||
|
data->interval = (uint32_t)atoi(argv[i]) * US_PER_MS;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* intentionally falls through */
|
||||||
|
case 's':
|
||||||
|
if ((++i) < argc) {
|
||||||
|
data->datalen = atoi(argv[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* intentionally falls through */
|
||||||
|
case 't':
|
||||||
|
if ((++i) < argc) {
|
||||||
|
data->hoplimit = atoi(argv[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* intentionally falls through */
|
||||||
|
case 'W':
|
||||||
|
if ((++i) < argc) {
|
||||||
|
data->timeout = (uint32_t)atoi(argv[i]) * US_PER_MS;
|
||||||
|
if (data->timeout > 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* intentionally falls through */
|
||||||
|
default:
|
||||||
|
res = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (res != 0) {
|
||||||
|
_usage(cmdname);
|
||||||
|
}
|
||||||
|
#ifdef MODULE_LUID
|
||||||
|
luid_custom(&data->id, sizeof(data->id), DEFAULT_ID);
|
||||||
|
#endif
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _pinger(_ping_data_t *data)
|
||||||
|
{
|
||||||
|
gnrc_pktsnip_t *pkt, *tmp;
|
||||||
|
ipv6_hdr_t *ipv6;
|
||||||
|
uint32_t timer;
|
||||||
|
uint8_t *databuf;
|
||||||
|
|
||||||
|
/* schedule next event (next ping or finish) ASAP */
|
||||||
|
if ((data->num_sent + 1) < data->count) {
|
||||||
|
/* didn't send all pings yet - schedule next in data->interval */
|
||||||
|
data->sched_msg.type = _SEND_NEXT_PING;
|
||||||
|
timer = data->interval;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Wait for the last ping to come back.
|
||||||
|
* data->timeout: wait for a response in milliseconds.
|
||||||
|
* Affects only timeout in absence of any responses,
|
||||||
|
* otherwise ping waits for two max RTTs. */
|
||||||
|
data->sched_msg.type = _PING_FINISH;
|
||||||
|
timer = data->timeout;
|
||||||
|
if (data->num_recv) {
|
||||||
|
/* approx. 2*tmax, in seconds (2 RTT) */
|
||||||
|
timer = (data->tmax / (512UL * 1024UL)) * US_PER_SEC;
|
||||||
|
if (timer == 0) {
|
||||||
|
timer = 1U * US_PER_SEC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xtimer_set_msg(&data->sched_timer, timer, &data->sched_msg,
|
||||||
|
sched_active_pid);
|
||||||
|
bf_unset(data->cktab, (size_t)data->num_sent % CKTAB_SIZE);
|
||||||
|
pkt = gnrc_icmpv6_echo_build(ICMPV6_ECHO_REQ, data->id,
|
||||||
|
(uint16_t)data->num_sent++,
|
||||||
|
NULL, data->datalen);
|
||||||
|
if (pkt == NULL) {
|
||||||
|
puts("error: packet buffer full");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
databuf = (uint8_t *)(pkt->data) + sizeof(icmpv6_echo_t);
|
||||||
|
memset(databuf, data->pattern, data->datalen);
|
||||||
|
tmp = gnrc_ipv6_hdr_build(pkt, NULL, &data->host);
|
||||||
|
if (tmp == NULL) {
|
||||||
|
puts("error: packet buffer full");
|
||||||
|
goto error_exit;
|
||||||
|
}
|
||||||
|
pkt = tmp;
|
||||||
|
ipv6 = pkt->data;
|
||||||
|
/* if data->hoplimit is unset (i.e. 0) gnrc_ipv6 will select hop limit */
|
||||||
|
ipv6->hl = data->hoplimit;
|
||||||
|
if (data->iface > KERNEL_PID_UNDEF) {
|
||||||
|
gnrc_netif_hdr_t *netif;
|
||||||
|
|
||||||
|
tmp = gnrc_netif_hdr_build(NULL, 0, NULL, 0);
|
||||||
|
if (tmp == NULL) {
|
||||||
|
puts("error: packet buffer full");
|
||||||
|
goto error_exit;
|
||||||
|
}
|
||||||
|
netif = tmp->data;
|
||||||
|
netif->if_pid = data->iface;
|
||||||
|
LL_PREPEND(pkt, tmp);
|
||||||
|
}
|
||||||
|
if (data->datalen >= sizeof(uint32_t)) {
|
||||||
|
*((uint32_t *)databuf) = xtimer_now_usec();
|
||||||
|
}
|
||||||
|
if (!gnrc_netapi_dispatch_send(GNRC_NETTYPE_IPV6,
|
||||||
|
GNRC_NETREG_DEMUX_CTX_ALL,
|
||||||
|
pkt)) {
|
||||||
|
puts("error: unable to send ICMPv6 echo request");
|
||||||
|
goto error_exit;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
error_exit:
|
||||||
|
gnrc_pktbuf_release(pkt);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _print_reply(_ping_data_t *data, gnrc_pktsnip_t *icmpv6,
|
||||||
|
ipv6_addr_t *from, unsigned hoplimit)
|
||||||
|
{
|
||||||
|
icmpv6_echo_t *icmpv6_hdr = icmpv6->data;
|
||||||
|
|
||||||
|
/* discard if too short */
|
||||||
|
if (icmpv6->size < (data->datalen + sizeof(icmpv6_echo_t))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (icmpv6_hdr->type == ICMPV6_ECHO_REP) {
|
||||||
|
char from_str[IPV6_ADDR_MAX_STR_LEN];
|
||||||
|
const char *dupmsg = " (DUP!)";
|
||||||
|
uint32_t triptime = 0;
|
||||||
|
uint16_t recv_seq;
|
||||||
|
|
||||||
|
/* not our ping */
|
||||||
|
if (byteorder_ntohs(icmpv6_hdr->id) != data->id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
recv_seq = byteorder_ntohs(icmpv6_hdr->seq);
|
||||||
|
ipv6_addr_to_str(&from_str[0], from, sizeof(from_str));
|
||||||
|
if (data->datalen >= sizeof(uint32_t)) {
|
||||||
|
triptime = xtimer_now_usec() - *((uint32_t *)(icmpv6_hdr + 1));
|
||||||
|
data->tsum += triptime;
|
||||||
|
if (triptime < data->tmin) {
|
||||||
|
data->tmin = triptime;
|
||||||
|
}
|
||||||
|
if (triptime > data->tmax) {
|
||||||
|
data->tmax = triptime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bf_isset(data->cktab, recv_seq % CKTAB_SIZE)) {
|
||||||
|
data->num_rept++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bf_set(data->cktab, recv_seq % CKTAB_SIZE);
|
||||||
|
data->num_recv++;
|
||||||
|
dupmsg += 7;
|
||||||
|
}
|
||||||
|
printf("%u bytes from %s: icmp_seq=%u ttl=%u", (unsigned)icmpv6->size,
|
||||||
|
from_str, recv_seq, hoplimit);
|
||||||
|
if (data->datalen >= sizeof(uint32_t)) {
|
||||||
|
printf(" time=%lu.%03lu ms", (long unsigned)triptime / 1000,
|
||||||
|
(long unsigned)triptime % 1000);
|
||||||
|
}
|
||||||
|
puts(dupmsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _handle_reply(_ping_data_t *data, gnrc_pktsnip_t *pkt)
|
||||||
|
{
|
||||||
|
gnrc_pktsnip_t *ipv6, *icmpv6;
|
||||||
|
ipv6_hdr_t *ipv6_hdr;
|
||||||
|
|
||||||
|
ipv6 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6);
|
||||||
|
icmpv6 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_ICMPV6);
|
||||||
|
if ((ipv6 == NULL) || (icmpv6 == NULL)) {
|
||||||
|
puts("No IPv6 or ICMPv6 header found in reply");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipv6_hdr = ipv6->data;
|
||||||
|
_print_reply(data, icmpv6, &ipv6_hdr->src, ipv6_hdr->hl);
|
||||||
|
#ifdef MODULE_GNRC_IPV6_NIB
|
||||||
|
/* successful ping to neighbor (NIB handles case if ipv6->src is not a
|
||||||
|
* neighbor) can be taken as upper-layer hint for reachability:
|
||||||
|
* https://tools.ietf.org/html/rfc4861#section-7.3.1 */
|
||||||
|
gnrc_ipv6_nib_nc_mark_reachable(&ipv6_hdr->src);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _finish(_ping_data_t *data)
|
||||||
|
{
|
||||||
|
unsigned long tmp, nrecv, ndup;
|
||||||
|
|
||||||
|
tmp = data->num_sent;
|
||||||
|
nrecv = data->num_recv;
|
||||||
|
ndup = data->num_rept;
|
||||||
|
printf("\n--- %s PING statistics ---\n"
|
||||||
|
"%lu packets transmitted, "
|
||||||
|
"%lu packets received, ",
|
||||||
|
data->hostname, tmp, nrecv);
|
||||||
|
if (ndup) {
|
||||||
|
printf("%lu duplicates, ", ndup);
|
||||||
|
}
|
||||||
|
if (tmp > 0) {
|
||||||
|
tmp = ((tmp - nrecv) * 100) / tmp;
|
||||||
|
}
|
||||||
|
printf("%lu%% packet loss\n", tmp);
|
||||||
|
if (data->tmin != UINT_MAX) {
|
||||||
|
unsigned tavg = data->tsum / (nrecv + ndup);
|
||||||
|
printf("round-trip min/avg/max = %u.%03u/%u.%03u/%u.%03u ms\n",
|
||||||
|
data->tmin / 1000, data->tmin % 1000,
|
||||||
|
tavg / 1000, tavg % 1000,
|
||||||
|
data->tmax / 1000, data->tmax % 1000);
|
||||||
|
}
|
||||||
|
/* if condition is true, exit with 1 -- 'failure' */
|
||||||
|
return (nrecv == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* MODULE_GNRC_ICMPV6 */
|
||||||
|
|
||||||
|
/** @} */
|
@ -1,351 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015 Martine Lenders <mlenders@inf.fu-berlin.de>
|
|
||||||
*
|
|
||||||
* This file is subject to the terms and conditions of the GNU Lesser General
|
|
||||||
* Public License v2.1. See the file LICENSE in the top level directory for
|
|
||||||
* more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ingroup sys_shell_commands
|
|
||||||
* @{
|
|
||||||
*
|
|
||||||
* @file
|
|
||||||
*
|
|
||||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
#ifdef MODULE_GNRC_ICMPV6
|
|
||||||
|
|
||||||
#include "byteorder.h"
|
|
||||||
#include "net/gnrc/icmpv6.h"
|
|
||||||
#include "net/ipv6/addr.h"
|
|
||||||
#ifdef MODULE_GNRC_IPV6_NIB
|
|
||||||
#include "net/gnrc/ipv6/nib/nc.h"
|
|
||||||
#endif
|
|
||||||
#include "net/gnrc/ipv6/hdr.h"
|
|
||||||
#include "net/gnrc/netif.h"
|
|
||||||
#include "net/gnrc.h"
|
|
||||||
#include "thread.h"
|
|
||||||
#include "utlist.h"
|
|
||||||
#include "xtimer.h"
|
|
||||||
|
|
||||||
static uint16_t id = 0x53;
|
|
||||||
static uint16_t min_seq_expected = 0;
|
|
||||||
static uint16_t max_seq_expected = 0;
|
|
||||||
static char ipv6_str[IPV6_ADDR_MAX_STR_LEN];
|
|
||||||
|
|
||||||
static void usage(char **argv)
|
|
||||||
{
|
|
||||||
printf("%s [<count>] <ipv6 addr>[%%<interface>] [<payload_len>] [<delay in ms>] [<stats interval>]\n", argv[0]);
|
|
||||||
puts("defaults:");
|
|
||||||
puts(" count = 3");
|
|
||||||
puts(" interface = first interface if only one present, only needed for link-local addresses");
|
|
||||||
puts(" payload_len = 4");
|
|
||||||
puts(" delay = 1000");
|
|
||||||
puts(" stats interval = count");
|
|
||||||
}
|
|
||||||
|
|
||||||
void _set_payload(icmpv6_echo_t *hdr, size_t payload_len)
|
|
||||||
{
|
|
||||||
size_t i = 0;
|
|
||||||
uint8_t *payload = (uint8_t *)(hdr + 1);
|
|
||||||
|
|
||||||
while (i < payload_len) {
|
|
||||||
if (id > 0xff) {
|
|
||||||
payload[i] = (uint8_t)(id >> 8);
|
|
||||||
payload[i + 1] = (uint8_t)(id & 0xff);
|
|
||||||
i += 2;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
payload[i++] = (uint8_t)(id & 0xff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i > payload_len) { /* when id > 0xff and payload_len is odd */
|
|
||||||
payload[payload_len - 1] = id >> 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool _expected_seq(uint16_t seq)
|
|
||||||
{
|
|
||||||
/* take integer overflows in account */
|
|
||||||
if (min_seq_expected > max_seq_expected) {
|
|
||||||
return (seq >= min_seq_expected) || (seq <= max_seq_expected);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return (seq >= min_seq_expected) && (seq <= max_seq_expected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int _handle_reply(gnrc_pktsnip_t *pkt, uint32_t time)
|
|
||||||
{
|
|
||||||
gnrc_pktsnip_t *ipv6, *icmpv6;
|
|
||||||
ipv6_hdr_t *ipv6_hdr;
|
|
||||||
icmpv6_echo_t *icmpv6_hdr;
|
|
||||||
uint16_t seq;
|
|
||||||
|
|
||||||
ipv6 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6);
|
|
||||||
icmpv6 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_ICMPV6);
|
|
||||||
|
|
||||||
if ((ipv6 == NULL) || (icmpv6 == NULL)) {
|
|
||||||
puts("error: IPv6 header or ICMPv6 header not found in reply");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ipv6_hdr = ipv6->data;
|
|
||||||
icmpv6_hdr = icmpv6->data;
|
|
||||||
seq = byteorder_ntohs(icmpv6_hdr->seq);
|
|
||||||
|
|
||||||
if ((byteorder_ntohs(icmpv6_hdr->id) == id) && _expected_seq(seq)) {
|
|
||||||
if (seq <= min_seq_expected) {
|
|
||||||
min_seq_expected++;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("%u bytes from %s: id=%" PRIu16 " seq=%" PRIu16 " hop limit=%u time = %"
|
|
||||||
PRIu32 ".%03" PRIu32 " ms\n", (unsigned) icmpv6->size,
|
|
||||||
ipv6_addr_to_str(ipv6_str, &(ipv6_hdr->src), sizeof(ipv6_str)),
|
|
||||||
byteorder_ntohs(icmpv6_hdr->id), seq, (unsigned)ipv6_hdr->hl,
|
|
||||||
time / US_PER_MS, time % US_PER_MS);
|
|
||||||
#ifdef MODULE_GNRC_IPV6_NIB
|
|
||||||
gnrc_ipv6_nib_nc_mark_reachable(&ipv6_hdr->src);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
puts("error: unexpected parameters");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _print_stats(char *addr_str, int success, int count, uint64_t total_time,
|
|
||||||
uint64_t sum_rtt, uint32_t min_rtt, uint32_t max_rtt)
|
|
||||||
{
|
|
||||||
printf("--- %s ping statistics ---\n", addr_str);
|
|
||||||
|
|
||||||
if (success > 0) {
|
|
||||||
uint32_t avg_rtt = (uint32_t)(sum_rtt / success); /* get average */
|
|
||||||
printf("%d packets transmitted, %d received, %d%% packet loss, time %"
|
|
||||||
PRIu32 ".06%" PRIu32 " s\n", count, success,
|
|
||||||
(100 - ((success * 100) / count)),
|
|
||||||
(uint32_t)total_time / US_PER_SEC, (uint32_t)total_time % US_PER_SEC);
|
|
||||||
printf("rtt min/avg/max = "
|
|
||||||
"%" PRIu32 ".%03" PRIu32 "/"
|
|
||||||
"%" PRIu32 ".%03" PRIu32 "/"
|
|
||||||
"%" PRIu32 ".%03" PRIu32 " ms\n",
|
|
||||||
min_rtt / US_PER_MS, min_rtt % US_PER_MS,
|
|
||||||
avg_rtt / US_PER_MS, avg_rtt % US_PER_MS,
|
|
||||||
max_rtt / US_PER_MS, max_rtt % US_PER_MS);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
printf("%d packets transmitted, 0 received, 100%% packet loss\n", count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int _icmpv6_ping(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int count = 3, success = 0, remaining, stat_interval = 0, stat_counter = 0;
|
|
||||||
size_t payload_len = 4;
|
|
||||||
uint32_t delay = 1 * MS_PER_SEC;
|
|
||||||
char *addr_str;
|
|
||||||
ipv6_addr_t addr;
|
|
||||||
kernel_pid_t src_iface;
|
|
||||||
msg_t msg;
|
|
||||||
gnrc_netreg_entry_t my_entry = GNRC_NETREG_ENTRY_INIT_PID(ICMPV6_ECHO_REP,
|
|
||||||
sched_active_pid);
|
|
||||||
uint32_t min_rtt = UINT32_MAX, max_rtt = 0;
|
|
||||||
uint64_t sum_rtt = 0;
|
|
||||||
uint64_t ping_start;
|
|
||||||
int param_offset = 0;
|
|
||||||
|
|
||||||
if (argc < 2) {
|
|
||||||
usage(argv);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else if ((argc > 2) && ((count = atoi(argv[1])) > 0)) {
|
|
||||||
param_offset = 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
count = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
stat_interval = count;
|
|
||||||
|
|
||||||
addr_str = argv[1 + param_offset];
|
|
||||||
if (argc > (2 + param_offset)) {
|
|
||||||
payload_len = atoi(argv[2 + param_offset]);
|
|
||||||
}
|
|
||||||
if (argc > (3 + param_offset)) {
|
|
||||||
delay = atoi(argv[3 + param_offset]);
|
|
||||||
}
|
|
||||||
if (argc > (4 + param_offset)) {
|
|
||||||
stat_interval = atoi(argv[4 + param_offset]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((int)payload_len < 0) {
|
|
||||||
usage(argv);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
src_iface = ipv6_addr_split_iface(addr_str);
|
|
||||||
if (src_iface == -1) {
|
|
||||||
src_iface = KERNEL_PID_UNDEF;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ipv6_addr_from_str(&addr, addr_str) == NULL) {
|
|
||||||
puts("error: malformed address");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ipv6_addr_is_link_local(&addr) || (src_iface != KERNEL_PID_UNDEF)) {
|
|
||||||
size_t ifnum = gnrc_netif_numof();
|
|
||||||
|
|
||||||
if (src_iface == KERNEL_PID_UNDEF) {
|
|
||||||
if (ifnum == 1) {
|
|
||||||
src_iface = gnrc_netif_iter(NULL)->pid;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
puts("error: link local target needs interface parameter (use \"<address>%<ifnum>\")\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (gnrc_netif_get_by_pid(src_iface) == NULL) {
|
|
||||||
printf("error: %"PRIkernel_pid" is not a valid interface.\n", src_iface);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gnrc_netreg_register(GNRC_NETTYPE_ICMPV6, &my_entry) < 0) {
|
|
||||||
puts("error: network registry is full");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
remaining = count;
|
|
||||||
|
|
||||||
ping_start = xtimer_now_usec64();
|
|
||||||
|
|
||||||
while ((remaining--) > 0) {
|
|
||||||
gnrc_pktsnip_t *pkt, *payload;
|
|
||||||
uint32_t start, timeout = 1 * US_PER_SEC;
|
|
||||||
|
|
||||||
payload = gnrc_icmpv6_echo_build(ICMPV6_ECHO_REQ, id,
|
|
||||||
++max_seq_expected,
|
|
||||||
NULL, payload_len);
|
|
||||||
|
|
||||||
if (payload == NULL) {
|
|
||||||
puts("error: packet buffer full");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_set_payload(payload->data, payload_len);
|
|
||||||
|
|
||||||
pkt = gnrc_ipv6_hdr_build(payload, NULL, &addr);
|
|
||||||
|
|
||||||
if (pkt == NULL) {
|
|
||||||
puts("error: packet buffer full");
|
|
||||||
gnrc_pktbuf_release(payload);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (src_iface != KERNEL_PID_UNDEF) {
|
|
||||||
payload = pkt;
|
|
||||||
pkt = gnrc_pktbuf_add(payload, NULL, sizeof(gnrc_netif_hdr_t),
|
|
||||||
GNRC_NETTYPE_NETIF);
|
|
||||||
|
|
||||||
if (pkt == NULL) {
|
|
||||||
puts("error: packet buffer full");
|
|
||||||
gnrc_pktbuf_release(payload);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
gnrc_netif_hdr_init(((gnrc_netif_hdr_t *)pkt->data), 0, 0);
|
|
||||||
((gnrc_netif_hdr_t *)pkt->data)->if_pid = src_iface;
|
|
||||||
}
|
|
||||||
|
|
||||||
start = xtimer_now_usec();
|
|
||||||
if (!gnrc_netapi_dispatch_send(GNRC_NETTYPE_IPV6, GNRC_NETREG_DEMUX_CTX_ALL, pkt)) {
|
|
||||||
puts("error: unable to send ICMPv6 echo request\n");
|
|
||||||
gnrc_pktbuf_release(pkt);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: replace when #4219 was fixed */
|
|
||||||
if (xtimer_msg_receive_timeout64(&msg, (uint64_t)timeout) >= 0) {
|
|
||||||
uint32_t stop;
|
|
||||||
switch (msg.type) {
|
|
||||||
case GNRC_NETAPI_MSG_TYPE_RCV:
|
|
||||||
stop = xtimer_now_usec() - start;
|
|
||||||
|
|
||||||
gnrc_pktsnip_t *pkt = msg.content.ptr;
|
|
||||||
success += _handle_reply(pkt, stop);
|
|
||||||
gnrc_pktbuf_release(pkt);
|
|
||||||
|
|
||||||
if (stop > max_rtt) {
|
|
||||||
max_rtt = stop;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stop < min_rtt) {
|
|
||||||
min_rtt = stop;
|
|
||||||
}
|
|
||||||
|
|
||||||
sum_rtt += stop;
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
/* requeue wrong packets */
|
|
||||||
msg_send(&msg, thread_getpid());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
puts("ping timeout");
|
|
||||||
}
|
|
||||||
|
|
||||||
while (msg_try_receive(&msg) > 0) {
|
|
||||||
if (msg.type == GNRC_NETAPI_MSG_TYPE_RCV) {
|
|
||||||
printf("dropping additional response packet (probably caused by duplicates)\n");
|
|
||||||
gnrc_pktsnip_t *pkt = msg.content.ptr;
|
|
||||||
gnrc_pktbuf_release(pkt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remaining > 0) {
|
|
||||||
xtimer_usleep64(delay * US_PER_MS);
|
|
||||||
}
|
|
||||||
if ((++stat_counter == stat_interval) || (remaining == 0)) {
|
|
||||||
uint64_t total_time = xtimer_now_usec64() - ping_start;
|
|
||||||
_print_stats(addr_str, success, (count - remaining), total_time, sum_rtt, min_rtt,
|
|
||||||
max_rtt);
|
|
||||||
stat_counter = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
max_seq_expected = 0;
|
|
||||||
id++;
|
|
||||||
|
|
||||||
gnrc_netreg_unregister(GNRC_NETTYPE_ICMPV6, &my_entry);
|
|
||||||
while (msg_try_receive(&msg) > 0) {
|
|
||||||
if (msg.type == GNRC_NETAPI_MSG_TYPE_RCV) {
|
|
||||||
printf("dropping additional response packet (probably caused by duplicates)\n");
|
|
||||||
gnrc_pktsnip_t *pkt = msg.content.ptr;
|
|
||||||
gnrc_pktbuf_release(pkt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return success ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @}
|
|
||||||
*/
|
|
@ -66,7 +66,7 @@ extern int _read_bytes(int argc, char **argv);
|
|||||||
|
|
||||||
#ifdef MODULE_GNRC_ICMPV6_ECHO
|
#ifdef MODULE_GNRC_ICMPV6_ECHO
|
||||||
#ifdef MODULE_XTIMER
|
#ifdef MODULE_XTIMER
|
||||||
extern int _icmpv6_ping(int argc, char **argv);
|
extern int _gnrc_icmpv6_ping(int argc, char **argv);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -170,7 +170,7 @@ const shell_command_t _shell_command_list[] = {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef MODULE_GNRC_ICMPV6_ECHO
|
#ifdef MODULE_GNRC_ICMPV6_ECHO
|
||||||
#ifdef MODULE_XTIMER
|
#ifdef MODULE_XTIMER
|
||||||
{ "ping6", "Ping via ICMPv6", _icmpv6_ping },
|
{ "ping6", "Ping via ICMPv6", _gnrc_icmpv6_ping },
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#ifdef MODULE_RANDOM
|
#ifdef MODULE_RANDOM
|
||||||
|
Loading…
Reference in New Issue
Block a user