shell/gnrc_icmpv6_echo: test for ICMPv6 echo reply corruption
The Linux ping utility has the nice feature that fills the ICMPv6 echo request payload with a pattern `payload_index & 0xFF`. Then the ICMPv6 echo response payload is checked to verify that the pattern is still intact. This way corrupted messages can be detected. In the past that revealed some 6lo-fragmentation bugs in Linux when corrupted replies arrived. This feature is also useful for RIOT, so implement it in RIOTs `ping` command.
This commit is contained in:
parent
c4df2cca51
commit
3b40ca2b26
@ -73,15 +73,14 @@ typedef struct {
|
|||||||
gnrc_netif_t *netif;
|
gnrc_netif_t *netif;
|
||||||
uint16_t id;
|
uint16_t id;
|
||||||
uint8_t hoplimit;
|
uint8_t hoplimit;
|
||||||
uint8_t pattern;
|
|
||||||
} _ping_data_t;
|
} _ping_data_t;
|
||||||
|
|
||||||
static void _usage(char *cmdname);
|
static void _usage(char *cmdname);
|
||||||
static int _configure(int argc, char **argv, _ping_data_t *data);
|
static int _configure(int argc, char **argv, _ping_data_t *data);
|
||||||
static void _pinger(_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);
|
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);
|
static int _finish(_ping_data_t *data);
|
||||||
|
|
||||||
int _gnrc_icmpv6_ping(int argc, char **argv)
|
int _gnrc_icmpv6_ping(int argc, char **argv)
|
||||||
@ -95,7 +94,6 @@ int _gnrc_icmpv6_ping(int argc, char **argv)
|
|||||||
.timeout = DEFAULT_TIMEOUT_USEC,
|
.timeout = DEFAULT_TIMEOUT_USEC,
|
||||||
.interval = DEFAULT_INTERVAL_USEC,
|
.interval = DEFAULT_INTERVAL_USEC,
|
||||||
.id = DEFAULT_ID,
|
.id = DEFAULT_ID,
|
||||||
.pattern = DEFAULT_ID,
|
|
||||||
};
|
};
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
@ -110,7 +108,7 @@ int _gnrc_icmpv6_ping(int argc, char **argv)
|
|||||||
msg_receive(&msg);
|
msg_receive(&msg);
|
||||||
switch (msg.type) {
|
switch (msg.type) {
|
||||||
case GNRC_NETAPI_MSG_TYPE_RCV: {
|
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);
|
gnrc_pktbuf_release(msg.content.ptr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -238,6 +236,44 @@ static int _configure(int argc, char **argv, _ping_data_t *data)
|
|||||||
return res;
|
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)
|
static void _pinger(_ping_data_t *data)
|
||||||
{
|
{
|
||||||
gnrc_pktsnip_t *pkt, *tmp;
|
gnrc_pktsnip_t *pkt, *tmp;
|
||||||
@ -277,7 +313,6 @@ static void _pinger(_ping_data_t *data)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
databuf = (uint8_t *)(pkt->data) + sizeof(icmpv6_echo_t);
|
databuf = (uint8_t *)(pkt->data) + sizeof(icmpv6_echo_t);
|
||||||
memset(databuf, data->pattern, data->datalen);
|
|
||||||
tmp = gnrc_ipv6_hdr_build(pkt, NULL, &data->host);
|
tmp = gnrc_ipv6_hdr_build(pkt, NULL, &data->host);
|
||||||
if (tmp == NULL) {
|
if (tmp == NULL) {
|
||||||
puts("error: packet buffer full");
|
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);
|
gnrc_netif_hdr_set_netif(tmp->data, data->netif);
|
||||||
pkt = gnrc_pkt_prepend(pkt, tmp);
|
pkt = gnrc_pkt_prepend(pkt, tmp);
|
||||||
}
|
}
|
||||||
if (data->datalen >= sizeof(uint32_t)) {
|
|
||||||
uint32_t now = xtimer_now_usec();
|
/* add TX timestamp & test data */
|
||||||
memcpy(databuf, &now, sizeof(now));
|
_fill_payload(databuf, data->datalen);
|
||||||
}
|
|
||||||
if (!gnrc_netapi_dispatch_send(GNRC_NETTYPE_IPV6,
|
if (!gnrc_netapi_dispatch_send(GNRC_NETTYPE_IPV6,
|
||||||
GNRC_NETREG_DEMUX_CTX_ALL,
|
GNRC_NETREG_DEMUX_CTX_ALL,
|
||||||
pkt)) {
|
pkt)) {
|
||||||
@ -311,7 +346,7 @@ error_exit:
|
|||||||
gnrc_pktbuf_release(pkt);
|
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,
|
ipv6_addr_t *from, unsigned hoplimit,
|
||||||
gnrc_netif_hdr_t *netif_hdr)
|
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;
|
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 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) {
|
if (icmpv6_hdr->type == ICMPV6_ECHO_REP) {
|
||||||
char from_str[IPV6_ADDR_MAX_STR_LEN];
|
char from_str[IPV6_ADDR_MAX_STR_LEN];
|
||||||
const char *dupmsg = " (DUP!)";
|
const char *dupmsg = " (DUP!)";
|
||||||
uint32_t triptime = 0;
|
uint32_t triptime = 0;
|
||||||
uint16_t recv_seq;
|
uint16_t recv_seq;
|
||||||
|
uint16_t corrupted;
|
||||||
|
|
||||||
/* not our ping */
|
/* not our ping */
|
||||||
if (byteorder_ntohs(icmpv6_hdr->id) != data->id) {
|
if (byteorder_ntohs(icmpv6_hdr->id) != data->id) {
|
||||||
@ -340,8 +376,38 @@ static void _print_reply(_ping_data_t *data, gnrc_pktsnip_t *icmpv6,
|
|||||||
}
|
}
|
||||||
recv_seq = byteorder_ntohs(icmpv6_hdr->seq);
|
recv_seq = byteorder_ntohs(icmpv6_hdr->seq);
|
||||||
ipv6_addr_to_str(&from_str[0], from, sizeof(from_str));
|
ipv6_addr_to_str(&from_str[0], from, sizeof(from_str));
|
||||||
|
|
||||||
|
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",
|
||||||
|
(unsigned)icmpv6->size,
|
||||||
|
from_str, recv_seq, hoplimit);
|
||||||
|
} else {
|
||||||
|
printf("%u bytes from %s%%%u: icmp_seq=%u ttl=%u",
|
||||||
|
(unsigned)icmpv6->size,
|
||||||
|
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)) {
|
if (data->datalen >= sizeof(uint32_t)) {
|
||||||
triptime = xtimer_now_usec() - unaligned_get_u32(icmpv6_hdr + 1);
|
|
||||||
data->tsum += triptime;
|
data->tsum += triptime;
|
||||||
if (triptime < data->tmin) {
|
if (triptime < data->tmin) {
|
||||||
data->tmin = triptime;
|
data->tmin = triptime;
|
||||||
@ -358,29 +424,12 @@ static void _print_reply(_ping_data_t *data, gnrc_pktsnip_t *icmpv6,
|
|||||||
data->num_recv++;
|
data->num_recv++;
|
||||||
dupmsg += 7;
|
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",
|
|
||||||
(unsigned)icmpv6->size,
|
|
||||||
from_str, recv_seq, hoplimit);
|
|
||||||
} else {
|
|
||||||
printf("%u bytes from %s%%%u: icmp_seq=%u ttl=%u",
|
|
||||||
(unsigned)icmpv6->size,
|
|
||||||
from_str, if_pid, recv_seq, hoplimit);
|
|
||||||
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
puts(dupmsg);
|
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_pktsnip_t *netif, *ipv6, *icmpv6;
|
||||||
gnrc_netif_hdr_t *netif_hdr;
|
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;
|
ipv6_hdr = ipv6->data;
|
||||||
netif_hdr = netif ? netif->data : NULL;
|
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
|
#ifdef MODULE_GNRC_IPV6_NIB
|
||||||
/* successful ping to neighbor (NIB handles case if ipv6->src is not a
|
/* successful ping to neighbor (NIB handles case if ipv6->src is not a
|
||||||
* neighbor) can be taken as upper-layer hint for reachability:
|
* neighbor) can be taken as upper-layer hint for reachability:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user