1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-18 12:52:44 +01:00

gnrc_icmpv6_echo: implement gnrc_icmpv6_echo_{send, req_handle}()

This commit is contained in:
Benjamin Valentin 2022-11-18 16:06:55 +01:00 committed by Benjamin Valentin
parent 7c0dd96a1c
commit f6c1ab35a3
2 changed files with 163 additions and 1 deletions

View File

@ -24,7 +24,9 @@
#include "byteorder.h" #include "byteorder.h"
#include "net/gnrc/netif.h" #include "net/gnrc/netif.h"
#include "net/gnrc/netif/hdr.h"
#include "net/ipv6/hdr.h" #include "net/ipv6/hdr.h"
#include "net/icmpv6.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { 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, void gnrc_icmpv6_echo_req_handle(gnrc_netif_t *netif, ipv6_hdr_t *ipv6_hdr,
icmpv6_echo_t *echo, uint16_t len); 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 #ifdef __cplusplus
} }
#endif #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; 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); ", seq=%" PRIu16, type, id, seq);
echo = (icmpv6_echo_t *)pkt->data; echo = (icmpv6_echo_t *)pkt->data;
echo->id = byteorder_htons(id); 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);
}
/** @} */ /** @} */