From 35dee8fdd2c9eeea77214de97876d63699e950dd Mon Sep 17 00:00:00 2001 From: zhanghongyu Date: Sun, 23 Apr 2023 11:25:28 +0800 Subject: [PATCH] icmpv6: add SOCK_RAW type support The third-party library we are porting will send and receive ICMPV6 messages (router_advert / router_solicit / neighbor_advert / neighbor_solicit etc.) from the user mode itself, so we added the SOCK_RAW related implementation for ICMPV6. Signed-off-by: zhanghongyu --- net/icmpv6/icmpv6.h | 18 ++- net/icmpv6/icmpv6_conn.c | 25 ++-- net/icmpv6/icmpv6_input.c | 188 ++++++++++++++++++---------- net/icmpv6/icmpv6_recvmsg.c | 66 ++-------- net/icmpv6/icmpv6_sendmsg.c | 21 ++-- net/icmpv6/icmpv6_sockif.c | 236 +++++++++++++++++++++++++++++++++++- net/inet/inet_sockif.c | 4 +- 7 files changed, 406 insertions(+), 152 deletions(-) diff --git a/net/icmpv6/icmpv6.h b/net/icmpv6/icmpv6.h index ad4d2fb23ac..259a873bcef 100644 --- a/net/icmpv6/icmpv6.h +++ b/net/icmpv6/icmpv6.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -84,7 +85,6 @@ struct icmpv6_conn_s /* ICMPv6-specific content follows */ uint16_t id; /* ICMPv6 ECHO request ID */ - uint8_t nreqs; /* Number of requests with no response received */ uint8_t crefs; /* Reference counts on this instance */ /* The device that the ICMPv6 request was sent on */ @@ -97,6 +97,7 @@ struct icmpv6_conn_s */ struct iob_queue_s readahead; /* Read-ahead buffering */ + struct icmp6_filter filter; /* ICMP6 type filter */ /* The following is a list of poll structures of threads waiting for * socket events. @@ -104,6 +105,11 @@ struct icmpv6_conn_s struct icmpv6_poll_s pollinfo[CONFIG_NET_ICMPv6_NPOLLWAITERS]; }; + +/* Callback from icmpv6_foreach() */ + +typedef int (*icmpv6_callback_t)(FAR struct icmpv6_conn_s *conn, + FAR void *arg); #endif #ifdef CONFIG_NET_ICMPv6_NEIGHBOR @@ -596,11 +602,12 @@ FAR struct icmpv6_conn_s *icmpv6_nextconn(FAR struct icmpv6_conn_s *conn); #endif /**************************************************************************** - * Name: icmpv6_findconn + * Name: icmpv6_foreach * * Description: - * Find an ICMPv6 connection structure that is expecting a ICMPv6 ECHO - * response with this ID from this device + * Enumerate each ICMPv6 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. @@ -608,8 +615,7 @@ FAR struct icmpv6_conn_s *icmpv6_nextconn(FAR struct icmpv6_conn_s *conn); ****************************************************************************/ #ifdef CONFIG_NET_ICMPv6_SOCKET -FAR struct icmpv6_conn_s *icmpv6_findconn(FAR struct net_driver_s *dev, - uint16_t id); +int icmpv6_foreach(icmpv6_callback_t callback, FAR void *arg); #endif /**************************************************************************** diff --git a/net/icmpv6/icmpv6_conn.c b/net/icmpv6/icmpv6_conn.c index ae5007a2b30..e6ee072a632 100644 --- a/net/icmpv6/icmpv6_conn.c +++ b/net/icmpv6/icmpv6_conn.c @@ -252,31 +252,36 @@ FAR struct icmpv6_conn_s *icmpv6_nextconn(FAR struct icmpv6_conn_s *conn) } /**************************************************************************** - * Name: icmpv6_findconn + * Name: icmpv6_foreach * * Description: - * Find an ICMPv6 connection structure that is expecting a ICMPv6 ECHO - * response with this ID from this device + * Enumerate each ICMPv6 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. * ****************************************************************************/ -FAR struct icmpv6_conn_s *icmpv6_findconn(FAR struct net_driver_s *dev, - uint16_t id) +int icmpv6_foreach(icmpv6_callback_t callback, FAR void *arg) { FAR struct icmpv6_conn_s *conn; + int ret = 0; - for (conn = icmpv6_nextconn(NULL); conn != NULL; - conn = icmpv6_nextconn(conn)) + if (callback != NULL) { - if (conn->id == id && conn->dev == dev && conn->nreqs > 0) + for (conn = icmpv6_nextconn(NULL); conn != NULL; + conn = icmpv6_nextconn(conn)) { - return conn; + ret = callback(conn, arg); + if (ret != 0) + { + break; + } } } - return conn; + return ret; } #endif /* CONFIG_NET_ICMP */ diff --git a/net/icmpv6/icmpv6_input.c b/net/icmpv6/icmpv6_input.c index 9216f5ac27e..16378a7bdfa 100644 --- a/net/icmpv6/icmpv6_input.c +++ b/net/icmpv6/icmpv6_input.c @@ -59,10 +59,32 @@ #define MLDREPORT_V2 ((FAR struct mld_mcast_listen_report_v2_s *)icmpv6) #define MLDDONE ((FAR struct mld_mcast_listen_done_s *)icmpv6) +#ifdef CONFIG_NET_ICMPv6_SOCKET + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct icmpv6_deliver_s +{ + FAR struct net_driver_s *dev; /* Current network device */ + unsigned int iplen; /* The size of the IPv6 header */ + bool delivered; /* Whether the message is delivered */ +}; + /**************************************************************************** * Private Functions ****************************************************************************/ +static bool icmpv6_filter(FAR const uint32_t *data, uint8_t type) +{ + /* We require only the four bytes of the ICMPv6 header. */ + + DEBUGASSERT(data != NULL); + + return (data[type >> 5] & (1u << (type & 31))) != 0; +} + /**************************************************************************** * Name: icmpv6_datahandler * @@ -82,7 +104,6 @@ * ****************************************************************************/ -#ifdef CONFIG_NET_ICMPv6_SOCKET static uint16_t icmpv6_datahandler(FAR struct net_driver_s *dev, FAR struct icmpv6_conn_s *conn, unsigned int iplen) @@ -93,9 +114,14 @@ static uint16_t icmpv6_datahandler(FAR struct net_driver_s *dev, uint16_t buflen; int ret; + iob = iob_tryalloc(false); + if (iob == NULL) + { + return -ENOMEM; + } + /* Put the IPv6 address at the beginning of the read-ahead buffer */ - iob = dev->d_iob; ipv6 = IPv6BUF; inaddr.sin6_family = AF_INET6; inaddr.sin6_port = 0; @@ -107,14 +133,20 @@ static uint16_t icmpv6_datahandler(FAR struct net_driver_s *dev, memcpy(iob->io_data, &inaddr, sizeof(struct sockaddr_in6)); - /* Copy the new ICMPv6 reply into the I/O buffer chain (without waiting) */ + iob_reserve(iob, sizeof(struct sockaddr_in6)); + + /* Copy the ICMPv6 message into the I/O buffer chain (without waiting) */ + + ret = iob_clone_partial(dev->d_iob, dev->d_iob->io_pktlen, + iplen, iob, 0, true, false); + if (ret < 0) + { + iob_free_chain(iob); + return ret; + } buflen = ICMPv6SIZE; - /* Trim l3 header */ - - iob = iob_trimhead(iob, iplen); - /* Add the new I/O buffer chain to the tail of the read-ahead queue (again * without waiting). */ @@ -130,12 +162,81 @@ static uint16_t icmpv6_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; } -#endif + +/**************************************************************************** + * Name: icmpv6_delivery_callback + * + * Description: + * Copy the icmpv6 package to the application according to the filter + * conditions, but ICMPv6_ECHO_REPLY is a special message type, if there + * is an application waiting, it will also copy. + * + * Input Parameters: + * conn - A pointer to the ICMPv6 connection structure. + * arg - The context information + * + ****************************************************************************/ + +static int icmpv6_delivery_callback(FAR struct icmpv6_conn_s *conn, + FAR void *arg) +{ + FAR struct icmpv6_deliver_s *info = arg; + FAR struct net_driver_s *dev = info->dev; + FAR struct icmpv6_hdr_s *icmpv6 = IPBUF(info->iplen); + + if (icmpv6_filter(conn->filter.icmp6_filt, icmpv6->type) && + (icmpv6->type != ICMPv6_ECHO_REPLY || conn->id != ICMPv6REPLY->id || + conn->dev != dev)) + { + return 0; + } + + info->delivered = true; + if (devif_conn_event(dev, ICMPv6_NEWDATA, conn->sconn.list) != + ICMPv6_NEWDATA) + { + dev->d_len = dev->d_iob->io_pktlen; + } + else + { + icmpv6_datahandler(dev, conn, info->iplen); + } + + return 0; +} + +/**************************************************************************** + * Name: icmpv6_deliver + * + * Description: + * Copy the icmpv6 package to the application according to the filter + * conditions, but ICMPv6_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. + * iplen - The size of the IPv6 header. This may be larger than + * IPv6_HDRLEN the IPv6 header if IPv6 extension headers are + * present. + * + ****************************************************************************/ + +static bool icmpv6_deliver(FAR struct net_driver_s *dev, unsigned int iplen) +{ + struct icmpv6_deliver_s info; + + info.dev = dev; + info.iplen = iplen; + info.delivered = false; + + icmpv6_foreach(icmpv6_delivery_callback, &info); + + return info.delivered; +} + +#endif /* CONFIG_NET_ICMPv6_SOCKET */ /**************************************************************************** * Public Functions @@ -166,6 +267,9 @@ void icmpv6_input(FAR struct net_driver_s *dev, unsigned int iplen) { FAR struct ipv6_hdr_s *ipv6 = IPv6BUF; FAR struct icmpv6_hdr_s *icmpv6 = IPBUF(iplen); +#ifdef CONFIG_NET_ICMPv6_SOCKET + bool delivered = icmpv6_deliver(dev, iplen); +#endif #ifdef CONFIG_NET_STATISTICS g_netstats.icmpv6.recv++; @@ -443,61 +547,6 @@ void icmpv6_input(FAR struct net_driver_s *dev, unsigned int iplen) } break; -#ifdef CONFIG_NET_ICMPv6_SOCKET - /* If an ICMPv6 echo reply is received then there should also be - * a thread waiting to received the echo response. - */ - - case ICMPv6_ECHO_REPLY: - { - FAR struct icmpv6_echo_reply_s *reply; - FAR struct icmpv6_conn_s *conn; - uint16_t flags = ICMPv6_NEWDATA; - - /* Nothing consumed the ICMP reply. That might be 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? */ - - reply = ICMPv6REPLY; - conn = icmpv6_findconn(dev, reply->id); - if (conn == NULL) - { - /* No.. drop the packet */ - - goto icmpv6_drop_packet; - } - - /* Dispatch the ECHO reply to the waiting thread */ - - flags = devif_conn_event(dev, flags, conn->sconn.list); - - /* Was the ECHO reply consumed by any waiting thread? */ - - if ((flags & ICMPv6_NEWDATA) != 0) - { - uint16_t nbuffered; - - /* Yes.. Add the ICMP echo reply to the IPPROTO_ICMP socket read - * ahead buffer. - */ - - nbuffered = icmpv6_datahandler(dev, conn, iplen); - if (nbuffered == 0) - { - /* Could not buffer the data.. drop the packet */ - - goto icmpv6_drop_packet; - } - } - - goto icmpv6_send_nothing; - } - break; -#endif - #ifdef CONFIG_NET_MLD /* Dispatch received Multicast Listener Discovery (MLD) packets. */ @@ -556,6 +605,13 @@ void icmpv6_input(FAR struct net_driver_s *dev, unsigned int iplen) default: { +#ifdef CONFIG_NET_ICMPv6_SOCKET + if (delivered) + { + goto icmpv6_send_nothing; + } +#endif + nwarn("WARNING: Unknown ICMPv6 type: %d\n", icmpv6->type); goto icmpv6_type_error; } diff --git a/net/icmpv6/icmpv6_recvmsg.c b/net/icmpv6/icmpv6_recvmsg.c index 63bed89cb86..ffffd2a23ae 100644 --- a/net/icmpv6/icmpv6_recvmsg.c +++ b/net/icmpv6/icmpv6_recvmsg.c @@ -95,9 +95,7 @@ static uint16_t recvfrom_eventhandler(FAR struct net_driver_s *dev, { FAR struct icmpv6_recvfrom_s *pstate = pvpriv; FAR struct socket *psock; - FAR struct icmpv6_conn_s *conn; FAR struct ipv6_hdr_s *ipv6; - FAR struct icmpv6_echo_reply_s *icmpv6; ninfo("flags: %04x\n", flags); @@ -112,18 +110,8 @@ 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 ICMPv6 ECHO reply. */ @@ -131,17 +119,6 @@ static uint16_t recvfrom_eventhandler(FAR struct net_driver_s *dev, { unsigned int recvsize; - /* Check if it is for us. - * REVISIT: What if there are IPv6 extension headers present? - */ - - icmpv6 = IPBUF(IPv6_HDRLEN); - if (conn->id != icmpv6->id) - { - ninfo("Wrong ID: %u vs %u\n", icmpv6->id, conn->id); - return flags; - } - ninfo("Received ICMPv6 reply\n"); /* What should we do if the received reply is larger that the @@ -171,15 +148,6 @@ static uint16_t recvfrom_eventhandler(FAR struct net_driver_s *dev, ipv6 = IPBUF(0); net_ipv6addr_hdrcopy(&pstate->recv_from, ipv6->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 &= ~ICMPv6_NEWDATA; @@ -310,7 +278,7 @@ ssize_t icmpv6_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg, FAR socklen_t *fromlen = &msg->msg_namelen; FAR struct sockaddr_in6 *inaddr; FAR struct icmpv6_conn_s *conn; - FAR struct net_driver_s *dev; + FAR struct net_driver_s *dev = NULL; struct icmpv6_recvfrom_s state; ssize_t ret; @@ -337,25 +305,18 @@ ssize_t icmpv6_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 ICMPv6 request. */ - /* Get the device that was used to send the ICMPv6 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 @@ -442,11 +403,10 @@ ssize_t icmpv6_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/icmpv6/icmpv6_sendmsg.c b/net/icmpv6/icmpv6_sendmsg.c index 43ac77781b6..17f7d064bd7 100644 --- a/net/icmpv6/icmpv6_sendmsg.c +++ b/net/icmpv6/icmpv6_sendmsg.c @@ -326,12 +326,11 @@ ssize_t icmpv6_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg, */ icmpv6 = (FAR struct icmpv6_echo_request_s *)buf; - if (icmpv6->type != ICMPv6_ECHO_REQUEST || icmpv6->id != conn->id || - dev != conn->dev) + if (psock->s_type != SOCK_RAW && (icmpv6->type != ICMPv6_ECHO_REQUEST || + icmpv6->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); } @@ -372,13 +371,12 @@ ssize_t icmpv6_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg, /* Setup to receive ICMPv6 ECHO replies */ - if (icmpv6->type == ICMPv6_ECHO_REQUEST) + if (psock->s_type != SOCK_RAW && icmpv6->type == ICMPv6_ECHO_REQUEST) { - conn->id = icmpv6->id; - conn->nreqs = 1; + conn->id = icmpv6->id; } - conn->dev = dev; + conn->dev = dev; /* Notify the device driver of the availability of TX data */ @@ -439,9 +437,8 @@ ssize_t icmpv6_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/icmpv6/icmpv6_sockif.c b/net/icmpv6/icmpv6_sockif.c index 1392104c02b..dc0a3c64017 100644 --- a/net/icmpv6/icmpv6_sockif.c +++ b/net/icmpv6/icmpv6_sockif.c @@ -37,6 +37,7 @@ #include #include "icmpv6/icmpv6.h" +#include "inet/inet.h" #ifdef CONFIG_NET_ICMPv6_SOCKET @@ -50,6 +51,12 @@ static void icmpv6_addref(FAR struct socket *psock); static int icmpv6_netpoll(FAR struct socket *psock, FAR struct pollfd *fds, bool setup); static int icmpv6_close(FAR struct socket *psock); +#ifdef CONFIG_NET_SOCKOPTS +static int icmpv6_getsockopt(FAR struct socket *psock, int level, + int option, FAR void *value, FAR socklen_t *value_len); +static int icmpv6_setsockopt(FAR struct socket *psock, int level, + int option, FAR const void *value, socklen_t value_len); +#endif /**************************************************************************** * Public Data @@ -70,7 +77,13 @@ const struct sock_intf_s g_icmpv6_sockif = icmpv6_sendmsg, /* si_sendmsg */ icmpv6_recvmsg, /* si_recvmsg */ icmpv6_close, /* si_close */ - icmpv6_ioctl /* si_ioctl */ + icmpv6_ioctl, /* si_ioctl */ + NULL, /* si_socketpair */ + NULL /* si_shutdown */ +#ifdef CONFIG_NET_SOCKOPTS + , icmpv6_getsockopt /* si_getsockopt */ + , icmpv6_setsockopt /* si_setsockopt */ +#endif }; /**************************************************************************** @@ -99,8 +112,8 @@ static int icmpv6_setup(FAR struct socket *psock) { /* SOCK_DGRAM or SOCK_CTRL and IPPROTO_ICMP6 are supported */ - if ((psock->s_type == SOCK_DGRAM || psock->s_type == SOCK_CTRL) && - psock->s_proto == IPPROTO_ICMP6) + if ((psock->s_type == SOCK_DGRAM || psock->s_type == SOCK_CTRL || + psock->s_type == SOCK_RAW) && psock->s_proto == IPPROTO_ICMP6) { /* Allocate the IPPROTO_ICMP6 socket connection structure and save in * the new socket instance. @@ -121,6 +134,10 @@ static int icmpv6_setup(FAR struct socket *psock) DEBUGASSERT(conn->crefs == 0); conn->crefs = 1; + if (psock->s_type != SOCK_RAW) + { + memset(&conn->filter, 0xff, sizeof(conn->filter)); + } /* Save the pre-allocated connection in the socket structure */ @@ -266,6 +283,219 @@ static int icmpv6_close(FAR struct socket *psock) return OK; } +#ifdef CONFIG_NET_SOCKOPTS + +/**************************************************************************** + * Name: icmpv6_getsockopt_internal + * + * Description: + * icmpv6_getsockopt_internal() sets the ICMPV6-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 ICMPV6 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 icmpv6_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 ICMP6_FILTER: + { + FAR struct icmpv6_conn_s *conn = psock->s_conn; + + if (*value_len > sizeof(struct icmp6_filter)) + { + *value_len = sizeof(struct icmp6_filter); + } + + memcpy(value, &conn->filter, *value_len); + ret = OK; + } + break; + + default: + nerr("ERROR: Unrecognized ICMPV6 option: %d\n", option); + ret = -ENOPROTOOPT; + break; + } + + net_unlock(); + return ret; +} + +/**************************************************************************** + * Name: icmpv6_getsockopt + * + * Description: + * icmpv6_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 icmpv6_getsockopt(FAR struct socket *psock, int level, int option, + FAR void *value, FAR socklen_t *value_len) +{ + switch (level) + { + case IPPROTO_IPV6: + return ipv6_getsockopt(psock, option, value, value_len); + + case IPPROTO_ICMPV6: + return icmpv6_getsockopt_internal(psock, option, value, value_len); + + default: + nerr("ERROR: Unrecognized ICMPV6 option: %d\n", option); + return -ENOPROTOOPT; + } +} + +/**************************************************************************** + * Name: icmpv6_setsockopt_internal + * + * Description: + * icmpv6_setsockopt_internal() sets the ICMPV6-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 ICMPV6 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 icmpv6_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 ICMP6_FILTER: + { + FAR struct icmpv6_conn_s *conn = psock->s_conn; + + if (value_len > sizeof(struct icmp6_filter)) + { + value_len = sizeof(struct icmp6_filter); + } + + 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: icmpv6_setsockopt + * + * Description: + * icmpv6_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 icmpv6_setsockopt(FAR struct socket *psock, int level, int option, + FAR const void *value, socklen_t value_len) +{ + switch (level) + { + case IPPROTO_IPV6: + return ipv6_setsockopt(psock, option, value, value_len); + + case IPPROTO_ICMPV6: + return icmpv6_setsockopt_internal(psock, option, value, value_len); + + default: + nerr("ERROR: Unrecognized ICMPV6 option: %d\n", option); + return -ENOPROTOOPT; + } +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ diff --git a/net/inet/inet_sockif.c b/net/inet/inet_sockif.c index 5f9e6085277..3933beadb75 100644 --- a/net/inet/inet_sockif.c +++ b/net/inet/inet_sockif.c @@ -2410,8 +2410,8 @@ inet_sockif(sa_family_t family, int type, int protocol) #if defined(HAVE_PFINET6_SOCKETS) && defined(CONFIG_NET_ICMPv6_SOCKET) /* PF_INET, ICMP data gram sockets are a special case of raw sockets */ - if (family == PF_INET6 && (type == SOCK_DGRAM || type == SOCK_CTRL) && - protocol == IPPROTO_ICMP6) + if (family == PF_INET6 && (type == SOCK_DGRAM || type == SOCK_CTRL || + type == SOCK_RAW) && protocol == IPPROTO_ICMPV6) { return &g_icmpv6_sockif; }