mirror of
https://github.com/apache/nuttx.git
synced 2026-08-01 20:28:58 +00:00
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:
parent
8fa4f2d61d
commit
3bd495c09d
8 changed files with 476 additions and 161 deletions
|
|
@ -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
|
||||
****************************************************************************/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
****************************************************************************/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue