icmp: add SOCK_RAW type support

Since ICMPv6 has added SOCK_RAW, a SOCK_RAW related implementation has also
been added for ICMP.

Signed-off-by: zhanghongyu <zhanghongyu@xiaomi.com>
This commit is contained in:
zhanghongyu 2023-05-09 19:09:53 +08:00 committed by Xiang Xiao
parent 8fa4f2d61d
commit 3bd495c09d
8 changed files with 476 additions and 161 deletions

View file

@ -119,6 +119,8 @@
#define ICMP_EXC_TTL 0 /* TTL count exceeded */
#define ICMP_EXC_FRAGTIME 1 /* Fragment Reassembly time exceeded */
#define ICMP_FILTER 1
/****************************************************************************
* Public Type Definitions
****************************************************************************/

View file

@ -32,6 +32,7 @@
#include <assert.h>
#include <nuttx/mm/iob.h>
#include <nuttx/net/icmp.h>
#include <nuttx/net/ip.h>
#include <nuttx/net/net.h>
#include <nuttx/net/netdev.h>
@ -82,9 +83,8 @@ struct icmp_conn_s
/* ICMP-specific content follows */
uint16_t id; /* ICMP ECHO request ID */
uint8_t nreqs; /* Number of requests with no response received */
uint8_t crefs; /* Reference counts on this instance */
uint16_t id; /* ICMP ECHO request ID */
uint8_t crefs; /* Reference counts on this instance */
/* The device that the ICMP request was sent on */
@ -96,6 +96,7 @@ struct icmp_conn_s
*/
struct iob_queue_s readahead; /* Read-ahead buffering */
uint32_t filter; /* ICMP type filter */
/* The following is a list of poll structures of threads waiting for
* socket events.
@ -103,6 +104,8 @@ struct icmp_conn_s
struct icmp_poll_s pollinfo[CONFIG_NET_ICMP_NPOLLWAITERS];
};
typedef int (*icmp_callback_t)(FAR struct icmp_conn_s *conn, FAR void *arg);
#endif
#ifdef CONFIG_NET_IPv4
@ -245,6 +248,23 @@ FAR struct icmp_conn_s *icmp_findconn(FAR struct net_driver_s *dev,
uint16_t id);
#endif
/****************************************************************************
* Name: icmp_foreach
*
* Description:
* Enumerate each ICMP connection structure. This function will terminate
* when either (1) all connection have been enumerated or (2) when a
* callback returns any non-zero value.
*
* Assumptions:
* This function is called from network logic at with the network locked.
*
****************************************************************************/
#ifdef CONFIG_NET_ICMP_SOCKET
int icmp_foreach(icmp_callback_t callback, FAR void *arg);
#endif
/****************************************************************************
* Name: icmp_poll
*

View file

@ -281,7 +281,7 @@ FAR struct icmp_conn_s *icmp_findconn(FAR struct net_driver_s *dev,
for (conn = icmp_nextconn(NULL); conn != NULL; conn = icmp_nextconn(conn))
{
if (conn->id == id && conn->dev == dev && conn->nreqs > 0)
if (conn->id == id && conn->dev == dev)
{
return conn;
}
@ -289,4 +289,38 @@ FAR struct icmp_conn_s *icmp_findconn(FAR struct net_driver_s *dev,
return conn;
}
/****************************************************************************
* Name: icmp_foreach
*
* Description:
* Enumerate each ICMP connection structure. This function will terminate
* when either (1) all connection have been enumerated or (2) when a
* callback returns any non-zero value.
*
* Assumptions:
* This function is called from network logic at with the network locked.
*
****************************************************************************/
int icmp_foreach(icmp_callback_t callback, FAR void *arg)
{
FAR struct icmp_conn_s *conn;
int ret = 0;
if (callback != NULL)
{
for (conn = icmp_nextconn(NULL); conn != NULL;
conn = icmp_nextconn(conn))
{
ret = callback(conn, arg);
if (ret != 0)
{
break;
}
}
}
return ret;
}
#endif /* CONFIG_NET_ICMP */

View file

