From 859fc54c967d26dbfa72bcd80a7cd811e650b970 Mon Sep 17 00:00:00 2001 From: daichuan Date: Fri, 28 Mar 2025 10:16:53 +0800 Subject: [PATCH] netutils/ping: change socket type to SOCK_RAW The ping socket creation method is changed from SOCK_DGRAM to SOCK_RAW, because SOCK_DGRAM may not have permission in some environments (e.g. Docker). Also add checksum calculation when send, and perform IP header processing when receive. Signed-off-by: daichuan --- netutils/ping/icmp_ping.c | 67 +++++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 17 deletions(-) diff --git a/netutils/ping/icmp_ping.c b/netutils/ping/icmp_ping.c index 55db84854..aa88d0a18 100644 --- a/netutils/ping/icmp_ping.c +++ b/netutils/ping/icmp_ping.c @@ -45,6 +45,7 @@ #include #include +#include #include "netutils/icmp_ping.h" @@ -64,7 +65,6 @@ struct ping_priv_s { struct sockaddr_in destaddr; struct sockaddr_in fromaddr; - struct icmp_hdr_s outhdr; struct pollfd recvfd; socklen_t addrlen; clock_t kickoff; @@ -74,6 +74,7 @@ struct ping_priv_s long elapsed; bool retry; int sockfd; + struct icmp_hdr_s outhdr; }; /**************************************************************************** @@ -181,6 +182,31 @@ static void icmp_callback(FAR struct ping_result_s *result, result->info->callback(result); } +/**************************************************************************** + * Name: icmp_checksum + ****************************************************************************/ + +static uint16_t icmp_checksum(FAR const void *buffer, size_t datalen) +{ + FAR const uint16_t *wptr = (FAR const uint16_t *)buffer; + size_t words = (sizeof(struct icmp_hdr_s) + datalen + 1) / 2; + uint32_t sum = 0; + size_t i; + + for (i = 0; i < words; i++) + { + sum += *wptr++; + if (sum & 0x80000000) + { + sum = (sum & 0XFFFF) + (sum >> 16); + } + } + + sum = (sum >> 16) + (sum & 0XFFFF); + sum += (sum >> 16); + return (uint16_t)(~sum); +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -196,6 +222,7 @@ void icmp_ping(FAR const struct ping_info_s *info) FAR struct icmp_hdr_s *inhdr; FAR uint8_t *iobuffer; FAR uint8_t *ptr; + int recvlen; int ret; int ch; int i; @@ -219,16 +246,17 @@ void icmp_ping(FAR const struct ping_info_s *info) /* Allocate memory to hold private data and ping buffer */ - priv = malloc(sizeof(*priv) + result.outsize); + priv = malloc(sizeof(*priv) + sizeof(struct ipv4_hdr_s) + result.outsize); + if (priv == NULL) { icmp_callback(&result, ICMP_E_MEMORY, 0); return; } - iobuffer = (FAR uint8_t *)(priv + 1); + iobuffer = (FAR uint8_t *)(&priv->outhdr); - priv->sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); + priv->sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if (priv->sockfd < 0) { icmp_callback(&result, ICMP_E_SOCKET, errno); @@ -257,11 +285,6 @@ void icmp_ping(FAR const struct ping_info_s *info) priv->destaddr.sin_port = 0; priv->destaddr.sin_addr.s_addr = result.dest.s_addr; - memset(&priv->outhdr, 0, sizeof(struct icmp_hdr_s)); - priv->outhdr.type = ICMP_ECHO_REQUEST; - priv->outhdr.id = htons(result.id); - priv->outhdr.seqno = htons(result.seqno); - icmp_callback(&result, ICMP_I_BEGIN, 0); while (result.nrequests < info->count) @@ -271,9 +294,10 @@ void icmp_ping(FAR const struct ping_info_s *info) break; } - /* Copy the ICMP header into the I/O buffer */ - - memcpy(iobuffer, &priv->outhdr, sizeof(struct icmp_hdr_s)); + memset(&priv->outhdr, 0, sizeof(struct icmp_hdr_s)); + priv->outhdr.type = ICMP_ECHO_REQUEST; + priv->outhdr.id = htons(result.id); + priv->outhdr.seqno = htons(result.seqno); /* Add some easily verifiable payload data */ @@ -289,6 +313,10 @@ void icmp_ping(FAR const struct ping_info_s *info) } } + /* Calculate checksum after data padding */ + + priv->outhdr.icmpchksum = icmp_checksum(iobuffer, info->datalen); + priv->start = clock(); result.nrequests++; priv->nsent = sendto(priv->sockfd, iobuffer, result.outsize, 0, @@ -334,9 +362,10 @@ void icmp_ping(FAR const struct ping_info_s *info) /* Get the ICMP response (ignoring the sender) */ + recvlen = sizeof(struct ipv4_hdr_s) + result.outsize; priv->addrlen = sizeof(struct sockaddr_in); priv->nrecvd = recvfrom(priv->sockfd, iobuffer, - result.outsize, 0, + recvlen, 0, (FAR struct sockaddr *)&priv->fromaddr, &priv->addrlen); if (priv->nrecvd < 0) @@ -350,8 +379,11 @@ void icmp_ping(FAR const struct ping_info_s *info) goto wait; } + /* Skip IP header, IP header length including options */ + priv->elapsed = TICK2USEC(clock() - priv->start); - inhdr = (FAR struct icmp_hdr_s *)iobuffer; + inhdr = (FAR struct icmp_hdr_s *) + (iobuffer + sizeof(struct ipv4_hdr_s)); if (inhdr->type == ICMP_ECHO_REPLY) { @@ -383,16 +415,17 @@ void icmp_ping(FAR const struct ping_info_s *info) /* Verify the payload data */ - if (priv->nrecvd != result.outsize) + if (priv->nrecvd != recvlen) { icmp_callback(&result, ICMP_W_RECVBIG, priv->nrecvd); verified = false; } else { - ptr = &iobuffer[sizeof(struct icmp_hdr_s)]; - ch = 0x20; + /* Data start offset: IP header + ICMP header */ + ptr = (FAR uint8_t *)(inhdr + 1); + ch = 0x20; for (i = 0; i < info->datalen; i++, ptr++) { if (*ptr != ch)