1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
18934: shell/gnrc_icmpv6_echo: separate ICMPv6 echo sending / parsing from shell command r=benpicco a=benpicco



Co-authored-by: Benjamin Valentin <benjamin.valentin@bht-berlin.de>
Co-authored-by: Benjamin Valentin <benjamin.valentin@ml-pa.com>
This commit is contained in:
bors[bot] 2023-01-19 12:30:37 +00:00 committed by GitHub
commit e248f954e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 257 additions and 174 deletions

View File

@ -48,6 +48,7 @@ BOARD_INSUFFICIENT_MEMORY := \
stm32f7508-dk \
stm32g0316-disco \
stm32l0538-disco \
stm32mp157c-dk2 \
telosb \
waspmote-pro \
yunjia-nrf51822 \

View File

@ -24,7 +24,9 @@
#include "byteorder.h"
#include "net/gnrc/netif.h"
#include "net/gnrc/netif/hdr.h"
#include "net/ipv6/hdr.h"
#include "net/icmpv6.h"
#ifdef __cplusplus
extern "C" {
@ -58,6 +60,49 @@ gnrc_pktsnip_t *gnrc_icmpv6_echo_build(uint8_t type, uint16_t id, uint16_t seq,
void gnrc_icmpv6_echo_req_handle(gnrc_netif_t *netif, ipv6_hdr_t *ipv6_hdr,
icmpv6_echo_t *echo, uint16_t len);
/**
* @brief Send out ICMPv6 echo request
*
* @param[in] netif The interface the echo request should be sent on.
* @param[in] addr The destination address of the echo request
* @param[in] id ID for the echo message in host byte-order
* @param[in] seq Sequence number for the echo message in host byte-order
* @param[in] ttl Hop limit of the echo request
* @param[in] len Length of the payload
*
* @return 0 on success
* @return <0 on error
*/
int gnrc_icmpv6_echo_send(const gnrc_netif_t *netif, const ipv6_addr_t *addr,
uint16_t id, uint16_t seq, uint8_t ttl, size_t len);
/**
* @brief ICMPv6 echo response callback
*
* @param[in] pkt Packet containing the ICMPv6 response
* @param[in] corrupt Offset of corrupt payload, -1 if no corruption detected
* @param[in] rtt_us round-trip-time in µs (0 if this information is not available)
* @param[in] ctx User supplied context
*
* @return 0 on success
* @return <0 on error
*/
typedef int (*gnrc_icmpv6_echo_rsp_handle_cb_t)(gnrc_pktsnip_t *pkt,
int corrupt, uint32_t rtt_us, void *ctx);
/**
* @brief Parse ICMPv6 echo response
*
* @param[in] pkt Incoming ICMPv6 packet
* @param[in] len Expected echo response payload length
* @param[in] cb Callback function to execute
* @param[in] ctx Callback function context
*
* @return 0 on success
* @return <0 on error
*/
int gnrc_icmpv6_echo_rsp_handle(gnrc_pktsnip_t *pkt, size_t len,
gnrc_icmpv6_echo_rsp_handle_cb_t cb, void *ctx);
#ifdef __cplusplus
}
#endif

View File

@ -35,7 +35,7 @@ gnrc_pktsnip_t *gnrc_icmpv6_echo_build(uint8_t type, uint16_t id, uint16_t seq,
return NULL;
}
DEBUG("icmpv6_echo: Building echo message with type=%" PRIu8 "id=%" PRIu16
DEBUG("icmpv6_echo: Building echo message with type=%" PRIu8 " id=%" PRIu16
", seq=%" PRIu16, type, id, seq);
echo = (icmpv6_echo_t *)pkt->data;
echo->id = byteorder_htons(id);
@ -107,4 +107,121 @@ void gnrc_icmpv6_echo_req_handle(gnrc_netif_t *netif, ipv6_hdr_t *ipv6_hdr,
}
}
static void _fill_payload(uint8_t *buf, size_t len, uint32_t now)
{
uint8_t i = 0;
if (len >= sizeof(uint32_t)) {
memcpy(buf, &now, sizeof(now));
len -= sizeof(now);
buf += sizeof(now);
}
while (len--) {
*buf++ = i++;
}
}
static void _check_payload(const void *buf, size_t len, uint32_t now,
uint32_t *triptime, int *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;
break;
}
}
}
int gnrc_icmpv6_echo_send(const gnrc_netif_t *netif, const ipv6_addr_t *addr,
uint16_t id, uint16_t seq, uint8_t ttl, size_t len)
{
int res = 0;
gnrc_pktsnip_t *pkt, *tmp;
ipv6_hdr_t *ipv6;
uint8_t *databuf;
pkt = gnrc_icmpv6_echo_build(ICMPV6_ECHO_REQ, id, seq, NULL, len);
if (pkt == NULL) {
DEBUG("error: packet buffer full\n");
return -ENOMEM;
}
databuf = (uint8_t *)(pkt->data) + sizeof(icmpv6_echo_t);
tmp = gnrc_ipv6_hdr_build(pkt, NULL, addr);
if (tmp == NULL) {
puts("error: packet buffer full");
goto error_exit;
}
pkt = tmp;
ipv6 = pkt->data;
/* if ttl is unset (i.e. 0) gnrc_ipv6 will select hop limit */
ipv6->hl = ttl;
if (netif != NULL) {
tmp = gnrc_netif_hdr_build(NULL, 0, NULL, 0);
if (tmp == NULL) {
DEBUG("error: packet buffer full");
res = -ENOMEM;
goto error_exit;
}
gnrc_netif_hdr_set_netif(tmp->data, netif);
pkt = gnrc_pkt_prepend(pkt, tmp);
}
/* add TX timestamp & test data */
_fill_payload(databuf, len, ztimer_now(ZTIMER_USEC));
res = !gnrc_netapi_dispatch_send(GNRC_NETTYPE_IPV6,
GNRC_NETREG_DEMUX_CTX_ALL,
pkt);
if (res) {
DEBUG("error: unable to send ICMPv6 echo request\n");
res = -EBADF;
}
error_exit:
if (res) {
gnrc_pktbuf_release(pkt);
}
return res;
}
int gnrc_icmpv6_echo_rsp_handle(gnrc_pktsnip_t *pkt, size_t len,
gnrc_icmpv6_echo_rsp_handle_cb_t cb, void *ctx)
{
gnrc_pktsnip_t *ipv6, *icmpv6;
ipv6_hdr_t *ipv6_hdr;
uint32_t now = ztimer_now(ZTIMER_USEC);
uint32_t triptime = 0;
int corrupted = -1;
ipv6 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6);
icmpv6 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_ICMPV6);
if ((ipv6 == NULL) || (icmpv6 == NULL)) {
DEBUG("No IPv6 or ICMPv6 header found in reply");
return -EINVAL;
}
ipv6_hdr = ipv6->data;
#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
icmpv6_echo_t *icmpv6_hdr = icmpv6->data;
_check_payload(icmpv6_hdr + 1, len, now, &triptime, &corrupted);
return cb(pkt, corrupted, triptime, ctx);
}
/** @} */