@ -69,12 +69,35 @@
* Pre-processor Definitions
****************************************************************************/
#define ICMPSIZE(hl) ((dev)->d_len - (hl))
#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
*
@ -84,9 +107,7 @@
* Input Parameters:
* dev - Device instance only the input packet in d_buf, length = d_len;
* conn - A pointer to the ICMP connection structure
* buffer - A pointer to the buffer to be copied to the read-ahead
* buffers
* buflen - The number of bytes to copy to the read-ahead buffer.
* iphdrlen - The size of the IPv4 header
*
* Returned Value:
* The number of bytes actually buffered is returned. This will be either
@ -94,20 +115,24 @@
*
****************************************************************************/
#ifdef CONFIG_NET_ICMP_SOCKET
static uint16_t icmp_datahandler(FAR struct net_driver_s *dev,
FAR struct icmp_conn_s *conn)
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 iphdrlen;
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 */
iob = dev->d_iob;
ipv4 = IPv4BUF;
inaddr.sin_family = AF_INET;
inaddr.sin_port = 0;
@ -116,23 +141,25 @@ static uint16_t icmp_datahandler(FAR struct net_driver_s *dev,
net_ip4addr_conv32(ipv4->srcipaddr));
memset(inaddr.sin_zero, 0, sizeof(inaddr.sin_zero));
/* Get the IP header length (accounting for possible options). */
iphdrlen = (ipv4->vhl & IPv4_HLMASK) << 2;
/* 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));
/* Copy the new ICMP reply into the I/O buffer chain (without waiting) */
iob_reserve(iob, sizeof(struct sockaddr_in));
buflen = ICMPSIZE(iphdrlen);
/* Copy the ICMP message into the I/O buffer chain (without waiting) */
/* Trim l3 header */
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;
}
iob = iob_trimhead(iob, iphdrlen);
buflen = dev->d_len;
/* Add the new I/O buffer chain to the tail of the read-ahead queue (again
* without waiting).
@ -149,12 +176,73 @@ static uint16_t icmp_datahandler(FAR struct net_driver_s *dev,
ninfo("Buffered %d bytes\n", buflen);
}
/* Device buffer must be enqueue or freed, clear the handle */
netdev_iob_clear(dev);
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
/****************************************************************************
@ -183,16 +271,18 @@ void icmp_input(FAR struct net_driver_s *dev)
{
FAR struct ipv4_hdr_s *ipv4 = IPv4BUF;
FAR struct icmp_hdr_s *icmp;
uint16_t iphdrlen;
/* 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
/* Get the IP header length (accounting for possible options). */
iphdrlen = (ipv4->vhl & IPv4_HLMASK) << 2;
/* The ICMP header immediately follows the IP header */
icmp = IPBUF(iphdrlen);
@ -254,51 +344,6 @@ void icmp_input(FAR struct net_driver_s *dev)
#endif
}
#ifdef CONFIG_NET_ICMP_SOCKET
/* If an ICMP echo reply is received then there should also be
* a thread waiting to received the echo response.
*/
else if (icmp->type == ICMP_ECHO_REPLY)
{
FAR struct icmp_conn_s *conn;
uint16_t flags;
/* Nothing consumed the ICMP reply. That might because this is
* an old, invalid reply or simply because the ping application
* has not yet put its poll or recv in place.
*/
/* Is there any connection that might expect this reply? */
conn = icmp_findconn(dev, icmp->id);
if (conn == NULL)
{
/* No.. drop the packet */
goto drop;
}
flags = devif_conn_event(dev, ICMP_NEWDATA, conn->sconn.list);
if ((flags & ICMP_NEWDATA) != 0)
{
uint16_t nbuffered;
/* Add the ICMP echo reply to the IPPROTO_ICMP socket read-ahead
* buffer.
*/
nbuffered = icmp_datahandler(dev, conn);
if (nbuffered == 0)
{
/* Could not buffer the data.. drop the packet */
goto drop;
}
}
}
#endif
#if CONFIG_NET_ICMP_PMTU_ENTRIES > 0
else if (icmp->type == ICMP_DEST_UNREACHABLE)
{
@ -336,6 +381,17 @@ void icmp_input(FAR struct net_driver_s *dev)
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;
}
@ -349,12 +405,12 @@ typeerr:
#ifdef CONFIG_NET_ICMP_SOCKET
drop:
#endif
#ifdef CONFIG_NET_STATISTICS
g_netstats.icmp.drop++;
#endif
#endif
#if CONFIG_NET_ICMP_PMTU_ENTRIES > 0
#if defined(CONFIG_NET_ICMP_SOCKET) || CONFIG_NET_ICMP_PMTU_ENTRIES > 0
icmp_send_nothing:
#endif

