diff --git a/include/nuttx/net/net.h b/include/nuttx/net/net.h index 8553e1e92bf..93a6deadc88 100644 --- a/include/nuttx/net/net.h +++ b/include/nuttx/net/net.h @@ -118,6 +118,7 @@ struct sock_intf_s CODE ssize_t (*si_recvfrom)(FAR struct socket *psock, FAR void *buf, size_t len, int flags, FAR struct sockaddr *from, FAR socklen_t *fromlen); + CODE int (*si_close)(FAR struct socket *psock); }; /* This is the internal representation of a socket reference by a file diff --git a/net/local/local_sockif.c b/net/local/local_sockif.c index 68f93e7df09..1b8d42b6408 100644 --- a/net/local/local_sockif.c +++ b/net/local/local_sockif.c @@ -69,6 +69,7 @@ static ssize_t local_send(FAR struct socket *psock, FAR const void *buf, static ssize_t local_sendto(FAR struct socket *psock, FAR const void *buf, size_t len, int flags, FAR const struct sockaddr *to, socklen_t tolen); +static int local_close(FAR struct socket *psock); /**************************************************************************** * Public Data @@ -83,7 +84,8 @@ const struct sock_intf_s g_local_sockif = local_accept, /* si_accept */ local_send, /* si_send */ local_sendto, /* si_sendto */ - local_recvfrom /* si_recvfrom */ + local_recvfrom, /* si_recvfrom */ + local_close /* si_close */ }; /**************************************************************************** @@ -444,6 +446,63 @@ ssize_t local_sendto(FAR struct socket *psock, FAR const void *buf, return nsent; } +/**************************************************************************** + * Name: local_close + * + * Description: + * Performs the close operation on a local, Unix socket instance + * + * Parameters: + * psock Socket instance + * + * Returned Value: + * 0 on success; a negated errno value is returned on any failure. + * + * Assumptions: + * + ****************************************************************************/ + +static int local_close(FAR struct socket *psock) +{ + /* Perform some pre-close operations for the local address type */ + + switch (psock->s_type) + { +#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_UDP) +#ifdef CONFIG_NET_TCP + case SOCK_STREAM: +#endif +#ifdef CONFIG_NET_UDP + case SOCK_DGRAM: +#endif + { + FAR struct local_conn_s *conn = psock->s_conn; + + /* Is this the last reference to the connection structure (there + * could be more if the socket was dup'ed). + */ + + if (conn->lc_crefs <= 1) + { + conn->lc_crefs = 0; + local_release(conn); + } + else + { + /* No.. Just decrement the reference count */ + + conn->lc_crefs--; + } + + return OK; + } +#endif /* CONFIG_NET_TCP || CONFIG_NET_UDP*/ + + default: + return -EBADF; + } +} + /**************************************************************************** * Public Functions ****************************************************************************/ diff --git a/net/pkt/pkt_sockif.c b/net/pkt/pkt_sockif.c index c110b578f64..ded05d6ea89 100644 --- a/net/pkt/pkt_sockif.c +++ b/net/pkt/pkt_sockif.c @@ -68,6 +68,7 @@ static ssize_t pkt_send(FAR struct socket *psock, FAR const void *buf, static ssize_t pkt_sendto(FAR struct socket *psock, FAR const void *buf, size_t len, int flags, FAR const struct sockaddr *to, socklen_t tolen); +static int pkt_close(FAR struct socket *psock); /**************************************************************************** * Public Data @@ -82,7 +83,8 @@ const struct sock_intf_s g_pkt_sockif = pkt_accept, /* si_accept */ pkt_send, /* si_send */ pkt_sendto, /* si_sendto */ - pkt_recvfrom /* si_recvfrom */ + pkt_recvfrom, /* si_recvfrom */ + pkt_close /* si_close */ }; /**************************************************************************** @@ -425,6 +427,59 @@ static ssize_t pkt_sendto(FAR struct socket *psock, FAR const void *buf, return -EAFNOSUPPORT; } +/**************************************************************************** + * Name: pkt_close + * + * Description: + * Performs the close operation on a raw packet socket instance + * + * Parameters: + * psock Socket instance + * + * Returned Value: + * 0 on success; a negated errno value is returned on any failure. + * + * Assumptions: + * + ****************************************************************************/ + +static int pkt_close(FAR struct socket *psock) +{ + /* Perform some pre-close operations for the raw packet address type */ + + switch (psock->s_type) + { + case SOCK_RAW: + { + FAR struct pkt_conn_s *conn = psock->s_conn; + + /* Is this the last reference to the connection structure (there + * could be more if the socket was dup'ed). + */ + + if (conn->crefs <= 1) + { + /* Yes... free the connection structure */ + + conn->crefs = 0; /* No more references on the connection */ + pkt_free(psock->s_conn); /* Free network resources */ + } + else + { + /* No.. Just decrement the reference count */ + + conn->crefs--; + } + + return OK; + } +#endif + + default: + return -EBADF; + } +} + /**************************************************************************** * Public Functions ****************************************************************************/ diff --git a/net/socket/Make.defs b/net/socket/Make.defs index 34deabf99eb..1b9c99d5d1e 100644 --- a/net/socket/Make.defs +++ b/net/socket/Make.defs @@ -44,9 +44,9 @@ SOCK_CSRCS += net_dupsd2.c net_clone.c net_poll.c net_vfcntl.c SOCK_CSRCS += net_sockif.c ifeq ($(CONFIG_NET_IPv4),y) -SOCK_CSRCS += inet_sockif.c inet_recvfrom.c inet_connect.c +SOCK_CSRCS += inet_sockif.c inet_recvfrom.c inet_connect.c inet_close.c else ifeq ($(CONFIG_NET_IPv6),y) -SOCK_CSRCS += inet_sockif.c inet_recvfrom.c inet_connect.c +SOCK_CSRCS += inet_sockif.c inet_recvfrom.c inet_connect.c inet_close.c endif # TCP/IP support diff --git a/net/socket/inet_close.c b/net/socket/inet_close.c new file mode 100644 index 00000000000..0461a60d10e --- /dev/null +++ b/net/socket/inet_close.c @@ -0,0 +1,610 @@ +/**************************************************************************** + * net/socket/inet_close.c + * + * Copyright (C) 2007-2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#ifdef CONFIG_NET + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#ifdef CONFIG_NET_SOLINGER +# include +#endif + +#include "netdev/netdev.h" +#include "devif/devif.h" +#include "tcp/tcp.h" +#include "udp/udp.h" +#include "pkt/pkt.h" +#include "local/local.h" +#include "socket/socket.h" +#include "usrsock/usrsock.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +#ifdef NET_TCP_HAVE_STACK +struct tcp_close_s +{ + FAR struct devif_callback_s *cl_cb; /* Reference to TCP callback instance */ +#ifdef CONFIG_NET_SOLINGER + FAR struct socket *cl_psock; /* Reference to the TCP socket */ + sem_t cl_sem; /* Signals disconnect completion */ + int cl_result; /* The result of the close */ + systime_t cl_start; /* Time close started (in ticks) */ +#endif +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: tcp_close_timeout + * + * Description: + * Check for a timeout on a lingering close. + * + * Parameters: + * pstate - close state structure + * + * Returned Value: + * TRUE:timeout FALSE:no timeout + * + * Assumptions: + * Running at the interrupt level + * + ****************************************************************************/ + +#if defined(NET_TCP_HAVE_STACK) && defined(CONFIG_NET_SOLINGER) +static inline int tcp_close_timeout(FAR struct tcp_close_s *pstate) +{ + FAR struct socket *psock = 0; + + /* Make sure that we are performing a lingering close */ + + if (pstate != NULL) + { + /* Yes Check for a timeout configured via setsockopts(SO_LINGER). + * If none... we well let the send wait forever. + */ + + psock = pstate->cl_psock; + if (psock && psock->s_linger != 0) + { + /* Check if the configured timeout has elapsed */ + + return net_timeo(pstate->cl_start, psock->s_linger); + } + } + + /* No timeout */ + + return FALSE; +} +#endif /* NET_TCP_HAVE_STACK && CONFIG_NET_SOLINGER */ + +/**************************************************************************** + * Name: tcp_close_interrupt + * + * Description: + * Handle network callback events. + * + * Parameters: + * conn - TCP connection structure + * + * Returned Value: + * None + * + * Assumptions: + * Called from normal user-level logic + * + ****************************************************************************/ + +#ifdef NET_TCP_HAVE_STACK +static uint16_t tcp_close_interrupt(FAR struct net_driver_s *dev, + FAR void *pvconn, FAR void *pvpriv, + uint16_t flags) +{ +#ifdef CONFIG_NET_SOLINGER + FAR struct tcp_close_s *pstate = (FAR struct tcp_close_s *)pvpriv; +#endif + FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)pvconn; + + DEBUGASSERT(conn != NULL); + + ninfo("conn: %p flags: %04x\n", conn, flags); + + /* TCP_DISCONN_EVENTS: + * TCP_CLOSE: The remote host has closed the connection + * TCP_ABORT: The remote host has aborted the connection + * TCP_TIMEDOUT: The remote did not respond, the connection timed out + * NETDEV_DOWN: The network device went down + */ + + if ((flags & TCP_DISCONN_EVENTS) != 0) + { + /* The disconnection is complete */ + +#ifdef CONFIG_NET_SOLINGER + /* pstate non-NULL means that we are performing a LINGERing close. */ + + if (pstate != NULL) + { + /* Wake up the waiting thread with a successful result */ + + pstate->cl_result = OK; + goto end_wait; + } + + /* Otherwise, nothing is waiting on the close event and we can perform + * the completion actions here. + */ + + else +#endif + { + /* Free connection resources */ + + tcp_free(conn); + + /* Stop further callbacks */ + + flags = 0; + } + } + +#ifdef CONFIG_NET_SOLINGER + /* Check for a timeout. */ + + else if (pstate && tcp_close_timeout(pstate)) + { + /* Yes.. Wake up the waiting thread and report the timeout */ + + nerr("ERROR: CLOSE timeout\n"); + pstate->cl_result = -ETIMEDOUT; + goto end_wait; + } + +#endif /* CONFIG_NET_SOLINGER */ + +#ifdef CONFIG_NET_TCP_WRITE_BUFFERS + /* Check if all outstanding bytes have been ACKed */ + + else if (conn->unacked != 0 || !sq_empty(&conn->write_q)) + { + /* No... we are still waiting for ACKs. Drop any received data, but + * do not yet report TCP_CLOSE in the response. + */ + + dev->d_len = 0; + flags = (flags & ~TCP_NEWDATA); + } + +#endif /* CONFIG_NET_TCP_WRITE_BUFFERS */ + + else + { + /* Drop data received in this state and make sure that TCP_CLOSE + * is set in the response + */ + + dev->d_len = 0; + flags = (flags & ~TCP_NEWDATA) | TCP_CLOSE; + } + + return flags; + +#ifdef CONFIG_NET_SOLINGER +end_wait: + pstate->cl_cb->flags = 0; + pstate->cl_cb->priv = NULL; + pstate->cl_cb->event = NULL; + sem_post(&pstate->cl_sem); + + ninfo("Resuming\n"); + return 0; +#endif +} +#endif /* NET_TCP_HAVE_STACK */ + +/**************************************************************************** + * Name: tcp_close_txnotify + * + * Description: + * Notify the appropriate device driver that we are have data ready to + * be send (TCP) + * + * Parameters: + * psock - Socket state structure + * conn - The TCP connection structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef NET_TCP_HAVE_STACK +static inline void tcp_close_txnotify(FAR struct socket *psock, + FAR struct tcp_conn_s *conn) +{ +#ifdef CONFIG_NET_IPv4 +#ifdef CONFIG_NET_IPv6 + /* If both IPv4 and IPv6 support are enabled, then we will need to select + * the device driver using the appropriate IP domain. + */ + + if (psock->s_domain == PF_INET) +#endif + { + /* Notify the device driver that send data is available */ + +#ifdef CONFIG_NETDEV_MULTINIC + netdev_ipv4_txnotify(conn->u.ipv4.laddr, conn->u.ipv4.raddr); +#else + netdev_ipv4_txnotify(conn->u.ipv4.raddr); +#endif + } +#endif /* CONFIG_NET_IPv4 */ + +#ifdef CONFIG_NET_IPv6 +#ifdef CONFIG_NET_IPv4 + else /* if (psock->s_domain == PF_INET6) */ +#endif /* CONFIG_NET_IPv4 */ + { + /* Notify the device driver that send data is available */ + + DEBUGASSERT(psock->s_domain == PF_INET6); +#ifdef CONFIG_NETDEV_MULTINIC + netdev_ipv6_txnotify(conn->u.ipv6.laddr, conn->u.ipv6.raddr); +#else + netdev_ipv6_txnotify(conn->u.ipv6.raddr); +#endif + } +#endif /* CONFIG_NET_IPv6 */ +} +#endif /* NET_TCP_HAVE_STACK */ + +/**************************************************************************** + * Name: tcp_close_disconnect + * + * Description: + * Break any current TCP connection + * + * Parameters: + * conn - TCP connection structure + * + * Returned Value: + * None + * + * Assumptions: + * Called from normal user-level logic + * + ****************************************************************************/ + +#ifdef NET_TCP_HAVE_STACK +static inline int tcp_close_disconnect(FAR struct socket *psock) +{ + struct tcp_close_s state; + FAR struct tcp_conn_s *conn; +#ifdef CONFIG_NET_SOLINGER + bool linger; +#endif + int ret = OK; + + /* Interrupts are disabled here to avoid race conditions */ + + net_lock(); + conn = (FAR struct tcp_conn_s *)psock->s_conn; + + /* If we have a semi-permanent write buffer callback in place, then + * release it now. + */ + +#ifdef CONFIG_NET_TCP_WRITE_BUFFERS + if (psock->s_sndcb) + { + psock->s_sndcb = NULL; + } +#endif + + DEBUGASSERT(conn != NULL); + + /* Check for the case where the host beat us and disconnected first */ + + if (conn->tcpstateflags == TCP_ESTABLISHED && + (state.cl_cb = tcp_callback_alloc(conn)) != NULL) + { + /* Set up to receive TCP data event callbacks */ + + state.cl_cb->flags = (TCP_NEWDATA | TCP_POLL | TCP_DISCONN_EVENTS); + state.cl_cb->event = tcp_close_interrupt; + +#ifdef CONFIG_NET_SOLINGER + /* Check for a lingering close */ + + linger = _SO_GETOPT(psock->s_options, SO_LINGER); + + /* Has a lingering close been requested */ + + if (linger) + { + /* A non-NULL value of the priv field means that lingering is + * enabled. + */ + + state.cl_cb->priv = (FAR void *)&state; + + /* Set up for the lingering wait */ + + state.cl_psock = psock; + state.cl_result = -EBUSY; + + /* This semaphore is used for signaling and, hence, should not have + * priority inheritance enabled. + */ + + sem_init(&state.cl_sem, 0, 0); + sem_setprotocol(&state.cl_sem, SEM_PRIO_NONE); + + /* Record the time that we started the wait (in ticks) */ + + state.cl_start = clock_systimer(); + } + else +#endif /* CONFIG_NET_SOLINGER */ + + { + /* We will close immediately. The NULL priv field signals this */ + + state.cl_cb->priv = NULL; + + /* No further references on the connection */ + + conn->crefs = 0; + } + + /* Notify the device driver of the availability of TX data */ + + tcp_close_txnotify(psock, conn); + +#ifdef CONFIG_NET_SOLINGER + /* Wait only if we are lingering */ + + if (linger) + { + /* Wait for the disconnect event */ + + (void)net_lockedwait(&state.cl_sem); + + /* We are now disconnected */ + + sem_destroy(&state.cl_sem); + tcp_callback_free(conn, state.cl_cb); + + /* Free the connection */ + + conn->crefs = 0; /* No more references on the connection */ + tcp_free(conn); /* Free network resources */ + + /* Get the result of the close */ + + ret = state.cl_result; + } +#endif /* CONFIG_NET_SOLINGER */ + } + else + { + tcp_free(conn); + } + + net_unlock(); + return ret; +} +#endif /* NET_TCP_HAVE_STACK */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: inet_close + * + * Description: + * Performs the close operation on an AF_INET or AF_INET6 socket instance + * + * Parameters: + * psock Socket instance + * + * Returned Value: + * 0 on success; -1 on error with errno set appropriately. + * + * Assumptions: + * + ****************************************************************************/ + +int inet_close(FAR struct socket *psock) +{ + /* Perform some pre-close operations for the AF_INET/AF_INET6 address + * types. + */ + + switch (psock->s_type) + { +#ifdef CONFIG_NET_TCP + case SOCK_STREAM: + { +#ifdef NET_TCP_HAVE_STACK + FAR struct tcp_conn_s *conn = psock->s_conn; + int ret; + + /* Is this the last reference to the connection structure (there + * could be more if the socket was dup'ed). + */ + + if (conn->crefs <= 1) + { + /* Yes... then perform the disconnection now */ + + tcp_unlisten(conn); /* No longer accepting connections */ + conn->crefs = 0; /* Discard our reference to the connection */ + + /* Break any current connections */ + + ret = tcp_close_disconnect(psock); + if (ret < 0) + { + /* This would normally occur only if there is a timeout + * from a lingering close. + */ + + nerr("ERROR: tcp_close_disconnect failed: %d\n", ret); + return ret; + } + + /* Stop the network monitor */ + + net_stopmonitor(conn); + } + else + { + /* No.. Just decrement the reference count */ + + conn->crefs--; + } +#else + nwarn("WARNING: SOCK_STREAM support is not available in this configuration\n"); + return -EAFNOSUPPORT; +#endif /* NET_TCP_HAVE_STACK */ + } + break; +#endif /* CONFIG_NET_TCP */ + +#ifdef CONFIG_NET_UDP + case SOCK_DGRAM: + { +#ifdef NET_UDP_HAVE_STACK + FAR struct udp_conn_s *conn = psock->s_conn; + + /* Is this the last reference to the connection structure (there + * could be more if the socket was dup'ed). + */ + + if (conn->crefs <= 1) + { + /* Yes... free the connection structure */ + + conn->crefs = 0; + udp_free(psock->s_conn); + } + else + { + /* No.. Just decrement the reference count */ + + conn->crefs--; + } +#else + nwarn("WARNING: SOCK_DGRAM support is not available in this configuration\n"); + return -EAFNOSUPPORT; +#endif /* NET_UDP_HAVE_STACK */ + } + break; +#endif /* CONFIG_NET_UDP */ + +#ifdef CONFIG_NET_USRSOCK + case SOCK_USRSOCK_TYPE: + { + FAR struct usrsock_conn_s *conn = psock->s_conn; + int ret; + + /* Is this the last reference to the connection structure (there + * could be more if the socket was dup'ed). + */ + + if (conn->crefs <= 1) + { + /* Yes... inform user-space daemon of socket close. */ + + ret = usrsock_close(conn); + + /* Free the connection structure */ + + conn->crefs = 0; + usrsock_free(psock->s_conn); + + if (ret < 0) + { + /* Return with error code, but free resources. */ + + nerr("ERROR: usrsock_close failed: %d\n", ret); + return ret; + } + } + else + { + /* No.. Just decrement the reference count */ + + conn->crefs--; + } + } + break; +#endif + + default: + return -EBADF; + } + + return OK; +} +#endif /* CONFIG_NET */ diff --git a/net/socket/inet_sockif.c b/net/socket/inet_sockif.c index 470315c2295..f1c1f9ae636 100644 --- a/net/socket/inet_sockif.c +++ b/net/socket/inet_sockif.c @@ -83,7 +83,8 @@ const struct sock_intf_s g_inet_sockif = inet_accept, /* si_accept */ inet_send, /* si_send */ inet_sendto, /* si_sendto */ - inet_recvfrom /* si_recvfrom */ + inet_recvfrom, /* si_recvfrom */ + inet_close /* si_close */ }; /**************************************************************************** diff --git a/net/socket/net_close.c b/net/socket/net_close.c index adb1812ad26..63cfb131b7d 100644 --- a/net/socket/net_close.c +++ b/net/socket/net_close.c @@ -38,7 +38,6 @@ ****************************************************************************/ #include -#ifdef CONFIG_NET #include #include @@ -48,450 +47,11 @@ #include #include -#include - -#include #include -#include -#include -#include -#ifdef CONFIG_NET_SOLINGER -# include -#endif - -#include "netdev/netdev.h" -#include "devif/devif.h" -#include "tcp/tcp.h" -#include "udp/udp.h" -#include "pkt/pkt.h" -#include "local/local.h" #include "socket/socket.h" -#include "usrsock/usrsock.h" -/**************************************************************************** - * Private Types - ****************************************************************************/ - -#ifdef NET_TCP_HAVE_STACK -struct tcp_close_s -{ - FAR struct devif_callback_s *cl_cb; /* Reference to TCP callback instance */ -#ifdef CONFIG_NET_SOLINGER - FAR struct socket *cl_psock; /* Reference to the TCP socket */ - sem_t cl_sem; /* Signals disconnect completion */ - int cl_result; /* The result of the close */ - systime_t cl_start; /* Time close started (in ticks) */ -#endif -}; -#endif - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: close_timeout - * - * Description: - * Check for a timeout on a lingering close. - * - * Parameters: - * pstate - close state structure - * - * Returned Value: - * TRUE:timeout FALSE:no timeout - * - * Assumptions: - * Running at the interrupt level - * - ****************************************************************************/ - -#if defined(NET_TCP_HAVE_STACK) && defined(CONFIG_NET_SOLINGER) -static inline int close_timeout(FAR struct tcp_close_s *pstate) -{ - FAR struct socket *psock = 0; - - /* Make sure that we are performing a lingering close */ - - if (pstate) - { - /* Yes Check for a timeout configured via setsockopts(SO_LINGER). - * If none... we well let the send wait forever. - */ - - psock = pstate->cl_psock; - if (psock && psock->s_linger != 0) - { - /* Check if the configured timeout has elapsed */ - - return net_timeo(pstate->cl_start, psock->s_linger); - } - } - - /* No timeout */ - - return FALSE; -} -#endif /* NET_TCP_HAVE_STACK && CONFIG_NET_SOLINGER */ - -/**************************************************************************** - * Name: netclose_interrupt - * - * Description: - * Handle network callback events. - * - * Parameters: - * conn - TCP connection structure - * - * Returned Value: - * None - * - * Assumptions: - * Called from normal user-level logic - * - ****************************************************************************/ - -#ifdef NET_TCP_HAVE_STACK -static uint16_t netclose_interrupt(FAR struct net_driver_s *dev, - FAR void *pvconn, FAR void *pvpriv, - uint16_t flags) -{ -#ifdef CONFIG_NET_SOLINGER - FAR struct tcp_close_s *pstate = (FAR struct tcp_close_s *)pvpriv; -#endif - FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)pvconn; - - DEBUGASSERT(conn != NULL); - - ninfo("conn: %p flags: %04x\n", conn, flags); - - /* TCP_DISCONN_EVENTS: - * TCP_CLOSE: The remote host has closed the connection - * TCP_ABORT: The remote host has aborted the connection - * TCP_TIMEDOUT: The remote did not respond, the connection timed out - * NETDEV_DOWN: The network device went down - */ - - if ((flags & TCP_DISCONN_EVENTS) != 0) - { - /* The disconnection is complete */ - -#ifdef CONFIG_NET_SOLINGER - /* pstate non-NULL means that we are performing a LINGERing close. */ - - if (pstate) - { - /* Wake up the waiting thread with a successful result */ - - pstate->cl_result = OK; - goto end_wait; - } - - /* Otherwise, nothing is waiting on the close event and we can perform - * the completion actions here. - */ - - else -#endif - { - /* Free connection resources */ - - tcp_free(conn); - - /* Stop further callbacks */ - - flags = 0; - } - } - -#ifdef CONFIG_NET_SOLINGER - /* Check for a timeout. */ - - else if (pstate && close_timeout(pstate)) - { - /* Yes.. Wake up the waiting thread and report the timeout */ - - nerr("ERROR: CLOSE timeout\n"); - pstate->cl_result = -ETIMEDOUT; - goto end_wait; - } - -#endif /* CONFIG_NET_SOLINGER */ - -#ifdef CONFIG_NET_TCP_WRITE_BUFFERS - /* Check if all outstanding bytes have been ACKed */ - - else if (conn->unacked != 0 || !sq_empty(&conn->write_q)) - { - /* No... we are still waiting for ACKs. Drop any received data, but - * do not yet report TCP_CLOSE in the response. - */ - - dev->d_len = 0; - flags = (flags & ~TCP_NEWDATA); - } - -#endif /* CONFIG_NET_TCP_WRITE_BUFFERS */ - - else - { - /* Drop data received in this state and make sure that TCP_CLOSE - * is set in the response - */ - - dev->d_len = 0; - flags = (flags & ~TCP_NEWDATA) | TCP_CLOSE; - } - - return flags; - -#ifdef CONFIG_NET_SOLINGER -end_wait: - pstate->cl_cb->flags = 0; - pstate->cl_cb->priv = NULL; - pstate->cl_cb->event = NULL; - sem_post(&pstate->cl_sem); - - ninfo("Resuming\n"); - return 0; -#endif -} -#endif /* NET_TCP_HAVE_STACK */ - -/**************************************************************************** - * Name: netclose_txnotify - * - * Description: - * Notify the appropriate device driver that we are have data ready to - * be send (TCP) - * - * Parameters: - * psock - Socket state structure - * conn - The TCP connection structure - * - * Returned Value: - * None - * - ****************************************************************************/ - -#ifdef NET_TCP_HAVE_STACK -static inline void netclose_txnotify(FAR struct socket *psock, - FAR struct tcp_conn_s *conn) -{ -#ifdef CONFIG_NET_IPv4 -#ifdef CONFIG_NET_IPv6 - /* If both IPv4 and IPv6 support are enabled, then we will need to select - * the device driver using the appropriate IP domain. - */ - - if (psock->s_domain == PF_INET) -#endif - { - /* Notify the device driver that send data is available */ - -#ifdef CONFIG_NETDEV_MULTINIC - netdev_ipv4_txnotify(conn->u.ipv4.laddr, conn->u.ipv4.raddr); -#else - netdev_ipv4_txnotify(conn->u.ipv4.raddr); -#endif - } -#endif /* CONFIG_NET_IPv4 */ - -#ifdef CONFIG_NET_IPv6 -#ifdef CONFIG_NET_IPv4 - else /* if (psock->s_domain == PF_INET6) */ -#endif /* CONFIG_NET_IPv4 */ - { - /* Notify the device driver that send data is available */ - - DEBUGASSERT(psock->s_domain == PF_INET6); -#ifdef CONFIG_NETDEV_MULTINIC - netdev_ipv6_txnotify(conn->u.ipv6.laddr, conn->u.ipv6.raddr); -#else - netdev_ipv6_txnotify(conn->u.ipv6.raddr); -#endif - } -#endif /* CONFIG_NET_IPv6 */ -} -#endif /* NET_TCP_HAVE_STACK */ - -/**************************************************************************** - * Name: netclose_disconnect - * - * Description: - * Break any current TCP connection - * - * Parameters: - * conn - TCP connection structure - * - * Returned Value: - * None - * - * Assumptions: - * Called from normal user-level logic - * - ****************************************************************************/ - -#ifdef NET_TCP_HAVE_STACK -static inline int netclose_disconnect(FAR struct socket *psock) -{ - struct tcp_close_s state; - FAR struct tcp_conn_s *conn; -#ifdef CONFIG_NET_SOLINGER - bool linger; -#endif - int ret = OK; - - /* Interrupts are disabled here to avoid race conditions */ - - net_lock(); - conn = (FAR struct tcp_conn_s *)psock->s_conn; - - /* If we have a semi-permanent write buffer callback in place, then - * release it now. - */ - -#ifdef CONFIG_NET_TCP_WRITE_BUFFERS - if (psock->s_sndcb) - { - psock->s_sndcb = NULL; - } -#endif - - DEBUGASSERT(conn != NULL); - - /* Check for the case where the host beat us and disconnected first */ - - if (conn->tcpstateflags == TCP_ESTABLISHED && - (state.cl_cb = tcp_callback_alloc(conn)) != NULL) - { - /* Set up to receive TCP data event callbacks */ - - state.cl_cb->flags = (TCP_NEWDATA | TCP_POLL | TCP_DISCONN_EVENTS); - state.cl_cb->event = netclose_interrupt; - -#ifdef CONFIG_NET_SOLINGER - /* Check for a lingering close */ - - linger = _SO_GETOPT(psock->s_options, SO_LINGER); - - /* Has a lingering close been requested */ - - if (linger) - { - /* A non-NULL value of the priv field means that lingering is - * enabled. - */ - - state.cl_cb->priv = (FAR void *)&state; - - /* Set up for the lingering wait */ - - state.cl_psock = psock; - state.cl_result = -EBUSY; - - /* This semaphore is used for signaling and, hence, should not have - * priority inheritance enabled. - */ - - sem_init(&state.cl_sem, 0, 0); - sem_setprotocol(&state.cl_sem, SEM_PRIO_NONE); - - /* Record the time that we started the wait (in ticks) */ - - state.cl_start = clock_systimer(); - } - else -#endif /* CONFIG_NET_SOLINGER */ - - { - /* We will close immediately. The NULL priv field signals this */ - - state.cl_cb->priv = NULL; - - /* No further references on the connection */ - - conn->crefs = 0; - } - - /* Notify the device driver of the availability of TX data */ - - netclose_txnotify(psock, conn); - -#ifdef CONFIG_NET_SOLINGER - /* Wait only if we are lingering */ - - if (linger) - { - /* Wait for the disconnect event */ - - (void)net_lockedwait(&state.cl_sem); - - /* We are now disconnected */ - - sem_destroy(&state.cl_sem); - tcp_callback_free(conn, state.cl_cb); - - /* Free the connection */ - - conn->crefs = 0; /* No more references on the connection */ - tcp_free(conn); /* Free network resources */ - - /* Get the result of the close */ - - ret = state.cl_result; - } -#endif /* CONFIG_NET_SOLINGER */ - } - else - { - tcp_free(conn); - } - - net_unlock(); - return ret; -} -#endif /* NET_TCP_HAVE_STACK */ - -/**************************************************************************** - * Name: local_close - * - * Description: - * Performs the close operation on a local socket instance - * - * Parameters: - * psock Socket instance - * - * Returned Value: - * 0 on success; -1 on error with errno set appropriately. - * - * Assumptions: - * - ****************************************************************************/ - -#ifdef CONFIG_NET_LOCAL -static void local_close(FAR struct socket *psock) -{ - FAR struct local_conn_s *conn = psock->s_conn; - - /* Is this the last reference to the connection structure (there could - * be more if the socket was dup'ed). - */ - - if (conn->lc_crefs <= 1) - { - conn->lc_crefs = 0; - local_release(conn); - } - else - { - /* No.. Just decrement the reference count */ - - conn->lc_crefs--; - } -} -#endif /* CONFIG_NET_LOCAL */ +#ifdef CONFIG_NET /**************************************************************************** * Public Functions @@ -516,6 +76,7 @@ static void local_close(FAR struct socket *psock) int psock_close(FAR struct socket *psock) { int errcode; + int ret; /* Verify that the sockfd corresponds to valid, allocated socket */ @@ -535,185 +96,17 @@ int psock_close(FAR struct socket *psock) if (psock->s_crefs <= 1 && psock->s_conn != NULL) { - /* Perform local side of the close depending on the protocol type */ + /* Let the address family's close() method handle the operation */ - switch (psock->s_type) + DEBUGASSERT(psock->s_sockif != NULL && psock->s_sockif->si_close != NULL); + ret = psock->s_sockif->si_close(psock); + + /* Was the close successful */ + + if (ret < 0) { -#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_LOCAL_STREAM) - case SOCK_STREAM: - { -#ifdef CONFIG_NET_LOCAL_STREAM -#ifdef CONFIG_NET_TCP - if (psock->s_domain == PF_LOCAL) -#endif - { - /* Release our reference to the local connection structure */ - - local_close(psock); - } -#endif /* CONFIG_NET_LOCAL_STREAM */ - -#ifdef CONFIG_NET_TCP -#ifdef CONFIG_NET_LOCAL_STREAM - else -#endif - { -#ifdef NET_TCP_HAVE_STACK - FAR struct tcp_conn_s *conn = psock->s_conn; - - /* Is this the last reference to the connection structure - * (there could be more if the socket was dup'ed). - */ - - if (conn->crefs <= 1) - { - /* Yes... then perform the disconnection now */ - - tcp_unlisten(conn); /* No longer accepting connections */ - conn->crefs = 0; /* Discard our reference to the connection */ - - /* Break any current connections */ - - errcode = netclose_disconnect(psock); - if (errcode < 0) - { - /* This would normally occur only if there is a - * timeout from a lingering close. - */ - - goto errout_with_psock; - } - - /* Stop the network monitor */ - - net_stopmonitor(conn); - } - else - { - /* No.. Just decrement the reference count */ - - conn->crefs--; - } -#endif /* NET_TCP_HAVE_STACK */ - } -#endif /* CONFIG_NET_TCP || CONFIG_NET_LOCAL_STREAM */ - } - break; -#endif - -#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_LOCAL_DGRAM) - case SOCK_DGRAM: - { -#ifdef CONFIG_NET_LOCAL_DGRAM -#ifdef CONFIG_NET_UDP - if (psock->s_domain == PF_LOCAL) -#endif - { - /* Release our reference to the local connection structure */ - - local_close(psock); - } -#endif /* CONFIG_NET_LOCAL_DGRAM */ - -#ifdef CONFIG_NET_UDP -#ifdef CONFIG_NET_LOCAL_DGRAM - else -#endif - { -#ifdef NET_UDP_HAVE_STACK - FAR struct udp_conn_s *conn = psock->s_conn; - - /* Is this the last reference to the connection structure - * (there could be more if the socket was dup'ed). - */ - - if (conn->crefs <= 1) - { - /* Yes... free the connection structure */ - - conn->crefs = 0; - udp_free(psock->s_conn); - } - else - { - /* No.. Just decrement the reference count */ - - conn->crefs--; - } -#endif /* NET_UDP_HAVE_STACK */ - } -#endif /* CONFIG_NET_UDP || CONFIG_NET_LOCAL_DGRAM */ - } - break; -#endif - -#ifdef CONFIG_NET_PKT - case SOCK_RAW: - { - FAR struct pkt_conn_s *conn = psock->s_conn; - - /* Is this the last reference to the connection structure (there - * could be more if the socket was dup'ed). - */ - - if (conn->crefs <= 1) - { - /* Yes... free the connection structure */ - - conn->crefs = 0; /* No more references on the connection */ - pkt_free(psock->s_conn); /* Free network resources */ - } - else - { - /* No.. Just decrement the reference count */ - - conn->crefs--; - } - } - break; -#endif - -#ifdef CONFIG_NET_USRSOCK - case SOCK_USRSOCK_TYPE: - { - FAR struct usrsock_conn_s *conn = psock->s_conn; - - /* Is this the last reference to the connection structure (there - * could be more if the socket was dup'ed). - */ - - if (conn->crefs <= 1) - { - /* Yes... inform user-space daemon of socket close. */ - - errcode = usrsock_close(conn); - - /* Free the connection structure */ - - conn->crefs = 0; - usrsock_free(psock->s_conn); - - if (errcode < 0) - { - /* Return with error code, but free resources. */ - - errcode = -errcode; - goto errout_with_psock; - } - } - else - { - /* No.. Just decrement the reference count */ - - conn->crefs--; - } - } - break; -#endif - - default: - errcode = EBADF; - goto errout; + errcode = -ret; + goto errout; } } @@ -722,11 +115,6 @@ int psock_close(FAR struct socket *psock) sock_release(psock); return OK; -#if defined(NET_TCP_HAVE_STACK) || defined(CONFIG_NET_USRSOCK) -errout_with_psock: - sock_release(psock); -#endif - errout: set_errno(errcode); return ERROR; diff --git a/net/socket/socket.h b/net/socket/socket.h index fec553de298..3465f74b964 100644 --- a/net/socket/socket.h +++ b/net/socket/socket.h @@ -506,6 +506,24 @@ ssize_t inet_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, int flags, FAR struct sockaddr *from, FAR socklen_t *fromlen); +/**************************************************************************** + * Name: inet_close + * + * Description: + * Performs the close operation on an AF_INET or AF_INET6 socket instance + * + * Parameters: + * psock Socket instance + * + * Returned Value: + * 0 on success; -1 on error with errno set appropriately. + * + * Assumptions: + * + ****************************************************************************/ + +int inet_close(FAR struct socket *psock); + #undef EXTERN #if defined(__cplusplus) }