View File

@ -80,9 +80,7 @@ typedef struct {
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, 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, uint32_t now_us);
static int _print_reply(gnrc_pktsnip_t *pkt, int corrupted, uint32_t rtt, void *ctx);
static int _finish(_ping_data_t *data);
static int _gnrc_icmpv6_ping(int argc, char **argv)
@ -110,7 +108,8 @@ static 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, ztimer_now(ZTIMER_USEC));
gnrc_icmpv6_echo_rsp_handle(msg.content.ptr, data.datalen,
_print_reply, &data);
gnrc_pktbuf_release(msg.content.ptr);
break;
}
@ -238,50 +237,10 @@ 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 = ztimer_now(ZTIMER_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;
ipv6_hdr_t *ipv6;
uint32_t timer;
uint8_t *databuf;
int res;
/* schedule next event (next ping or finish) ASAP */
if ((data->num_sent + 1) < data->count) {
@ -307,150 +266,111 @@ static void _pinger(_ping_data_t *data)
ztimer_set_msg(ZTIMER_USEC, &data->sched_timer, timer, &data->sched_msg,
thread_getpid());
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);
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->netif != NULL) {
tmp = gnrc_netif_hdr_build(NULL, 0, NULL, 0);
if (tmp == NULL) {
puts("error: packet buffer full");
goto error_exit;
}
gnrc_netif_hdr_set_netif(tmp->data, data->netif);
pkt = gnrc_pkt_prepend(pkt, tmp);
}
/* add TX timestamp & test data */
_fill_payload(databuf, data->datalen);
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;
res = gnrc_icmpv6_echo_send(data->netif, &data->host, data->id,
data->num_sent++, data->hoplimit, data->datalen);
switch (-res) {
case 0:
break;
case ENOMEM:
puts("error: packet buffer full");
break;
default:
printf("error: %d\n", res);
break;
}
return;
error_exit:
gnrc_pktbuf_release(pkt);
}
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)
static int _print_reply(gnrc_pktsnip_t *pkt, int corrupted, uint32_t triptime, void *ctx)
{
_ping_data_t *data = ctx;
gnrc_pktsnip_t *netif = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_NETIF);
gnrc_pktsnip_t *ipv6 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6);
gnrc_pktsnip_t *icmpv6 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_ICMPV6);
ipv6_hdr_t *ipv6_hdr = ipv6->data;
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;
kernel_pid_t if_pid = KERNEL_PID_UNDEF;
int16_t rssi = GNRC_NETIF_HDR_NO_RSSI;
int16_t truncated;
if (netif) {
gnrc_netif_hdr_t *netif_hdr = netif->data;
if_pid = netif_hdr->if_pid;
rssi = netif_hdr->rssi;
}
/* check if payload size matches expectation */
truncated = (data->datalen + sizeof(icmpv6_echo_t)) - icmpv6->size;
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) {
return;
}
if (!ipv6_addr_is_multicast(&data->host) &&
!ipv6_addr_equal(from, &data->host)) {
return;
}
recv_seq = byteorder_ntohs(icmpv6_hdr->seq);
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);
}
/* we can only calculate RTT (triptime) if payload was large enough for
a TX timestamp */
if (data->datalen >= sizeof(uint32_t)) {
printf(" time=%lu.%03lu ms", (long unsigned)triptime / 1000,
(long unsigned)triptime % 1000);
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);
if (icmpv6_hdr->type != ICMPV6_ECHO_REP) {
return -EINVAL;
}
}
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;
ipv6_hdr_t *ipv6_hdr;
char from_str[IPV6_ADDR_MAX_STR_LEN];
const char *dupmsg = " (DUP!)";
uint16_t recv_seq;
netif = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_NETIF);
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;
/* not our ping */
if (byteorder_ntohs(icmpv6_hdr->id) != data->id) {
return -EINVAL;
}
ipv6_hdr = ipv6->data;
netif_hdr = netif ? netif->data : NULL;
_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:
* https://tools.ietf.org/html/rfc4861#section-7.3.1 */
gnrc_ipv6_nib_nc_mark_reachable(&ipv6_hdr->src);
#endif
if (!ipv6_addr_is_multicast(&data->host) &&
!ipv6_addr_equal(&ipv6_hdr->src, &data->host)) {
return -EINVAL;
}
recv_seq = byteorder_ntohs(icmpv6_hdr->seq);
ipv6_addr_to_str(&from_str[0], &ipv6_hdr->src, sizeof(from_str));
if (gnrc_netif_highlander() || (if_pid == KERNEL_PID_UNDEF) ||
!ipv6_addr_is_link_local(&ipv6_hdr->src)) {
printf("%u bytes from %s: icmp_seq=%u ttl=%u",
(unsigned)icmpv6->size,
from_str, recv_seq, ipv6_hdr->hl);
} else {
printf("%u bytes from %s%%%u: icmp_seq=%u ttl=%u",
(unsigned)icmpv6->size,
from_str, if_pid, recv_seq, ipv6_hdr->hl);
}
/* check if payload size matches */
if (truncated) {
printf(" truncated by %d byte", truncated);
}
/* check response for corruption */
else if (corrupted >= 0) {
printf(" corrupted at offset %u", corrupted);
}
if (rssi != GNRC_NETIF_HDR_NO_RSSI) {
printf(" rssi=%"PRId16" dBm", rssi);
}
/* we can only calculate RTT (triptime) if payload was large enough for
a TX timestamp */
if (triptime) {
printf(" time=%lu.%03lu ms", (long unsigned)triptime / 1000,
(long unsigned)triptime % 1000);
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);
return 0;
}
static int _finish(_ping_data_t *data)