View file

@ -39,12 +39,6 @@
#ifdef CONFIG_NET_ICMP_SOCKET
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define ICMPSIZE ((dev)->d_len - IPv4_HDRLEN)
/****************************************************************************
* Private Types
****************************************************************************/
@ -94,9 +88,7 @@ static uint16_t recvfrom_eventhandler(FAR struct net_driver_s *dev,
{
FAR struct icmp_recvfrom_s *pstate = pvpriv;
FAR struct socket *psock;
FAR struct icmp_conn_s *conn;
FAR struct ipv4_hdr_s *ipv4;
FAR struct icmp_hdr_s *icmp;
ninfo("flags: %04x\n", flags);
@ -111,42 +103,32 @@ static uint16_t recvfrom_eventhandler(FAR struct net_driver_s *dev,
goto end_wait;
}
/* Is this a response on the same device that we sent the request out
* on?
*/
psock = pstate->recv_sock;
DEBUGASSERT(psock != NULL && psock->s_conn != NULL);
conn = psock->s_conn;
if (dev != conn->dev)
{
ninfo("Wrong device\n");
return flags;
}
/* Check if we have just received a ICMP ECHO reply. */
if ((flags & ICMP_NEWDATA) != 0) /* No incoming data */
{
unsigned int recvsize;
/* Check if it is for us */
icmp = IPBUF(IPv4_HDRLEN);
if (conn->id != icmp->id)
{
ninfo("Wrong ID: %u vs %u\n", icmp->id, conn->id);
return flags;
}
unsigned int offset = 0;
ninfo("Received ICMP reply\n");
ipv4 = IPv4BUF;
/* What should we do if the received reply is larger that the
/* What should we do if the received message is larger that the
* buffer that the caller of sendto provided? Truncate? Error
* out?
*/
recvsize = ICMPSIZE;
if (psock->s_type != SOCK_RAW)
{
/* Skip L3 header when not SOCK_RAW */
offset = (ipv4->vhl & IPv4_HLMASK) << 2;
}
recvsize = dev->d_len - offset;
if (recvsize > pstate->recv_buflen)
{
recvsize = pstate->recv_buflen;
@ -154,7 +136,7 @@ static uint16_t recvfrom_eventhandler(FAR struct net_driver_s *dev,
/* Copy the ICMP ECHO reply to the user provided buffer */
memcpy(pstate->recv_buf, IPBUF(IPv4_HDRLEN), recvsize);
memcpy(pstate->recv_buf, IPBUF(offset), recvsize);
/* Return the size of the returned data */
@ -163,18 +145,8 @@ static uint16_t recvfrom_eventhandler(FAR struct net_driver_s *dev,
/* Return the IPv4 address of the sender from the IPv4 header */
ipv4 = IPv4BUF;
net_ipv4addr_hdrcopy(&pstate->recv_from, ipv4->srcipaddr);
/* Decrement the count of outstanding requests. I suppose this
* could have already been decremented of there were multiple
* threads calling sendto() or recvfrom(). If there finds, we
* may have to beef up the design.
*/
DEBUGASSERT(conn->nreqs > 0);
conn->nreqs--;
/* Indicate that the data has been consumed */
flags &= ~ICMP_NEWDATA;
@ -225,7 +197,7 @@ end_wait:
static inline ssize_t icmp_readahead(FAR struct icmp_conn_s *conn,
FAR void *buf, size_t buflen,
FAR struct sockaddr_in *from,
FAR socklen_t *fromlen)
FAR socklen_t *fromlen, bool raw)
{
FAR struct iob_s *iob;
ssize_t ret = -ENODATA;
@ -236,6 +208,8 @@ static inline ssize_t icmp_readahead(FAR struct icmp_conn_s *conn,
if ((iob = iob_peek_queue(&conn->readahead)) != NULL)
{
unsigned int offset = 0;
DEBUGASSERT(iob->io_pktlen > 0);
/* Then get address */
@ -247,7 +221,17 @@ static inline ssize_t icmp_readahead(FAR struct icmp_conn_s *conn,
/* Copy to user */
ret = (ssize_t)iob_copyout(buf, iob, buflen, 0);
if (!raw)
{
FAR struct ipv4_hdr_s *ipv4 =
(FAR struct ipv4_hdr_s *)IOB_DATA(iob);
/* Skip L3 header when not SOCK_RAW */
offset = (ipv4->vhl & IPv4_HLMASK) << 2;
}
ret = (ssize_t)iob_copyout(buf, iob, buflen, offset);
ninfo("Received %ld bytes (of %u)\n", (long)ret, iob->io_pktlen);
@ -305,7 +289,7 @@ ssize_t icmp_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg,
FAR socklen_t *fromlen = &msg->msg_namelen;
FAR struct sockaddr_in *inaddr;
FAR struct icmp_conn_s *conn;
FAR struct net_driver_s *dev;
FAR struct net_driver_s *dev = NULL;
struct icmp_recvfrom_s state;
ssize_t ret;
@ -332,25 +316,18 @@ ssize_t icmp_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg,
net_lock();
/* We cannot receive a response from a device until a request has been
* sent to the devivce.
*/
conn = psock->s_conn;
if (conn->nreqs < 1)
if (psock->s_type != SOCK_RAW)
{
ret = -EPROTO;
goto errout;
}
/* Get the device that was used to send the ICMP request. */
/* Get the device that was used to send the ICMP request. */
dev = conn->dev;
DEBUGASSERT(dev != NULL);
if (dev == NULL)
{
ret = -EPROTO;
goto errout;
dev = conn->dev;
DEBUGASSERT(dev != NULL);
if (dev == NULL)
{
ret = -EPROTO;
goto errout;
}
}
/* Check if there is buffered read-ahead data for this socket. We may have
@ -359,8 +336,8 @@ ssize_t icmp_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg,
if (!IOB_QEMPTY(&conn->readahead))
{
ret = icmp_readahead(conn, buf, len,
(FAR struct sockaddr_in *)from, fromlen);
ret = icmp_readahead(conn, buf, len, (FAR struct sockaddr_in *)from,
fromlen, psock->s_type == SOCK_RAW);
}
else if (_SS_ISNONBLOCK(conn->sconn.s_flags) ||
(flags & MSG_DONTWAIT) != 0)
@ -434,11 +411,10 @@ ssize_t icmp_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg,
*/
errout:
if (conn->nreqs < 1)
if (ret < 0)
{
conn->id = 0;
conn->nreqs = 0;
conn->dev = NULL;
conn->id = 0;
conn->dev = NULL;
iob_free_queue(&conn->readahead);
}

View file

@ -339,12 +339,11 @@ ssize_t icmp_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg,
*/
icmp = (FAR struct icmp_hdr_s *)buf;
if (icmp->type != ICMP_ECHO_REQUEST || icmp->id != conn->id ||
dev != conn->dev)
if (psock->s_type != SOCK_RAW && (icmp->type != ICMP_ECHO_REQUEST ||
icmp->id != conn->id || dev != conn->dev))
{
conn->id = 0;
conn->nreqs = 0;
conn->dev = NULL;
conn->id = 0;
conn->dev = NULL;
iob_free_queue(&conn->readahead);
}
@ -379,19 +378,18 @@ ssize_t icmp_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg,
state.snd_cb = icmp_callback_alloc(dev, conn);
if (state.snd_cb != NULL)
{
state.snd_cb->flags = (ICMP_POLL | NETDEV_DOWN);
state.snd_cb->priv = (FAR void *)&state;
state.snd_cb->event = sendto_eventhandler;
state.snd_cb->flags = (ICMP_POLL | NETDEV_DOWN);
state.snd_cb->priv = (FAR void *)&state;
state.snd_cb->event = sendto_eventhandler;
/* Setup to receive ICMP ECHO replies */
if (icmp->type == ICMP_ECHO_REQUEST)
if (psock->s_type != SOCK_RAW && icmp->type == ICMP_ECHO_REQUEST)
{
conn->id = icmp->id;
conn->nreqs = 1;
conn->id = icmp->id;
}
conn->dev = dev;
conn->dev = dev;
/* Notify the device driver of the availability of TX data */
@ -452,9 +450,8 @@ ssize_t icmp_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg,
return len;
errout:
conn->id = 0;
conn->nreqs = 0;
conn->dev = NULL;
conn->id = 0;
conn->dev = NULL;
iob_free_queue(&conn->readahead);
return ret;

View file

@ -34,9 +34,11 @@
#include <nuttx/mm/iob.h>
#include <nuttx/net/net.h>
#include <nuttx/net/icmp.h>
#include <socket/socket.h>
#include "icmp/icmp.h"
#include "inet/inet.h"
#ifdef CONFIG_NET_ICMP_SOCKET
@ -50,6 +52,12 @@ static void icmp_addref(FAR struct socket *psock);
static int icmp_netpoll(FAR struct socket *psock,
FAR struct pollfd *fds, bool setup);
static int icmp_close(FAR struct socket *psock);
#ifdef CONFIG_NET_SOCKOPTS
static int icmp_getsockopt(FAR struct socket *psock, int level,
int option, FAR void *value, FAR socklen_t *value_len);
static int icmp_setsockopt(FAR struct socket *psock, int level,
int option, FAR const void *value, socklen_t value_len);
#endif
/****************************************************************************
* Public Data
@ -70,7 +78,13 @@ const struct sock_intf_s g_icmp_sockif =
icmp_sendmsg, /* si_sendmsg */
icmp_recvmsg, /* si_recvmsg */
icmp_close, /* si_close */
icmp_ioctl /* si_ioctl */
icmp_ioctl, /* si_ioctl */
NULL, /* si_socketpair */
NULL /* si_shutdown */
#ifdef CONFIG_NET_SOCKOPTS
, icmp_getsockopt /* si_getsockopt */
, icmp_setsockopt /* si_setsockopt */
#endif
};
/****************************************************************************
@ -99,8 +113,8 @@ static int icmp_setup(FAR struct socket *psock)
{
/* SOCK_DGRAM or SOCK_CTRL and IPPROTO_ICMP are supported */
if ((psock->s_type == SOCK_DGRAM || psock->s_type == SOCK_CTRL) &&
psock->s_proto == IPPROTO_ICMP)
if ((psock->s_type == SOCK_DGRAM || psock->s_type == SOCK_CTRL ||
psock->s_type == SOCK_RAW) && psock->s_proto == IPPROTO_ICMP)
{
/* Allocate the IPPROTO_ICMP socket connection structure and save in
* the new socket instance.
@ -121,6 +135,10 @@ static int icmp_setup(FAR struct socket *psock)
DEBUGASSERT(conn->crefs == 0);
conn->crefs = 1;
if (psock->s_type != SOCK_RAW)
{
conn->filter = UINT32_MAX;
}
/* Save the pre-allocated connection in the socket structure */
@ -266,6 +284,218 @@ static int icmp_close(FAR struct socket *psock)
return OK;
}
#ifdef CONFIG_NET_SOCKOPTS
/****************************************************************************
* Name: icmp_getsockopt_internal
*
* Description:
* icmp_getsockopt_internal() sets the ICMP-protocol socket option
* specified by the 'option' argument to the value pointed to by the
* 'value' argument for the socket specified by the 'psock' argument.
*
* See <netinet/in.h> for the a complete list of values of ICMP protocol
* socket options.
*
* Input Parameters:
* psock Socket structure of socket to operate on
* option identifies the option to set
* value Points to the argument value
* value_len The length of the argument value
*
* Returned Value:
* Returns zero (OK) on success. On failure, it returns a negated errno
* value to indicate the nature of the error.
*
****************************************************************************/
static int icmp_getsockopt_internal(FAR struct socket *psock, int option,
FAR void *value,
FAR socklen_t *value_len)
{
int ret;
ninfo("option: %d\n", option);
if (psock->s_type != SOCK_RAW)
{
return ENOPROTOOPT;
}
net_lock();
switch (option)
{
case ICMP_FILTER:
{
FAR struct icmp_conn_s *conn = psock->s_conn;
if (*value_len > sizeof(uint32_t))
{
*value_len = sizeof(uint32_t);
}
memcpy(value, &conn->filter, *value_len);
ret = OK;
}
break;
default:
nerr("ERROR: Unrecognized ICMP option: %d\n", option);
ret = -ENOPROTOOPT;
break;
}
net_unlock();
return ret;
}
/****************************************************************************
* Name: icmp_getsockopt
*
* Description:
* icmp_getsockopt() retrieve the value for the option specified by the
* 'option' argument at the protocol level specified by the 'level'
* argument. If the size of the option value is greater than 'value_len',
* the value stored in the object pointed to by the 'value' argument will
* be silently truncated. Otherwise, the length pointed to by the
* 'value_len' argument will be modified to indicate the actual length
* of the 'value'.
*
* The 'level' argument specifies the protocol level of the option. To
* retrieve options at the socket level, specify the level argument as
* SOL_SOCKET.
*
* See <sys/socket.h> a complete list of values for the 'option' argument.
*
* Input Parameters:
* psock Socket structure of the socket to query
* level Protocol level to set the option
* option identifies the option to get
* value Points to the argument value
* value_len The length of the argument value
*
****************************************************************************/
static int icmp_getsockopt(FAR struct socket *psock, int level, int option,
FAR void *value, FAR socklen_t *value_len)
{
switch (level)
{
case IPPROTO_IP:
return ipv4_getsockopt(psock, option, value, value_len);
case IPPROTO_ICMP:
return icmp_getsockopt_internal(psock, option, value, value_len);
default:
nerr("ERROR: Unrecognized ICMP option: %d\n", option);
return -ENOPROTOOPT;
}
}
/****************************************************************************
* Name: icmp_setsockopt_internal
*
* Description:
* icmp_setsockopt_internal() sets the ICMP-protocol socket option
* specified by the 'option' argument to the value pointed to by the
* 'value' argument for the socket specified by the 'psock' argument.
*
* See <netinet/in.h> for the a complete list of values of ICMP protocol
* socket options.
*
* Input Parameters:
* psock Socket structure of socket to operate on
* option identifies the option to set
* value Points to the argument value
* value_len The length of the argument value
*
* Returned Value:
* Returns zero (OK) on success. On failure, it returns a negated errno
* value to indicate the nature of the error. See psock_setcockopt() for
* the list of possible error values.
*
****************************************************************************/
static int icmp_setsockopt_internal(FAR struct socket *psock, int option,
FAR const void *value,
socklen_t value_len)
{
int ret;
ninfo("option: %d\n", option);
if (psock->s_type != SOCK_RAW)
{
return ENOPROTOOPT;
}
net_lock();
switch (option)
{
case ICMP_FILTER:
{
FAR struct icmp_conn_s *conn = psock->s_conn;
if (value_len > sizeof(uint32_t))
{
value_len = sizeof(uint32_t);
}
memcpy(&conn->filter, value, value_len);
ret = OK;
}
break;
default:
nerr("ERROR: Unrecognized ICMP6 option: %d\n", option);
ret = -ENOPROTOOPT;
break;
}
net_unlock();
return ret;
}
/****************************************************************************
* Name: icmp_setsockopt
*
* Description:
* icmp_setsockopt() sets the option specified by the 'option' argument,
* at the protocol level specified by the 'level' argument, to the value
* pointed to by the 'value' argument for the connection.
*
* The 'level' argument specifies the protocol level of the option. To set
* options at the socket level, specify the level argument as SOL_SOCKET.
*
* See <sys/socket.h> a complete list of values for the 'option' argument.
*
* Input Parameters:
* psock Socket structure of the socket to query
* level Protocol level to set the option
* option identifies the option to set
* value Points to the argument value
* value_len The length of the argument value
*
****************************************************************************/
static int icmp_setsockopt(FAR struct socket *psock, int level, int option,
FAR const void *value, socklen_t value_len)
{
switch (level)
{
case IPPROTO_IP:
return ipv4_setsockopt(psock, option, value, value_len);
case IPPROTO_ICMP:
return icmp_setsockopt_internal(psock, option, value, value_len);
default:
nerr("ERROR: Unrecognized ICMP option: %d\n", option);
return -ENOPROTOOPT;
}
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/

View file

@ -2441,8 +2441,8 @@ inet_sockif(sa_family_t family, int type, int protocol)
#if defined(HAVE_PFINET_SOCKETS) && defined(CONFIG_NET_ICMP_SOCKET)
/* PF_INET, ICMP data gram sockets are a special case of raw sockets */
if (family == PF_INET && (type == SOCK_DGRAM || type == SOCK_CTRL) &&
protocol == IPPROTO_ICMP)
if (family == PF_INET && (type == SOCK_DGRAM || type == SOCK_CTRL ||
type == SOCK_RAW) && protocol == IPPROTO_ICMP)
{
return &g_icmp_sockif;
}