Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sys/shell/commands/gnrc_icmpv6_echo: test for ICMPv6 reply corruption #15622

Merged
merged 2 commits into from Aug 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 18 additions & 4 deletions dist/pythonlibs/riotctrl_shell/gnrc.py
Expand Up @@ -22,6 +22,8 @@ def __init__(self):
self.c_reply = re.compile(r"\d+ bytes from "
r"(?P<source>[0-9a-f:]+(%\S+)?): "
r"icmp_seq=(?P<seq>\d+) ttl=(?P<ttl>\d+)"
r"( corrupted at offset (?P<corrupted>\d+))?"
r"( truncated by (?P<truncated>\d+) byte)?"
r"( rssi=(?P<rssi>-?\d+) dBm)?"
r"( time=(?P<rtt>\d+.\d+) ms)?"
r"(?P<dup> \(DUP\))?")
Expand All @@ -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:
Expand Down Expand Up @@ -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")
Expand All @@ -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"]
Expand All @@ -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"]
Expand Down
119 changes: 84 additions & 35 deletions sys/shell/commands/sc_gnrc_icmpv6_echo.c
Expand Up @@ -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)
Expand All @@ -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;

Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand All @@ -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)) {
Expand All @@ -311,24 +346,25 @@ 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)
{
icmpv6_echo_t *icmpv6_hdr = icmpv6->data;

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) {
Expand All @@ -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);
benpicco marked this conversation as resolved.
Show resolved Hide resolved
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",
Expand All @@ -369,18 +388,48 @@ 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);
}
if (data->datalen >= sizeof(uint32_t)) {
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;
Expand All @@ -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:
Expand Down