From 8819eeaf15e777626338411f0072afda582aa297 Mon Sep 17 00:00:00 2001 From: Zhe Weng Date: Wed, 18 Jan 2023 18:28:32 +0800 Subject: [PATCH] net: Implement shutdown() interface and tcp shutdown Signed-off-by: Zhe Weng --- include/nuttx/net/net.h | 1 + include/sys/syscall_lookup.h | 1 + libs/libc/net/Make.defs | 2 +- libs/libc/net/lib_shutdown.c | 74 --------------- net/can/can_sockif.c | 3 +- net/inet/inet_sockif.c | 39 +++++++- net/local/local_sockif.c | 33 ++++++- net/socket/Make.defs | 2 +- net/socket/shutdown.c | 146 ++++++++++++++++++++++++++++++ net/tcp/Make.defs | 2 +- net/tcp/tcp.h | 20 ++++- net/tcp/tcp_shutdown.c | 168 +++++++++++++++++++++++++++++++++++ net/usrsock/usrsock_sockif.c | 3 +- syscall/syscall.csv | 1 + 14 files changed, 413 insertions(+), 82 deletions(-) delete mode 100644 libs/libc/net/lib_shutdown.c create mode 100644 net/socket/shutdown.c create mode 100644 net/tcp/tcp_shutdown.c diff --git a/include/nuttx/net/net.h b/include/nuttx/net/net.h index 01182b4a4a..84d19840e8 100644 --- a/include/nuttx/net/net.h +++ b/include/nuttx/net/net.h @@ -167,6 +167,7 @@ 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]); + CODE int (*si_shutdown)(FAR struct socket *psock, int how); #ifdef CONFIG_NET_SOCKOPTS CODE int (*si_getsockopt)(FAR struct socket *psock, int level, int option, FAR void *value, FAR socklen_t *value_len); diff --git a/include/sys/syscall_lookup.h b/include/sys/syscall_lookup.h index b0a690d9ca..12a75bc014 100644 --- a/include/sys/syscall_lookup.h +++ b/include/sys/syscall_lookup.h @@ -366,6 +366,7 @@ SYSCALL_LOOKUP(munmap, 2) SYSCALL_LOOKUP(sendto, 6) SYSCALL_LOOKUP(sendmsg, 3) SYSCALL_LOOKUP(setsockopt, 5) + SYSCALL_LOOKUP(shutdown, 2) SYSCALL_LOOKUP(socket, 3) SYSCALL_LOOKUP(socketpair, 4) #endif diff --git a/libs/libc/net/Make.defs b/libs/libc/net/Make.defs index 7a98c7b519..52b0df6128 100644 --- a/libs/libc/net/Make.defs +++ b/libs/libc/net/Make.defs @@ -26,7 +26,7 @@ CSRCS += lib_inetntop.c lib_inetpton.c lib_inetnetwork.c CSRCS += lib_etherntoa.c lib_etheraton.c ifeq ($(CONFIG_NET),y) -CSRCS += lib_accept.c lib_shutdown.c +CSRCS += lib_accept.c endif ifeq ($(CONFIG_NET_LOOPBACK),y) diff --git a/libs/libc/net/lib_shutdown.c b/libs/libc/net/lib_shutdown.c deleted file mode 100644 index bdc55fa99b..0000000000 --- a/libs/libc/net/lib_shutdown.c +++ /dev/null @@ -1,74 +0,0 @@ -/**************************************************************************** - * libs/libc/net/lib_shutdown.c - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - ****************************************************************************/ - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include - -#include - -#ifdef CONFIG_NET - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: shutdown - * - * Description: - * The shutdown() function will cause all or part of a full-duplex - * connection on the socket associated with the file descriptor socket to - * be shut down. - * - * The shutdown() function disables subsequent send and/or receive - * operations on a socket, depending on the value of the how argument. - * - * Input Parameters: - * sockfd - Specifies the file descriptor of the socket. - * how - Specifies the type of shutdown. The values are as follows: - * - * SHUT_RD - Disables further receive operations. - * SHUT_WR - Disables further send operations. - * SHUT_RDWR - Disables further send and receive operations. - * - * Returned Value: - * Upon successful completion, shutdown() will return 0; otherwise, -1 will - * be returned and errno set to indicate the error. - * - * EBADF - The socket argument is not a valid file descriptor. - * EINVAL - The how argument is invalid. - * ENOTCONN - The socket is not connected. - * ENOTSOCK - The socket argument does not refer to a socket. - * ENOBUFS - Insufficient resources were available in the system to - * perform the operation. - * - ****************************************************************************/ - -int shutdown(int sockfd, int how) -{ - /* REVISIT: Not implemented. */ - - return OK; -} - -#endif /* CONFIG_NET */ diff --git a/net/can/can_sockif.c b/net/can/can_sockif.c index 3fb1623a2f..9855e3b5fd 100644 --- a/net/can/can_sockif.c +++ b/net/can/can_sockif.c @@ -86,7 +86,8 @@ const struct sock_intf_s g_can_sockif = can_recvmsg, /* si_recvmsg */ can_close, /* si_close */ NULL, /* si_ioctl */ - NULL /* si_socketpair */ + NULL, /* si_socketpair */ + NULL /* si_shutdown */ #if defined(CONFIG_NET_SOCKOPTS) && defined(CONFIG_NET_CANPROTO_OPTIONS) , can_getsockopt /* si_getsockopt */ , can_setsockopt /* si_setsockopt */ diff --git a/net/inet/inet_sockif.c b/net/inet/inet_sockif.c index 8a71c93f71..61843db855 100644 --- a/net/inet/inet_sockif.c +++ b/net/inet/inet_sockif.c @@ -91,6 +91,7 @@ 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]); +static int inet_shutdown(FAR struct socket *psock, int how); #ifdef CONFIG_NET_SOCKOPTS static int inet_getsockopt(FAR struct socket *psock, int level, int option, FAR void *value, FAR socklen_t *value_len); @@ -123,7 +124,8 @@ static const struct sock_intf_s g_inet_sockif = inet_recvmsg, /* si_recvmsg */ inet_close, /* si_close */ inet_ioctl, /* si_ioctl */ - inet_socketpair /* si_socketpair */ + inet_socketpair, /* si_socketpair */ + inet_shutdown /* si_shutdown */ #ifdef CONFIG_NET_SOCKOPTS , inet_getsockopt /* si_getsockopt */ , inet_setsockopt /* si_setsockopt */ @@ -1980,6 +1982,41 @@ errout: #endif /* CONFIG_NET_TCP || CONFIG_NET_UDP */ } +/**************************************************************************** + * Name: inet_shutdown + * + * Description: + * Performs the shutdown operation on an AF_INET or AF_INET6 socket + * + * Input Parameters: + * psock Socket instance + * how Specifies the type of shutdown + * + * Returned Value: + * 0: Success; Negated errno on failure + * + ****************************************************************************/ + +static int inet_shutdown(FAR struct socket *psock, int how) +{ + switch (psock->s_type) + { +#ifdef CONFIG_NET_TCP + case SOCK_STREAM: +#ifdef NET_TCP_HAVE_STACK + return tcp_shutdown(psock, how); +#else + nwarn("WARNING: SOCK_STREAM support is not available in this " + "configuration\n"); + return -EAFNOSUPPORT; +#endif /* NET_TCP_HAVE_STACK */ +#endif /* CONFIG_NET_TCP */ + + default: + return -EOPNOTSUPP; + } +} + /**************************************************************************** * Name: inet_sendfile * diff --git a/net/local/local_sockif.c b/net/local/local_sockif.c index ff6fd18c9e..5131f8ba54 100644 --- a/net/local/local_sockif.c +++ b/net/local/local_sockif.c @@ -72,6 +72,7 @@ static int local_close(FAR struct socket *psock); static int local_ioctl(FAR struct socket *psock, int cmd, unsigned long arg); static int local_socketpair(FAR struct socket *psocks[2]); +static int local_shutdown(FAR struct socket *psock, int how); #ifdef CONFIG_NET_SOCKOPTS static int local_getsockopt(FAR struct socket *psock, int level, int option, FAR void *value, FAR socklen_t *value_len); @@ -99,7 +100,8 @@ const struct sock_intf_s g_local_sockif = local_recvmsg, /* si_recvmsg */ local_close, /* si_close */ local_ioctl, /* si_ioctl */ - local_socketpair /* si_socketpair */ + local_socketpair, /* si_socketpair */ + local_shutdown /* si_shutdown */ #ifdef CONFIG_NET_SOCKOPTS , local_getsockopt /* si_getsockopt */ , local_setsockopt /* si_setsockopt */ @@ -958,6 +960,35 @@ errout: #endif /* CONFIG_NET_LOCAL_STREAM || CONFIG_NET_LOCAL_DGRAM */ } +/**************************************************************************** + * Name: local_shutdown + * + * Description: + * The shutdown() function will cause all or part of a full-duplex + * connection on the socket associated with the file descriptor socket to + * be shut down. + * + * The shutdown() function disables subsequent send and/or receive + * operations on a socket, depending on the value of the how argument. + * + * Input Parameters: + * sockfd - Specifies the file descriptor of the socket. + * how - Specifies the type of shutdown. The values are as follows: + * + * SHUT_RD - Disables further receive operations. + * SHUT_WR - Disables further send operations. + * SHUT_RDWR - Disables further send and receive operations. + * + ****************************************************************************/ + +static int local_shutdown(FAR struct socket *psock, int how) +{ + /* TODO: implement this. */ + + nerr("ERROR: local_shutdown is not implemented"); + return -ENOTSUP; +} + /**************************************************************************** * Public Functions ****************************************************************************/ diff --git a/net/socket/Make.defs b/net/socket/Make.defs index 2f0e351189..ca28a4edc7 100644 --- a/net/socket/Make.defs +++ b/net/socket/Make.defs @@ -22,7 +22,7 @@ SOCK_CSRCS += accept.c bind.c connect.c getsockname.c getpeername.c SOCK_CSRCS += listen.c recv.c recvfrom.c send.c sendto.c socket.c -SOCK_CSRCS += socketpair.c net_close.c recvmsg.c sendmsg.c +SOCK_CSRCS += socketpair.c net_close.c recvmsg.c sendmsg.c shutdown.c SOCK_CSRCS += net_dup2.c net_sockif.c net_poll.c net_fstat.c # Socket options diff --git a/net/socket/shutdown.c b/net/socket/shutdown.c new file mode 100644 index 0000000000..74e536c42a --- /dev/null +++ b/net/socket/shutdown.c @@ -0,0 +1,146 @@ +/**************************************************************************** + * net/socket/shutdown.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include + +#include "socket/socket.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: psock_shutdown + * + * Description: + * The shutdown() function will cause all or part of a full-duplex + * connection on the socket associated with the file descriptor socket to + * be shut down. + * + * The shutdown() function disables subsequent send and/or receive + * operations on a socket, depending on the value of the how argument. + * + * Input Parameters: + * sockfd - Specifies the file descriptor of the socket. + * how - Specifies the type of shutdown. The values are as follows: + * + * SHUT_RD - Disables further receive operations. + * SHUT_WR - Disables further send operations. + * SHUT_RDWR - Disables further send and receive operations. + * + * Returned Value: + * EINVAL - The how argument is invalid. + * ENOTCONN - The socket is not connected. + * ENOTSOCK - The socket argument does not refer to a socket. + * ENOBUFS - Insufficient resources were available in the system to + * perform the operation. + * EOPNOTSUPP - The operation is not supported for this socket's protocol. + * + ****************************************************************************/ + +int psock_shutdown(FAR struct socket *psock, int how) +{ + /* Verify that the psock corresponds to valid, allocated socket */ + + if (psock == NULL || psock->s_conn == NULL) + { + return -EBADF; + } + + /* Let the address family's shutdown() method handle the operation */ + + if (psock->s_sockif && psock->s_sockif->si_shutdown) + { + return psock->s_sockif->si_shutdown(psock, how); + } + + return -EOPNOTSUPP; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: shutdown + * + * Description: + * The shutdown() function will cause all or part of a full-duplex + * connection on the socket associated with the file descriptor socket to + * be shut down. + * + * The shutdown() function disables subsequent send and/or receive + * operations on a socket, depending on the value of the how argument. + * + * Input Parameters: + * sockfd - Specifies the file descriptor of the socket. + * how - Specifies the type of shutdown. The values are as follows: + * + * SHUT_RD - Disables further receive operations. + * SHUT_WR - Disables further send operations. + * SHUT_RDWR - Disables further send and receive operations. + * + * Returned Value: + * Upon successful completion, shutdown() will return 0; otherwise, -1 will + * be returned and errno set to indicate the error. + * + * EBADF - The socket argument is not a valid file descriptor. + * EINVAL - The how argument is invalid. + * ENOTCONN - The socket is not connected. + * ENOTSOCK - The socket argument does not refer to a socket. + * ENOBUFS - Insufficient resources were available in the system to + * perform the operation. + * EOPNOTSUPP - The operation is not supported for this socket's protocol + * + ****************************************************************************/ + +int shutdown(int sockfd, int how) +{ + FAR struct socket *psock; + int ret; + + /* Get the underlying socket structure */ + + ret = sockfd_socket(sockfd, &psock); + + /* Then let psock_shutdown() do all of the work */ + + if (ret == OK) + { + ret = psock_shutdown(psock, how); + } + + if (ret < 0) + { + _SO_SETERRNO(psock, -ret); + ret = ERROR; + } + + return ret; +} diff --git a/net/tcp/Make.defs b/net/tcp/Make.defs index e5e71cb208..faa03db524 100644 --- a/net/tcp/Make.defs +++ b/net/tcp/Make.defs @@ -53,7 +53,7 @@ endif NET_CSRCS += tcp_conn.c tcp_seqno.c tcp_devpoll.c tcp_finddev.c tcp_timer.c NET_CSRCS += tcp_send.c tcp_input.c tcp_appsend.c tcp_listen.c tcp_close.c NET_CSRCS += tcp_monitor.c tcp_callback.c tcp_backlog.c tcp_ipselect.c -NET_CSRCS += tcp_recvwindow.c tcp_netpoll.c tcp_ioctl.c +NET_CSRCS += tcp_recvwindow.c tcp_netpoll.c tcp_ioctl.c tcp_shutdown.c # TCP write buffering diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h index 188e46c2b8..11bc4c4f6e 100644 --- a/net/tcp/tcp.h +++ b/net/tcp/tcp.h @@ -344,8 +344,9 @@ struct tcp_conn_s FAR struct devif_callback_s *connevents; FAR struct devif_callback_s *connevents_tail; - /* Reference to TCP close callback instance */ + /* Reference to TCP shutdown/close callback instance */ + FAR struct devif_callback_s *shdcb; FAR struct devif_callback_s *clscb; struct work_s clswork; @@ -767,6 +768,23 @@ void tcp_lost_connection(FAR struct tcp_conn_s *conn, int tcp_close(FAR struct socket *psock); +/**************************************************************************** + * Name: tcp_shutdown + * + * Description: + * Gracefully shutdown a TCP connection by sending a SYN + * + * Input Parameters: + * psock - An instance of the internal socket structure. + * how - Specifies the type of shutdown. + * + * Assumptions: + * Called from normal user-level logic + * + ****************************************************************************/ + +int tcp_shutdown(FAR struct socket *psock, int how); + /**************************************************************************** * Name: tcp_ipv4_select * diff --git a/net/tcp/tcp_shutdown.c b/net/tcp/tcp_shutdown.c new file mode 100644 index 0000000000..c96d62dc23 --- /dev/null +++ b/net/tcp/tcp_shutdown.c @@ -0,0 +1,168 @@ +/**************************************************************************** + * net/tcp/tcp_shutdown.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#ifdef CONFIG_NET_TCP + +#include +#include +#include + +#include +#include +#include + +#include "netdev/netdev.h" +#include "devif/devif.h" +#include "tcp/tcp.h" +#include "socket/socket.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: tcp_shutdown_eventhandler + ****************************************************************************/ + +static uint16_t tcp_shutdown_eventhandler(FAR struct net_driver_s *dev, + FAR void *pvpriv, uint16_t flags) +{ + FAR struct tcp_conn_s *conn = pvpriv; + + ninfo("flags: %04x\n", flags); + +#ifdef CONFIG_NET_TCP_WRITE_BUFFERS + /* We don't need the send callback anymore. */ + + if (conn->sndcb != NULL) + { + conn->sndcb->flags = 0; + conn->sndcb->event = NULL; + + /* The callback will be freed by tcp_free. */ + + conn->sndcb = NULL; + } +#endif + + dev->d_len = 0; + flags = (flags & ~TCP_NEWDATA) | TCP_CLOSE; + + if (conn->shdcb != NULL) + { + tcp_callback_free(conn, conn->shdcb); + conn->shdcb = NULL; + } + + return flags; +} + +/**************************************************************************** + * Name: tcp_send_fin + * + * Description: + * Send a FIN for TCP connection + * + * Input Parameters: + * conn - TCP connection structure + * + * Returned Value: + * None + * + * Assumptions: + * Called from normal user-level logic + * + ****************************************************************************/ + +static inline int tcp_send_fin(FAR struct socket *psock) +{ + FAR struct tcp_conn_s *conn; + int ret = OK; + + /* Interrupts are disabled here to avoid race conditions */ + + net_lock(); + + conn = (FAR struct tcp_conn_s *)psock->s_conn; + DEBUGASSERT(conn != NULL); + + if ((conn->tcpstateflags == TCP_ESTABLISHED || + conn->tcpstateflags == TCP_SYN_SENT || + conn->tcpstateflags == TCP_SYN_RCVD)) + { + if ((conn->shdcb = tcp_callback_alloc(conn)) == NULL) + { + ret = -EBUSY; + goto out; + } + + /* Set up to receive TCP data event callbacks */ + + conn->shdcb->flags = TCP_POLL; + conn->shdcb->event = tcp_shutdown_eventhandler; + conn->shdcb->priv = conn; /* reference for event handler to free cb */ + + /* Notify the device driver of the availability of TX data */ + + tcp_send_txnotify(psock, conn); + } + +out: + net_unlock(); + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: tcp_shutdown + * + * Description: + * Gracefully shutdown a TCP connection by sending a FIN + * + * Input Parameters: + * psock - An instance of the internal socket structure. + * how - Specifies the type of shutdown. + * + * Assumptions: + * Called from normal user-level logic + * + ****************************************************************************/ + +int tcp_shutdown(FAR struct socket *psock, int how) +{ + if (!(how & SHUT_WR)) + { + return -EOPNOTSUPP; + } + + tcp_unlisten(psock->s_conn); /* No longer accepting connections */ + + return tcp_send_fin(psock); +} + +#endif /* CONFIG_NET_TCP */ diff --git a/net/usrsock/usrsock_sockif.c b/net/usrsock/usrsock_sockif.c index d5810a5c7c..7eb32f01a1 100644 --- a/net/usrsock/usrsock_sockif.c +++ b/net/usrsock/usrsock_sockif.c @@ -67,7 +67,8 @@ const struct sock_intf_s g_usrsock_sockif = usrsock_recvmsg, /* si_recvmsg */ usrsock_sockif_close, /* si_close */ usrsock_ioctl, /* si_ioctl */ - NULL /* si_socketpair */ + NULL, /* si_socketpair */ + NULL /* si_shutdown */ #ifdef CONFIG_NET_SOCKOPTS , usrsock_getsockopt /* si_getsockopt */ , usrsock_setsockopt /* si_setsockopt */ diff --git a/syscall/syscall.csv b/syscall/syscall.csv index 6d93154b39..eb054b0fec 100644 --- a/syscall/syscall.csv +++ b/syscall/syscall.csv @@ -152,6 +152,7 @@ "shmctl","sys/shm.h","defined(CONFIG_MM_SHM)","int","int","int","FAR struct shmid_ds *" "shmdt","sys/shm.h","defined(CONFIG_MM_SHM)","int","FAR const void *" "shmget","sys/shm.h","defined(CONFIG_MM_SHM)","int","key_t","size_t","int" +"shutdown","sys/socket.h","defined(CONFIG_NET)","int","int","int" "sigaction","signal.h","","int","int","FAR const struct sigaction *","FAR struct sigaction *" "sigpending","signal.h","","int","FAR sigset_t *" "sigprocmask","signal.h","","int","int","FAR const sigset_t *","FAR sigset_t *"