From e6f8ccda4a7e533effc8483a2d2a53f7839d4d62 Mon Sep 17 00:00:00 2001 From: dongjiuzhu1 Date: Tue, 22 Nov 2022 21:15:06 +0800 Subject: [PATCH] net/get/setsockopt: add si_get/setsockopt interface to simply get/setsockopt move private option to protocol sockif Signed-off-by: dongjiuzhu1 --- include/nuttx/net/net.h | 9 +- net/can/can.h | 8 +- net/can/can_callback.c | 2 +- net/can/can_getsockopt.c | 20 +- net/can/can_recvmsg.c | 10 +- net/can/can_setsockopt.c | 34 ++- net/can/can_sockif.c | 8 +- net/inet/inet_sockif.c | 471 ++++++++++++++++++++++++++++++- net/socket/getsockopt.c | 198 ++----------- net/socket/setsockopt.c | 283 +------------------ net/usrsock/usrsock.h | 14 +- net/usrsock/usrsock_getsockopt.c | 22 +- net/usrsock/usrsock_setsockopt.c | 20 +- net/usrsock/usrsock_sockif.c | 7 +- 14 files changed, 620 insertions(+), 486 deletions(-) diff --git a/include/nuttx/net/net.h b/include/nuttx/net/net.h index cc7fab801e..bdabcbaf83 100644 --- a/include/nuttx/net/net.h +++ b/include/nuttx/net/net.h @@ -166,6 +166,12 @@ struct sock_intf_s CODE int (*si_ioctl)(FAR struct socket *psock, int cmd, unsigned long arg); CODE int (*si_socketpair)(FAR struct socket *psocks[2]); +#ifdef CONFIG_NET_SOCKOPTS + CODE int (*si_getsockopt)(FAR struct socket *psock, int level, + int option, FAR void *value, FAR socklen_t *value_len); + CODE int (*si_setsockopt)(FAR struct socket *psock, int level, + int option, FAR const void *value, socklen_t value_len); +#endif #ifdef CONFIG_NET_SENDFILE CODE ssize_t (*si_sendfile)(FAR struct socket *psock, FAR struct file *infile, FAR off_t *offset, @@ -210,9 +216,6 @@ struct socket_conn_s #ifdef CONFIG_NET_SOLINGER socktimeo_t s_linger; /* Linger timeout value (in deciseconds) */ #endif -#ifdef CONFIG_NET_TIMESTAMP - int32_t s_timestamp; /* Socket timestamp enabled/disabled */ -#endif #ifdef CONFIG_NET_BINDTODEVICE uint8_t s_boundto; /* Index of the interface we are bound to. * Unbound: 0, Bound: 1-MAX_IFINDEX */ diff --git a/net/can/can.h b/net/can/can.h index d317fea6f7..b1180c11aa 100644 --- a/net/can/can.h +++ b/net/can/can.h @@ -111,6 +111,9 @@ struct can_conn_s int32_t tx_deadline; # endif #endif +#ifdef CONFIG_NET_TIMESTAMP + int32_t timestamp; /* Socket timestamp enabled/disabled */ +#endif }; /**************************************************************************** @@ -353,6 +356,7 @@ void can_readahead_signal(FAR struct can_conn_s *conn); * * Input Parameters: * psock Socket structure of socket to operate on + * 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 @@ -365,7 +369,7 @@ void can_readahead_signal(FAR struct can_conn_s *conn); ****************************************************************************/ #ifdef CONFIG_NET_CANPROTO_OPTIONS -int can_setsockopt(FAR struct socket *psock, int option, +int can_setsockopt(FAR struct socket *psock, int level, int option, FAR const void *value, socklen_t value_len); #endif @@ -399,7 +403,7 @@ int can_setsockopt(FAR struct socket *psock, int option, ****************************************************************************/ #ifdef CONFIG_NET_CANPROTO_OPTIONS -int can_getsockopt(FAR struct socket *psock, int option, +int can_getsockopt(FAR struct socket *psock, int level, int option, FAR void *value, FAR socklen_t *value_len); #endif diff --git a/net/can/can_callback.c b/net/can/can_callback.c index 394c1d51ac..a5f3e17937 100644 --- a/net/can/can_callback.c +++ b/net/can/can_callback.c @@ -123,7 +123,7 @@ uint16_t can_callback(FAR struct net_driver_s *dev, #ifdef CONFIG_NET_TIMESTAMP /* TIMESTAMP sockopt is activated, create timestamp and copy to iob */ - if (conn->sconn.s_timestamp) + if (conn->timestamp) { struct timespec *ts = (struct timespec *) &dev->d_appdata[dev->d_len]; diff --git a/net/can/can_getsockopt.c b/net/can/can_getsockopt.c index 17a4b18850..af6253ca3a 100644 --- a/net/can/can_getsockopt.c +++ b/net/can/can_getsockopt.c @@ -74,7 +74,7 @@ * ****************************************************************************/ -int can_getsockopt(FAR struct socket *psock, int option, +int can_getsockopt(FAR struct socket *psock, int level, int option, FAR void *value, FAR socklen_t *value_len) { FAR struct can_conn_s *conn; @@ -84,6 +84,24 @@ int can_getsockopt(FAR struct socket *psock, int option, psock->s_conn != NULL); conn = (FAR struct can_conn_s *)psock->s_conn; +#ifdef CONFIG_NET_TIMESTAMP + if (level == SOL_SOCKET && option == SO_TIMESTAMP) + { + if (*value_len != sizeof(int32_t)) + { + return -EINVAL; + } + + *(FAR int32_t *)value = conn->timestamp; + return OK; + } +#endif + + if (level != SOL_CAN_RAW) + { + return -ENOPROTOOPT; + } + if (psock->s_type != SOCK_RAW) { nerr("ERROR: Not a RAW CAN socket\n"); diff --git a/net/can/can_recvmsg.c b/net/can/can_recvmsg.c index 189505f717..8ef711de8b 100644 --- a/net/can/can_recvmsg.c +++ b/net/can/can_recvmsg.c @@ -434,9 +434,9 @@ static uint16_t can_recvfrom_eventhandler(FAR struct net_driver_s *dev, #endif { #ifdef CONFIG_NET_TIMESTAMP - if ((conn->sconn.s_timestamp && (dev->d_len > + if ((conn->timestamp && (dev->d_len > sizeof(struct can_frame) + sizeof(struct timeval))) - || (!conn->sconn.s_timestamp && (dev->d_len > + || (!conn->timestamp && (dev->d_len > sizeof(struct can_frame)))) #else if (dev->d_len > sizeof(struct can_frame)) @@ -454,7 +454,7 @@ static uint16_t can_recvfrom_eventhandler(FAR struct net_driver_s *dev, can_newdata(dev, pstate); #ifdef CONFIG_NET_TIMESTAMP - if (conn->sconn.s_timestamp) + if (conn->timestamp) { if (pstate->pr_msglen == sizeof(struct timeval)) { @@ -587,7 +587,7 @@ ssize_t can_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg, state.pr_buffer = msg->msg_iov->iov_base; #ifdef CONFIG_NET_TIMESTAMP - if (conn->sconn.s_timestamp && msg->msg_controllen >= + if (conn->timestamp && msg->msg_controllen >= (sizeof(struct cmsghdr) + sizeof(struct timeval))) { struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); @@ -619,7 +619,7 @@ ssize_t can_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg, if (ret > 0) { #ifdef CONFIG_NET_TIMESTAMP - if (conn->sconn.s_timestamp) + if (conn->timestamp) { if (state.pr_msglen == sizeof(struct timeval)) { diff --git a/net/can/can_setsockopt.c b/net/can/can_setsockopt.c index 2b43c1de98..efa87ef9d5 100644 --- a/net/can/can_setsockopt.c +++ b/net/can/can_setsockopt.c @@ -58,6 +58,7 @@ * * Input Parameters: * psock Socket structure of socket to operate on + * 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 @@ -69,7 +70,7 @@ * ****************************************************************************/ -int can_setsockopt(FAR struct socket *psock, int option, +int can_setsockopt(FAR struct socket *psock, int level, int option, FAR const void *value, socklen_t value_len) { FAR struct can_conn_s *conn; @@ -81,6 +82,37 @@ int can_setsockopt(FAR struct socket *psock, int option, conn = (FAR struct can_conn_s *)psock->s_conn; +#ifdef CONFIG_NET_TIMESTAMP + + /* Generates a timestamp for each incoming packet */ + + if (level == SOL_SOCKET && option == SO_TIMESTAMP) + { + /* Verify that option is at least the size of an integer. */ + + if (value_len < sizeof(int32_t)) + { + return -EINVAL; + } + + /* Lock the network so that we have exclusive access to the socket + * options. + */ + + net_lock(); + + conn->timestamp = *((FAR int32_t *)value); + + net_unlock(); + return OK; + } +#endif + + if (level != SOL_CAN_RAW) + { + return -ENOPROTOOPT; + } + if (psock->s_type != SOCK_RAW) { nerr("ERROR: Not a RAW CAN socket\n"); diff --git a/net/can/can_sockif.c b/net/can/can_sockif.c index 3e9ccc27b0..3fb1623a2f 100644 --- a/net/can/can_sockif.c +++ b/net/can/can_sockif.c @@ -84,7 +84,13 @@ const struct sock_intf_s g_can_sockif = can_poll_local, /* si_poll */ can_sendmsg, /* si_sendmsg */ can_recvmsg, /* si_recvmsg */ - can_close /* si_close */ + can_close, /* si_close */ + NULL, /* si_ioctl */ + NULL /* si_socketpair */ +#if defined(CONFIG_NET_SOCKOPTS) && defined(CONFIG_NET_CANPROTO_OPTIONS) + , can_getsockopt /* si_getsockopt */ + , can_setsockopt /* si_setsockopt */ +#endif }; /**************************************************************************** diff --git a/net/inet/inet_sockif.c b/net/inet/inet_sockif.c index c198c14c87..a7b1030a80 100644 --- a/net/inet/inet_sockif.c +++ b/net/inet/inet_sockif.c @@ -91,6 +91,12 @@ static ssize_t inet_recvmsg(FAR struct socket *psock, static int inet_ioctl(FAR struct socket *psock, int cmd, unsigned long arg); static int inet_socketpair(FAR struct socket *psocks[2]); +#ifdef CONFIG_NET_SOCKOPTS +static int inet_getsockopt(FAR struct socket *psock, int level, + int option, FAR void *value, FAR socklen_t *value_len); +static int inet_setsockopt(FAR struct socket *psock, int level, + int option, FAR const void *value, socklen_t value_len); +#endif #ifdef CONFIG_NET_SENDFILE static ssize_t inet_sendfile(FAR struct socket *psock, FAR struct file *infile, FAR off_t *offset, @@ -118,9 +124,12 @@ static const struct sock_intf_s g_inet_sockif = inet_close, /* si_close */ inet_ioctl, /* si_ioctl */ inet_socketpair /* si_socketpair */ +#ifdef CONFIG_NET_SOCKOPTS + , inet_getsockopt /* si_getsockopt */ + , inet_setsockopt /* si_setsockopt */ +#endif #ifdef CONFIG_NET_SENDFILE - , - inet_sendfile /* si_sendfile */ + , inet_sendfile /* si_sendfile */ #endif }; @@ -562,6 +571,464 @@ static int inet_getpeername(FAR struct socket *psock, } } +#ifdef CONFIG_NET_SOCKOPTS + +/**************************************************************************** + * Name: inet_get_socketlevel_option + * + * Description: + * inet_get_socketlevel_option() retrieve the value for the option + * specified by the 'option' argument for the socket specified by the + * 'psock' 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; to retrieve options at the TCP-protocol level, the level + * argument is SOL_TCP. + * + * See a complete list of values for the socket-level + * 'option' argument. Protocol-specific options are are protocol specific + * header files (such as netinet/tcp.h for the case of the TCP protocol). + * + * 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 + * + * Returned Value: + * Returns zero (OK) on success. On failure, it returns a negated errno + * value to indicate the nature of the error. See psock_getsockopt() for + * the complete list of appropriate return error codes. + * + ****************************************************************************/ + +static int inet_get_socketlevel_option(FAR struct socket *psock, int option, + FAR void *value, + FAR socklen_t *value_len) +{ + switch (option) + { +#if CONFIG_NET_RECV_BUFSIZE > 0 + case SO_RCVBUF: /* Reports receive buffer size */ + { + if (*value_len != sizeof(int)) + { + return -EINVAL; + } + +#if defined(CONFIG_NET_TCP) && !defined(CONFIG_NET_TCP_NO_STACK) + if (psock->s_type == SOCK_STREAM) + { + FAR struct tcp_conn_s *tcp = psock->s_conn; + *(FAR int *)value = tcp->rcv_bufs; + } + else +#endif +#if defined(CONFIG_NET_UDP) && !defined(CONFIG_NET_UDP_NO_STACK) + if (psock->s_type == SOCK_DGRAM) + { + FAR struct udp_conn_s *udp = psock->s_conn; + *(FAR int *)value = udp->rcvbufs; + } + else +#endif + { + return -ENOPROTOOPT; + } + } + break; +#endif + +#if CONFIG_NET_SEND_BUFSIZE > 0 + case SO_SNDBUF: /* Reports send buffer size */ + { + if (*value_len != sizeof(int)) + { + return -EINVAL; + } + +#if defined(CONFIG_NET_TCP) && !defined(CONFIG_NET_TCP_NO_STACK) + if (psock->s_type == SOCK_STREAM) + { + FAR struct tcp_conn_s *tcp = psock->s_conn; + *(FAR int *)value = tcp->snd_bufs; + } + else +#endif +#if defined(CONFIG_NET_UDP) && !defined(CONFIG_NET_UDP_NO_STACK) + if (psock->s_type == SOCK_DGRAM) + { + FAR struct udp_conn_s *udp = psock->s_conn; + + /* Save the send buffer size */ + + *(FAR int *)value = udp->sndbufs; + } + else +#endif + { + return -ENOPROTOOPT; + } + } + break; +#endif + +#ifdef CONFIG_NET_TCPPROTO_OPTIONS + case SO_KEEPALIVE: + { + /* Any connection-oriented protocol could potentially support + * SO_KEEPALIVE. However, this option is currently only available + * for TCP/IP. + * + * NOTE: SO_KEEPALIVE is not really a socket-level option; it is a + * protocol-level option. A given TCP connection may service + * multiple sockets (via dup'ing of the socket). There is, however, + * still only one connection to be monitored and that is a global + * attribute across all of the clones that may use the underlying + * connection. + */ + + /* Verifies TCP connections active by enabling the periodic + * transmission of probes. + */ + + return tcp_getsockopt(psock, option, value, value_len); + } +#endif + + default: + return -ENOPROTOOPT; + } + + return OK; +} + +/**************************************************************************** + * Name: inet_getsockopt + * + * Description: + * inet_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 inet_getsockopt(FAR struct socket *psock, int level, int option, + FAR void *value, FAR socklen_t *value_len) +{ + if (level == SOL_SOCKET) + { + return inet_get_socketlevel_option(psock, option, value, value_len); + } +#ifdef CONFIG_NET_TCPPROTO_OPTIONS + else if (level == IPPROTO_TCP) + { + return tcp_getsockopt(psock, option, value, value_len); + } +#endif + else + { + return -ENOPROTOOPT; + } +} + +/**************************************************************************** + * Name: inet_set_socketlevel_option + * + * Description: + * inet_set_socketlevel_option() sets the socket-level option specified by + * the 'option' argument to the value pointed to by the 'value' argument + * for the socket specified by the 'psock' argument. + * + * See a complete list of values for the socket level + * 'option' argument. + * + * 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 inet_set_socketlevel_option(FAR struct socket *psock, int option, + FAR const void *value, + socklen_t value_len) +{ + switch (option) + { +#ifdef CONFIG_NET_TCPPROTO_OPTIONS + case SO_KEEPALIVE: + { + /* Any connection-oriented protocol could potentially support + * SO_KEEPALIVE. However, this option is currently only available + * for TCP/IP. + * + * NOTE: SO_KEEPALIVE is not really a socket-level option; it is a + * protocol-level option. A given TCP connection may service + * multiple sockets (via dup'ing of the socket). There is, however, + * still only one connection to be monitored and that is a global + * attribute across all of the clones that may use the underlying + * connection. + */ + + /* Verifies TCP connections active by enabling the + * periodic transmission of probes + */ + + return tcp_setsockopt(psock, option, value, value_len); + } +#endif + +#ifdef CONFIG_NET_SOLINGER + case SO_LINGER: + { + /* Lingers on a close() if data is present */ + + FAR struct socket_conn_s *conn = psock->s_conn; + FAR struct linger *setting; + + /* Verify that option is at least the size of an 'struct linger'. */ + + if (value_len < sizeof(struct linger)) + { + return -EINVAL; + } + + /* Get the value. Is the option being set or cleared? */ + + setting = (FAR struct linger *)value; + + /* Lock the network so that we have exclusive access to the socket + * options. + */ + + net_lock(); + + /* Set or clear the linger option bit and linger time + * (in deciseconds) + */ + + if (setting->l_onoff) + { + _SO_SETOPT(conn->s_options, option); + conn->s_linger = 10 * setting->l_linger; + } + else + { + _SO_CLROPT(conn->s_options, option); + conn->s_linger = 0; + } + + net_unlock(); + } + break; +#endif + +#if CONFIG_NET_RECV_BUFSIZE > 0 + case SO_RCVBUF: /* Sets receive buffer size */ + { + int buffersize; + + /* Verify that option is the size of an 'int'. Should also check + * that 'value' is properly aligned for an 'int' + */ + + if (value_len != sizeof(int)) + { + return -EINVAL; + } + + /* Get the value. Is the option being set or cleared? */ + + buffersize = *(FAR int *)value; + if (buffersize < 0) + { + return -EINVAL; + } + + net_lock(); + +#if defined(CONFIG_NET_TCP) && !defined(CONFIG_NET_TCP_NO_STACK) + if (psock->s_type == SOCK_STREAM) + { + FAR struct tcp_conn_s *tcp = psock->s_conn; + + /* Save the receive buffer size */ + + tcp->rcv_bufs = buffersize; + } + else +#endif +#if defined(CONFIG_NET_UDP) && !defined(CONFIG_NET_UDP_NO_STACK) + if (psock->s_type == SOCK_DGRAM) + { + FAR struct udp_conn_s *udp = psock->s_conn; + + /* Save the receive buffer size */ + + udp->rcvbufs = buffersize; + } + else +#endif + { + net_unlock(); + return -ENOPROTOOPT; + } + + net_unlock(); + } + break; +#endif + +#if CONFIG_NET_SEND_BUFSIZE > 0 + case SO_SNDBUF: /* Sets send buffer size */ + { + int buffersize; + + /* Verify that option is the size of an 'int'. Should also check + * that 'value' is properly aligned for an 'int' + */ + + if (value_len != sizeof(int)) + { + return -EINVAL; + } + + /* Get the value. Is the option being set or cleared? */ + + buffersize = *(FAR int *)value; + + if (buffersize < 0) + { + return -EINVAL; + } + + net_lock(); + +#if defined(CONFIG_NET_TCP) && !defined(CONFIG_NET_TCP_NO_STACK) + if (psock->s_type == SOCK_STREAM) + { + FAR struct tcp_conn_s *tcp = psock->s_conn; + + /* Save the send buffer size */ + + tcp->snd_bufs = buffersize; + } + else +#endif +#if defined(CONFIG_NET_UDP) && !defined(CONFIG_NET_UDP_NO_STACK) + if (psock->s_type == SOCK_DGRAM) + { + FAR struct udp_conn_s *udp = psock->s_conn; + + /* Save the send buffer size */ + + udp->sndbufs = buffersize; + } + else +#endif + { + net_unlock(); + return -ENOPROTOOPT; + } + + net_unlock(); + } + break; +#endif + + default: + return -ENOPROTOOPT; + } + + return OK; +} + +/**************************************************************************** + * Name: inet_setsockopt + * + * Description: + * inet_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 inet_setsockopt(FAR struct socket *psock, int level, int option, + FAR const void *value, socklen_t value_len) +{ + switch (level) + { + case SOL_SOCKET: + return inet_set_socketlevel_option(psock, option, value, value_len); + +#ifdef CONFIG_NET_TCPPROTO_OPTIONS + case IPPROTO_TCP:/* TCP protocol socket options (see include/netinet/tcp.h) */ + return tcp_setsockopt(psock, option, value, value_len); +#endif + +#ifdef CONFIG_NET_UDPPROTO_OPTIONS + case IPPROTO_UDP:/* UDP protocol socket options (see include/netinet/udp.h) */ + return udp_setsockopt(psock, option, value, value_len); +#endif + +#ifdef CONFIG_NET_IPv4 + case IPPROTO_IP:/* TCP protocol socket options (see include/netinet/in.h) */ + return ipv4_setsockopt(psock, option, value, value_len); +#endif + +#ifdef CONFIG_NET_IPv6 + case IPPROTO_IPV6:/* TCP protocol socket options (see include/netinet/in.h) */ + return ipv6_setsockopt(psock, option, value, value_len); +#endif + default: + return -ENOPROTOOPT; + } +} + +#endif + /**************************************************************************** * Name: inet_listen * diff --git a/net/socket/getsockopt.c b/net/socket/getsockopt.c index 4c3c75878c..f2f45e73e2 100644 --- a/net/socket/getsockopt.c +++ b/net/socket/getsockopt.c @@ -33,11 +33,7 @@ #include #include "socket/socket.h" -#include "tcp/tcp.h" -#include "udp/udp.h" -#include "usrsock/usrsock.h" #include "utils/utils.h" -#include "can/can.h" /**************************************************************************** * Private Functions @@ -128,39 +124,18 @@ static int psock_socketlevel_option(FAR struct socket *psock, int option, net_dsec2timeval(timeo, (struct timeval *)value); *value_len = sizeof(struct timeval); } + break; - return OK; - } - -#ifdef CONFIG_NET_USRSOCK - if (psock->s_type == SOCK_USRSOCK_TYPE) - { - if (option == SO_TYPE) - { - FAR struct usrsock_conn_s *uconn = psock->s_conn; - - /* Return the actual socket type */ - - *(FAR int *)value = uconn->type; - *value_len = sizeof(int); - - return OK; - } - - return -ENOPROTOOPT; - } -#endif - - switch (option) - { case SO_ACCEPTCONN: /* Reports whether socket listening is enabled */ - if (*value_len < sizeof(int)) - { - return -EINVAL; - } + { + if (*value_len < sizeof(int)) + { + return -EINVAL; + } - *(FAR int *)value = _SS_ISLISTENING(conn->s_flags); - *value_len = sizeof(int); + *(FAR int *)value = _SS_ISLISTENING(conn->s_flags); + *value_len = sizeof(int); + } break; /* The following options take a point to an integer boolean value. @@ -171,10 +146,8 @@ static int psock_socketlevel_option(FAR struct socket *psock, int option, case SO_BROADCAST: /* Permits sending of broadcast messages */ case SO_DEBUG: /* Enables recording of debugging information */ case SO_DONTROUTE: /* Requests outgoing messages bypass standard routing */ -#ifndef CONFIG_NET_TCPPROTO_OPTIONS case SO_KEEPALIVE: /* Verifies TCP connections active by enabling the * periodic transmission of probes */ -#endif case SO_OOBINLINE: /* Leaves received out-of-band data inline */ case SO_REUSEADDR: /* Allow reuse of local addresses */ { @@ -201,23 +174,6 @@ static int psock_socketlevel_option(FAR struct socket *psock, int option, } break; -#ifdef CONFIG_NET_TCPPROTO_OPTIONS - /* Any connection-oriented protocol could potentially support - * SO_KEEPALIVE. However, this option is currently only available for - * TCP/IP. - * - * NOTE: SO_KEEPALIVE is not really a socket-level option; it is a - * protocol-level option. A given TCP connection may service multiple - * sockets (via dup'ing of the socket). There is, however, still only - * one connection to be monitored and that is a global attribute across - * all of the clones that may use the underlying connection. - */ - - case SO_KEEPALIVE: /* Verifies TCP connections active by enabling the - * periodic transmission of probes */ - return tcp_getsockopt(psock, option, value, value_len); -#endif - case SO_TYPE: /* Reports the socket type */ { /* Verify that option is the size of an 'int'. Should also check @@ -248,97 +204,6 @@ static int psock_socketlevel_option(FAR struct socket *psock, int option, } break; -#ifdef CONFIG_NET_TIMESTAMP - case SO_TIMESTAMP: - { - if (*value_len != sizeof(int)) - { - return -EINVAL; - } - - *(FAR int *)value = (int)conn->s_timestamp; - } - break; -#endif - -#if CONFIG_NET_RECV_BUFSIZE > 0 - case SO_RCVBUF: /* Reports receive buffer size */ - { - if (*value_len != sizeof(int)) - { - return -EINVAL; - } - -#if defined(CONFIG_NET_TCP) && !defined(CONFIG_NET_TCP_NO_STACK) - if (psock->s_type == SOCK_STREAM) - { - FAR struct tcp_conn_s *tcp; - - tcp = (FAR struct tcp_conn_s *)conn; - - *(FAR int *)value = tcp->rcv_bufs; - } - else -#endif -#if defined(CONFIG_NET_UDP) && !defined(CONFIG_NET_UDP_NO_STACK) - if (psock->s_type == SOCK_DGRAM) - { - FAR struct udp_conn_s *udp; - - udp = (FAR struct udp_conn_s *)conn; - - *(FAR int *)value = udp->rcvbufs; - } - else -#endif - { - return -ENOPROTOOPT; - } - - break; - } -#endif - -#if CONFIG_NET_SEND_BUFSIZE > 0 - case SO_SNDBUF: /* Reports send buffer size */ - { - if (*value_len != sizeof(int)) - { - return -EINVAL; - } - -#if defined(CONFIG_NET_TCP) && !defined(CONFIG_NET_TCP_NO_STACK) - if (psock->s_type == SOCK_STREAM) - { - FAR struct tcp_conn_s *tcp; - - tcp = (FAR struct tcp_conn_s *)conn; - - *(FAR int *)value = tcp->snd_bufs; - } - else -#endif -#if defined(CONFIG_NET_UDP) && !defined(CONFIG_NET_UDP_NO_STACK) - if (psock->s_type == SOCK_DGRAM) - { - FAR struct udp_conn_s *udp; - - udp = (FAR struct udp_conn_s *)conn; - - /* Save the send buffer size */ - - *(FAR int *)value = udp->sndbufs; - } - else -#endif - { - return -ENOPROTOOPT; - } - - break; - } -#endif - default: return -ENOPROTOOPT; } @@ -397,7 +262,7 @@ static int psock_socketlevel_option(FAR struct socket *psock, int option, int psock_getsockopt(FAR struct socket *psock, int level, int option, FAR void *value, FAR socklen_t *value_len) { - int ret; + int ret = -ENOPROTOOPT; /* Verify that the sockfd corresponds to valid, allocated socket */ @@ -406,49 +271,20 @@ int psock_getsockopt(FAR struct socket *psock, int level, int option, return -EBADF; } - /* Handle retrieval of the socket option according to the level at which - * option should be applied. - */ + /* Perform the socket interface operation */ - switch (level) + if (psock->s_sockif->si_getsockopt != NULL) { - case SOL_SOCKET: /* Socket-level options (see include/sys/socket.h) */ - ret = psock_socketlevel_option(psock, option, value, value_len); - break; - -#ifdef CONFIG_NET_TCPPROTO_OPTIONS - case IPPROTO_TCP: /* TCP protocol socket options (see include/netinet/tcp.h) */ - ret = tcp_getsockopt(psock, option, value, value_len); - break; -#endif - -#ifdef CONFIG_NET_CANPROTO_OPTIONS - case SOL_CAN_RAW:/* CAN protocol socket options (see include/netpacket/can.h) */ - ret = can_getsockopt(psock, option, value, value_len); - break; -#endif - - /* These levels are defined in sys/socket.h, but are not yet - * implemented. - */ - - case IPPROTO_IP: /* TCP protocol socket options (see include/netinet/ip.h) */ - case IPPROTO_IPV6: /* TCP protocol socket options (see include/netinet/ip6.h) */ - case IPPROTO_UDP: /* TCP protocol socket options (see include/netinit/udp.h) */ - default: /* The provided level is invalid */ - ret = -ENOPROTOOPT; - break; + ret = psock->s_sockif->si_getsockopt(psock, level, option, + value, value_len); } -#ifdef CONFIG_NET_USRSOCK - /* Try usrsock further if the protocol not available */ + /* Try socket level if the socket interface operation is not available */ - if (ret == -ENOPROTOOPT && psock->s_type == SOCK_USRSOCK_TYPE) + if (ret == -ENOPROTOOPT && level == SOL_SOCKET) { - ret = usrsock_getsockopt(psock->s_conn, level, - option, value, value_len); + ret = psock_socketlevel_option(psock, option, value, value_len); } -#endif return ret; } diff --git a/net/socket/setsockopt.c b/net/socket/setsockopt.c index ba72e84696..a7ede25744 100644 --- a/net/socket/setsockopt.c +++ b/net/socket/setsockopt.c @@ -34,15 +34,11 @@ #include #include +#include #include #include "socket/socket.h" -#include "inet/inet.h" -#include "tcp/tcp.h" -#include "udp/udp.h" -#include "usrsock/usrsock.h" #include "utils/utils.h" -#include "can/can.h" /**************************************************************************** * Public Functions @@ -134,27 +130,14 @@ static int psock_socketlevel_option(FAR struct socket *psock, int option, { _SO_SETOPT(conn->s_options, option); } - - return OK; } - } + break; -#ifdef CONFIG_NET_USRSOCK - if (psock->s_type == SOCK_USRSOCK_TYPE) - { - return -ENOPROTOOPT; - } -#endif - - switch (option) - { case SO_BROADCAST: /* Permits sending of broadcast messages */ case SO_DEBUG: /* Enables recording of debugging information */ case SO_DONTROUTE: /* Requests outgoing messages bypass standard routing */ -#ifndef CONFIG_NET_TCPPROTO_OPTIONS case SO_KEEPALIVE: /* Verifies TCP connections active by enabling the * periodic transmission of probes */ -#endif case SO_OOBINLINE: /* Leaves received out-of-band data inline */ case SO_REUSEADDR: /* Allow reuse of local addresses */ { @@ -194,212 +177,6 @@ static int psock_socketlevel_option(FAR struct socket *psock, int option, } break; -#ifdef CONFIG_NET_TCPPROTO_OPTIONS - /* Any connection-oriented protocol could potentially support - * SO_KEEPALIVE. However, this option is currently only available for - * TCP/IP. - * - * NOTE: SO_KEEPALIVE is not really a socket-level option; it is a - * protocol-level option. A given TCP connection may service multiple - * sockets (via dup'ing of the socket). There is, however, still only - * one connection to be monitored and that is a global attribute across - * all of the clones that may use the underlying connection. - */ - - case SO_KEEPALIVE: /* Verifies TCP connections active by enabling the - * periodic transmission of probes */ - return tcp_setsockopt(psock, option, value, value_len); -#endif - -#ifdef CONFIG_NET_SOLINGER - case SO_LINGER: /* Lingers on a close() if data is present */ - { - FAR struct linger *setting; - - /* Verify that option is at least the size of an 'struct linger'. */ - - if (value_len < sizeof(FAR struct linger)) - { - return -EINVAL; - } - - /* Get the value. Is the option being set or cleared? */ - - setting = (FAR struct linger *)value; - - /* Lock the network so that we have exclusive access to the socket - * options. - */ - - net_lock(); - - /* Set or clear the linger option bit and linger time - * (in deciseconds) - */ - - if (setting->l_onoff) - { - _SO_SETOPT(conn->s_options, option); - conn->s_linger = 10 * setting->l_linger; - } - else - { - _SO_CLROPT(conn->s_options, option); - conn->s_linger = 0; - } - - net_unlock(); - } - break; -#endif - -#ifdef CONFIG_NET_TIMESTAMP - case SO_TIMESTAMP: /* Generates a timestamp for each incoming packet */ - { - /* Verify that option is at least the size of an integer. */ - - if (value_len < sizeof(FAR int32_t)) - { - return -EINVAL; - } - - /* Lock the network so that we have exclusive access to the socket - * options. - */ - - net_lock(); - - conn->s_timestamp = *((FAR int32_t *)value); - - net_unlock(); - } - break; -#endif - -#if CONFIG_NET_RECV_BUFSIZE > 0 - case SO_RCVBUF: /* Sets receive buffer size */ - { - int buffersize; - - /* Verify that option is the size of an 'int'. Should also check - * that 'value' is properly aligned for an 'int' - */ - - if (value_len != sizeof(int)) - { - return -EINVAL; - } - - /* Get the value. Is the option being set or cleared? */ - - buffersize = *(FAR int *)value; - - if (buffersize < 0) - { - return -EINVAL; - } - - net_lock(); - -#if defined(CONFIG_NET_TCP) && !defined(CONFIG_NET_TCP_NO_STACK) - if (psock->s_type == SOCK_STREAM) - { - FAR struct tcp_conn_s *tcp; - - tcp = (FAR struct tcp_conn_s *)conn; - - /* Save the receive buffer size */ - - tcp->rcv_bufs = buffersize; - } - else -#endif -#if defined(CONFIG_NET_UDP) && !defined(CONFIG_NET_UDP_NO_STACK) - if (psock->s_type == SOCK_DGRAM) - { - FAR struct udp_conn_s *udp; - - udp = (FAR struct udp_conn_s *)conn; - - /* Save the receive buffer size */ - - udp->rcvbufs = buffersize; - } - else -#endif - { - net_unlock(); - return -ENOPROTOOPT; - } - - net_unlock(); - - break; - } -#endif - -#if CONFIG_NET_SEND_BUFSIZE > 0 - case SO_SNDBUF: /* Sets send buffer size */ - { - int buffersize; - - /* Verify that option is the size of an 'int'. Should also check - * that 'value' is properly aligned for an 'int' - */ - - if (value_len != sizeof(int)) - { - return -EINVAL; - } - - /* Get the value. Is the option being set or cleared? */ - - buffersize = *(FAR int *)value; - - if (buffersize < 0) - { - return -EINVAL; - } - - net_lock(); - -#if defined(CONFIG_NET_TCP) && !defined(CONFIG_NET_TCP_NO_STACK) - if (psock->s_type == SOCK_STREAM) - { - FAR struct tcp_conn_s *tcp; - - tcp = (FAR struct tcp_conn_s *)conn; - - /* Save the send buffer size */ - - tcp->snd_bufs = buffersize; - } - else -#endif -#if defined(CONFIG_NET_UDP) && !defined(CONFIG_NET_UDP_NO_STACK) - if (psock->s_type == SOCK_DGRAM) - { - FAR struct udp_conn_s *udp; - - udp = (FAR struct udp_conn_s *)conn; - - /* Save the send buffer size */ - - udp->sndbufs = buffersize; - } - else -#endif - { - net_unlock(); - return -ENOPROTOOPT; - } - - net_unlock(); - - break; - } -#endif - #ifdef CONFIG_NET_BINDTODEVICE /* Handle the SO_BINDTODEVICE socket-level option. * @@ -516,7 +293,7 @@ static int psock_socketlevel_option(FAR struct socket *psock, int option, int psock_setsockopt(FAR struct socket *psock, int level, int option, FAR const void *value, socklen_t value_len) { - int ret; + int ret = -ENOPROTOOPT; /* Verify that the sockfd corresponds to valid, allocated socket */ @@ -525,60 +302,20 @@ int psock_setsockopt(FAR struct socket *psock, int level, int option, return -EBADF; } - /* Handle setting of the socket option according to the level at which - * option should be applied. - */ + /* Perform the socket interface operation */ - switch (level) + if (psock->s_sockif->si_setsockopt != NULL) { - case SOL_SOCKET: /* Socket-level options (see include/sys/socket.h) */ - ret = psock_socketlevel_option(psock, option, value, value_len); - break; - -#ifdef CONFIG_NET_TCPPROTO_OPTIONS - case IPPROTO_TCP:/* TCP protocol socket options (see include/netinet/tcp.h) */ - ret = tcp_setsockopt(psock, option, value, value_len); - break; -#endif - -#ifdef CONFIG_NET_UDPPROTO_OPTIONS - case IPPROTO_UDP:/* UDP protocol socket options (see include/netinet/udp.h) */ - ret = udp_setsockopt(psock, option, value, value_len); - break; -#endif - -#ifdef CONFIG_NET_IPv4 - case IPPROTO_IP:/* TCP protocol socket options (see include/netinet/in.h) */ - ret = ipv4_setsockopt(psock, option, value, value_len); - break; -#endif - -#ifdef CONFIG_NET_IPv6 - case IPPROTO_IPV6:/* TCP protocol socket options (see include/netinet/in.h) */ - ret = ipv6_setsockopt(psock, option, value, value_len); - break; -#endif - -#ifdef CONFIG_NET_CANPROTO_OPTIONS - case SOL_CAN_RAW: /* CAN protocol socket options (see include/netpacket/can.h) */ - ret = can_setsockopt(psock, option, value, value_len); - break; -#endif - - default: /* The provided level is invalid */ - ret = -ENOPROTOOPT; - break; + ret = psock->s_sockif->si_setsockopt(psock, level, option, + value, value_len); } -#ifdef CONFIG_NET_USRSOCK - /* Try usrsock further if the protocol not available */ + /* Try socket level if the socket interface operation is not available */ - if (ret == -ENOPROTOOPT && psock->s_type == SOCK_USRSOCK_TYPE) + if (ret == -ENOPROTOOPT && level == SOL_SOCKET) { - ret = usrsock_setsockopt(psock->s_conn, level, - option, value, value_len); + ret = psock_socketlevel_option(psock, option, value, value_len); } -#endif return ret; } diff --git a/net/usrsock/usrsock.h b/net/usrsock/usrsock.h index 061afc3b10..7cbb0fd7dd 100644 --- a/net/usrsock/usrsock.h +++ b/net/usrsock/usrsock.h @@ -546,7 +546,7 @@ ssize_t usrsock_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg, * See a complete list of values for the 'option' argument. * * Input Parameters: - * conn usrsock socket connection structure + * 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 @@ -554,9 +554,8 @@ ssize_t usrsock_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg, * ****************************************************************************/ -int usrsock_getsockopt(FAR struct usrsock_conn_s *conn, int level, - int option, FAR void *value, - FAR socklen_t *value_len); +int usrsock_getsockopt(FAR struct socket *psock, int level, int option, + FAR void *value, FAR socklen_t *value_len); /**************************************************************************** * Name: usrsock_setsockopt @@ -572,7 +571,7 @@ int usrsock_getsockopt(FAR struct usrsock_conn_s *conn, int level, * See a complete list of values for the 'option' argument. * * Input Parameters: - * conn usrsock socket connection structure + * 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 @@ -580,9 +579,8 @@ int usrsock_getsockopt(FAR struct usrsock_conn_s *conn, int level, * ****************************************************************************/ -int usrsock_setsockopt(FAR struct usrsock_conn_s *conn, int level, - int option, FAR const void *value, - FAR socklen_t value_len); +int usrsock_setsockopt(FAR struct socket *psock, int level, int option, + FAR const void *value, socklen_t value_len); /**************************************************************************** * Name: usrsock_getsockname diff --git a/net/usrsock/usrsock_getsockopt.c b/net/usrsock/usrsock_getsockopt.c index 0b8b25e39e..07f57a26e8 100644 --- a/net/usrsock/usrsock_getsockopt.c +++ b/net/usrsock/usrsock_getsockopt.c @@ -160,7 +160,7 @@ static int do_getsockopt_request(FAR struct usrsock_conn_s *conn, int level, * See a complete list of values for the 'option' argument. * * Input Parameters: - * conn usrsock socket connection structure + * 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 @@ -168,10 +168,10 @@ static int do_getsockopt_request(FAR struct usrsock_conn_s *conn, int level, * ****************************************************************************/ -int usrsock_getsockopt(FAR struct usrsock_conn_s *conn, - int level, int option, +int usrsock_getsockopt(FAR struct socket *psock, int level, int option, FAR void *value, FAR socklen_t *value_len) { + FAR struct usrsock_conn_s *conn = psock->s_conn; struct usrsock_data_reqstate_s state = { }; @@ -179,6 +179,22 @@ int usrsock_getsockopt(FAR struct usrsock_conn_s *conn, struct iovec inbufs[1]; int ret; + if (level == SOL_SOCKET) + { + if (option == SO_TYPE) + { + /* Return the actual socket type */ + + *(FAR int *)value = conn->type; + *value_len = sizeof(int); + return OK; + } + else + { + return -ENOPROTOOPT; + } + } + net_lock(); if (conn->state == USRSOCK_CONN_STATE_UNINITIALIZED || diff --git a/net/usrsock/usrsock_setsockopt.c b/net/usrsock/usrsock_setsockopt.c index 70302f6c98..922da98e27 100644 --- a/net/usrsock/usrsock_setsockopt.c +++ b/net/usrsock/usrsock_setsockopt.c @@ -149,7 +149,7 @@ static int do_setsockopt_request(FAR struct usrsock_conn_s *conn, * See a complete list of values for the 'option' argument. * * Input Parameters: - * conn usrsock socket connection structure + * 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 @@ -157,10 +157,10 @@ static int do_setsockopt_request(FAR struct usrsock_conn_s *conn, * ****************************************************************************/ -int usrsock_setsockopt(FAR struct usrsock_conn_s *conn, - int level, int option, - FAR const void *value, FAR socklen_t value_len) +int usrsock_setsockopt(FAR struct socket *psock, int level, int option, + FAR const void *value, socklen_t value_len) { + FAR struct usrsock_conn_s *conn = psock->s_conn; struct usrsock_reqstate_s state = { }; @@ -168,6 +168,18 @@ int usrsock_setsockopt(FAR struct usrsock_conn_s *conn, int ret; DEBUGASSERT(conn); + if (level == SOL_SOCKET) + { + if (option == SO_RCVTIMEO || option == SO_SNDTIMEO) + { + return -ENOPROTOOPT; + } + else + { + return -EINVAL; + } + } + net_lock(); if (conn->state == USRSOCK_CONN_STATE_UNINITIALIZED || diff --git a/net/usrsock/usrsock_sockif.c b/net/usrsock/usrsock_sockif.c index 5791ec6e68..6f83ca30fa 100644 --- a/net/usrsock/usrsock_sockif.c +++ b/net/usrsock/usrsock_sockif.c @@ -66,7 +66,12 @@ const struct sock_intf_s g_usrsock_sockif = usrsock_sendmsg, /* si_sendmsg */ usrsock_recvmsg, /* si_recvmsg */ usrsock_sockif_close, /* si_close */ - usrsock_ioctl /* si_ioctl */ + usrsock_ioctl, /* si_ioctl */ + NULL /* si_socketpair */ +#ifdef CONFIG_NET_SOCKOPTS + , usrsock_getsockopt /* si_getsockopt */ + , usrsock_setsockopt /* si_setsockopt */ +#endif }; /****************************************************************************