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 <zhanghongyu@xiaomi.com>
This commit is contained in:
parent
6bf989159e
commit
35dee8fdd2
@ -30,6 +30,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <netinet/icmp6.h>
|
||||
|
||||
#include <nuttx/mm/iob.h>
|
||||
#include <nuttx/net/ip.h>
|
||||
@ -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
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include <socket/socket.h>
|
||||
|
||||
#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 <netinet/in.h> 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 <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 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 <netinet/in.h> 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 <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 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
|
||||
****************************************************************************/
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user