Files
nuttx/net/icmp/icmp_input.c
T
Piyush Patle 0dccc8ba21 include/debug.h: Move to include/nuttx/debug.h
debug.h is a NuttX-specific, non-POSIX header. Placing it in the
top-level include/ directory creates naming conflicts with external
projects that define their own debug.h.
This commit moves the canonical header to include/nuttx/debug.h,
following the NuttX convention for non-POSIX/non-standard headers,
and updates all in-tree references.

A backward-compatibility shim is left at include/debug.h that
emits a deprecation #warning and re-includes <nuttx/debug.h>,
allowing out-of-tree code to continue building while migrating.

Signed-off-by: Piyush Patle <piyushpatle228@gmail.com>
2026-04-07 07:50:06 -03:00

551 lines
15 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/****************************************************************************
* net/icmp/icmp_input.c
*
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (C) 2007-2009, 2012, 2014-2015, 2017, 2019 Gregory Nutt. All
* rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Adapted for NuttX from logic in uIP which also has a BSD-like license:
*
* Original author Adam Dunkels <adam@dunkels.com>
* Copyright () 2001-2003, Adam Dunkels.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#ifdef CONFIG_NET
#include <stdint.h>
#include <string.h>
#include <nuttx/debug.h>
#include <sys/time.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <nuttx/net/netconfig.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/netstats.h>
#include <nuttx/net/ip.h>
#include "devif/devif.h"
#include "icmp/icmp.h"
#include "utils/utils.h"
#ifdef CONFIG_NET_ICMP
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifdef CONFIG_NET_ICMP_SOCKET
/****************************************************************************
* Private Types
****************************************************************************/
struct icmp_deliver_s
{
FAR struct net_driver_s *dev; /* Current network device */
uint16_t iphdrlen; /* The size of the IPv4 header */
bool delivered; /* Whether the message is delivered */
};
/****************************************************************************
* Private Functions
****************************************************************************/
static bool icmp_filter(uint32_t filter, uint8_t type)
{
if (type < 32)
{
return ((1u << type) & filter) != 0;
}
/* Do not block unknown ICMP types */
return 0;
}
/****************************************************************************
* Name: icmp_datahandler
*
* Description:
* Handle ICMP echo replies that are not accepted by the application.
*
* Input Parameters:
* dev - Device instance only the input packet in d_buf, length = d_len;
* conn - A pointer to the ICMP connection structure
* iphdrlen - The size of the IPv4 header
*
* Returned Value:
* The number of bytes actually buffered is returned. This will be either
* zero or equal to buflen; partial packets are not buffered.
*
****************************************************************************/
static uint16_t icmp_datahandler(FAR struct net_driver_s *dev,
FAR struct icmp_conn_s *conn,
uint16_t iphdrlen)
{
FAR struct ipv4_hdr_s *ipv4;
struct sockaddr_in inaddr;
FAR struct iob_s *iob;
uint16_t buflen;
int ret;
iob = iob_tryalloc(false);
if (iob == NULL)
{
return -ENOMEM;
}
/* Put the IPv4 address at the beginning of the read-ahead buffer */
ipv4 = IPv4BUF;
inaddr.sin_family = AF_INET;
inaddr.sin_port = 0;
net_ipv4addr_copy(inaddr.sin_addr.s_addr,
net_ip4addr_conv32(ipv4->srcipaddr));
memset(inaddr.sin_zero, 0, sizeof(inaddr.sin_zero));
/* Copy the src address info into the front of I/O buffer chain which
* overwrites the contents of the packet header field.
*/
memcpy(iob->io_data, &inaddr, sizeof(struct sockaddr_in));
iob_reserve(iob, sizeof(struct sockaddr_in));
/* Copy the ICMP message into the I/O buffer chain (without waiting) */
ret = iob_clone_partial(dev->d_iob, dev->d_iob->io_pktlen,
0, iob, 0, true, false);
if (ret < 0)
{
iob_free_chain(iob);
return ret;
}
buflen = dev->d_len;
/* Add the new I/O buffer chain to the tail of the read-ahead queue (again
* without waiting).
*/
conn_lock(&conn->sconn);
ret = iob_tryadd_queue(iob, &conn->readahead);
if (ret < 0)
{
nerr("ERROR: Failed to queue the I/O buffer chain: %d\n", ret);
iob_free_chain(iob);
}
else
{
ninfo("Buffered %d bytes\n", buflen);
}
conn_unlock(&conn->sconn);
return buflen;
}
/****************************************************************************
* Name: icmp_delivery_callback
*
* Description:
* Copy the icmp package to the application according to the filter
* conditions, but ICMP_ECHO_REPLY is a special message type, if there
* is an application waiting, it will also copy.
*
* Input Parameters:
* conn - A pointer to the ICMP connection structure.
* arg - The context information
*
****************************************************************************/
static int icmp_delivery_callback(FAR struct icmp_conn_s *conn,
FAR void *arg)
{
FAR struct icmp_deliver_s *info = arg;
FAR struct net_driver_s *dev = info->dev;
FAR struct icmp_hdr_s *icmp = IPBUF(info->iphdrlen);
if (icmp_filter(conn->filter, icmp->type) &&
(icmp->type != ICMP_ECHO_REPLY || conn->id != icmp->id ||
conn->dev != dev))
{
return 0;
}
info->delivered = true;
if (devif_conn_event(dev, ICMP_NEWDATA, conn->sconn.list) == ICMP_NEWDATA)
{
icmp_datahandler(dev, conn, info->iphdrlen);
}
return 0;
}
/****************************************************************************
* Name: icmp_deliver
*
* Description:
* Copy the icmp package to the application according to the filter
* conditions, but ICMP_ECHO_REPLY is a special message type, if there
* is an application waiting, it will also copy.
*
* Input Parameters:
* dev - Reference to a device driver structure.
* iphdrlen - The size of the IP header. This may be larger than
* IPv4_HDRLEN if IP option are present.
*
****************************************************************************/
static bool icmp_deliver(FAR struct net_driver_s *dev, uint16_t iphdrlen)
{
struct icmp_deliver_s info;
info.dev = dev;
info.iphdrlen = iphdrlen;
info.delivered = false;
icmp_foreach(icmp_delivery_callback, &info);
return info.delivered;
}
#endif
/****************************************************************************
* Name: icmp_timestamp
*
* Description:
* Return milliseconds since midnight.
*
****************************************************************************/
static uint32_t icmp_timestamp(void)
{
struct timespec ts;
uint32_t secs;
uint32_t msecs;
nxclock_gettime(CLOCK_REALTIME, &ts);
/* Get secs since midnight. */
secs = ts.tv_sec % 86400;
/* Convert to msecs. */
msecs = secs * MSEC_PER_SEC;
/* Convert nsec to msec. */
msecs += ts.tv_nsec / NSEC_PER_MSEC;
/* Convert to network byte order. */
return msecs;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: icmp_input
*
* Description:
* Handle incoming ICMP input
*
* Input Parameters:
* dev - The device driver structure containing the received ICMP
* packet
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
void icmp_input(FAR struct net_driver_s *dev)
{
FAR struct ipv4_hdr_s *ipv4 = IPv4BUF;
FAR struct icmp_hdr_s *icmp;
#ifdef CONFIG_NET_ICMP_CHECKSUMS
uint16_t csum;
#endif
/* Get the IP header length (accounting for possible options). */
uint16_t iphdrlen = (ipv4->vhl & IPv4_HLMASK) << 2;
#ifdef CONFIG_NET_ICMP_SOCKET
bool delivered = icmp_deliver(dev, iphdrlen);
#endif
#ifdef CONFIG_NET_STATISTICS
g_netstats.icmp.recv++;
#endif
/* The ICMP header immediately follows the IP header */
icmp = IPBUF(iphdrlen);
#ifdef CONFIG_NET_ICMP_CHECKSUMS
csum = icmp_chksum_iob(dev->d_iob);
if (csum != 0xffff)
{
ninfo("ICMP checksum error\n");
#ifdef CONFIG_NET_STATISTICS
g_netstats.icmp.csumerr++;
#endif
goto drop;
}
#endif
/* ICMP echo (i.e., ping) processing. This is simple, we only change the
* ICMP type from ECHO to ECHO_REPLY and adjust the ICMP checksum before
* we return the packet.
*/
if (icmp->type == ICMP_ECHO_REQUEST)
{
in_addr_t src_ipaddr;
/* According to RFC1122 section 3.2.2.6 an ICMP Echo Request
* which has a broadcast/multicast ip address should be discarded
*/
src_ipaddr = net_ip4addr_conv32(ipv4->srcipaddr);
if (net_ipv4addr_cmp(src_ipaddr, INADDR_BROADCAST) ||
IN_MULTICAST(NTOHL(src_ipaddr)))
{
ninfo("ICMP ECHO request from broadcast/multicast address\n");
goto typeerr;
}
/* Change the ICMP type */
icmp->type = ICMP_ECHO_REPLY;
/* Swap IP addresses. */
net_ipv4addr_hdrcopy(ipv4->destipaddr, ipv4->srcipaddr);
net_ipv4addr_hdrcopy(ipv4->srcipaddr, &dev->d_ipaddr);
/* Recalculate the ICMP checksum */
#if 0
/* Get the IP header length (accounting for possible options). */
iphdrlen = (ipv4->vhl & IPv4_HLMASK) << 2;
/* The slow way... sum over the ICMP message */
icmp->icmpchksum = 0;
icmp->icmpchksum = ~icmp_chksum(dev,
(((uint16_t)ipv4->len[0] << 8) |
(uint16_t)ipv4->len[1]) - iphdrlen);
if (icmp->icmpchksum == 0)
{
icmp->icmpchksum = 0xffff;
}
#else
/* The quick way -- Since only the type has changed, just adjust the
* checksum for the change of type
*/
#ifdef CONFIG_NET_ICMP_CHECKSUMS
if (icmp->icmpchksum >= HTONS(0xffff - (ICMP_ECHO_REQUEST << 8)))
{
icmp->icmpchksum += HTONS(ICMP_ECHO_REQUEST << 8) + 1;
}
else
{
icmp->icmpchksum += HTONS(ICMP_ECHO_REQUEST << 8);
}
#else
icmp->icmpchksum = 0;
#endif
#endif
ninfo("Outgoing ICMP packet length: %d (%d)\n",
dev->d_len, (ipv4->len[0] << 8) | ipv4->len[1]);
#ifdef CONFIG_NET_STATISTICS
g_netstats.icmp.sent++;
g_netstats.ipv4.sent++;
#endif
}
#if CONFIG_NET_ICMP_PMTU_ENTRIES > 0
else if (icmp->type == ICMP_DEST_UNREACHABLE)
{
if (icmp->icode == ICMP_FRAG_NEEDED)
{
FAR struct icmp_pmtu_entry *entry;
FAR struct ipv4_hdr_s *inner;
int mtu;
mtu = ntohs(icmp->data[0]) << 16 | ntohs(icmp->data[1]);
if (mtu <= 0)
{
goto typeerr;
}
inner = (FAR struct ipv4_hdr_s *)(icmp + 1);
entry = icmpv4_find_pmtu_entry(
net_ip4addr_conv32(inner->destipaddr));
if (entry == NULL)
{
icmpv4_add_pmtu_entry(
net_ip4addr_conv32(inner->destipaddr), mtu);
}
else
{
entry->pmtu = mtu;
}
goto icmp_send_nothing;
}
else
{
goto typeerr;
}
}
#endif
else if (icmp->type == ICMP_TIMESTAMP_REQUEST)
{
uint16_t len = iphdrlen + sizeof(struct icmp_hdr_s) +
3 * sizeof(uint32_t);
uint32_t timestamp;
FAR uint8_t *buf;
/* Change the ICMP type */
icmp->type = ICMP_TIMESTAMP_REPLY;
/* Swap IP addresses. */
net_ipv4addr_hdrcopy(ipv4->destipaddr, ipv4->srcipaddr);
net_ipv4addr_hdrcopy(ipv4->srcipaddr, &dev->d_ipaddr);
ipv4->len[0] = (len >> 8);
ipv4->len[1] = (len & 0xff);
ipv4->ipchksum = 0;
#ifdef CONFIG_NET_IPV4_CHECKSUMS
ipv4->ipchksum = ~ipv4_chksum(ipv4);
#endif
dev->d_len = len;
/* Update device buffer length */
iob_update_pktlen(dev->d_iob, dev->d_len, false);
/* Set Receive Timestamp and Transmit Timestamp */
buf = (FAR uint8_t *)(icmp + 1);
timestamp = htonl(icmp_timestamp());
memcpy(buf + 4, &timestamp, 4);
memcpy(buf + 8, &timestamp, 4);
/* Recalculate the ICMP checksum */
icmp->icmpchksum = 0;
#ifdef CONFIG_NET_ICMP_CHECKSUMS
icmp->icmpchksum = ~icmp_chksum_iob(dev->d_iob);
if (icmp->icmpchksum == 0)
{
icmp->icmpchksum = 0xffff;
}
#endif
ninfo("Outgoing ICMP packet length: %d (%d)\n",
dev->d_len, (ipv4->len[0] << 8) | ipv4->len[1]);
#ifdef CONFIG_NET_STATISTICS
g_netstats.icmp.sent++;
g_netstats.ipv4.sent++;
#endif
}
/* Otherwise the ICMP input was not processed */
else
{
#ifdef CONFIG_NET_ICMP_SOCKET
if (delivered)
{
goto icmp_send_nothing;
}
else if (icmp->type == ICMP_ECHO_REQUEST)
{
goto drop;
}
#endif
nwarn("WARNING: Unknown ICMP cmd: %d\n", icmp->type);
goto typeerr;
}
return;
typeerr:
#ifdef CONFIG_NET_STATISTICS
g_netstats.icmp.typeerr++;
#endif
#if defined(CONFIG_NET_ICMP_SOCKET) || defined(CONFIG_NET_ICMP_CHECKSUMS)
drop:
#ifdef CONFIG_NET_STATISTICS
g_netstats.icmp.drop++;
#endif
#endif
#if defined(CONFIG_NET_ICMP_SOCKET) || CONFIG_NET_ICMP_PMTU_ENTRIES > 0
icmp_send_nothing:
#endif
dev->d_len = 0;
}
#endif /* CONFIG_NET_ICMP */
#endif /* CONFIG_NET */