diff --git a/dist/pythonlibs/riotctrl_shell/gnrc.py b/dist/pythonlibs/riotctrl_shell/gnrc.py index a01d4a566f65..efb45f7b83ab 100644 --- a/dist/pythonlibs/riotctrl_shell/gnrc.py +++ b/dist/pythonlibs/riotctrl_shell/gnrc.py @@ -22,6 +22,8 @@ def __init__(self): self.c_reply = re.compile(r"\d+ bytes from " r"(?P[0-9a-f:]+(%\S+)?): " r"icmp_seq=(?P\d+) ttl=(?P\d+)" + r"( corrupted at offset (?P\d+))?" + r"( truncated by (?P\d+) byte)?" r"( rssi=(?P-?\d+) dBm)?" r"( time=(?P\d+.\d+) ms)?" r"(?P \(DUP\))?") @@ -48,6 +50,10 @@ def _add_reply(res, reply): reply["rssi"] = int(reply["rssi"]) else: reply.pop("rssi", None) + if reply.get("truncated") is not None: + reply["truncated"] = int(reply["truncated"]) + if reply.get("corrupted") is not None: + reply["corrupted"] = int(reply["corrupted"]) if "replies" in res: res["replies"].append(reply) else: @@ -79,10 +85,12 @@ def parse(self, cmd_output): >>> res = parser.parse( ... "12 bytes from fe80::385d:f965:106b:1114%6: " ... "icmp_seq=0 ttl=64 rssi=-34 dBm time=8.839 ms\\n" + ... "6 bytes from fe80::385d:f965:106b:1114%6: " + ... "icmp_seq=1 ttl=64 truncated by 6 byte " + ... "rssi=-34 dBm time=6.925 ms\\n" ... "12 bytes from fe80::385d:f965:106b:1114%6: " - ... "icmp_seq=1 ttl=64 rssi=-34 dBm time=6.925 ms\\n" - ... "12 bytes from fe80::385d:f965:106b:1114%6: " - ... "icmp_seq=2 ttl=64 rssi=-34 dBm time=7.885 ms\\n" + ... "icmp_seq=2 ttl=64 corrupted at offset 7 " + ... "rssi=-34 dBm time=7.885 ms\\n" ... "--- fe80::385d:f965:106b:1114 PING statistics ---\\n" ... "3 packets transmitted, 3 packets received, 0% packet loss\\n" ... "round-trip min/avg/max = 6.925/7.883/8.839 ms\\n") @@ -91,7 +99,7 @@ def parse(self, cmd_output): >>> len(res["replies"]) 3 >>> sorted(res["replies"][0]) - ['rssi', 'rtt', 'seq', 'source', 'ttl'] + ['corrupted', 'rssi', 'rtt', 'seq', 'source', 'truncated', 'ttl'] >>> res["replies"][0]["source"] 'fe80::385d:f965:106b:1114%6' >>> res["replies"][0]["seq"] @@ -102,6 +110,12 @@ def parse(self, cmd_output): -34 >>> res["replies"][0]["rtt"] 8.839 + >>> res["replies"][0]["corrupted"] + >>> res["replies"][0]["truncated"] + >>> res["replies"][1]["truncated"] + 6 + >>> res["replies"][2]["corrupted"] + 7 >>> sorted(res["stats"]) ['packet_loss', 'rx', 'tx'] >>> res["stats"]["tx"] diff --git a/sys/shell/commands/sc_gnrc_icmpv6_echo.c b/sys/shell/commands/sc_gnrc_icmpv6_echo.c index 2b368ece93d3..c2117b76e431 100644 --- a/sys/shell/commands/sc_gnrc_icmpv6_echo.c +++ b/sys/shell/commands/sc_gnrc_icmpv6_echo.c @@ -73,15 +73,14 @@ typedef struct { gnrc_netif_t *netif; 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, +static void _print_reply(_ping_data_t *data, gnrc_pktsnip_t *icmpv6, uint32_t now_us, ipv6_addr_t *from, unsigned hoplimit, gnrc_netif_hdr_t *netif_hdr); -static void _handle_reply(_ping_data_t *data, gnrc_pktsnip_t *pkt); +static void _handle_reply(_ping_data_t *data, gnrc_pktsnip_t *pkt, uint32_t now_us); static int _finish(_ping_data_t *data); int _gnrc_icmpv6_ping(int argc, char **argv) @@ -95,7 +94,6 @@ int _gnrc_icmpv6_ping(int argc, char **argv) .timeout = DEFAULT_TIMEOUT_USEC, .interval = DEFAULT_INTERVAL_USEC, .id = DEFAULT_ID, - .pattern = DEFAULT_ID, }; int res; @@ -110,7 +108,7 @@ int _gnrc_icmpv6_ping(int argc, char **argv) msg_receive(&msg); switch (msg.type) { case GNRC_NETAPI_MSG_TYPE_RCV: { - _handle_reply(&data, msg.content.ptr); + _handle_reply(&data, msg.content.ptr, xtimer_now_usec()); gnrc_pktbuf_release(msg.content.ptr); break; } @@ -238,6 +236,44 @@ static int _configure(int argc, char **argv, _ping_data_t *data) return res; } +static void _fill_payload(uint8_t *buf, size_t len) +{ + uint8_t i = 0; + + if (len >= sizeof(uint32_t)) { + uint32_t now = xtimer_now_usec(); + memcpy(buf, &now, sizeof(now)); + len -= sizeof(now); + buf += sizeof(now); + } + + while (len--) { + *buf++ = i++; + } +} + +static bool _check_payload(const void *buf, size_t len, uint32_t now, + uint32_t *triptime, uint16_t *corrupt) +{ + uint8_t i = 0; + const uint8_t *data = buf; + + if (len >= sizeof(uint32_t)) { + *triptime = now - unaligned_get_u32(buf); + len -= sizeof(uint32_t); + data += sizeof(uint32_t); + } + + while (len--) { + if (*data++ != i++) { + *corrupt = data - (uint8_t *)buf - 1; + return true; + } + } + + return false; +} + static void _pinger(_ping_data_t *data) { gnrc_pktsnip_t *pkt, *tmp; @@ -277,7 +313,6 @@ static void _pinger(_ping_data_t *data) 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"); @@ -296,10 +331,10 @@ static void _pinger(_ping_data_t *data) gnrc_netif_hdr_set_netif(tmp->data, data->netif); pkt = gnrc_pkt_prepend(pkt, tmp); } - if (data->datalen >= sizeof(uint32_t)) { - uint32_t now = xtimer_now_usec(); - memcpy(databuf, &now, sizeof(now)); - } + + /* add TX timestamp & test data */ + _fill_payload(databuf, data->datalen); + if (!gnrc_netapi_dispatch_send(GNRC_NETTYPE_IPV6, GNRC_NETREG_DEMUX_CTX_ALL, pkt)) { @@ -311,7 +346,7 @@ static void _pinger(_ping_data_t *data) gnrc_pktbuf_release(pkt); } -static void _print_reply(_ping_data_t *data, gnrc_pktsnip_t *icmpv6, +static void _print_reply(_ping_data_t *data, gnrc_pktsnip_t *icmpv6, uint32_t now, ipv6_addr_t *from, unsigned hoplimit, gnrc_netif_hdr_t *netif_hdr) { @@ -319,16 +354,17 @@ static void _print_reply(_ping_data_t *data, gnrc_pktsnip_t *icmpv6, kernel_pid_t if_pid = netif_hdr ? netif_hdr->if_pid : KERNEL_PID_UNDEF; int16_t rssi = netif_hdr ? netif_hdr->rssi : GNRC_NETIF_HDR_NO_RSSI; + int16_t truncated; + + /* check if payload size matches expectation */ + truncated = (data->datalen + sizeof(icmpv6_echo_t)) - icmpv6->size; - /* 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; + uint16_t corrupted; /* not our ping */ if (byteorder_ntohs(icmpv6_hdr->id) != data->id) { @@ -340,24 +376,7 @@ static void _print_reply(_ping_data_t *data, gnrc_pktsnip_t *icmpv6, } 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() - unaligned_get_u32(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; - } + if (gnrc_netif_highlander() || (if_pid == KERNEL_PID_UNDEF) || !ipv6_addr_is_link_local(from)) { printf("%u bytes from %s: icmp_seq=%u ttl=%u", @@ -369,6 +388,15 @@ static void _print_reply(_ping_data_t *data, gnrc_pktsnip_t *icmpv6, from_str, if_pid, recv_seq, hoplimit); } + /* check if payload size matches */ + if (truncated) { + printf(" truncated by %d byte", truncated); + } + /* check response for corruption */ + else if (_check_payload(icmpv6_hdr + 1, data->datalen, now, + &triptime, &corrupted)) { + printf(" corrupted at offset %u", corrupted); + } if (rssi != GNRC_NETIF_HDR_NO_RSSI) { printf(" rssi=%"PRId16" dBm", rssi); } @@ -376,11 +404,32 @@ static void _print_reply(_ping_data_t *data, gnrc_pktsnip_t *icmpv6, printf(" time=%lu.%03lu ms", (long unsigned)triptime / 1000, (long unsigned)triptime % 1000); } + + /* we can only calculate RTT (triptime) if payload was large enough for + a TX timestamp */ + if (data->datalen >= sizeof(uint32_t)) { + 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; + } + puts(dupmsg); } } -static void _handle_reply(_ping_data_t *data, gnrc_pktsnip_t *pkt) +static void _handle_reply(_ping_data_t *data, gnrc_pktsnip_t *pkt, uint32_t now) { gnrc_pktsnip_t *netif, *ipv6, *icmpv6; gnrc_netif_hdr_t *netif_hdr; @@ -395,7 +444,7 @@ static void _handle_reply(_ping_data_t *data, gnrc_pktsnip_t *pkt) } ipv6_hdr = ipv6->data; netif_hdr = netif ? netif->data : NULL; - _print_reply(data, icmpv6, &ipv6_hdr->src, ipv6_hdr->hl, netif_hdr); + _print_reply(data, icmpv6, now, &ipv6_hdr->src, ipv6_hdr->hl, netif_hdr); #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: