From 45ec7668710de1f282cd1ceca8463357428ec5ae Mon Sep 17 00:00:00 2001
From: Martine Lenders <m.lenders@fu-berlin.de>
Date: Sat, 7 Jul 2018 14:54:31 +0200
Subject: [PATCH] shell_commands: adapt ping6 to original-ping-like
 implementation

Loosely based on [the original ping] and [netutil]'s ping

New features (compared to old RIOT version):
 - non-positional parameters
 - Better duplicate detection (addresses #9387)
 - Better asynchronous behavior
 - Potential for future move to `sock_ip`
 - (Optional) DNS-support
 - Multithreading-safe (in case shell-command handler gets called
   from multiple threads)

[the original ping]: http://ftp.arl.army.mil/~mike/ping.html
[netutil]: https://www.gnu.org/software/inetutils/
---
 sys/shell/commands/Makefile              |   2 +-
 sys/shell/commands/sc_gnrc_icmpv6_echo.c | 425 +++++++++++++++++++++++
 sys/shell/commands/sc_icmpv6_echo.c      | 351 -------------------
 sys/shell/commands/shell_commands.c      |   4 +-
 4 files changed, 428 insertions(+), 354 deletions(-)
 create mode 100644 sys/shell/commands/sc_gnrc_icmpv6_echo.c
 delete mode 100644 sys/shell/commands/sc_icmpv6_echo.c

diff --git a/sys/shell/commands/Makefile b/sys/shell/commands/Makefile
index 268a6481c0..90bd2381c2 100644
--- a/sys/shell/commands/Makefile
+++ b/sys/shell/commands/Makefile
@@ -37,7 +37,7 @@ ifneq (,$(filter gnrc_ipv6_blacklist,$(USEMODULE)))
 endif
 ifneq (,$(filter gnrc_icmpv6_echo,$(USEMODULE)))
 ifneq (,$(filter xtimer,$(USEMODULE)))
-  SRC += sc_icmpv6_echo.c
+  SRC += sc_gnrc_icmpv6_echo.c
 endif
 endif
 ifneq (,$(filter gnrc_pktbuf_cmd,$(USEMODULE)))
diff --git a/sys/shell/commands/sc_gnrc_icmpv6_echo.c b/sys/shell/commands/sc_gnrc_icmpv6_echo.c
new file mode 100644
index 0000000000..b2a0f7d825
--- /dev/null
+++ b/sys/shell/commands/sc_gnrc_icmpv6_echo.c
@@ -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 */
+
+/** @} */
diff --git a/sys/shell/commands/sc_icmpv6_echo.c b/sys/shell/commands/sc_icmpv6_echo.c
deleted file mode 100644
index bc5a9220c6..0000000000
--- a/sys/shell/commands/sc_icmpv6_echo.c
+++ /dev/null
@@ -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
-
-/**
- * @}
- */
diff --git a/sys/shell/commands/shell_commands.c b/sys/shell/commands/shell_commands.c
index 2531f0326f..9f47f4dabc 100644
--- a/sys/shell/commands/shell_commands.c
+++ b/sys/shell/commands/shell_commands.c
@@ -66,7 +66,7 @@ extern int _read_bytes(int argc, char **argv);
 
 #ifdef MODULE_GNRC_ICMPV6_ECHO
 #ifdef MODULE_XTIMER
-extern int _icmpv6_ping(int argc, char **argv);
+extern int _gnrc_icmpv6_ping(int argc, char **argv);
 #endif
 #endif
 
@@ -170,7 +170,7 @@ const shell_command_t _shell_command_list[] = {
 #endif
 #ifdef MODULE_GNRC_ICMPV6_ECHO
 #ifdef MODULE_XTIMER
-    { "ping6", "Ping via ICMPv6", _icmpv6_ping },
+    { "ping6", "Ping via ICMPv6", _gnrc_icmpv6_ping },
 #endif
 #endif
 #ifdef MODULE_RANDOM
-- 
GitLab