From 3bd495c09d85428f15ab433fc6f8c43fd48a71de Mon Sep 17 00:00:00 2001 From: zhanghongyu Date: Tue, 9 May 2023 19:09:53 +0800 Subject: [PATCH] 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 --- include/nuttx/net/icmp.h | 2 + net/icmp/icmp.h | 26 ++++- net/icmp/icmp_conn.c | 36 +++++- net/icmp/icmp_input.c | 200 +++++++++++++++++++++------------ net/icmp/icmp_recvmsg.c | 106 +++++++----------- net/icmp/icmp_sendmsg.c | 27 ++--- net/icmp/icmp_sockif.c | 236 ++++++++++++++++++++++++++++++++++++++- net/inet/inet_sockif.c | 4 +- 8 files changed, 476 insertions(+), 161 deletions(-) diff --git a/include/nuttx/net/icmp.h b/include/nuttx/net/icmp.h index b2523ee7400..1afd13b77fa 100644 --- a/include/nuttx/net/icmp.h +++ b/include/nuttx/net/icmp.h @@ -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 ****************************************************************************/ diff --git a/net/icmp/icmp.h b/net/icmp/icmp.h index 071e818a79b..e755ffe7029 100644 --- a/net/icmp/icmp.h +++ b/net/icmp/icmp.h @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -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 * diff --git a/net/icmp/icmp_conn.c b/net/icmp/icmp_conn.c index a727ce7e1e7..0cda4685b2b 100644 --- a/net/icmp/icmp_conn.c +++ b/net/icmp/icmp_conn.c @@ -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 */ diff --git a/net/icmp/icmp_input.c b/net/icmp/icmp_input.c index fa123fc1a84..da0435f82c8 100644 --- a/net/icmp/icmp_input.c +++ b/net/icmp/icmp_input.c @@ -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 diff --git a/net/icmp/icmp_recvmsg.c b/net/icmp/icmp_recvmsg.c index 4b294198579..359c9c562fb 100644 --- a/net/icmp/icmp_recvmsg.c +++ b/net/icmp/icmp_recvmsg.c @@ -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); } diff --git a/net/icmp/icmp_sendmsg.c b/net/icmp/icmp_sendmsg.c index e218d3f9833..4d767a8c74b 100644 --- a/net/icmp/icmp_sendmsg.c +++ b/net/icmp/icmp_sendmsg.c @@ -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; diff --git a/net/icmp/icmp_sockif.c b/net/icmp/icmp_sockif.c index 17fd885a09d..ae16cec664e 100644 --- a/net/icmp/icmp_sockif.c +++ b/net/icmp/icmp_sockif.c @@ -34,9 +34,11 @@ #include #include +#include #include #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 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 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 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 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 ****************************************************************************/ diff --git a/net/inet/inet_sockif.c b/net/inet/inet_sockif.c index a45c083a0dd..3b858807971 100644 --- a/net/inet/inet_sockif.c +++ b/net/inet/inet_sockif.c @@ -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; }